Make tags match their parents case
https://github.com/ankitects/anki/pull/900/#issuecomment-762018745
This commit is contained in:
parent
318cc01c73
commit
3159cf4ab6
@ -2,7 +2,8 @@
|
|||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
use super::SqliteStorage;
|
use super::SqliteStorage;
|
||||||
use crate::{err::Result, tags::Tag, types::Usn};
|
use crate::err::{AnkiError, Result};
|
||||||
|
use crate::{tags::Tag, types::Usn};
|
||||||
|
|
||||||
use rusqlite::{params, Row, NO_PARAMS};
|
use rusqlite::{params, Row, NO_PARAMS};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -15,6 +16,10 @@ fn row_to_tag(row: &Row) -> Result<Tag> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn immediate_parent_name(tag_name: &str) -> Option<&str> {
|
||||||
|
tag_name.rsplitn(2, "::").nth(1)
|
||||||
|
}
|
||||||
|
|
||||||
impl SqliteStorage {
|
impl SqliteStorage {
|
||||||
/// All tags in the collection, in alphabetical order.
|
/// All tags in the collection, in alphabetical order.
|
||||||
pub(crate) fn all_tags(&self) -> Result<Vec<Tag>> {
|
pub(crate) fn all_tags(&self) -> Result<Vec<Tag>> {
|
||||||
@ -56,12 +61,43 @@ impl SqliteStorage {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn preferred_tag_case(&self, tag: &str) -> Result<Option<String>> {
|
/// If parent tag(s) exist, rewrite name to match their case.
|
||||||
|
fn match_parents(&self, tag: &str) -> Result<String> {
|
||||||
|
let child_split: Vec<_> = tag.split("::").collect();
|
||||||
|
let t = if let Some(parent_tag) = self.first_existing_parent(&tag)? {
|
||||||
|
let parent_count = parent_tag.matches("::").count() + 1;
|
||||||
|
format!(
|
||||||
|
"{}::{}",
|
||||||
|
parent_tag,
|
||||||
|
&child_split[parent_count..].join("::")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
tag.into()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_existing_parent(&self, mut tag: &str) -> Result<Option<String>> {
|
||||||
|
while let Some(parent_name) = immediate_parent_name(tag) {
|
||||||
|
if let Some(parent_tag) = self.get_tag(parent_name)? {
|
||||||
|
return Ok(Some(parent_tag.name));
|
||||||
|
}
|
||||||
|
tag = parent_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get stored tag name or the same passed name if it doesn't exist, rewritten to match parents case.
|
||||||
|
// Returns a tuple of the preferred name and a boolean indicating if the tag exists.
|
||||||
|
pub(crate) fn preferred_tag_case(&self, tag: &str) -> Result<(Result<String>, bool)> {
|
||||||
self.db
|
self.db
|
||||||
.prepare_cached("select tag from tags where tag = ?")?
|
.prepare_cached("select tag from tags where tag = ?")?
|
||||||
.query_and_then(params![tag], |row| row.get(0))?
|
.query_row(params![tag], |row| {
|
||||||
.next()
|
Ok((self.match_parents(row.get_raw(0).as_str()?), true))
|
||||||
.transpose()
|
})
|
||||||
|
.or_else::<AnkiError, _>(|_| Ok((self.match_parents(tag), false)))
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,13 +225,13 @@ impl Collection {
|
|||||||
if normalized_name.is_empty() {
|
if normalized_name.is_empty() {
|
||||||
return Ok((t, false));
|
return Ok((t, false));
|
||||||
}
|
}
|
||||||
if let Some(preferred) = self.storage.preferred_tag_case(&normalized_name)? {
|
let (preferred, exists) = self.storage.preferred_tag_case(&normalized_name)?;
|
||||||
t.name = preferred;
|
t.name = preferred?;
|
||||||
Ok((t, false))
|
if !exists {
|
||||||
} else {
|
|
||||||
self.storage.register_tag(&t)?;
|
self.storage.register_tag(&t)?;
|
||||||
Ok((t, true))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok((t, !exists))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_unused_tags(&self) -> Result<()> {
|
pub fn clear_unused_tags(&self) -> Result<()> {
|
||||||
@ -533,6 +533,14 @@ mod test {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// children should match the case of their parents
|
||||||
|
col.storage.clear_tags()?;
|
||||||
|
*(&mut note.tags[0]) = "FOO".into();
|
||||||
|
*(&mut note.tags[1]) = "foo::BAR".into();
|
||||||
|
*(&mut note.tags[2]) = "foo::bar::baz".into();
|
||||||
|
col.update_note(&mut note)?;
|
||||||
|
assert_eq!(note.tags, vec!["FOO", "FOO::BAR", "FOO::BAR::baz"]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user