melib: add tags() method in MailBackend
Add tags() method that returns Option<Arc<RwLock<BTreeMap<u64, String>>>>. 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.jmap
parent
49dccb94a5
commit
258b6c8fe8
|
@ -41,9 +41,11 @@ use self::maildir::MaildirType;
|
||||||
use self::mbox::MboxType;
|
use self::mbox::MboxType;
|
||||||
use super::email::{Envelope, EnvelopeHash, Flag};
|
use super::email::{Envelope, EnvelopeHash, Flag};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use std;
|
use std;
|
||||||
|
@ -234,6 +236,9 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,10 @@ use crate::conf::AccountSettings;
|
||||||
use crate::email::{Envelope, EnvelopeHash, Flag};
|
use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||||
use crate::error::{MeliError, Result};
|
use crate::error::{MeliError, Result};
|
||||||
use crate::shellexpand::ShellExpandTrait;
|
use crate::shellexpand::ShellExpandTrait;
|
||||||
|
use crate::structs::StackVec;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
@ -32,6 +34,7 @@ pub struct NotmuchDb {
|
||||||
database: DbWrapper,
|
database: DbWrapper,
|
||||||
folders: Arc<RwLock<FnvHashMap<FolderHash, NotmuchFolder>>>,
|
folders: Arc<RwLock<FnvHashMap<FolderHash, NotmuchFolder>>>,
|
||||||
index: Arc<RwLock<FnvHashMap<EnvelopeHash, &'static CStr>>>,
|
index: Arc<RwLock<FnvHashMap<EnvelopeHash, &'static CStr>>>,
|
||||||
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
save_messages_to: Option<PathBuf>,
|
save_messages_to: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
@ -170,6 +173,8 @@ impl NotmuchDb {
|
||||||
},
|
},
|
||||||
path,
|
path,
|
||||||
index: Arc::new(RwLock::new(Default::default())),
|
index: Arc::new(RwLock::new(Default::default())),
|
||||||
|
tag_index: Arc::new(RwLock::new(Default::default())),
|
||||||
|
|
||||||
folders: Arc::new(RwLock::new(folders)),
|
folders: Arc::new(RwLock::new(folders)),
|
||||||
save_messages_to: None,
|
save_messages_to: None,
|
||||||
}))
|
}))
|
||||||
|
@ -194,6 +199,39 @@ impl NotmuchDb {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn search(&self, query_s: &str) -> Result<crate::structs::StackVec<EnvelopeHash>> {
|
||||||
|
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 {
|
impl MailBackend for NotmuchDb {
|
||||||
|
@ -205,6 +243,7 @@ impl MailBackend for NotmuchDb {
|
||||||
let folder_hash = folder.hash();
|
let folder_hash = folder.hash();
|
||||||
let database = self.database.clone();
|
let database = self.database.clone();
|
||||||
let index = self.index.clone();
|
let index = self.index.clone();
|
||||||
|
let tag_index = self.tag_index.clone();
|
||||||
let folders = self.folders.clone();
|
let folders = self.folders.clone();
|
||||||
let handle = {
|
let handle = {
|
||||||
let tx = w.tx();
|
let tx = w.tx();
|
||||||
|
@ -275,7 +314,21 @@ impl MailBackend for NotmuchDb {
|
||||||
index: index.clone(),
|
index: index.clone(),
|
||||||
bytes: Some(response),
|
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);
|
ret.push(env);
|
||||||
} else {
|
} else {
|
||||||
debug!("could not parse path {:?}", c_str);
|
debug!("could not parse path {:?}", c_str);
|
||||||
|
@ -347,6 +400,10 @@ impl MailBackend for NotmuchDb {
|
||||||
fn as_any(&self) -> &dyn::std::any::Any {
|
fn as_any(&self) -> &dyn::std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||||
|
Some(self.tag_index.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
*/
|
*/
|
||||||
use crate::backends::SpecialUseMailbox;
|
use crate::backends::SpecialUseMailbox;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
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)]
|
#[derive(Debug, Serialize, Default, Clone)]
|
||||||
pub struct AccountSettings {
|
pub struct AccountSettings {
|
||||||
|
@ -82,6 +83,8 @@ pub struct FolderConf {
|
||||||
pub ignore: ToggleFlag,
|
pub ignore: ToggleFlag,
|
||||||
#[serde(default = "none")]
|
#[serde(default = "none")]
|
||||||
pub usage: Option<SpecialUseMailbox>,
|
pub usage: Option<SpecialUseMailbox>,
|
||||||
|
#[serde(default, deserialize_with = "tag_set_de")]
|
||||||
|
pub ignore_tags: HashSet<u64>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub extra: HashMap<String, String>,
|
pub extra: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
@ -94,6 +97,7 @@ impl Default for FolderConf {
|
||||||
subscribe: ToggleFlag::Unset,
|
subscribe: ToggleFlag::Unset,
|
||||||
ignore: ToggleFlag::Unset,
|
ignore: ToggleFlag::Unset,
|
||||||
usage: None,
|
usage: None,
|
||||||
|
ignore_tags: HashSet::default(),
|
||||||
extra: HashMap::default(),
|
extra: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,3 +184,17 @@ impl Serialize for ToggleFlag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tag_set_de<'de, D>(deserializer: D) -> std::result::Result<HashSet<u64>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(<Vec<String>>::deserialize(deserializer)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|tag| {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
hasher.write(tag.as_bytes());
|
||||||
|
hasher.finish()
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
|
@ -135,6 +135,7 @@ pub struct Envelope {
|
||||||
|
|
||||||
flags: Flag,
|
flags: Flag,
|
||||||
has_attachments: bool,
|
has_attachments: bool,
|
||||||
|
labels: crate::structs::StackVec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Envelope {
|
impl fmt::Debug for Envelope {
|
||||||
|
@ -172,6 +173,7 @@ impl Envelope {
|
||||||
hash,
|
hash,
|
||||||
has_attachments: false,
|
has_attachments: false,
|
||||||
flags: Flag::default(),
|
flags: Flag::default(),
|
||||||
|
labels: crate::structs::StackVec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,6 +599,14 @@ impl Envelope {
|
||||||
pub fn has_attachments(&self) -> bool {
|
pub fn has_attachments(&self) -> bool {
|
||||||
self.has_attachments
|
self.has_attachments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn labels(&self) -> &crate::structs::StackVec<u64> {
|
||||||
|
&self.labels
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn labels_mut(&mut self) -> &mut crate::structs::StackVec<u64> {
|
||||||
|
&mut self.labels
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Envelope {}
|
impl Eq for Envelope {}
|
||||||
|
|
|
@ -23,7 +23,7 @@ use std::iter::{Extend, FromIterator};
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
|
||||||
const STACK_VEC_CAPACITY: usize = 32;
|
const STACK_VEC_CAPACITY: usize = 32;
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
pub struct StackVec<T: Default + Copy + std::fmt::Debug> {
|
pub struct StackVec<T: Default + Copy + std::fmt::Debug> {
|
||||||
len: usize,
|
len: usize,
|
||||||
array: [T; STACK_VEC_CAPACITY],
|
array: [T; STACK_VEC_CAPACITY],
|
||||||
|
|
Loading…
Reference in New Issue