From 258b6c8fe81c7d80c943f8e827f87c936c38698e Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sat, 30 Nov 2019 17:37:00 +0200 Subject: [PATCH] melib: add tags() method in MailBackend Add tags() method that returns Option>>>. The BTreeMap holds available tags in a mail backend and uses the tag's hash as key. The method returns an Option because not all backends may support tagging. --- melib/src/backends.rs | 5 +++ melib/src/backends/notmuch.rs | 59 ++++++++++++++++++++++++++++++++++- melib/src/conf.rs | 20 +++++++++++- melib/src/email.rs | 10 ++++++ melib/src/structs.rs | 2 +- 5 files changed, 93 insertions(+), 3 deletions(-) diff --git a/melib/src/backends.rs b/melib/src/backends.rs index 238bb4b3..2aea2917 100644 --- a/melib/src/backends.rs +++ b/melib/src/backends.rs @@ -41,9 +41,11 @@ use self::maildir::MaildirType; use self::mbox::MboxType; use super::email::{Envelope, EnvelopeHash, Flag}; use std::any::Any; +use std::collections::BTreeMap; use std::fmt; use std::fmt::Debug; use std::ops::Deref; +use std::sync::{Arc, RwLock}; use fnv::FnvHashMap; use std; @@ -234,6 +236,9 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync { Ok(()) } + fn tags(&self) -> Option>>> { + None + } fn as_any(&self) -> &dyn Any; } diff --git a/melib/src/backends/notmuch.rs b/melib/src/backends/notmuch.rs index 5132b703..6c73600b 100644 --- a/melib/src/backends/notmuch.rs +++ b/melib/src/backends/notmuch.rs @@ -7,8 +7,10 @@ use crate::conf::AccountSettings; use crate::email::{Envelope, EnvelopeHash, Flag}; use crate::error::{MeliError, Result}; use crate::shellexpand::ShellExpandTrait; +use crate::structs::StackVec; use fnv::FnvHashMap; use std::collections::hash_map::DefaultHasher; +use std::collections::BTreeMap; use std::ffi::CStr; use std::hash::{Hash, Hasher}; use std::io::Read; @@ -32,6 +34,7 @@ pub struct NotmuchDb { database: DbWrapper, folders: Arc>>, index: Arc>>, + tag_index: Arc>>, path: PathBuf, save_messages_to: Option, } @@ -170,6 +173,8 @@ impl NotmuchDb { }, path, index: Arc::new(RwLock::new(Default::default())), + tag_index: Arc::new(RwLock::new(Default::default())), + folders: Arc::new(RwLock::new(folders)), save_messages_to: None, })) @@ -194,6 +199,39 @@ impl NotmuchDb { } Ok(()) } + + pub fn search(&self, query_s: &str) -> Result> { + let database_lck = self.database.inner.read().unwrap(); + let query_str = std::ffi::CString::new(query_s).unwrap(); + let query: *mut notmuch_query_t = + unsafe { notmuch_query_create(*database_lck, query_str.as_ptr()) }; + if query.is_null() { + return Err(MeliError::new("Could not create query. Out of memory?")); + } + let mut messages: *mut notmuch_messages_t = std::ptr::null_mut(); + let status = unsafe { notmuch_query_search_messages(query, &mut messages as *mut _) }; + if status != 0 { + return Err(MeliError::new(format!( + "Search for {} returned {}", + query_s, status, + ))); + } + assert!(!messages.is_null()); + let iter = MessageIterator { messages }; + let mut ret = StackVec::new(); + for message in iter { + let fs_path = unsafe { notmuch_message_get_filename(message) }; + let c_str = unsafe { CStr::from_ptr(fs_path) }; + let env_hash = { + let mut hasher = DefaultHasher::default(); + c_str.hash(&mut hasher); + hasher.finish() + }; + ret.push(env_hash); + } + + Ok(ret) + } } impl MailBackend for NotmuchDb { @@ -205,6 +243,7 @@ impl MailBackend for NotmuchDb { let folder_hash = folder.hash(); let database = self.database.clone(); let index = self.index.clone(); + let tag_index = self.tag_index.clone(); let folders = self.folders.clone(); let handle = { let tx = w.tx(); @@ -275,7 +314,21 @@ impl MailBackend for NotmuchDb { index: index.clone(), bytes: Some(response), }); - if let Some(env) = Envelope::from_token(op, env_hash) { + if let Some(mut env) = Envelope::from_token(op, env_hash) { + let mut tag_lock = tag_index.write().unwrap(); + for tag in (TagIterator { + tags: unsafe { notmuch_message_get_tags(message) }, + }) { + let tag = tag.to_string_lossy().into_owned(); + + let mut hasher = DefaultHasher::new(); + hasher.write(tag.as_bytes()); + let num = hasher.finish(); + if !tag_lock.contains_key(&num) { + tag_lock.insert(num, tag); + } + env.labels_mut().push(num); + } ret.push(env); } else { debug!("could not parse path {:?}", c_str); @@ -347,6 +400,10 @@ impl MailBackend for NotmuchDb { fn as_any(&self) -> &dyn::std::any::Any { self } + + fn tags(&self) -> Option>>> { + Some(self.tag_index.clone()) + } } #[derive(Debug)] diff --git a/melib/src/conf.rs b/melib/src/conf.rs index a269a34d..a6a2d643 100644 --- a/melib/src/conf.rs +++ b/melib/src/conf.rs @@ -20,7 +20,8 @@ */ use crate::backends::SpecialUseMailbox; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::hash_map::HashMap; +use std::collections::{hash_map::DefaultHasher, HashMap, HashSet}; +use std::hash::Hasher; #[derive(Debug, Serialize, Default, Clone)] pub struct AccountSettings { @@ -82,6 +83,8 @@ pub struct FolderConf { pub ignore: ToggleFlag, #[serde(default = "none")] pub usage: Option, + #[serde(default, deserialize_with = "tag_set_de")] + pub ignore_tags: HashSet, #[serde(flatten)] pub extra: HashMap, } @@ -94,6 +97,7 @@ impl Default for FolderConf { subscribe: ToggleFlag::Unset, ignore: ToggleFlag::Unset, usage: None, + ignore_tags: HashSet::default(), extra: HashMap::default(), } } @@ -180,3 +184,17 @@ impl Serialize for ToggleFlag { } } } + +pub fn tag_set_de<'de, D>(deserializer: D) -> std::result::Result, D::Error> +where + D: Deserializer<'de>, +{ + Ok(>::deserialize(deserializer)? + .into_iter() + .map(|tag| { + let mut hasher = DefaultHasher::new(); + hasher.write(tag.as_bytes()); + hasher.finish() + }) + .collect()) +} diff --git a/melib/src/email.rs b/melib/src/email.rs index d84bf2cc..6fa261bd 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -135,6 +135,7 @@ pub struct Envelope { flags: Flag, has_attachments: bool, + labels: crate::structs::StackVec, } impl fmt::Debug for Envelope { @@ -172,6 +173,7 @@ impl Envelope { hash, has_attachments: false, flags: Flag::default(), + labels: crate::structs::StackVec::new(), } } @@ -597,6 +599,14 @@ impl Envelope { pub fn has_attachments(&self) -> bool { self.has_attachments } + + pub fn labels(&self) -> &crate::structs::StackVec { + &self.labels + } + + pub fn labels_mut(&mut self) -> &mut crate::structs::StackVec { + &mut self.labels + } } impl Eq for Envelope {} diff --git a/melib/src/structs.rs b/melib/src/structs.rs index ed9f82e4..a6073c4d 100644 --- a/melib/src/structs.rs +++ b/melib/src/structs.rs @@ -23,7 +23,7 @@ use std::iter::{Extend, FromIterator}; use std::ops::Index; const STACK_VEC_CAPACITY: usize = 32; -#[derive(Debug, Default)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct StackVec { len: usize, array: [T; STACK_VEC_CAPACITY],