diff --git a/notmuch/src/lib.rs b/notmuch/src/lib.rs index 24266c6..65d7af2 100644 --- a/notmuch/src/lib.rs +++ b/notmuch/src/lib.rs @@ -503,15 +503,28 @@ impl Notmuch { self.tags_for_query("*") } - #[instrument(skip_all, fields(tag=tag,search_term=search_term))] pub fn tag_add(&self, tag: &str, search_term: &str) -> Result<(), NotmuchError> { - self.run_notmuch(["tag", &format!("+{tag}"), search_term])?; + self.tags_add(tag, &[search_term]) + } + + #[instrument(skip_all, fields(tag=tag,search_term=?search_term))] + pub fn tags_add(&self, tag: &str, search_term: &[&str]) -> Result<(), NotmuchError> { + let tag = format!("+{tag}"); + let mut args = vec!["tag", &tag]; + args.extend(search_term); + self.run_notmuch(&args)?; Ok(()) } - #[instrument(skip_all, fields(tag=tag,search_term=search_term))] pub fn tag_remove(&self, tag: &str, search_term: &str) -> Result<(), NotmuchError> { - self.run_notmuch(["tag", &format!("-{tag}"), search_term])?; + self.tags_remove(tag, &[search_term]) + } + #[instrument(skip_all, fields(tag=tag,search_term=?search_term))] + pub fn tags_remove(&self, tag: &str, search_term: &[&str]) -> Result<(), NotmuchError> { + let tag = format!("-{tag}"); + let mut args = vec!["tag", &tag]; + args.extend(search_term); + self.run_notmuch(&args)?; Ok(()) } diff --git a/server/src/nm.rs b/server/src/nm.rs index a2c8050..22bfbee 100644 --- a/server/src/nm.rs +++ b/server/src/nm.rs @@ -982,6 +982,8 @@ pub async fn label_unprocessed( } else { &ids[..] }; + let mut add_mutations = HashMap::new(); + let mut rm_mutations = HashMap::new(); for id in ids { let id = format!("id:{id}"); let files = nm.files(&id)?; @@ -1001,20 +1003,63 @@ pub async fn label_unprocessed( .get_first_value("subject") .expect("no subject header") ); - } else { - for t in &add_tags { - nm.tag_add(t, &id)?; - } - if add_tags.contains("spam") || add_tags.contains("Spam") { - nm.tag_remove("unread", &id)?; - } - if !add_tags.contains("inbox") { - nm.tag_remove("inbox", &id)?; - } - nm.tag_remove("unprocessed", &id)?; + } + for t in &add_tags { + //nm.tag_add(t, &id)?; + add_mutations + .entry(t.to_string()) + .or_insert_with(|| Vec::new()) + .push(id.clone()); + } + if add_tags.contains("spam") || add_tags.contains("Spam") { + //nm.tag_remove("unread", &id)?; + let t = "unread".to_string(); + rm_mutations + .entry(t) + .or_insert_with(|| Vec::new()) + .push(id.clone()); + } + if !add_tags.contains("inbox") { + //nm.tag_remove("inbox", &id)?; + let t = "inbox".to_string(); + rm_mutations + .entry(t) + .or_insert_with(|| Vec::new()) + .push(id.clone()); + } + //nm.tag_remove("unprocessed", &id)?; + let t = "unprocessed".to_string(); + rm_mutations + .entry(t) + .or_insert_with(|| Vec::new()) + .push(id.clone()); + } else { + if add_tags.is_empty() { + let t = "Grey".to_string(); + add_mutations + .entry(t) + .or_insert_with(|| Vec::new()) + .push(id.clone()); } } } + println!("Adding {} distinct labels", add_mutations.len()); + for (tag, ids) in add_mutations.iter() { + println!(" {tag}: {}", ids.len()); + if !dryrun { + let ids: Vec<_> = ids.iter().map(|s| s.as_str()).collect(); + nm.tags_add(tag, &ids)?; + } + } + println!("Removing {} distinct labels", rm_mutations.len()); + for (tag, ids) in rm_mutations.iter() { + println!(" {tag}: {}", ids.len()); + if !dryrun { + let ids: Vec<_> = ids.iter().map(|s| s.as_str()).collect(); + nm.tags_remove(tag, &ids)?; + } + } + Ok(()) } fn find_tags<'a, 'b>(rules: &'a [Rule], headers: &'b [MailHeader]) -> (bool, HashSet<&'a str>) {