aboutsummaryrefslogtreecommitdiffstats
path: root/notmuch/src/message.rs
diff options
context:
space:
mode:
Diffstat (limited to 'notmuch/src/message.rs')
-rw-r--r--notmuch/src/message.rs329
1 files changed, 329 insertions, 0 deletions
diff --git a/notmuch/src/message.rs b/notmuch/src/message.rs
new file mode 100644
index 0000000..55a544f
--- /dev/null
+++ b/notmuch/src/message.rs
@@ -0,0 +1,329 @@
+use std::ffi::CString;
+use std::path::PathBuf;
+use std::cell::RefCell;
+use std::borrow::Cow;
+use std::ptr;
+
+use supercow::{Supercow};
+
+use error::{Error, Result};
+use ffi;
+use utils::{ToStr, ScopedPhantomcow, ScopedSupercow};
+use Filenames;
+use FilenamesOwner;
+use Messages;
+use MessageProperties;
+use Tags;
+use TagsOwner;
+use IndexOpts;
+
+pub trait MessageOwner: Send + Sync {}
+
+#[derive(Debug)]
+pub struct Message<'o, O>
+where
+ O: MessageOwner + 'o,
+{
+ pub(crate) ptr: *mut ffi::notmuch_message_t,
+ marker: RefCell<ScopedPhantomcow<'o, O>>,
+}
+
+impl<'o, O> MessageOwner for Message<'o, O> where O: MessageOwner + 'o {}
+impl<'o, O> FilenamesOwner for Message<'o, O> where O: MessageOwner + 'o {}
+impl<'o, O> TagsOwner for Message<'o, O> where O: MessageOwner + 'o {}
+
+
+// impl<'o, O> PartialEq for Message<'o, O>
+// where
+// O: MessageOwner + 'o
+// {
+// fn eq(self: &Self, other: &Message<'o, O>) -> bool{
+// self.id() == other.id()
+// }
+// }
+
+impl<'o, O> Message<'o, O>
+where
+ O: MessageOwner + 'o,
+{
+ pub(crate) fn from_ptr<P>(ptr: *mut ffi::notmuch_message_t, owner: P) -> Message<'o, O>
+ where
+ P: Into<ScopedPhantomcow<'o, O>>,
+ {
+ Message {
+ ptr,
+ marker: RefCell::new(owner.into()),
+ }
+ }
+
+ pub fn id(self: &Self) -> Cow<'_, str> {
+ let mid = unsafe { ffi::notmuch_message_get_message_id(self.ptr) };
+ mid.to_string_lossy()
+ }
+
+ pub fn thread_id(self: &Self) -> Cow<'_, str> {
+ let tid = unsafe { ffi::notmuch_message_get_thread_id(self.ptr) };
+ tid.to_string_lossy()
+ }
+
+ pub fn replies(self: &Self) -> Messages<'o, O> {
+ Messages::<'o, O>::from_ptr(
+ unsafe { ffi::notmuch_message_get_replies(self.ptr) },
+ // will never panic since the borrow is released immediately
+ ScopedPhantomcow::<'o, O>::share(&mut *(self.marker.borrow_mut()))
+ )
+ }
+
+ #[cfg(feature = "v0_26")]
+ pub fn count_files(self: &Self) -> i32 {
+ unsafe { ffi::notmuch_message_count_files(self.ptr) }
+ }
+
+ pub fn filenames(self: &Self) -> Filenames<Self> {
+ <Self as MessageExt<'o, O>>::filenames(self)
+ }
+
+ pub fn filename(self: &Self) -> PathBuf {
+ PathBuf::from(
+ unsafe { ffi::notmuch_message_get_filename(self.ptr) }
+ .to_str()
+ .unwrap(),
+ )
+ }
+
+ pub fn date(&self) -> i64 {
+ unsafe { ffi::notmuch_message_get_date(self.ptr) as i64 }
+ }
+
+ pub fn header(&self, name: &str) -> Result<Option<Cow<'_, str>>> {
+ let name = CString::new(name).unwrap();
+ let ret = unsafe { ffi::notmuch_message_get_header(self.ptr, name.as_ptr()) };
+ if ret.is_null() {
+ Err(Error::UnspecifiedError)
+ } else {
+ let ret_str = ret.to_string_lossy();
+ if ret_str.is_empty() {
+ Ok(None)
+ } else{
+ Ok(Some(ret_str))
+ }
+ }
+ }
+
+ pub fn tags(&self) -> Tags<Self> {
+ <Self as MessageExt<'o, O>>::tags(self)
+ }
+
+ pub fn add_tag(self: &Self, tag: &str) -> Result<()> {
+ let tag = CString::new(tag).unwrap();
+ unsafe { ffi::notmuch_message_add_tag(self.ptr, tag.as_ptr()) }.as_result()
+ }
+
+ pub fn remove_tag(self: &Self, tag: &str) -> Result<()> {
+ let tag = CString::new(tag).unwrap();
+ unsafe { ffi::notmuch_message_remove_tag(self.ptr, tag.as_ptr()) }.as_result()
+ }
+
+ pub fn remove_all_tags(self: &Self) -> Result<()> {
+ unsafe { ffi::notmuch_message_remove_all_tags(self.ptr) }.as_result()
+ }
+
+ pub fn tags_to_maildir_flags(self: &Self) -> Result<()> {
+ unsafe { ffi::notmuch_message_tags_to_maildir_flags(self.ptr) }.as_result()
+ }
+
+ pub fn maildir_flags_to_tags(self: &Self) -> Result<()> {
+ unsafe { ffi::notmuch_message_maildir_flags_to_tags(self.ptr) }.as_result()
+ }
+
+ pub fn reindex<'d>(self: &Self, indexopts: IndexOpts<'d>) -> Result<()> {
+ unsafe { ffi::notmuch_message_reindex(self.ptr, indexopts.ptr) }.as_result()
+ }
+
+ pub fn freeze(self: &Self) -> Result<()> {
+ unsafe { ffi::notmuch_message_freeze(self.ptr) }.as_result()
+ }
+
+ pub fn thaw(self: &Self) -> Result<()> {
+ unsafe { ffi::notmuch_message_thaw(self.ptr) }.as_result()
+ }
+
+ pub fn properties<'m>(&'m self, key: &str, exact: bool) -> MessageProperties<'m, 'o, O> {
+ <Self as MessageExt<'o, O>>::properties(self, key, exact)
+ }
+
+ pub fn remove_all_properties(&self, key: Option<&str>) -> Result<()>
+ {
+ match key {
+ Some(k) => {
+ let key_str = CString::new(k).unwrap();
+ unsafe {
+ ffi::notmuch_message_remove_all_properties(self.ptr, key_str.as_ptr())
+ }.as_result()
+ },
+ None => {
+ let p = ptr::null();
+ unsafe {
+ ffi::notmuch_message_remove_all_properties(self.ptr, p)
+ }.as_result()
+ }
+ }
+ }
+
+ pub fn remove_all_properties_with_prefix(&self, prefix: Option<&str>) -> Result<()>
+ {
+ match prefix {
+ Some(k) => {
+ let key_str = CString::new(k).unwrap();
+ unsafe {
+ ffi::notmuch_message_remove_all_properties_with_prefix(self.ptr, key_str.as_ptr())
+ }.as_result()
+ },
+ None => {
+ let p = ptr::null();
+ unsafe {
+ ffi::notmuch_message_remove_all_properties_with_prefix(self.ptr, p)
+ }.as_result()
+ }
+ }
+ }
+
+
+ pub fn count_properties(&self, key: &str) -> Result<u32>
+ {
+ let key_str = CString::new(key).unwrap();
+ let mut cnt = 0;
+ unsafe {
+ ffi::notmuch_message_count_properties(self.ptr, key_str.as_ptr(), &mut cnt)
+ }.as_result()?;
+
+ Ok(cnt)
+ }
+
+ pub fn property(&self, key: &str) -> Result<Cow<'_, str>>
+ {
+ let key_str = CString::new(key).unwrap();
+ let mut prop = ptr::null();
+ unsafe {
+ ffi::notmuch_message_get_property(self.ptr, key_str.as_ptr(), &mut prop)
+ }.as_result()?;
+
+ if prop.is_null() {
+ Err(Error::UnspecifiedError)
+ } else {
+ // TODO: the unwrap here is not good
+ Ok(prop.to_string_lossy())
+ }
+ }
+
+ pub fn add_property(&self, key: &str, value: &str) -> Result<()>
+ {
+ let key_str = CString::new(key).unwrap();
+ let value_str = CString::new(value).unwrap();
+ unsafe {
+ ffi::notmuch_message_add_property(self.ptr, key_str.as_ptr(), value_str.as_ptr())
+ }.as_result()
+ }
+
+ pub fn remove_property(&self, key: &str, value: &str) -> Result<()>
+ {
+ let key_str = CString::new(key).unwrap();
+ let value_str = CString::new(value).unwrap();
+ unsafe {
+ ffi::notmuch_message_remove_property(self.ptr, key_str.as_ptr(), value_str.as_ptr())
+ }.as_result()
+ }
+}
+
+pub trait MessageExt<'o, O>
+where
+ O: MessageOwner + 'o,
+{
+ fn tags<'m, M>(message: M) -> Tags<'m, Message<'o, O>>
+ where
+ M: Into<ScopedSupercow<'m, Message<'o, O>>>,
+ {
+ let messageref = message.into();
+ Tags::from_ptr(
+ unsafe { ffi::notmuch_message_get_tags(messageref.ptr) },
+ Supercow::phantom(messageref),
+ )
+ }
+
+ // fn replies<'s, S>(message: S) -> Messages<'s, Message<'o, O>>
+ // where
+ // S: Into<ScopedSupercow<'s, Message<'o, O>>>,
+ // {
+ // let messageref = message.into();
+ // Messages::from_ptr(
+ // unsafe { ffi::notmuch_message_get_replies(messageref.ptr) },
+ // Supercow::phantom(messageref),
+ // )
+ // }
+
+ fn filenames<'m, M>(message: M) -> Filenames<'m, Message<'o, O>>
+ where
+ M: Into<ScopedSupercow<'m, Message<'o, O>>>,
+ {
+ let messageref = message.into();
+ Filenames::from_ptr(
+ unsafe { ffi::notmuch_message_get_filenames(messageref.ptr) },
+ Supercow::phantom(messageref),
+ )
+ }
+
+ fn properties<'m, M>(message: M, key: &str, exact: bool) -> MessageProperties<'m, 'o, O>
+ where
+ M: Into<ScopedSupercow<'m, Message<'o, O>>>,
+ {
+ let messageref = message.into();
+ let key_str = CString::new(key).unwrap();
+
+ let props = unsafe {
+ ffi::notmuch_message_get_properties(messageref.ptr, key_str.as_ptr(), exact as i32)
+ };
+
+ MessageProperties::from_ptr(props, Supercow::phantom(messageref))
+ }
+}
+
+impl<'o, O> MessageExt<'o, O> for Message<'o, O> where O: MessageOwner + 'o {}
+
+unsafe impl<'o, O> Send for Message<'o, O> where O: MessageOwner + 'o {}
+unsafe impl<'o, O> Sync for Message<'o, O> where O: MessageOwner + 'o {}
+
+
+pub struct FrozenMessage<'m ,'o, O>
+where
+ O: MessageOwner + 'o
+{
+ message: ScopedSupercow<'m, Message<'o, O>>
+}
+
+
+impl<'m, 'o, O> FrozenMessage<'m, 'o, O>
+where
+ O: MessageOwner + 'o
+{
+ pub fn new<M>(message: M) -> Result<Self>
+ where
+ M: Into<ScopedSupercow<'m, Message<'o, O>>>
+ {
+ let msg = message.into();
+ msg.freeze()?;
+ Ok(FrozenMessage{
+ message: msg
+ })
+ }
+}
+
+impl<'m, 'o, O> Drop for FrozenMessage<'m, 'o, O>
+where
+ O: MessageOwner + 'o
+{
+ fn drop(&mut self) {
+ let _ = self.message.thaw();
+ }
+}
+
+