From 96e31baa354908b40e0bbfbb52ecf5aadcc60b95 Mon Sep 17 00:00:00 2001 From: Dirk Van Haerenborgh Date: Wed, 20 Oct 2021 15:15:59 +0200 Subject: [PATCH] replace lifetimes with refcounts --- Cargo.toml | 4 +- README.md | 39 +---- src/config_list.rs | 53 +++---- src/database.rs | 289 +++++++++++----------------------- src/directory.rs | 55 +++---- src/error.rs | 2 +- src/filenames.rs | 65 ++++---- src/index.rs | 43 ----- src/index_opts.rs | 43 +++++ src/lib.rs | 36 ++--- src/message.rs | 320 ++++++++++++++------------------------ src/message_properties.rs | 68 ++++---- src/messages.rs | 132 +++++++--------- src/query.rs | 126 ++++++--------- src/tags.rs | 80 +++++----- src/thread.rs | 151 +++++++----------- src/threads.rs | 71 ++++----- src/utils.rs | 27 +--- tests/test_database.rs | 14 +- tests/test_message.rs | 28 ++-- tests/test_query.rs | 178 +++++++++++++++------ tests/test_tags.rs | 72 +++++---- tests/test_thread.rs | 59 ++++--- 23 files changed, 856 insertions(+), 1099 deletions(-) delete mode 100644 src/index.rs create mode 100644 src/index_opts.rs diff --git a/Cargo.toml b/Cargo.toml index 70c5a71..03e0c3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "notmuch" -version = "0.6.1" +version = "0.7.0" authors = ["Dirk Van Haerenborgh "] homepage = "https://github.com/vhdirk/notmuch-rs" repository = "https://github.com/vhdirk/notmuch-rs" @@ -16,7 +16,7 @@ travis-ci = { repository = "vhdirk/notmuch-rs" } [dependencies] libc = "0.2" # clippy = { version = "0.0.211", optional = true } -supercow = "0.1.0" +from_variants = "0.6.0" [dev-dependencies] dirs = "1.0" diff --git a/README.md b/README.md index 1fcbddf..f1122f9 100644 --- a/README.md +++ b/README.md @@ -52,44 +52,7 @@ fn main() { Notmuch makes no claims regarding thread safety. It does not seem to use any thread locals, but I did not spot any locks. So, as far as I am concerned, it is -not thread safe. -So why do all structs implement ```Send``` and ```Sync```? Well, it _is_ safe to -access pointers from different threads (as long as you know what you are doing :) ). -Up till now I haven't done a lot of multithreaded stuff with notmuch-rs. If you -feel this is too permissive, let me know. - -## Lifetime - -All structs are strictly linked together with their lifetime. The root of the -tree is ```Database```, which has a lifetime that must outlive any child -objects, for instance ```Query```. The ```Threads``` iterator that you can get -from a ```Query``` is always outlived by the parent query. The ```Threads``` -does not own any individual ```Thread```. These are bound to the owner of -the ```Threads``` iterator itself. Each structure keeps a ```PhantomCow``` -marker for its owner. - -Typically, using a lifetimes structure like this in an application poses -significant difficulties in satisfying these lifetime requirements. While other -libraries force the application developers towards crates like ```owningref``` -or ```rental``` to get around this, ```notmuch-rs``` makes use of the -excellent [Supercow](https://crates.io/crates/supercow), to alleviate this. - -This way, you get to choose your own container type, and even keep the parent -object alive so you don't have to juggle lifetimes. To use this, most types -are accompagnied with an ```*Ext``` trait, that accepts ```Rc```, ```Arc``` or -comparable. - -```rust - use std::sync::Arc; - use notmuch::{DatabaseExt}; - - let query = { - let dbr = Arc::new(db); - - ::create_query(dbr.clone(), &"".to_string()).unwrap() - }; - -``` +not thread safe. Hence, all pointers are internally tracked with `Rc`s. ## Acknowledgements diff --git a/src/config_list.rs b/src/config_list.rs index 2c1a6a4..1d27a24 100644 --- a/src/config_list.rs +++ b/src/config_list.rs @@ -1,58 +1,59 @@ use std::ops::Drop; +use std::rc::Rc; -use ffi; use Database; -use utils::{ToStr, ScopedPhantomcow}; - +use ffi; +use utils::ToStr; #[derive(Debug)] -pub struct ConfigList<'d> { - ptr: *mut ffi::notmuch_config_list_t, - marker: ScopedPhantomcow<'d, Database>, +pub struct ConfigListPtr(*mut ffi::notmuch_config_list_t); + +#[derive(Clone, Debug)] +pub struct ConfigList { + ptr: Rc, + owner: Database, } -impl<'d> Drop for ConfigList<'d> { +impl Drop for ConfigListPtr { fn drop(&mut self) { - unsafe { ffi::notmuch_config_list_destroy(self.ptr) }; + unsafe { ffi::notmuch_config_list_destroy(self.0) }; } } -impl<'d> ConfigList<'d> { - pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_config_list_t, owner: O) -> ConfigList<'d> - where - O: Into>, - { +impl ConfigList { + pub(crate) fn from_ptr( + ptr: *mut ffi::notmuch_config_list_t, + owner: Database, + ) -> ConfigList { ConfigList { - ptr, - marker: owner.into(), + ptr: Rc::new(ConfigListPtr(ptr)), + owner, } } } - -impl<'d> Iterator for ConfigList<'d> -{ +impl Iterator for ConfigList { type Item = (String, String); fn next(&mut self) -> Option { - let valid = unsafe { ffi::notmuch_config_list_valid(self.ptr) }; + let valid = unsafe { ffi::notmuch_config_list_valid(self.ptr.0) }; if valid == 0 { return None; } let (k, v) = unsafe { - let key = ffi::notmuch_config_list_key(self.ptr); - let value = ffi::notmuch_config_list_value(self.ptr); + let key = ffi::notmuch_config_list_key(self.ptr.0); + let value = ffi::notmuch_config_list_value(self.ptr.0); - ffi::notmuch_config_list_move_to_next(self.ptr); + ffi::notmuch_config_list_move_to_next(self.ptr.0); (key, value) }; - Some((k.to_string_lossy().to_string(), v.to_string_lossy().to_string())) + Some(( + k.to_string_lossy().to_string(), + v.to_string_lossy().to_string(), + )) } } - -unsafe impl<'d> Send for ConfigList<'d> {} -unsafe impl<'d> Sync for ConfigList<'d> {} diff --git a/src/database.rs b/src/database.rs index 44ad040..d287c1f 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,32 +1,27 @@ use std::ffi::{CStr, CString}; +use std::fmt::Debug; use std::ops::Drop; use std::path::Path; use std::ptr; - -use supercow::Supercow; +use std::rc::Rc; use libc; -use std::cmp::{PartialEq, PartialOrd, Ordering}; +use std::cmp::{Ordering, PartialEq, PartialOrd}; use error::{Error, Result}; use ffi; use ffi::Status; use utils::ToStr; +use ConfigList; use Directory; +use IndexOpts; +use Message; use Query; use Tags; -use TagsOwner; -use Message; -use MessageOwner; -use IndexOpts; -use ConfigList; -use utils::ScopedSupercow; - // Re-exported under database module for pretty namespacin'. pub use ffi::DatabaseMode; - #[derive(Clone, Debug)] pub struct Revision { pub revision: libc::c_ulong, @@ -34,13 +29,13 @@ pub struct Revision { } impl PartialEq for Revision { - fn eq(&self, other: &Revision) -> bool{ + fn eq(&self, other: &Revision) -> bool { self.uuid == other.uuid && self.revision == other.revision } } impl PartialOrd for Revision { - fn partial_cmp(&self, other: &Revision) -> Option{ + fn partial_cmp(&self, other: &Revision) -> Option { if self.uuid != other.uuid { return None; } @@ -48,23 +43,22 @@ impl PartialOrd for Revision { } } - #[derive(Debug)] -pub struct Database { - pub(crate) ptr: *mut ffi::notmuch_database_t, -} +pub(crate) struct DatabasePtr(*mut ffi::notmuch_database_t); -impl Drop for Database { +impl Drop for DatabasePtr { fn drop(&mut self) { - unsafe { ffi::notmuch_database_destroy(self.ptr) }; + unsafe { ffi::notmuch_database_destroy(self.0) }; } } -impl TagsOwner for Database {} -impl MessageOwner for Database {} +#[derive(Clone, Debug)] +pub struct Database { + ptr: Rc, +} impl Database { - pub fn create

(path: &P) -> Result + pub fn create

(path: P) -> Result where P: AsRef, { @@ -74,11 +68,11 @@ impl Database { unsafe { ffi::notmuch_database_create(path_str.as_ptr(), &mut db) }.as_result()?; Ok(Database { - ptr: db, + ptr: Rc::new(DatabasePtr(db)), }) } - pub fn open

(path: &P, mode: DatabaseMode) -> Result + pub fn open

(path: P, mode: DatabaseMode) -> Result where P: AsRef, { @@ -89,17 +83,17 @@ impl Database { .as_result()?; Ok(Database { - ptr: db, + ptr: Rc::new(DatabasePtr(db)), }) } pub fn close(&self) -> Result<()> { - unsafe { ffi::notmuch_database_close(self.ptr) }.as_result()?; + unsafe { ffi::notmuch_database_close(self.ptr.0) }.as_result()?; Ok(()) } - pub fn compact(path: &P, backup_path: Option<&P>) -> Result<()> + pub fn compact(path: P, backup_path: Option<&P>) -> Result<()> where P: AsRef, F: FnMut(&str), @@ -108,7 +102,7 @@ impl Database { Database::_compact(path, backup_path, status) } - pub fn compact_with_status(path: &P, backup_path: Option<&P>, status: F) -> Result<()> + pub fn compact_with_status(path: P, backup_path: Option<&P>, status: F) -> Result<()> where P: AsRef, F: FnMut(&str), @@ -116,7 +110,7 @@ impl Database { Database::_compact(path, backup_path, Some(status)) } - fn _compact(path: &P, backup_path: Option<&P>, status: Option) -> Result<()> + fn _compact(path: P, backup_path: Option<&P>, status: Option) -> Result<()> where P: AsRef, F: FnMut(&str), @@ -144,21 +138,22 @@ impl Database { }, status.map_or(ptr::null_mut(), |f| &f as *const _ as *mut libc::c_void), ) - }.as_result()?; + } + .as_result()?; Ok(()) } pub fn path(&self) -> &Path { Path::new( - unsafe { ffi::notmuch_database_get_path(self.ptr) } + unsafe { ffi::notmuch_database_get_path(self.ptr.0) } .to_str() .unwrap(), ) } pub fn version(&self) -> u32 { - unsafe { ffi::notmuch_database_get_version(self.ptr) } + unsafe { ffi::notmuch_database_get_version(self.ptr.0) } } #[cfg(feature = "v0_21")] @@ -166,7 +161,7 @@ impl Database { let uuid_p: *const libc::c_char = ptr::null(); let revision = unsafe { ffi::notmuch_database_get_revision( - self.ptr, + self.ptr.0, (&uuid_p) as *const _ as *mut *const libc::c_char, ) }; @@ -180,10 +175,10 @@ impl Database { } pub fn needs_upgrade(&self) -> bool { - unsafe { ffi::notmuch_database_needs_upgrade(self.ptr) == 1 } + unsafe { ffi::notmuch_database_needs_upgrade(self.ptr.0) == 1 } } - pub fn upgrade(&mut self) -> Result<()> + pub fn upgrade(&self) -> Result<()> where F: FnMut(f64), { @@ -191,14 +186,14 @@ impl Database { self._upgrade(status) } - pub fn upgrade_with_status(&mut self, status: F) -> Result<()> + pub fn upgrade_with_status(&self, status: F) -> Result<()> where F: FnMut(f64), { self._upgrade(Some(status)) } - fn _upgrade(&mut self, status: Option) -> Result<()> + fn _upgrade(&self, status: Option) -> Result<()> where F: FnMut(f64), { @@ -213,7 +208,7 @@ impl Database { unsafe { ffi::notmuch_database_upgrade( - self.ptr, + self.ptr.0, if status.is_some() { Some(wrapper::) } else { @@ -221,250 +216,156 @@ impl Database { }, status.map_or(ptr::null_mut(), |f| &f as *const _ as *mut libc::c_void), ) - }.as_result()?; + } + .as_result()?; Ok(()) } - pub fn directory<'d, P>(&'d self, path: &P) -> Result>> - where - P: AsRef, - { - ::directory(self, path) - } - - pub fn config_list<'d>(&'d self, prefix: &str) -> Result> - { - ::config_list(self, prefix) - } - - pub fn create_query<'d>(&'d self, query_string: &str) -> Result> { - ::create_query(self, query_string) - } - - pub fn all_tags<'d>(&'d self) -> Result> { - ::all_tags(self) - } - - pub fn find_message<'d>(&'d self, message_id: &str) -> Result>> { - ::find_message(self, message_id) - } - - pub fn find_message_by_filename<'d, P>(&'d self, filename: &P) -> Result>> - where - P: AsRef, - { - ::find_message_by_filename(self, filename) - } - - pub fn remove_message<'d, P>(&'d self, path: &P) -> Result<()> - where - P: AsRef, - { - ::remove_message(self, path) - } - - pub fn default_indexopts<'d, P>(&'d self) -> Result> - { - ::default_indexopts(self) - } - - pub fn index_file<'d, P>(&'d self, path: &P, indexopts: Option>) -> Result> - where - P: AsRef, - { - ::index_file(self, path, indexopts) - } - - pub fn begin_atomic(&self) -> Result<()> { - unsafe { ffi::notmuch_database_begin_atomic(self.ptr) }.as_result() - } - - pub fn end_atomic(&self) -> Result<()> { - unsafe { ffi::notmuch_database_end_atomic(self.ptr) }.as_result() - } -} - -pub trait DatabaseExt { - fn create_query<'d, D>(database: D, query_string: &str) -> Result> - where - D: Into>, - { - let dbref = database.into(); - let query_str = CString::new(query_string).unwrap(); - - let query = unsafe { ffi::notmuch_query_create(dbref.ptr, query_str.as_ptr()) }; - - Ok(Query::from_ptr(query, Supercow::phantom(dbref))) - } - - fn all_tags<'d, D>(database: D) -> Result> + pub fn directory

(&self, path: P) -> Result> where - D: Into>, - { - let dbref = database.into(); - - let tags = unsafe { ffi::notmuch_database_get_all_tags(dbref.ptr) }; - - Ok(Tags::from_ptr(tags, ScopedSupercow::phantom(dbref))) - } - - fn directory<'d, D, P>(database: D, path: &P) -> Result>> - where - D: Into>, P: AsRef, { - let dbref = database.into(); - let path_str = CString::new(path.as_ref().to_str().unwrap()).unwrap(); let mut dir = ptr::null_mut(); - unsafe { - ffi::notmuch_database_get_directory(dbref.ptr, path_str.as_ptr(), &mut dir) - }.as_result()?; + unsafe { ffi::notmuch_database_get_directory(self.ptr.0, path_str.as_ptr(), &mut dir) } + .as_result()?; if dir.is_null() { Ok(None) } else { - Ok(Some(Directory::from_ptr(dir, Supercow::phantom(dbref)))) + Ok(Some(Directory::from_ptr(dir, self.clone()))) } } - fn config_list<'d, D>(database: D, prefix: &str) -> Result> - where - D: Into> - { - let dbref = database.into(); - + pub fn config_list(&self, prefix: &str) -> Result { let prefix_str = CString::new(prefix).unwrap(); let mut cfgs = ptr::null_mut(); unsafe { - ffi::notmuch_database_get_config_list(dbref.ptr, prefix_str.as_ptr(), &mut cfgs) - }.as_result()?; + ffi::notmuch_database_get_config_list(self.ptr.0, prefix_str.as_ptr(), &mut cfgs) + } + .as_result()?; - Ok(ConfigList::from_ptr(cfgs, Supercow::phantom(dbref))) + Ok(ConfigList::from_ptr(cfgs, self.clone())) } - fn find_message<'d, D>(database: D, message_id: &str) -> Result>> - where - D: Into> - { - let dbref = database.into(); + pub fn create_query(&self, query_string: &str) -> Result { + let query_str = CString::new(query_string).unwrap(); + + let query = unsafe { ffi::notmuch_query_create(self.ptr.0, query_str.as_ptr()) }; + + Ok(Query::from_ptr(query, self.clone())) + } + + pub fn all_tags(&self) -> Result { + let tags = unsafe { ffi::notmuch_database_get_all_tags(self.ptr.0) }; + + Ok(Tags::from_ptr(tags, self.clone())) + } + + pub fn find_message(&self, message_id: &str) -> Result> { let message_id_str = CString::new(message_id).unwrap(); let mut msg = ptr::null_mut(); unsafe { - ffi::notmuch_database_find_message(dbref.ptr, message_id_str.as_ptr(), &mut msg) - }.as_result()?; + ffi::notmuch_database_find_message(self.ptr.0, message_id_str.as_ptr(), &mut msg) + } + .as_result()?; if msg.is_null() { Ok(None) } else { - Ok(Some(Message::from_ptr(msg, Supercow::phantom(dbref)))) + Ok(Some(Message::from_ptr(msg, self.clone()))) } } - fn find_message_by_filename<'d, D, P>(database: D, filename: &P) -> Result>> + pub fn find_message_by_filename

(&self, filename: &P) -> Result> where - D: Into>, - P: AsRef + P: AsRef, { - let dbref = database.into(); let path_str = CString::new(filename.as_ref().to_str().unwrap()).unwrap(); let mut msg = ptr::null_mut(); unsafe { - ffi::notmuch_database_find_message_by_filename(dbref.ptr, path_str.as_ptr(), &mut msg) - }.as_result()?; + ffi::notmuch_database_find_message_by_filename(self.ptr.0, path_str.as_ptr(), &mut msg) + } + .as_result()?; if msg.is_null() { Ok(None) } else { - Ok(Some(Message::from_ptr(msg, Supercow::phantom(dbref)))) + Ok(Some(Message::from_ptr(msg, self.clone()))) } } - fn remove_message<'d, D, P>(database: D, path: &P) -> Result<()> + pub fn remove_message

(&self, path: P) -> Result<()> where - D: Into>, P: AsRef, { - let dbref = database.into(); match path.as_ref().to_str() { Some(path_str) => { let msg_path = CString::new(path_str).unwrap(); - unsafe { ffi::notmuch_database_remove_message(dbref.ptr, msg_path.as_ptr()) } + unsafe { ffi::notmuch_database_remove_message(self.ptr.0, msg_path.as_ptr()) } .as_result() } None => Err(Error::NotmuchError(Status::FileError)), } } - fn default_indexopts<'d, D>(database: D) -> Result> - where - D: Into> - { - let dbref = database.into(); - - let opts = unsafe { ffi::notmuch_database_get_default_indexopts(dbref.ptr) }; + pub fn default_indexopts(&self) -> Result { + let opts = unsafe { ffi::notmuch_database_get_default_indexopts(self.ptr.0) }; - Ok(IndexOpts::from_ptr(opts, ScopedSupercow::phantom(dbref))) + Ok(IndexOpts::from_ptr(opts, self.clone())) } - - fn index_file<'d, D, P>(database: D, path: &P, indexopts: Option>) -> Result> + pub fn index_file

(&self, path: P, indexopts: Option) -> Result where - D: Into>, P: AsRef, { - let dbref = database.into(); - - let opts = indexopts.map_or(ptr::null_mut(), |opt| opt.ptr); + let opts = indexopts.map_or(ptr::null_mut(), |opt| opt.ptr.0); match path.as_ref().to_str() { Some(path_str) => { let msg_path = CString::new(path_str).unwrap(); let mut msg = ptr::null_mut(); - unsafe { ffi::notmuch_database_index_file(dbref.ptr, msg_path.as_ptr(), opts, &mut msg) } - .as_result()?; - - Ok(Message::from_ptr(msg, ScopedSupercow::phantom(dbref))) + unsafe { + ffi::notmuch_database_index_file(self.ptr.0, msg_path.as_ptr(), opts, &mut msg) + } + .as_result()?; + + Ok(Message::from_ptr(msg, self.clone())) } None => Err(Error::NotmuchError(Status::FileError)), } } -} - -impl DatabaseExt for Database {} -unsafe impl Send for Database {} -unsafe impl Sync for Database {} + pub fn begin_atomic(&self) -> Result<()> { + unsafe { ffi::notmuch_database_begin_atomic(self.ptr.0) }.as_result() + } + pub fn end_atomic(&self) -> Result<()> { + unsafe { ffi::notmuch_database_end_atomic(self.ptr.0) }.as_result() + } +} #[derive(Debug)] -pub struct AtomicOperation<'d> { - database: ScopedSupercow<'d, Database>, +pub struct AtomicOperation { + database: Database, } -impl<'d> AtomicOperation<'d> { - pub fn new(db: D) -> Result - where - D: Into>, - { - let database = db.into(); +impl AtomicOperation { + pub fn new(database: &Database) -> Result { database.begin_atomic()?; - Ok(AtomicOperation{ - database - }) + Ok(AtomicOperation { database: database.clone() }) } } -impl<'d> Drop for AtomicOperation<'d> { +impl Drop for AtomicOperation { fn drop(&mut self) { let _ = self.database.end_atomic(); } } - diff --git a/src/directory.rs b/src/directory.rs index 1c8a3df..2e4e7cb 100644 --- a/src/directory.rs +++ b/src/directory.rs @@ -1,57 +1,40 @@ use std::ops::Drop; -use supercow::Supercow; +use std::rc::Rc; use ffi; use Database; use Filenames; -use FilenamesOwner; -use utils::{ScopedSupercow, ScopedPhantomcow}; - #[derive(Debug)] -pub struct Directory<'d> { - ptr: *mut ffi::notmuch_directory_t, - marker: ScopedPhantomcow<'d, Database>, +pub(crate) struct DirectoryPtr(*mut ffi::notmuch_directory_t); + +#[derive(Debug, Clone)] +pub struct Directory { + ptr: Rc, + owner: Database, } -impl<'d> Drop for Directory<'d> { +impl Drop for Directory { fn drop(&mut self) { - unsafe { ffi::notmuch_directory_destroy(self.ptr) }; + unsafe { ffi::notmuch_directory_destroy(self.ptr.0) }; } } -impl<'d> FilenamesOwner for Directory<'d> {} - -impl<'d> Directory<'d> { - pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_directory_t, owner: O) -> Directory<'d> - where - O: Into>, - { +impl Directory { + pub(crate) fn from_ptr( + ptr: *mut ffi::notmuch_directory_t, + owner: Database, + ) -> Directory { Directory { - ptr, - marker: owner.into(), + ptr: Rc::new(DirectoryPtr(ptr)), + owner, } } - pub fn child_directories(&self) -> Filenames { - ::child_directories(self) - } -} - -pub trait DirectoryExt<'d> { - fn child_directories<'s, S>(directory: S) -> Filenames<'s, Directory<'d>> - where - S: Into>>, - { - let dir = directory.into(); + fn child_directories(&self) -> Filenames { Filenames::from_ptr( - unsafe { ffi::notmuch_directory_get_child_directories(dir.ptr) }, - Supercow::phantom(dir), + unsafe { ffi::notmuch_directory_get_child_directories(self.ptr.0) }, + self.clone(), ) } } - -impl<'d> DirectoryExt<'d> for Directory<'d> {} - -unsafe impl<'d> Send for Directory<'d> {} -unsafe impl<'d> Sync for Directory<'d> {} diff --git a/src/error.rs b/src/error.rs index ff03eea..74045b1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -27,7 +27,7 @@ impl error::Error for Error { match &self { Error::IoError(e) => Some(e), Error::NotmuchError(e) => Some(e), - Error::UnspecifiedError => None + Error::UnspecifiedError => None, } } } diff --git a/src/filenames.rs b/src/filenames.rs index 12e5857..38ce4cb 100644 --- a/src/filenames.rs +++ b/src/filenames.rs @@ -2,67 +2,64 @@ use std::ffi::CStr; use std::iter::Iterator; use std::ops::Drop; use std::path::PathBuf; +use std::rc::Rc; + +use from_variants::FromVariants; + +use Directory; +use Message; use ffi; -use utils::ScopedPhantomcow; -pub trait FilenamesOwner {} +#[derive(Clone, Debug, FromVariants)] +pub(crate) enum FilenamesOwner { + Directory(Directory), + Message(Message), +} #[derive(Debug)] -pub struct Filenames<'o, O> -where - O: FilenamesOwner + 'o, -{ - pub(crate) ptr: *mut ffi::notmuch_filenames_t, - pub(crate) marker: ScopedPhantomcow<'o, O>, -} +pub(crate) struct FilenamesPtr(*mut ffi::notmuch_filenames_t); -impl<'o, O> Drop for Filenames<'o, O> -where - O: FilenamesOwner + 'o, -{ - fn drop(self: &mut Self) { - unsafe { ffi::notmuch_filenames_destroy(self.ptr) }; +impl Drop for FilenamesPtr { + fn drop(&mut self) { + unsafe { ffi::notmuch_filenames_destroy(self.0) }; } } -impl<'o, O> Filenames<'o, O> -where - O: FilenamesOwner + 'o, -{ - pub(crate) fn from_ptr

(ptr: *mut ffi::notmuch_filenames_t, owner: P) -> Filenames<'o, O> +#[derive(Debug, Clone)] +pub struct Filenames { + ptr: Rc, + owner: FilenamesOwner, +} + +impl Filenames { + pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_filenames_t, owner: O) -> Self where - P: Into>, + O: Into, { Filenames { - ptr, - marker: owner.into(), + ptr: Rc::new(FilenamesPtr(ptr)), + owner: owner.into(), } } } -impl<'o, O> Iterator for Filenames<'o, O> -where - O: FilenamesOwner + 'o, -{ +impl Iterator for Filenames { type Item = PathBuf; - fn next(self: &mut Self) -> Option { - let valid = unsafe { ffi::notmuch_filenames_valid(self.ptr) }; + fn next(&mut self) -> Option { + let valid = unsafe { ffi::notmuch_filenames_valid(self.ptr.0) }; if valid == 0 { return None; } let ctag = unsafe { - let t = ffi::notmuch_filenames_get(self.ptr); - ffi::notmuch_filenames_move_to_next(self.ptr); + let t = ffi::notmuch_filenames_get(self.ptr.0); + ffi::notmuch_filenames_move_to_next(self.ptr.0); CStr::from_ptr(t) }; Some(PathBuf::from(ctag.to_str().unwrap())) } } - -unsafe impl<'o, O> Send for Filenames<'o, O> where O: FilenamesOwner + 'o {} -unsafe impl<'o, O> Sync for Filenames<'o, O> where O: FilenamesOwner + 'o {} diff --git a/src/index.rs b/src/index.rs deleted file mode 100644 index 4db6bc7..0000000 --- a/src/index.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::ops::Drop; - -use error::Result; -use ffi; -use ffi::DecryptionPolicy; -use Database; -use utils::ScopedPhantomcow; - - -#[derive(Debug)] -pub struct IndexOpts<'d> { - pub(crate) ptr: *mut ffi::notmuch_indexopts_t, - marker: ScopedPhantomcow<'d, Database>, -} - -impl<'d> Drop for IndexOpts<'d> { - fn drop(&mut self) { - unsafe { ffi::notmuch_indexopts_destroy(self.ptr) }; - } -} - -impl<'d> IndexOpts<'d> { - pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_indexopts_t, owner: O) -> IndexOpts<'d> - where - O: Into>, - { - IndexOpts { - ptr, - marker: owner.into(), - } - } - - pub fn set_decrypt_policy(self: &Self, decrypt_policy: DecryptionPolicy) -> Result<()> { - unsafe { ffi::notmuch_indexopts_set_decrypt_policy(self.ptr, decrypt_policy.into()) }.as_result() - } - - pub fn decrypt_policy(self: &Self) -> DecryptionPolicy { - unsafe { ffi::notmuch_indexopts_get_decrypt_policy(self.ptr)}.into() - } -} - -unsafe impl<'d> Send for IndexOpts<'d> {} -unsafe impl<'d> Sync for IndexOpts<'d> {} \ No newline at end of file diff --git a/src/index_opts.rs b/src/index_opts.rs new file mode 100644 index 0000000..ae8a6da --- /dev/null +++ b/src/index_opts.rs @@ -0,0 +1,43 @@ +use std::ops::Drop; +use std::rc::Rc; + +use Database; +use error::Result; +use ffi; +use ffi::DecryptionPolicy; + +#[derive(Debug)] +pub(crate) struct IndexOptsPtr(pub(crate) *mut ffi::notmuch_indexopts_t); + +impl Drop for IndexOptsPtr { + fn drop(&mut self) { + unsafe { ffi::notmuch_indexopts_destroy(self.0) }; + } +} + +#[derive(Debug)] +pub struct IndexOpts { + pub(crate) ptr: Rc, + owner: Database, +} + +impl IndexOpts { + pub(crate) fn from_ptr( + ptr: *mut ffi::notmuch_indexopts_t, + owner: Database, + ) -> IndexOpts { + IndexOpts { + ptr: Rc::new(IndexOptsPtr(ptr)), + owner, + } + } + + pub fn set_decrypt_policy(&self, decrypt_policy: DecryptionPolicy) -> Result<()> { + unsafe { ffi::notmuch_indexopts_set_decrypt_policy(self.ptr.0, decrypt_policy.into()) } + .as_result() + } + + pub fn decrypt_policy(&self) -> DecryptionPolicy { + unsafe { ffi::notmuch_indexopts_get_decrypt_policy(self.ptr.0) }.into() + } +} diff --git a/src/lib.rs b/src/lib.rs index dacf30b..7acf9bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,40 +4,38 @@ #[macro_use] mod macros; +extern crate from_variants; extern crate libc; -extern crate supercow; mod ffi; mod utils; +mod config_list; mod database; mod directory; mod error; mod filenames; +mod index_opts; mod message; +mod message_properties; mod messages; mod query; mod tags; mod thread; mod threads; -mod index; -mod config_list; -mod message_properties; -pub use database::{Database, DatabaseExt, AtomicOperation, Revision}; -pub use directory::{Directory, DirectoryExt}; -pub use error::Error; -pub use filenames::{Filenames, FilenamesOwner}; -pub use message::{Message, MessageExt, MessageOwner, FrozenMessage}; -pub use messages::{Messages, MessagesExt}; -pub use message_properties::{MessageProperties}; -pub use query::{Query, QueryExt}; -pub use tags::{Tags, TagsExt, TagsOwner}; -pub use thread::{Thread, ThreadExt}; -pub use threads::{Threads, ThreadsExt}; -pub use index::IndexOpts; pub use config_list::ConfigList; +pub use database::{AtomicOperation, Database, Revision}; +pub use directory::Directory; +pub use error::Error; +pub use filenames::Filenames; +pub use index_opts::IndexOpts; +pub use message::{FrozenMessage, Message}; +pub use message_properties::MessageProperties; +pub use messages::Messages; +pub use query::Query; +pub use tags::Tags; +pub use thread::Thread; +pub use threads::Threads; -pub use ffi::{Status, DatabaseMode, Sort, DecryptionPolicy}; - -pub use utils::{ScopedSupercow, ScopedPhantomcow}; +pub use ffi::{DatabaseMode, DecryptionPolicy, Sort, Status}; diff --git a/src/message.rs b/src/message.rs index 55a544f..c5774f9 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,212 +1,212 @@ -use std::ffi::CString; -use std::path::PathBuf; -use std::cell::RefCell; +use from_variants::FromVariants; use std::borrow::Cow; +use std::ffi::CString; +use std::path::Path; use std::ptr; - -use supercow::{Supercow}; +use std::rc::Rc; use error::{Error, Result}; use ffi; -use utils::{ToStr, ScopedPhantomcow, ScopedSupercow}; +use utils::ToStr; use Filenames; -use FilenamesOwner; -use Messages; +use IndexOpts; use MessageProperties; +use Database; +use Messages; +use Query; +use Thread; use Tags; -use TagsOwner; -use IndexOpts; -pub trait MessageOwner: Send + Sync {} +#[derive(Clone, Debug, FromVariants)] +pub(crate) enum MessageOwner { + Database(Database), + Messages(Messages), + Thread(Thread), + Query(Query), +} #[derive(Debug)] -pub struct Message<'o, O> -where - O: MessageOwner + 'o, -{ - pub(crate) ptr: *mut ffi::notmuch_message_t, - marker: RefCell>, +pub(crate) struct MessagePtr(*mut ffi::notmuch_message_t); + + +impl Drop for MessagePtr { + fn drop(&mut self) { + unsafe { ffi::notmuch_message_destroy(self.0) }; + } +} + +#[derive(Clone, Debug)] +pub struct Message { + ptr: Rc, + owner: Box, } -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

(ptr: *mut ffi::notmuch_message_t, owner: P) -> Message<'o, O> +impl Message { + pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_message_t, owner: O) -> Message where - P: Into>, + O: Into, { Message { - ptr, - marker: RefCell::new(owner.into()), + ptr: Rc::new(MessagePtr(ptr)), + owner: Box::new(owner.into()), } } - pub fn id(self: &Self) -> Cow<'_, str> { - let mid = unsafe { ffi::notmuch_message_get_message_id(self.ptr) }; + pub fn id(&self) -> Cow<'_, str> { + let mid = unsafe { ffi::notmuch_message_get_message_id(self.ptr.0) }; mid.to_string_lossy() } - pub fn thread_id(self: &Self) -> Cow<'_, str> { - let tid = unsafe { ffi::notmuch_message_get_thread_id(self.ptr) }; + pub fn thread_id(&self) -> Cow<'_, str> { + let tid = unsafe { ffi::notmuch_message_get_thread_id(self.ptr.0) }; 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())) + pub fn replies(&self) -> Messages { + Messages::from_ptr( + unsafe { ffi::notmuch_message_get_replies(self.ptr.0) }, + self.clone(), ) } #[cfg(feature = "v0_26")] - pub fn count_files(self: &Self) -> i32 { - unsafe { ffi::notmuch_message_count_files(self.ptr) } + pub fn count_files(&self) -> i32 { + unsafe { ffi::notmuch_message_count_files(self.ptr.0) } } - pub fn filenames(self: &Self) -> Filenames { - >::filenames(self) + pub fn filenames(&self) -> Filenames { + Filenames::from_ptr( + unsafe { ffi::notmuch_message_get_filenames(self.ptr.0) }, + self.clone(), + ) } - pub fn filename(self: &Self) -> PathBuf { - PathBuf::from( - unsafe { ffi::notmuch_message_get_filename(self.ptr) } + pub fn filename(&self) -> &Path { + Path::new( + unsafe { ffi::notmuch_message_get_filename(self.ptr.0) } .to_str() .unwrap(), ) } pub fn date(&self) -> i64 { - unsafe { ffi::notmuch_message_get_date(self.ptr) as i64 } + unsafe { ffi::notmuch_message_get_date(self.ptr.0) as i64 } } pub fn header(&self, name: &str) -> Result>> { let name = CString::new(name).unwrap(); - let ret = unsafe { ffi::notmuch_message_get_header(self.ptr, name.as_ptr()) }; + let ret = unsafe { ffi::notmuch_message_get_header(self.ptr.0, 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{ + } else { Ok(Some(ret_str)) } } } - pub fn tags(&self) -> Tags { - >::tags(self) + pub fn tags(&self) -> Tags { + Tags::from_ptr( + unsafe { ffi::notmuch_message_get_tags(self.ptr.0) }, + self.clone(), + ) } - pub fn add_tag(self: &Self, tag: &str) -> Result<()> { + pub fn add_tag(&self, tag: &str) -> Result<()> { let tag = CString::new(tag).unwrap(); - unsafe { ffi::notmuch_message_add_tag(self.ptr, tag.as_ptr()) }.as_result() + unsafe { ffi::notmuch_message_add_tag(self.ptr.0, tag.as_ptr()) }.as_result() } - pub fn remove_tag(self: &Self, tag: &str) -> Result<()> { + pub fn remove_tag(&self, tag: &str) -> Result<()> { let tag = CString::new(tag).unwrap(); - unsafe { ffi::notmuch_message_remove_tag(self.ptr, tag.as_ptr()) }.as_result() + unsafe { ffi::notmuch_message_remove_tag(self.ptr.0, 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 remove_all_tags(&self) -> Result<()> { + unsafe { ffi::notmuch_message_remove_all_tags(self.ptr.0) }.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 tags_to_maildir_flags(&self) -> Result<()> { + unsafe { ffi::notmuch_message_tags_to_maildir_flags(self.ptr.0) }.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 maildir_flags_to_tags(&self) -> Result<()> { + unsafe { ffi::notmuch_message_maildir_flags_to_tags(self.ptr.0) }.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 reindex(&self, indexopts: IndexOpts) -> Result<()> { + unsafe { ffi::notmuch_message_reindex(self.ptr.0, indexopts.ptr.0) }.as_result() } - pub fn freeze(self: &Self) -> Result<()> { - unsafe { ffi::notmuch_message_freeze(self.ptr) }.as_result() + pub fn freeze(&self) -> Result<()> { + unsafe { ffi::notmuch_message_freeze(self.ptr.0) }.as_result() } - pub fn thaw(self: &Self) -> Result<()> { - unsafe { ffi::notmuch_message_thaw(self.ptr) }.as_result() + pub fn thaw(&self) -> Result<()> { + unsafe { ffi::notmuch_message_thaw(self.ptr.0) }.as_result() } - pub fn properties<'m>(&'m self, key: &str, exact: bool) -> MessageProperties<'m, 'o, O> { - >::properties(self, key, exact) + pub fn properties(&self, key: &str, exact: bool) -> MessageProperties { + let key_str = CString::new(key).unwrap(); + + let props = unsafe { + ffi::notmuch_message_get_properties(self.ptr.0, key_str.as_ptr(), exact as i32) + }; + + MessageProperties::from_ptr(props, self.clone()) } - pub fn remove_all_properties(&self, key: Option<&str>) -> Result<()> - { + 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() - }, + unsafe { ffi::notmuch_message_remove_all_properties(self.ptr.0, key_str.as_ptr()) } + .as_result() + } None => { let p = ptr::null(); - unsafe { - ffi::notmuch_message_remove_all_properties(self.ptr, p) - }.as_result() + unsafe { ffi::notmuch_message_remove_all_properties(self.ptr.0, p) }.as_result() } } } - pub fn remove_all_properties_with_prefix(&self, prefix: Option<&str>) -> 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() - }, + ffi::notmuch_message_remove_all_properties_with_prefix( + self.ptr.0, + 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() + unsafe { ffi::notmuch_message_remove_all_properties_with_prefix(self.ptr.0, p) } + .as_result() } } } - - pub fn count_properties(&self, key: &str) -> Result - { + pub fn count_properties(&self, key: &str) -> Result { 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()?; + unsafe { ffi::notmuch_message_count_properties(self.ptr.0, key_str.as_ptr(), &mut cnt) } + .as_result()?; Ok(cnt) } - pub fn property(&self, key: &str) -> Result> - { + pub fn property(&self, key: &str) -> Result> { 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()?; + unsafe { ffi::notmuch_message_get_property(self.ptr.0, key_str.as_ptr(), &mut prop) } + .as_result()?; if prop.is_null() { Err(Error::UnspecifiedError) @@ -216,114 +216,36 @@ where } } - pub fn add_property(&self, key: &str, value: &str) -> Result<()> - { + 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() + ffi::notmuch_message_add_property(self.ptr.0, key_str.as_ptr(), value_str.as_ptr()) + } + .as_result() } - pub fn remove_property(&self, key: &str, value: &str) -> 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>>, - { - 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>>, - // { - // 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>>, - { - 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>>, - { - 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)) + ffi::notmuch_message_remove_property(self.ptr.0, key_str.as_ptr(), value_str.as_ptr()) + } + .as_result() } } -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(Message); - -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(message: M) -> Result - where - M: Into>> - { - let msg = message.into(); - msg.freeze()?; - Ok(FrozenMessage{ - message: msg - }) +impl FrozenMessage { + pub fn new(message: &Message) -> Result { + message.freeze()?; + Ok(FrozenMessage(message.clone())) } } -impl<'m, 'o, O> Drop for FrozenMessage<'m, 'o, O> -where - O: MessageOwner + 'o -{ +impl Drop for FrozenMessage { fn drop(&mut self) { - let _ = self.message.thaw(); + let _ = self.0.thaw(); } } - - diff --git a/src/message_properties.rs b/src/message_properties.rs index bd20aa0..1890843 100644 --- a/src/message_properties.rs +++ b/src/message_properties.rs @@ -1,73 +1,59 @@ -use std::ops::Drop; use std::ffi::CStr; +use std::ops::Drop; +use std::rc::Rc; use ffi; use Message; -use MessageOwner; -use utils::{ScopedPhantomcow}; - #[derive(Debug)] -pub struct MessageProperties<'m, 'o, O> -where - O: MessageOwner + 'o -{ - ptr: *mut ffi::notmuch_message_properties_t, - marker: ScopedPhantomcow<'m, Message<'o, O>>, -} +pub(crate) struct MessagePropertiesPtr(*mut ffi::notmuch_message_properties_t); -impl<'m, 'o, O> Drop for MessageProperties<'m, 'o, O> -where - O: MessageOwner + 'o -{ +impl Drop for MessagePropertiesPtr { fn drop(&mut self) { - unsafe { ffi::notmuch_message_properties_destroy(self.ptr) }; + unsafe { ffi::notmuch_message_properties_destroy(self.0) }; } } -impl<'m, 'o, O> MessageProperties<'m, 'o, O> -where - O: MessageOwner + 'o -{ - pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_message_properties_t, owner: S) -> MessageProperties<'m, 'o, O> - where - S: Into>>, - { +#[derive(Clone, Debug)] +pub struct MessageProperties { + ptr: Rc, + owner: Message, +} + +impl MessageProperties { + pub(crate) fn from_ptr( + ptr: *mut ffi::notmuch_message_properties_t, + owner: Message, + ) -> MessageProperties { MessageProperties { - ptr, - marker: owner.into(), + ptr: Rc::new(MessagePropertiesPtr(ptr)), + owner, } } } - -impl<'m, 'o, O> Iterator for MessageProperties<'m, 'o, O> -where - O: MessageOwner + 'o -{ +impl Iterator for MessageProperties { type Item = (String, String); fn next(&mut self) -> Option { - let valid = unsafe { ffi::notmuch_message_properties_valid(self.ptr) }; + let valid = unsafe { ffi::notmuch_message_properties_valid(self.ptr.0) }; if valid == 0 { return None; } let (k, v) = unsafe { - let key = CStr::from_ptr(ffi::notmuch_message_properties_key(self.ptr)); - let value = CStr::from_ptr(ffi::notmuch_message_properties_value(self.ptr)); + let key = CStr::from_ptr(ffi::notmuch_message_properties_key(self.ptr.0)); + let value = CStr::from_ptr(ffi::notmuch_message_properties_value(self.ptr.0)); - ffi::notmuch_message_properties_move_to_next(self.ptr); + ffi::notmuch_message_properties_move_to_next(self.ptr.0); (key, value) }; - Some((k.to_string_lossy().to_string(), v.to_string_lossy().to_string())) + Some(( + k.to_string_lossy().to_string(), + v.to_string_lossy().to_string(), + )) } } - -unsafe impl<'m, 'o, O> Send for MessageProperties<'m, 'o, O> where - O: MessageOwner + 'o {} -unsafe impl<'m, 'o, O> Sync for MessageProperties<'m, 'o, O> where - O: MessageOwner + 'o {} diff --git a/src/messages.rs b/src/messages.rs index cfba547..433e358 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -1,50 +1,52 @@ +use std::marker::PhantomData; +use std::ops::Deref; +use std::ops::DerefMut; +use std::rc::Rc; + +use from_variants::FromVariants; + use ffi; -use utils::ScopedPhantomcow; -use MessageOwner; +use Query; +use Thread; use Message; use Tags; -use TagsOwner; + +#[derive(Clone, Debug, FromVariants)] +pub(crate) enum MessagesOwner { + Query(Query), + Message(Message), + Messages(Messages), + Thread(Thread), +} #[derive(Debug)] -pub struct Messages<'o, O> -where - O: MessageOwner + 'o, -{ - pub(crate) ptr: *mut ffi::notmuch_messages_t, - marker: ScopedPhantomcow<'o, O>, +pub(crate) struct MessagesPtr(*mut ffi::notmuch_messages_t); + +impl Drop for MessagesPtr { + fn drop(&mut self) { + unsafe { ffi::notmuch_messages_destroy(self.0) }; + } } -// impl<'o, O> Drop for Messages<'o, O> -// where -// O: MessageOwner + 'o, -// { -// fn drop(self: &mut Self) { -// unsafe { ffi::notmuch_messages_destroy(self.ptr) }; -// } -// } - -impl<'o, O> Messages<'o, O> -where - O: MessageOwner + 'o, -{ - pub(crate) fn from_ptr

(ptr: *mut ffi::notmuch_messages_t, owner: P) -> Messages<'o, O> +#[derive(Clone, Debug)] +pub struct Messages { + ptr: Rc, + owner: Box, +} + +impl Messages { + pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_messages_t, owner: O) -> Messages where - P: Into>, + O: Into, { Messages { - ptr, - marker: owner.into(), + ptr: Rc::new(MessagesPtr(ptr)), + owner: Box::new(owner.into()), } } } -impl<'o, O> MessageOwner for Messages<'o, O> where O: MessageOwner + 'o {} -impl<'o, O> TagsOwner for Messages<'o, O> where O: MessageOwner + 'o {} - -impl<'o, O> Messages<'o, O> -where - O: MessageOwner + 'o, -{ +impl Messages { /** * Return a list of tags from all messages. * @@ -58,75 +60,59 @@ where * * The function returns NULL on error. */ - pub fn collect_tags<'m>(self: &'o Self) -> Tags<'m, Self> { + pub fn collect_tags(&self) -> Tags { Tags::from_ptr( - unsafe { ffi::notmuch_messages_collect_tags(self.ptr) }, - self, + unsafe { ffi::notmuch_messages_collect_tags(self.ptr.0) }, + self.clone(), ) } } -impl<'o, O> Iterator for Messages<'o, O> -where - O: MessageOwner + 'o, -{ - type Item = Message<'o, O>; +impl Iterator for Messages { + type Item = Message; fn next(&mut self) -> Option { - let valid = unsafe { ffi::notmuch_messages_valid(self.ptr) }; + let valid = unsafe { ffi::notmuch_messages_valid(self.ptr.0) }; if valid == 0 { return None; } - let cthrd = unsafe { - let thrd = ffi::notmuch_messages_get(self.ptr); - ffi::notmuch_messages_move_to_next(self.ptr); - thrd + let cmsg = unsafe { + let msg = ffi::notmuch_messages_get(self.ptr.0); + ffi::notmuch_messages_move_to_next(self.ptr.0); + msg }; - Some(Message::from_ptr(cthrd, ScopedPhantomcow::<'o, O>::share(&mut self.marker))) + Some(Message::from_ptr(cmsg, self.clone())) } } - - -pub trait MessagesExt<'o, O> -where - O: MessageOwner + 'o, -{ -} - -impl<'o, O> MessagesExt<'o, O> for Messages<'o, O> where O: MessageOwner + 'o {} - - -unsafe impl<'o, O> Send for Messages<'o, O> where O: MessageOwner + 'o {} -unsafe impl<'o, O> Sync for Messages<'o, O> where O: MessageOwner + 'o {} - #[cfg(test)] mod tests { + use std::rc::Rc; + // This will not compile if ownership can't be subject to recursion - fn descend<'o, O: 'o + super::MessageOwner, T: Iterator>>(iter: T) - -> usize { - iter.map(|msg| descend(msg.replies()) ).count() + fn descend(iter: T) -> usize + where + T: Iterator, + { + iter.map(|msg| descend(msg.replies())).count() } - - use query::Query; + use database; - + use query::Query; + #[test] #[should_panic] // until test data is filled in fn recurse() { - match database::Database::open( - &String::new(), - database::DatabaseMode::ReadOnly, - ) { + match database::Database::open(&String::new(), database::DatabaseMode::ReadOnly) { /* This will not happen without test data, but will force the compiler to compile * the descend function. */ Ok(db) => { - let q = Query::create(db, &String::new()).unwrap(); - descend::>(q.search_messages().unwrap()); + let q = Query::create(&db, &String::new()).unwrap(); + descend(q.search_messages().unwrap()); } Err(err) => { panic!("Got error while trying to open db: {:?}", err); diff --git a/src/query.rs b/src/query.rs index 50b56e5..ba9cdb7 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1,132 +1,106 @@ +use std::rc::Rc; +use std::ffi::{CStr, CString}; use std::ops::Drop; use std::ptr; -use std::ffi::{CStr, CString}; - -use supercow::{Phantomcow, Supercow}; use error::Result; use ffi; -use ffi::{Sort, Exclude}; +use ffi::{Exclude, Sort}; use Database; use Messages; -use MessageOwner; use Threads; -use DatabaseExt; -use utils::ScopedSupercow; #[derive(Debug)] -pub struct Query<'d> { - pub(crate) ptr: *mut ffi::notmuch_query_t, - marker: Phantomcow<'d, Database>, -} +pub(crate) struct QueryPtr(*mut ffi::notmuch_query_t); -impl<'d> Drop for Query<'d> { +impl Drop for QueryPtr { fn drop(&mut self) { - unsafe { ffi::notmuch_query_destroy(self.ptr) }; + unsafe { ffi::notmuch_query_destroy(self.0) }; } } -impl<'d> MessageOwner for Query<'d> {} +#[derive(Clone, Debug)] +pub struct Query{ + ptr: Rc, + owner: Box, +} -impl<'d> Query<'d> { - pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_query_t, owner: O) -> Query<'d> - where - O: Into>, +impl Query { + pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_query_t, owner: Database) -> Self { Query { - ptr, - marker: owner.into(), + ptr: Rc::new(QueryPtr(ptr)), + owner: Box::new(owner), } } - pub fn create(db: D, query_string: &str) -> Result - where - D: Into>, + pub fn create(database: &Database, query_string: &str) -> Result { - ::create_query(db, query_string) + database.create_query(query_string) } - pub fn query_string(self: &Self) -> String { - let qstring = unsafe { - CStr::from_ptr(ffi::notmuch_query_get_query_string(self.ptr)) - }; + pub fn query_string(&self) -> String + { + let qstring = + unsafe { CStr::from_ptr(ffi::notmuch_query_get_query_string(self.ptr.0)) }; qstring.to_str().unwrap().to_string() } /// Specify the sorting desired for this query. - pub fn set_sort(self: &Self, sort: Sort) { - unsafe { ffi::notmuch_query_set_sort(self.ptr, sort.into()) } + pub fn set_sort(&self, sort: Sort) + { + unsafe { ffi::notmuch_query_set_sort(self.ptr.0, sort.into()) } } /// Return the sort specified for this query. See /// `set_sort`. - pub fn sort(self: &Self) -> Sort { - unsafe { ffi::notmuch_query_get_sort(self.ptr) }.into() + pub fn sort(&self) -> Sort + { + unsafe { ffi::notmuch_query_get_sort(self.ptr.0) }.into() } /// Filter messages according to the query and return - pub fn search_messages<'q>(self: &'d Self) -> Result> { - ::search_messages(self) + pub fn search_messages(&self) -> Result + { + let mut msgs = ptr::null_mut(); + unsafe { ffi::notmuch_query_search_messages(self.ptr.0, &mut msgs) }.as_result()?; + + Ok(Messages::from_ptr(msgs, self.clone())) } - pub fn count_messages(self: &Self) -> Result { + pub fn count_messages(&self) -> Result + { let mut cnt = 0; - unsafe { ffi::notmuch_query_count_messages(self.ptr, &mut cnt) }.as_result()?; + unsafe { ffi::notmuch_query_count_messages(self.ptr.0, &mut cnt) }.as_result()?; Ok(cnt) } - pub fn search_threads<'q>(self: &'d Self) -> Result> { - as QueryExt>::search_threads(self) + pub fn search_threads(&self) -> Result + { + let mut thrds = ptr::null_mut(); + unsafe { ffi::notmuch_query_search_threads(self.ptr.0, &mut thrds) }.as_result()?; + + Ok(Threads::from_ptr(thrds, self.clone())) } - pub fn count_threads(self: &Self) -> Result { + pub fn count_threads(&self) -> Result + { let mut cnt = 0; - unsafe { ffi::notmuch_query_count_threads(self.ptr, &mut cnt) }.as_result()?; + unsafe { ffi::notmuch_query_count_threads(self.ptr.0, &mut cnt) }.as_result()?; Ok(cnt) } - pub fn add_tag_exclude(self: &Self, tag: &str) -> Result<()> + pub fn add_tag_exclude(&self, tag: &str) -> Result<()> { let tag_str = CString::new(tag).unwrap(); - unsafe { ffi::notmuch_query_add_tag_exclude(self.ptr, tag_str.as_ptr()) }.as_result() - } - - pub fn set_omit_excluded(self: &Self, omit_excluded: Exclude) { - unsafe { ffi::notmuch_query_set_omit_excluded(self.ptr, omit_excluded.into()) } + unsafe { ffi::notmuch_query_add_tag_exclude(self.ptr.0, tag_str.as_ptr()) } + .as_result() } -} -pub trait QueryExt<'d> { - fn search_threads<'q, Q>(query: Q) -> Result> - where - Q: Into>>, + pub fn set_omit_excluded(&self, omit_excluded: Exclude) { - let queryref = query.into(); - - let mut thrds = ptr::null_mut(); - unsafe { ffi::notmuch_query_search_threads(queryref.ptr, &mut thrds) } - .as_result()?; - - Ok(Threads::from_ptr(thrds, ScopedSupercow::phantom(queryref))) - } - - fn search_messages<'q, Q>(query: Q) -> Result>> - where - Q: Into>>, - { - let queryref = query.into(); - - let mut msgs = ptr::null_mut(); - unsafe { ffi::notmuch_query_search_messages(queryref.ptr, &mut msgs) } - .as_result()?; - - Ok(Messages::from_ptr(msgs, ScopedSupercow::phantom(queryref))) + unsafe { ffi::notmuch_query_set_omit_excluded(self.ptr.0, omit_excluded.into()) } } } - -impl<'d> QueryExt<'d> for Query<'d> {} - -unsafe impl<'d> Send for Query<'d> {} -unsafe impl<'d> Sync for Query<'d> {} diff --git a/src/tags.rs b/src/tags.rs index 5f3ec60..88c7760 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -1,85 +1,81 @@ +use std::rc::Rc; use std::cmp::PartialEq; use std::ffi::CStr; use std::iter::Iterator; use std::ops::Drop; +use from_variants::FromVariants; + use ffi; -use utils::ScopedPhantomcow; +use Thread; +use Database; +use Message; +use Messages; -pub trait TagsOwner {} +#[derive(Clone, Debug, FromVariants)] +pub(crate) enum TagsOwner { + Database(Database), + Message(Message), + Messages(Messages), + Thread(Thread), +} #[derive(Debug)] -pub struct Tags<'o, O> where - O: TagsOwner + 'o, -{ - pub(crate) ptr: *mut ffi::notmuch_tags_t, - marker: ScopedPhantomcow<'o, O>, -} +pub(crate) struct TagsPtr(*mut ffi::notmuch_tags_t); -impl<'o, O> Drop for Tags<'o, O> -where - O: TagsOwner + 'o, +impl Drop for TagsPtr { fn drop(&mut self) { - unsafe { ffi::notmuch_tags_destroy(self.ptr) }; + unsafe { ffi::notmuch_tags_destroy(self.0) }; } } -impl<'o, O> PartialEq for Tags<'o, O> -where - O: TagsOwner + 'o, -{ +impl PartialEq for TagsPtr{ fn eq(&self, other: &Self) -> bool { - self.ptr == other.ptr + self.0 == other.0 } } -impl<'o, O> Tags<'o, O> -where - O: TagsOwner + 'o, + +#[derive(Clone, Debug)] +pub struct Tags +{ + ptr: Rc, + owner: TagsOwner, +} + + +impl Tags { - pub(crate) fn from_ptr

(ptr: *mut ffi::notmuch_tags_t, owner: P) -> Tags<'o, O> + pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_tags_t, owner: O) -> Tags where - P: Into>, + O: Into, { Tags { - ptr, - marker: owner.into(), + ptr: Rc::new(TagsPtr(ptr)), + owner: owner.into(), } } } -impl<'o, O> Iterator for Tags<'o, O> -where - O: TagsOwner + 'o, +impl Iterator for Tags { type Item = String; fn next(&mut self) -> Option { - let valid = unsafe { ffi::notmuch_tags_valid(self.ptr) }; + let valid = unsafe { ffi::notmuch_tags_valid(self.ptr.0) }; if valid == 0 { return None; } let ctag = unsafe { - let t = ffi::notmuch_tags_get(self.ptr); - ffi::notmuch_tags_move_to_next(self.ptr); + let t = ffi::notmuch_tags_get(self.ptr.0); + ffi::notmuch_tags_move_to_next(self.ptr.0); CStr::from_ptr(t) }; Some(ctag.to_string_lossy().to_string()) } -} - -pub trait TagsExt<'o, O> -where - O: TagsOwner + 'o, -{ -} - -impl<'o, O> TagsExt<'o, O> for Tags<'o, O> where O: TagsOwner + 'o {} - -unsafe impl<'o, O> Send for Tags<'o, O> where O: TagsOwner + 'o {} -unsafe impl<'o, O> Sync for Tags<'o, O> where O: TagsOwner + 'o {} +} \ No newline at end of file diff --git a/src/thread.rs b/src/thread.rs index ffc1f2f..790f76b 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -1,88 +1,96 @@ -use std::ops::Drop; use std::borrow::Cow; +use std::ops::Drop; +use std::rc::Rc; + +use from_variants::FromVariants; use ffi; -use utils::{ToStr, ScopedSupercow, ScopedPhantomcow}; +use utils::ToStr; +use Query; +use Threads; use Messages; -use MessageOwner; use Tags; -use TagsOwner; -use Query; -#[derive(Debug)] -pub struct Thread<'d, 'q> -where - 'd: 'q -{ - pub(crate) ptr: *mut ffi::notmuch_thread_t, - pub(crate) marker: ScopedPhantomcow<'q, Query<'d>>, +#[derive(Clone, Debug, FromVariants)] +pub(crate) enum ThreadOwner { + Query(Query), + Threads(Threads), } -impl<'d, 'q> Drop for Thread<'d, 'q> -where - 'd: 'q -{ +#[derive(Debug)] +pub(crate) struct ThreadPtr(*mut ffi::notmuch_thread_t); + +impl Drop for ThreadPtr { fn drop(&mut self) { - unsafe { ffi::notmuch_thread_destroy(self.ptr) }; + unsafe { ffi::notmuch_thread_destroy(self.0) }; } } -impl<'d, 'q> MessageOwner for Thread<'d, 'q> where 'd: 'q {} -impl<'d, 'q> TagsOwner for Thread<'d, 'q> where 'd: 'q {} +#[derive(Clone, Debug)] +pub struct Thread { + ptr: Rc, + owner: Box, +} -impl<'d, 'q> Thread<'d, 'q> -where - 'd: 'q -{ - pub(crate) fn from_ptr

(ptr: *mut ffi::notmuch_thread_t, owner: P) -> Thread<'d, 'q> +impl Thread { + pub(crate) fn from_ptr

(ptr: *mut ffi::notmuch_thread_t, owner: P) -> Thread where - P: Into>>, + P: Into, { Thread { - ptr, - marker: owner.into(), + ptr: Rc::new(ThreadPtr(ptr)), + owner: Box::new(owner.into()), } } - pub fn id(self: &Self) -> &str { - let tid = unsafe { ffi::notmuch_thread_get_thread_id(self.ptr) }; + pub fn id(&self) -> &str { + let tid = unsafe { ffi::notmuch_thread_get_thread_id(self.ptr.0) }; tid.to_str().unwrap() } - pub fn total_messages(self: &Self) -> i32 { - unsafe { ffi::notmuch_thread_get_total_messages(self.ptr) } + pub fn total_messages(&self) -> i32 { + unsafe { ffi::notmuch_thread_get_total_messages(self.ptr.0) } } #[cfg(feature = "0.26")] - pub fn total_files(self: &Self) -> i32 { - unsafe { ffi::notmuch_thread_get_total_files(self.ptr) } + pub fn total_files(&self) -> i32 { + unsafe { ffi::notmuch_thread_get_total_files(self.ptr.0) } } - pub fn toplevel_messages(self: &Self) -> Messages<'_, Self> { - >::toplevel_messages(self) + pub fn toplevel_messages(&self) -> Messages { + Messages::from_ptr( + unsafe { ffi::notmuch_thread_get_toplevel_messages(self.ptr.0) }, + self.clone(), + ) } - pub fn matched_messages(self: &Self) -> i32 { - unsafe { ffi::notmuch_thread_get_matched_messages(self.ptr) } + pub fn matched_messages(&self) -> i32 { + unsafe { ffi::notmuch_thread_get_matched_messages(self.ptr.0) } } /// Get a `Messages` iterator for all messages in 'thread' in /// oldest-first order. - pub fn messages(self: &Self) -> Messages<'_, Self> { - >::messages(self) + pub fn messages(&self) -> Messages { + Messages::from_ptr( + unsafe { ffi::notmuch_thread_get_messages(self.ptr.0) }, + self.clone(), + ) } - pub fn tags(&self) -> Tags<'_, Self> { - >::tags(self) + pub fn tags(&self) -> Tags { + Tags::from_ptr( + unsafe { ffi::notmuch_thread_get_tags(self.ptr.0) }, + self.clone(), + ) } - pub fn subject(self: &Self) -> Cow<'_, str> { - let sub = unsafe { ffi::notmuch_thread_get_subject(self.ptr) }; + pub fn subject(&self) -> Cow<'_, str> { + let sub = unsafe { ffi::notmuch_thread_get_subject(self.ptr.0) }; sub.to_string_lossy() } - pub fn authors(self: &Self) -> Vec { - let athrs = unsafe { ffi::notmuch_thread_get_authors(self.ptr) }; + pub fn authors(&self) -> Vec { + let athrs = unsafe { ffi::notmuch_thread_get_authors(self.ptr.0) }; athrs .to_string_lossy() @@ -92,57 +100,12 @@ where } /// Get the date of the oldest message in 'thread' as a time_t value. - pub fn oldest_date(self: &Self) -> i64 { - unsafe { ffi::notmuch_thread_get_oldest_date(self.ptr) as i64 } + pub fn oldest_date(&self) -> i64 { + unsafe { ffi::notmuch_thread_get_oldest_date(self.ptr.0) as i64 } } /// Get the date of the newest message in 'thread' as a time_t value. - pub fn newest_date(self: &Self) -> i64 { - unsafe { ffi::notmuch_thread_get_newest_date(self.ptr) as i64 } - } -} - -pub trait ThreadExt<'d, 'q> -where - 'd: 'q -{ - fn tags<'s, S>(thread: S) -> Tags<'s, Thread<'d, 'q>> - where - S: Into>>, - { - let threadref = thread.into(); - Tags::from_ptr( - unsafe { ffi::notmuch_thread_get_tags(threadref.ptr) }, - ScopedSupercow::phantom(threadref), - ) - } - - fn toplevel_messages<'s, S>(thread: S) -> Messages<'s, Thread<'d, 'q>> - where - S: Into>>, - { - let threadref = thread.into(); - Messages::from_ptr( - unsafe { ffi::notmuch_thread_get_toplevel_messages(threadref.ptr) }, - ScopedSupercow::phantom(threadref), - ) - } - - /// Get a `Messages` iterator for all messages in 'thread' in - /// oldest-first order. - fn messages<'s, S>(thread: S) -> Messages<'s, Thread<'d, 'q>> - where - S: Into>>, - { - let threadref = thread.into(); - Messages::from_ptr( - unsafe { ffi::notmuch_thread_get_messages(threadref.ptr) }, - ScopedSupercow::phantom(threadref), - ) + pub fn newest_date(&self) -> i64 { + unsafe { ffi::notmuch_thread_get_newest_date(self.ptr.0) as i64 } } } - -impl<'d, 'q> ThreadExt<'d, 'q> for Thread<'d, 'q> where 'd: 'q {} - -unsafe impl<'d, 'q> Send for Thread<'d, 'q> where 'd: 'q {} -unsafe impl<'d, 'q> Sync for Thread<'d, 'q> where 'd: 'q {} diff --git a/src/threads.rs b/src/threads.rs index 9adb2c5..2f75ec7 100644 --- a/src/threads.rs +++ b/src/threads.rs @@ -1,76 +1,65 @@ +use std::rc::Rc; use std::ops::Drop; +use std::fmt::Debug; + +use from_variants::FromVariants; use ffi; -use Thread; use Query; -use utils::ScopedPhantomcow; +use Thread; +#[derive(Clone, Debug, FromVariants)] +pub(crate) enum ThreadsOwner { + Query(Query), +} #[derive(Debug)] -pub struct Threads<'d, 'q> -where - 'd: 'q -{ - ptr: *mut ffi::notmuch_threads_t, - marker: ScopedPhantomcow<'q, Query<'d>>, -} +pub struct ThreadsPtr(*mut ffi::notmuch_threads_t); -impl<'d, 'q> Drop for Threads<'d, 'q> -where - 'd: 'q, +impl Drop for ThreadsPtr { fn drop(&mut self) { - unsafe { ffi::notmuch_threads_destroy(self.ptr) }; + unsafe { ffi::notmuch_threads_destroy(self.0) }; } } -impl<'d, 'q> Threads<'d, 'q> -where - 'd: 'q, +#[derive(Clone, Debug)] +pub struct Threads +{ + ptr: Rc, + owner: Box, +} + +impl Threads { - pub(crate) fn from_ptr

(ptr: *mut ffi::notmuch_threads_t, owner: P) -> Threads<'d, 'q> + pub(crate) fn from_ptr(ptr: *mut ffi::notmuch_threads_t, owner: O) -> Threads where - P: Into>>, + O: Into, { Threads { - ptr, - marker: owner.into(), + ptr: Rc::new(ThreadsPtr(ptr)), + owner: Box::new(owner.into()), } } } -impl<'d, 'q> Iterator for Threads<'d, 'q> -where - 'd: 'q, +impl Iterator for Threads { - type Item = Thread<'d, 'q>; + type Item = Thread; fn next(&mut self) -> Option { - let valid = unsafe { ffi::notmuch_threads_valid(self.ptr) }; + let valid = unsafe { ffi::notmuch_threads_valid(self.ptr.0) }; if valid == 0 { return None; } let cthrd = unsafe { - let thrd = ffi::notmuch_threads_get(self.ptr); - ffi::notmuch_threads_move_to_next(self.ptr); + let thrd = ffi::notmuch_threads_get(self.ptr.0); + ffi::notmuch_threads_move_to_next(self.ptr.0); thrd }; - Some(Thread::from_ptr(cthrd, ScopedPhantomcow::<'q, Query<'d>>::share(&mut self.marker))) + Some(Thread::from_ptr(cthrd, self.clone())) } } - - -pub trait ThreadsExt<'d, 'q> -where - 'd: 'q, -{ -} - -impl<'d, 'q> ThreadsExt<'d, 'q> for Threads<'d, 'q> where 'd: 'q {} - - -unsafe impl<'d, 'q> Send for Threads<'d, 'q> where 'd: 'q {} -unsafe impl<'d, 'q> Sync for Threads<'d, 'q> where 'd: 'q {} diff --git a/src/utils.rs b/src/utils.rs index b5b90f0..855f005 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,6 @@ use libc; use std::{ffi, str}; use std::borrow::Cow; -use supercow::{Supercow, DefaultFeatures/*, NonSyncFeatures*/}; -use supercow::ext::{BoxedStorage}; pub trait ToStr { @@ -35,27 +33,4 @@ impl ToString for *const libc::c_char { fn to_string(&self) -> String { unsafe { ffi::CStr::from_ptr(*self).to_string_lossy().into_owned() } } -} - - -#[cfg(not(nonsync))] -pub type ScopedPhantomcow<'a, OWNED, BORROWED = OWNED, - SHARED = Box + 'a>, - STORAGE = BoxedStorage> = - Supercow<'a, OWNED, BORROWED, SHARED, STORAGE, ()>; - -#[cfg(not(nonsync))] -pub type ScopedSupercow<'a, OWNED, BORROWED = OWNED, SHARED = Box + 'a>> = - Supercow<'a, OWNED, BORROWED, SHARED, BoxedStorage>; - - -#[cfg(nonsync)] -pub type ScopedPhantomcow<'a, OWNED, BORROWED = OWNED, - SHARED = Box + 'a>, - STORAGE = BoxedStorage> = - Supercow<'a, OWNED, BORROWED, SHARED, STORAGE, ()>; - -#[cfg(nonsync)] -pub type ScopedSupercow<'a, OWNED, BORROWED = OWNED, SHARED = Box + 'a>> = - Supercow<'a, OWNED, BORROWED, SHARED, BoxedStorage>; - +} \ No newline at end of file diff --git a/tests/test_database.rs b/tests/test_database.rs index 098e271..12f411a 100644 --- a/tests/test_database.rs +++ b/tests/test_database.rs @@ -153,7 +153,7 @@ mod revision { // TODO: add tests for revisions comparisons } - + mod messages { use super::*; @@ -168,7 +168,7 @@ mod messages { assert_eq!(msg.filename(), filename); assert_eq!(msg.id(), msgid); - + } #[test] @@ -183,7 +183,7 @@ mod messages { db.remove_message(&filename).unwrap(); assert!(db.find_message(&msgid).unwrap().is_none()); } - + #[test] fn test_find_message() { let mailbox = MailBox::new(); @@ -191,7 +191,7 @@ mod messages { let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap(); let msg0 = db.index_file(&filename, None).unwrap(); - + let msg1 = db.find_message(&msgid).unwrap().unwrap(); assert_eq!(msg0.id(), msgid); assert_eq!(msg0.id(), msg1.id()); @@ -207,7 +207,7 @@ mod messages { assert!(db.find_message(&"foo").unwrap().is_none()); } - + } mod tags { @@ -241,7 +241,7 @@ mod tags { fn test_iters() { let mailbox = MailBox::new(); let db = notmuch::Database::create(&mailbox.path()).unwrap(); - + let t1: Vec = db.all_tags().unwrap().collect(); let t2: Vec = db.all_tags().unwrap().collect(); assert_eq!(t1, t2); @@ -269,7 +269,7 @@ impl DatabaseFixture { cmd.run(vec!["new"]).unwrap(); let database = notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap(); - + Self { mailbox, database diff --git a/tests/test_message.rs b/tests/test_message.rs index 7af6f2c..0f850e8 100644 --- a/tests/test_message.rs +++ b/tests/test_message.rs @@ -5,9 +5,9 @@ use fixtures::MailBox; struct MessageFixture { // Return a single thread with 2 messages pub mailbox: MailBox, - pub database: Arc, + pub database: notmuch::Database, pub maildir_msg: (String, PathBuf), - pub message: notmuch::Message<'static, notmuch::Database>, + pub message: notmuch::Message, } impl MessageFixture { @@ -16,9 +16,9 @@ impl MessageFixture { let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap(); - let database = Arc::new(notmuch::Database::create(&mailbox.path()).unwrap()); - let message = ::index_file(database.clone(), &filename, None).unwrap(); - + let database = notmuch::Database::create(&mailbox.path()).unwrap(); + let message = database.index_file(&filename, None).unwrap(); + Self { mailbox, database, @@ -35,14 +35,14 @@ mod message { #[test] fn test_messageid() { let msg = MessageFixture::new(); - let copy = ::find_message_by_filename(msg.database.clone(), &msg.message.filename()).unwrap().unwrap(); + let copy = msg.database.find_message_by_filename(&msg.message.filename()).unwrap().unwrap(); assert_eq!(msg.message.id(), copy.id()) } #[test] fn test_messageid_find() { let msg = MessageFixture::new(); - let copy = ::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + let copy = msg.database.find_message(&msg.message.id()).unwrap().unwrap(); assert_eq!(msg.message.id(), copy.id()) } @@ -113,20 +113,20 @@ mod message { fn test_freeze_err() { // not sure if this test is ok? let msg = MessageFixture::new(); - + msg.message.add_tag(&"foo").unwrap(); - + msg.message.freeze().unwrap(); assert!(msg.message.remove_all_tags().is_ok()); - let copy = ::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + let copy = msg.database.find_message(&msg.message.id()).unwrap().unwrap(); assert!(copy.tags().any(|x| x == "foo")); msg.message.thaw().unwrap(); assert!(!msg.message.tags().any(|x| x == "foo")); - let copy2 = ::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + let copy2 = msg.database.find_message(&msg.message.id()).unwrap().unwrap(); assert!(!copy2.tags().any(|x| x == "foo")); } @@ -141,11 +141,11 @@ mod message { assert!(msg.message.remove_all_tags().is_ok()); assert!(!msg.message.tags().any(|x| x == "foo")); - let copy = ::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + let copy = msg.database.find_message(&msg.message.id()).unwrap().unwrap(); assert!(copy.tags().any(|x| x == "foo")); } - let copy2 = ::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + let copy2 = msg.database.find_message(&msg.message.id()).unwrap().unwrap(); assert!(!copy2.tags().any(|x| x == "foo")); assert!(!msg.message.tags().any(|x| x == "foo")); } @@ -164,7 +164,7 @@ mod message { // # UTC or the other way around. // now = int(time.time()) // assert abs(now - msg.date) < 3600*24 - + mod properties { use super::*; diff --git a/tests/test_query.rs b/tests/test_query.rs index ad8f299..8f51883 100644 --- a/tests/test_query.rs +++ b/tests/test_query.rs @@ -1,59 +1,121 @@ +use fixtures::{MailBox, NotmuchCommand}; use std::sync::Arc; -use fixtures::{NotmuchCommand, MailBox}; - struct QueryFixture { - // Return a single thread with 2 messages pub mailbox: MailBox, - pub query: notmuch::Query<'static>, + pub database: notmuch::Database, + pub query: notmuch::Query, } impl QueryFixture { - pub fn new() -> Self{ + pub fn new() -> Self { let mailbox = MailBox::new(); - let (msgid, _) = mailbox.deliver(None, Some("foo".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); - mailbox.deliver(None, Some("bar".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); - mailbox.deliver(None, Some("baz".to_string()), None, None, vec![("In-Reply-To".to_string(), format!("<{}>", msgid))], true, None, false, false, false).unwrap(); - mailbox.deliver(None, Some("foo qux".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); - mailbox.deliver(None, Some("foo quux".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); + let (msgid, _) = mailbox + .deliver( + None, + Some("foo".to_string()), + None, + None, + vec![], + true, + None, + false, + false, + false, + ) + .unwrap(); + mailbox + .deliver( + None, + Some("bar".to_string()), + None, + None, + vec![], + true, + None, + false, + false, + false, + ) + .unwrap(); + mailbox + .deliver( + None, + Some("baz".to_string()), + None, + None, + vec![("In-Reply-To".to_string(), format!("<{}>", msgid))], + true, + None, + false, + false, + false, + ) + .unwrap(); + mailbox + .deliver( + None, + Some("foo qux".to_string()), + None, + None, + vec![], + true, + None, + false, + false, + false, + ) + .unwrap(); + mailbox + .deliver( + None, + Some("foo quux".to_string()), + None, + None, + vec![], + true, + None, + false, + false, + false, + ) + .unwrap(); let cmd = NotmuchCommand::new(&mailbox.path()); cmd.run(vec!["new"]).unwrap(); - let query = { - let database = Arc::new(notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap()); + let database = notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap(); - notmuch::Query::create(database, &"foo".to_string()).unwrap() - }; - - Self { - mailbox, - query - } + let query = notmuch::Query::create(&database, &"foo".to_string()).unwrap(); + Self { mailbox, database, query } } } #[test] -fn test_iter_threads() { - let q = QueryFixture::new(); - - let threads = q.query.search_threads().unwrap(); +fn test_find_message() { + let f = QueryFixture::new(); + let mut messages = f.query.search_messages().unwrap(); - let mut num = 0; - for _thread in threads { - num += 1; - } + let message_from_query = messages.next().unwrap(); - assert_eq!(num, 3); - + let message_from_id = f.database.find_message(&message_from_query.id()).unwrap().unwrap(); + + dbg!("got message"); + message_from_id.tags(); + drop(message_from_query); + + // If notmuch would reuse the message pointer, the following call would produce a segfault. + // Luckily, that is not the case. + + dbg!("got message"); + message_from_id.tags(); } #[test] -fn test_iter_threads_ext() { +fn test_iter_threads() { let q = QueryFixture::new(); - - let threads = ::search_threads(q.query).unwrap(); + let threads = q.query.search_threads().unwrap(); let mut num = 0; for _thread in threads { @@ -61,14 +123,11 @@ fn test_iter_threads_ext() { } assert_eq!(num, 3); - } - #[test] fn test_iter_messages() { let q = QueryFixture::new(); - let messages = q.query.search_messages().unwrap(); let mut num = 0; @@ -77,21 +136,50 @@ fn test_iter_messages() { } assert_eq!(num, 3); - } #[test] -fn test_iter_messages_ext() { +fn test_iter_messages_drop() { let q = QueryFixture::new(); - - let messages = ::search_messages(q.query).unwrap(); - let mut num = 0; - for _message in messages { - num += 1; - } + let mut messages = q.query.search_messages().unwrap(); + let message1 = messages.next().unwrap(); + dbg!("got message"); + message1.tags(); - assert_eq!(num, 3); - + drop(message1); + + let message2 = messages.next().unwrap(); + dbg!("got message"); + message2.tags(); + drop(message2); +} +#[test] +fn test_iter_messages_drop2() { + let q = QueryFixture::new(); + + let message = { + let mut messages = q.query.search_messages().unwrap(); + messages.next().unwrap() + }; + dbg!("got message"); + message.tags(); +} + +#[test] +fn test_iter_threads_drop() { + let q = QueryFixture::new(); + + let mut threads = q.query.search_threads().unwrap(); + let thread1 = threads.next().unwrap(); + + dbg!("got thread"); + drop(thread1); + + let thread2 = threads.next().unwrap(); + drop(threads); + + dbg!("got thread"); + drop(thread2); } diff --git a/tests/test_tags.rs b/tests/test_tags.rs index 3d39486..086ddf2 100644 --- a/tests/test_tags.rs +++ b/tests/test_tags.rs @@ -1,4 +1,3 @@ -use std::sync::Arc; use fixtures::{MailBox, NotmuchCommand}; struct TagSetFixture { @@ -6,26 +5,50 @@ struct TagSetFixture { // This will have the default new mail tags: inbox, unread. pub mailbox: MailBox, pub cmd: NotmuchCommand, - pub database: Arc, - pub message: notmuch::Message<'static, notmuch::Database> + pub database: notmuch::Database, + pub message: notmuch::Message, } impl TagSetFixture { - pub fn new(mutable: bool, flagged: bool) -> Self{ + pub fn new(mutable: bool, flagged: bool) -> Self { let mailbox = MailBox::new(); - let (_msg, filename) = mailbox.deliver(None, None, None, None, vec![], !flagged, None, false, false, flagged).unwrap(); - + let (_msg, filename) = mailbox + .deliver( + None, + None, + None, + None, + vec![], + !flagged, + None, + false, + false, + flagged, + ) + .unwrap(); + let cmd = NotmuchCommand::new(&mailbox.path()); cmd.run(vec!["new"]).unwrap(); - let database = Arc::new(notmuch::Database::open(&mailbox.path(), if !mutable {notmuch::DatabaseMode::ReadOnly} else { notmuch::DatabaseMode::ReadWrite }).unwrap()); - let message = ::find_message_by_filename(database.clone(), &filename).unwrap().unwrap(); + let database = notmuch::Database::open( + &mailbox.path(), + if !mutable { + notmuch::DatabaseMode::ReadOnly + } else { + notmuch::DatabaseMode::ReadWrite + }, + ) + .unwrap(); + let message = database + .find_message_by_filename(&filename) + .unwrap() + .unwrap(); Self { mailbox, database, - cmd, - message + cmd, + message, } } } @@ -35,20 +58,22 @@ mod immutable { use super::*; #[test] - fn test_neg(){ + fn test_neg() { let tagset = TagSetFixture::new(false, false); let tags: Vec = tagset.database.all_tags().unwrap().collect(); tagset.cmd.run(vec!["tag", "+foo", "*"]).unwrap(); - let database = notmuch::Database::open(&tagset.mailbox.path(), notmuch::DatabaseMode::ReadOnly).unwrap(); + let database = + notmuch::Database::open(&tagset.mailbox.path(), notmuch::DatabaseMode::ReadOnly) + .unwrap(); let ntags: Vec = database.all_tags().unwrap().collect(); assert_ne!(tags, ntags); } #[test] - fn test_contains(){ + fn test_contains() { let tagset = TagSetFixture::new(false, false); let tags: Vec = tagset.database.all_tags().unwrap().collect(); @@ -56,13 +81,11 @@ mod immutable { assert!(!tags.iter().any(|x| x == "foo")); } - #[test] - fn test_len(){ + fn test_len() { let tagset = TagSetFixture::new(false, false); assert_eq!(tagset.database.all_tags().unwrap().count(), 2); } - } mod mutable { @@ -70,7 +93,7 @@ mod mutable { use super::*; #[test] - fn test_add(){ + fn test_add() { let tagset = TagSetFixture::new(true, false); assert!(!tagset.message.tags().any(|x| x == "foo")); @@ -79,7 +102,7 @@ mod mutable { } #[test] - fn test_discard(){ + fn test_discard() { let tagset = TagSetFixture::new(true, false); assert!(tagset.message.tags().any(|x| x == "inbox")); @@ -88,7 +111,7 @@ mod mutable { } #[test] - fn test_discard_not_present(){ + fn test_discard_not_present() { let tagset = TagSetFixture::new(true, false); assert!(!tagset.message.tags().any(|x| x == "foo")); @@ -96,7 +119,7 @@ mod mutable { } #[test] - fn test_clear(){ + fn test_clear() { let tagset = TagSetFixture::new(true, false); assert!(tagset.message.tags().count() > 0); tagset.message.remove_all_tags().unwrap(); @@ -105,7 +128,7 @@ mod mutable { } #[test] - fn test_from_maildir_flags(){ + fn test_from_maildir_flags() { let tagset = TagSetFixture::new(true, true); tagset.message.remove_tag(&"flagged").unwrap(); @@ -114,10 +137,8 @@ mod mutable { assert!(tagset.message.tags().any(|x| x == "flagged")); } - #[test] - fn test_to_maildir_flags(){ - + fn test_to_maildir_flags() { let tagset = TagSetFixture::new(true, true); let filename = tagset.message.filename(); @@ -138,5 +159,4 @@ mod mutable { let flags = file_parts.last().unwrap(); assert!(!flags.contains('F')); } - -} \ No newline at end of file +} diff --git a/tests/test_thread.rs b/tests/test_thread.rs index 89f1ea0..5377314 100644 --- a/tests/test_thread.rs +++ b/tests/test_thread.rs @@ -1,36 +1,58 @@ -use std::sync::Arc; -use fixtures::{NotmuchCommand, MailBox}; - +use fixtures::{MailBox, NotmuchCommand}; struct ThreadFixture { // Return a single thread with 2 messages pub mailbox: MailBox, - pub thread: notmuch::Thread<'static, 'static>, + pub thread: notmuch::Thread, } impl ThreadFixture { - pub fn new() -> Self{ + pub fn new() -> Self { let mailbox = MailBox::new(); - let (msgid, _) = mailbox.deliver(None, Some("foo".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); - mailbox.deliver(None, Some("bar".to_string()), None, None, vec![("In-Reply-To".to_string(), format!("<{}>", msgid))], true, None, false, false, false).unwrap(); + let (msgid, _) = mailbox + .deliver( + None, + Some("foo".to_string()), + None, + None, + vec![], + true, + None, + false, + false, + false, + ) + .unwrap(); + mailbox + .deliver( + None, + Some("bar".to_string()), + None, + None, + vec![("In-Reply-To".to_string(), format!("<{}>", msgid))], + true, + None, + false, + false, + false, + ) + .unwrap(); let cmd = NotmuchCommand::new(&mailbox.path()); cmd.run(vec!["new"]).unwrap(); let mut threads = { - let database = Arc::new(notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap()); + let database = + notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap(); - let query = notmuch::Query::create(database.clone(), &"foo".to_string()).unwrap(); + let query = notmuch::Query::create(&database, &"foo".to_string()).unwrap(); - ::search_threads(query).unwrap() + query.search_threads().unwrap() }; let thread = threads.next().unwrap(); - - Self { - mailbox, - thread - } + + Self { mailbox, thread } } } @@ -40,7 +62,6 @@ fn test_threadid() { assert!(!thread.thread.id().is_empty()); } - #[test] fn test_toplevel() { let thread = ThreadFixture::new(); @@ -49,7 +70,6 @@ fn test_toplevel() { assert_eq!(msgs.count(), 1); } - #[test] fn test_toplevel_reply() { let thread = ThreadFixture::new(); @@ -73,7 +93,6 @@ fn test_matched() { assert_eq!(thread.thread.matched_messages(), 1); } - #[test] fn test_authors() { let thread = ThreadFixture::new(); @@ -81,7 +100,6 @@ fn test_authors() { assert_eq!(thread.thread.authors(), vec!["src@example.com".to_string()]); } - #[test] fn test_subject() { let thread = ThreadFixture::new(); @@ -90,8 +108,6 @@ fn test_subject() { assert_eq!(thread.thread.subject(), "Test mail"); } - - #[test] fn test_tags() { let thread = ThreadFixture::new(); @@ -99,4 +115,3 @@ fn test_tags() { let tags: Vec = thread.thread.tags().collect(); assert!(tags.iter().any(|x| x == "inbox")); } - \ No newline at end of file