|
|
|
@ -75,14 +75,14 @@ impl Error for NotmuchError {
|
|
|
|
|
|
|
|
|
|
macro_rules! try_call {
|
|
|
|
|
($lib:expr, $call:expr) => {{
|
|
|
|
|
let status = unsafe { $call };
|
|
|
|
|
let status = $call;
|
|
|
|
|
if status == _notmuch_status_NOTMUCH_STATUS_SUCCESS {
|
|
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
let c_str = unsafe { call!($lib, notmuch_status_to_string)(status) };
|
|
|
|
|
Err(NotmuchError(unsafe {
|
|
|
|
|
CStr::from_ptr(c_str).to_string_lossy().into_owned()
|
|
|
|
|
}))
|
|
|
|
|
let c_str = call!($lib, notmuch_status_to_string)(status);
|
|
|
|
|
Err(NotmuchError(
|
|
|
|
|
CStr::from_ptr(c_str).to_string_lossy().into_owned(),
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}};
|
|
|
|
|
}
|
|
|
|
@ -551,14 +551,16 @@ impl MailBackend for NotmuchDb {
|
|
|
|
|
let database_lck = database.inner.read().unwrap();
|
|
|
|
|
index.write().unwrap().retain(|&env_hash, msg_id| {
|
|
|
|
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
|
|
|
|
if let Err(err) = try_call!(
|
|
|
|
|
if let Err(err) = unsafe {
|
|
|
|
|
try_call!(
|
|
|
|
|
lib,
|
|
|
|
|
call!(lib, notmuch_database_find_message)(
|
|
|
|
|
*database_lck,
|
|
|
|
|
msg_id.as_ptr(),
|
|
|
|
|
&mut message as *mut _,
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
)
|
|
|
|
|
} {
|
|
|
|
|
debug!(err);
|
|
|
|
|
false
|
|
|
|
|
} else {
|
|
|
|
@ -638,97 +640,54 @@ impl MailBackend for NotmuchDb {
|
|
|
|
|
Ok(Box::pin(async { Ok(()) }))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn as_any(&self) -> &dyn ::std::any::Any {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
|
|
|
|
Some(self.tag_index.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct NotmuchOp {
|
|
|
|
|
hash: EnvelopeHash,
|
|
|
|
|
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
|
|
|
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
|
|
|
|
database: Arc<DbConnection>,
|
|
|
|
|
bytes: Option<String>,
|
|
|
|
|
lib: Arc<libloading::Library>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BackendOp for NotmuchOp {
|
|
|
|
|
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
|
|
|
|
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
|
|
|
|
let index_lck = self.index.write().unwrap();
|
|
|
|
|
unsafe {
|
|
|
|
|
call!(self.lib, notmuch_database_find_message)(
|
|
|
|
|
*self.database.inner.read().unwrap(),
|
|
|
|
|
index_lck[&self.hash].as_ptr(),
|
|
|
|
|
&mut message as *mut _,
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let fs_path = unsafe { call!(self.lib, notmuch_message_get_filename)(message) };
|
|
|
|
|
let c_str = unsafe { CStr::from_ptr(fs_path) };
|
|
|
|
|
let mut f = std::fs::File::open(&OsStr::from_bytes(c_str.to_bytes()))?;
|
|
|
|
|
let mut response = String::new();
|
|
|
|
|
f.read_to_string(&mut response)?;
|
|
|
|
|
self.bytes = Some(response);
|
|
|
|
|
let ret = Ok(self.bytes.as_ref().unwrap().as_bytes().to_vec());
|
|
|
|
|
Ok(Box::pin(async move { ret }))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fetch_flags(&self) -> ResultFuture<Flag> {
|
|
|
|
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
|
|
|
|
let index_lck = self.index.write().unwrap();
|
|
|
|
|
unsafe {
|
|
|
|
|
call!(self.lib, notmuch_database_find_message)(
|
|
|
|
|
*self.database.inner.read().unwrap(),
|
|
|
|
|
index_lck[&self.hash].as_ptr(),
|
|
|
|
|
&mut message as *mut _,
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let (flags, _tags) = TagIterator::new(self.lib.clone(), message).collect_flags_and_tags();
|
|
|
|
|
Ok(Box::pin(async move { Ok(flags) }))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_flag(&mut self, f: Flag, value: bool) -> ResultFuture<()> {
|
|
|
|
|
let mut flags = futures::executor::block_on(self.fetch_flags()?)?;
|
|
|
|
|
flags.set(f, value);
|
|
|
|
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
|
|
|
|
fn set_flags(
|
|
|
|
|
&mut self,
|
|
|
|
|
env_hashes: EnvelopeHashBatch,
|
|
|
|
|
_mailbox_hash: MailboxHash,
|
|
|
|
|
flags: SmallVec<[(std::result::Result<Flag, String>, bool); 8]>,
|
|
|
|
|
) -> ResultFuture<()> {
|
|
|
|
|
let database = Self::new_connection(self.path.as_path(), self.lib.clone(), true)?;
|
|
|
|
|
let tag_index = self.tag_index.clone();
|
|
|
|
|
let mut index_lck = self.index.write().unwrap();
|
|
|
|
|
for env_hash in env_hashes.iter() {
|
|
|
|
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
|
|
|
|
unsafe {
|
|
|
|
|
call!(self.lib, notmuch_database_find_message)(
|
|
|
|
|
*self.database.inner.read().unwrap(),
|
|
|
|
|
index_lck[&self.hash].as_ptr(),
|
|
|
|
|
*database.inner.read().unwrap(),
|
|
|
|
|
index_lck[&env_hash].as_ptr(),
|
|
|
|
|
&mut message as *mut _,
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
if message.is_null() {
|
|
|
|
|
return Err(MeliError::new(format!(
|
|
|
|
|
"Error, message with path {:?} not found in notmuch database.",
|
|
|
|
|
index_lck[&self.hash]
|
|
|
|
|
index_lck[&env_hash]
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: freeze/thaw message.
|
|
|
|
|
let tags = TagIterator::new(self.lib.clone(), message).collect::<Vec<&CStr>>();
|
|
|
|
|
debug!(&tags);
|
|
|
|
|
//flags.set(f, value);
|
|
|
|
|
|
|
|
|
|
macro_rules! cstr {
|
|
|
|
|
($l:literal) => {
|
|
|
|
|
CStr::from_bytes_with_nul_unchecked($l)
|
|
|
|
|
&CStr::from_bytes_with_nul_unchecked($l)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
macro_rules! add_tag {
|
|
|
|
|
($l:literal) => {{
|
|
|
|
|
if tags.contains(unsafe { &cstr!($l) }) {
|
|
|
|
|
return Ok(Box::pin(async { Ok(()) }));
|
|
|
|
|
add_tag!(unsafe { cstr!($l) })
|
|
|
|
|
}};
|
|
|
|
|
($l:expr) => {{
|
|
|
|
|
let l = $l;
|
|
|
|
|
if tags.contains(l) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if let Err(err) = try_call!(
|
|
|
|
|
if let Err(err) = unsafe {
|
|
|
|
|
try_call!(
|
|
|
|
|
self.lib,
|
|
|
|
|
call!(self.lib, notmuch_message_add_tag)(message, cstr!($l).as_ptr())
|
|
|
|
|
) {
|
|
|
|
|
call!(self.lib, notmuch_message_add_tag)(message, l.as_ptr())
|
|
|
|
|
)
|
|
|
|
|
} {
|
|
|
|
|
return Err(
|
|
|
|
|
MeliError::new("Could not set tag.").set_source(Some(Arc::new(err)))
|
|
|
|
|
);
|
|
|
|
@ -737,13 +696,19 @@ impl BackendOp for NotmuchOp {
|
|
|
|
|
}
|
|
|
|
|
macro_rules! remove_tag {
|
|
|
|
|
($l:literal) => {{
|
|
|
|
|
if !tags.contains(unsafe { &cstr!($l) }) {
|
|
|
|
|
return Ok(Box::pin(async { Ok(()) }));
|
|
|
|
|
remove_tag!(unsafe { cstr!($l) })
|
|
|
|
|
}};
|
|
|
|
|
($l:expr) => {{
|
|
|
|
|
let l = $l;
|
|
|
|
|
if !tags.contains(l) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if let Err(err) = try_call!(
|
|
|
|
|
if let Err(err) = unsafe {
|
|
|
|
|
try_call!(
|
|
|
|
|
self.lib,
|
|
|
|
|
call!(self.lib, notmuch_message_remove_tag)(message, cstr!($l).as_ptr())
|
|
|
|
|
) {
|
|
|
|
|
call!(self.lib, notmuch_message_remove_tag)(message, l.as_ptr())
|
|
|
|
|
)
|
|
|
|
|
} {
|
|
|
|
|
return Err(
|
|
|
|
|
MeliError::new("Could not set tag.").set_source(Some(Arc::new(err)))
|
|
|
|
|
);
|
|
|
|
@ -751,42 +716,82 @@ impl BackendOp for NotmuchOp {
|
|
|
|
|
}};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (f, v) in flags.iter() {
|
|
|
|
|
let value = *v;
|
|
|
|
|
match f {
|
|
|
|
|
Flag::DRAFT if value => add_tag!(b"draft\0"),
|
|
|
|
|
Flag::DRAFT => remove_tag!(b"draft\0"),
|
|
|
|
|
Flag::FLAGGED if value => add_tag!(b"flagged\0"),
|
|
|
|
|
Flag::FLAGGED => remove_tag!(b"flagged\0"),
|
|
|
|
|
Flag::PASSED if value => add_tag!(b"passed\0"),
|
|
|
|
|
Flag::PASSED => remove_tag!(b"passed\0"),
|
|
|
|
|
Flag::REPLIED if value => add_tag!(b"replied\0"),
|
|
|
|
|
Flag::REPLIED => remove_tag!(b"replied\0"),
|
|
|
|
|
Flag::SEEN if value => remove_tag!(b"unread\0"),
|
|
|
|
|
Flag::SEEN => add_tag!(b"unread\0"),
|
|
|
|
|
Flag::TRASHED if value => add_tag!(b"trashed\0"),
|
|
|
|
|
Flag::TRASHED => remove_tag!(b"trashed\0"),
|
|
|
|
|
_ => debug!("flags is {:?} value = {}", f, value),
|
|
|
|
|
Ok(Flag::DRAFT) if value => add_tag!(b"draft\0"),
|
|
|
|
|
Ok(Flag::DRAFT) => remove_tag!(b"draft\0"),
|
|
|
|
|
Ok(Flag::FLAGGED) if value => add_tag!(b"flagged\0"),
|
|
|
|
|
Ok(Flag::FLAGGED) => remove_tag!(b"flagged\0"),
|
|
|
|
|
Ok(Flag::PASSED) if value => add_tag!(b"passed\0"),
|
|
|
|
|
Ok(Flag::PASSED) => remove_tag!(b"passed\0"),
|
|
|
|
|
Ok(Flag::REPLIED) if value => add_tag!(b"replied\0"),
|
|
|
|
|
Ok(Flag::REPLIED) => remove_tag!(b"replied\0"),
|
|
|
|
|
Ok(Flag::SEEN) if value => remove_tag!(b"unread\0"),
|
|
|
|
|
Ok(Flag::SEEN) => add_tag!(b"unread\0"),
|
|
|
|
|
Ok(Flag::TRASHED) if value => add_tag!(b"trashed\0"),
|
|
|
|
|
Ok(Flag::TRASHED) => remove_tag!(b"trashed\0"),
|
|
|
|
|
Ok(_) => debug!("flags is {:?} value = {}", f, value),
|
|
|
|
|
Err(tag) if value => {
|
|
|
|
|
let c_tag = CString::new(tag.as_str()).unwrap();
|
|
|
|
|
add_tag!(&c_tag.as_ref());
|
|
|
|
|
}
|
|
|
|
|
Err(tag) => {
|
|
|
|
|
let c_tag = CString::new(tag.as_str()).unwrap();
|
|
|
|
|
add_tag!(&c_tag.as_ref());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update message filesystem path. */
|
|
|
|
|
if let Err(err) = try_call!(
|
|
|
|
|
if let Err(err) = unsafe {
|
|
|
|
|
try_call!(
|
|
|
|
|
self.lib,
|
|
|
|
|
call!(self.lib, notmuch_message_tags_to_maildir_flags)(message)
|
|
|
|
|
) {
|
|
|
|
|
return Err(MeliError::new("Could not set tag.").set_source(Some(Arc::new(err))));
|
|
|
|
|
)
|
|
|
|
|
} {
|
|
|
|
|
return Err(MeliError::new("Could not set flags.").set_source(Some(Arc::new(err))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let msg_id = unsafe { call!(self.lib, notmuch_message_get_message_id)(message) };
|
|
|
|
|
let c_str = unsafe { CStr::from_ptr(msg_id) };
|
|
|
|
|
if let Some(p) = index_lck.get_mut(&self.hash) {
|
|
|
|
|
if let Some(p) = index_lck.get_mut(&env_hash) {
|
|
|
|
|
*p = c_str.into();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (f, v) in flags.iter() {
|
|
|
|
|
if let (Err(tag), true) = (f, v) {
|
|
|
|
|
let hash = tag_hash!(tag);
|
|
|
|
|
tag_index.write().unwrap().insert(hash, tag.to_string());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Box::pin(async { Ok(()) }))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_tag(&mut self, tag: String, value: bool) -> ResultFuture<()> {
|
|
|
|
|
fn as_any(&self) -> &dyn ::std::any::Any {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
|
|
|
|
Some(self.tag_index.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct NotmuchOp {
|
|
|
|
|
hash: EnvelopeHash,
|
|
|
|
|
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
|
|
|
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
|
|
|
|
database: Arc<DbConnection>,
|
|
|
|
|
bytes: Option<String>,
|
|
|
|
|
lib: Arc<libloading::Library>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BackendOp for NotmuchOp {
|
|
|
|
|
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
|
|
|
|
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
|
|
|
|
let index_lck = self.index.read().unwrap();
|
|
|
|
|
let index_lck = self.index.write().unwrap();
|
|
|
|
|
unsafe {
|
|
|
|
|
call!(self.lib, notmuch_database_find_message)(
|
|
|
|
|
*self.database.inner.read().unwrap(),
|
|
|
|
@ -794,35 +799,28 @@ impl BackendOp for NotmuchOp {
|
|
|
|
|
&mut message as *mut _,
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
if message.is_null() {
|
|
|
|
|
return Err(MeliError::new(format!(
|
|
|
|
|
"Error, message with path {:?} not found in notmuch database.",
|
|
|
|
|
index_lck[&self.hash]
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
let c_tag = CString::new(tag.as_str()).unwrap();
|
|
|
|
|
if value {
|
|
|
|
|
if let Err(err) = try_call!(
|
|
|
|
|
self.lib,
|
|
|
|
|
call!(self.lib, notmuch_message_add_tag)(message, c_tag.as_ptr(),)
|
|
|
|
|
) {
|
|
|
|
|
return Err(MeliError::new("Could not set tag.").set_source(Some(Arc::new(err))));
|
|
|
|
|
}
|
|
|
|
|
debug!("added tag {}", &tag);
|
|
|
|
|
} else {
|
|
|
|
|
if let Err(err) = try_call!(
|
|
|
|
|
self.lib,
|
|
|
|
|
call!(self.lib, notmuch_message_remove_tag)(message, c_tag.as_ptr(),)
|
|
|
|
|
) {
|
|
|
|
|
return Err(MeliError::new("Could not set tag.").set_source(Some(Arc::new(err))));
|
|
|
|
|
}
|
|
|
|
|
debug!("removed tag {}", &tag);
|
|
|
|
|
}
|
|
|
|
|
let hash = tag_hash!(tag);
|
|
|
|
|
if value {
|
|
|
|
|
self.tag_index.write().unwrap().insert(hash, tag);
|
|
|
|
|
let fs_path = unsafe { call!(self.lib, notmuch_message_get_filename)(message) };
|
|
|
|
|
let c_str = unsafe { CStr::from_ptr(fs_path) };
|
|
|
|
|
let mut f = std::fs::File::open(&OsStr::from_bytes(c_str.to_bytes()))?;
|
|
|
|
|
let mut response = String::new();
|
|
|
|
|
f.read_to_string(&mut response)?;
|
|
|
|
|
self.bytes = Some(response);
|
|
|
|
|
let ret = Ok(self.bytes.as_ref().unwrap().as_bytes().to_vec());
|
|
|
|
|
Ok(Box::pin(async move { ret }))
|
|
|
|
|
}
|
|
|
|
|
Ok(Box::pin(async { Ok(()) }))
|
|
|
|
|
|
|
|
|
|
fn fetch_flags(&self) -> ResultFuture<Flag> {
|
|
|
|
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
|
|
|
|
let index_lck = self.index.write().unwrap();
|
|
|
|
|
unsafe {
|
|
|
|
|
call!(self.lib, notmuch_database_find_message)(
|
|
|
|
|
*self.database.inner.read().unwrap(),
|
|
|
|
|
index_lck[&self.hash].as_ptr(),
|
|
|
|
|
&mut message as *mut _,
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let (flags, _tags) = TagIterator::new(self.lib.clone(), message).collect_flags_and_tags();
|
|
|
|
|
Ok(Box::pin(async move { Ok(flags) }))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -976,11 +974,13 @@ impl<'s> Query<'s> {
|
|
|
|
|
|
|
|
|
|
fn count(&self) -> Result<u32> {
|
|
|
|
|
let mut count = 0_u32;
|
|
|
|
|
unsafe {
|
|
|
|
|
try_call!(
|
|
|
|
|
self.lib,
|
|
|
|
|
call!(self.lib, notmuch_query_count_messages)(self.ptr, &mut count as *mut _)
|
|
|
|
|
)
|
|
|
|
|
.map_err(|err| err.0)?;
|
|
|
|
|
}
|
|
|
|
|
Ok(count)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|