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
|
||||
|
||||
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 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 {
|
||||
/// All tags in the collection, in alphabetical order.
|
||||
pub(crate) fn all_tags(&self) -> Result<Vec<Tag>> {
|
||||
@ -56,12 +61,43 @@ impl SqliteStorage {
|
||||
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
|
||||
.prepare_cached("select tag from tags where tag = ?")?
|
||||
.query_and_then(params![tag], |row| row.get(0))?
|
||||
.next()
|
||||
.transpose()
|
||||
.query_row(params![tag], |row| {
|
||||
Ok((self.match_parents(row.get_raw(0).as_str()?), true))
|
||||
})
|
||||
.or_else::<AnkiError, _>(|_| Ok((self.match_parents(tag), false)))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
@ -225,13 +225,13 @@ impl Collection {
|
||||
if normalized_name.is_empty() {
|
||||
return Ok((t, false));
|
||||
}
|
||||
if let Some(preferred) = self.storage.preferred_tag_case(&normalized_name)? {
|
||||
t.name = preferred;
|
||||
Ok((t, false))
|
||||
} else {
|
||||
let (preferred, exists) = self.storage.preferred_tag_case(&normalized_name)?;
|
||||
t.name = preferred?;
|
||||
if !exists {
|
||||
self.storage.register_tag(&t)?;
|
||||
Ok((t, true))
|
||||
}
|
||||
|
||||
Ok((t, !exists))
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user