melib/notmuch: wrap *mut struct fields in NonNull<_>
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>pull/329/head
parent
506ae9f594
commit
ebe1b3da7e
|
@ -19,6 +19,8 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{marker::PhantomData, ptr::NonNull};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
notmuch::ffi::{
|
||||
|
@ -33,10 +35,10 @@ use crate::{
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Message<'m> {
|
||||
pub lib: Arc<libloading::Library>,
|
||||
pub message: *mut ffi::notmuch_message_t,
|
||||
pub lib: Arc<NotmuchLibrary>,
|
||||
pub message: NonNull<ffi::notmuch_message_t>,
|
||||
pub is_from_thread: bool,
|
||||
pub _ph: std::marker::PhantomData<&'m ffi::notmuch_message_t>,
|
||||
pub _ph: PhantomData<&'m ffi::notmuch_message_t>,
|
||||
}
|
||||
|
||||
impl<'m> Message<'m> {
|
||||
|
@ -50,29 +52,30 @@ impl<'m> Message<'m> {
|
|||
std::ptr::addr_of_mut!(message),
|
||||
)
|
||||
};
|
||||
if message.is_null() {
|
||||
return Err(Error::new(format!(
|
||||
"Message with message id {:?} not found in notmuch database.",
|
||||
msg_id
|
||||
)));
|
||||
}
|
||||
Ok(Message {
|
||||
lib,
|
||||
message,
|
||||
message: NonNull::new(message).ok_or_else(|| {
|
||||
Error::new(format!(
|
||||
"Message with message id {:?} not found in notmuch database.",
|
||||
msg_id
|
||||
))
|
||||
})?,
|
||||
is_from_thread: false,
|
||||
_ph: std::marker::PhantomData,
|
||||
_ph: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn env_hash(&self) -> EnvelopeHash {
|
||||
let msg_id = unsafe { call!(self.lib, notmuch_message_get_message_id)(self.message) };
|
||||
let msg_id =
|
||||
unsafe { call!(self.lib, notmuch_message_get_message_id)(self.message.as_ptr()) };
|
||||
let c_str = unsafe { CStr::from_ptr(msg_id) };
|
||||
EnvelopeHash::from_bytes(c_str.to_bytes_with_nul())
|
||||
}
|
||||
|
||||
pub fn header(&self, header: &CStr) -> Option<&[u8]> {
|
||||
let header_val =
|
||||
unsafe { call!(self.lib, notmuch_message_get_header)(self.message, header.as_ptr()) };
|
||||
let header_val = unsafe {
|
||||
call!(self.lib, notmuch_message_get_header)(self.message.as_ptr(), header.as_ptr())
|
||||
};
|
||||
if header_val.is_null() {
|
||||
None
|
||||
} else {
|
||||
|
@ -86,12 +89,13 @@ impl<'m> Message<'m> {
|
|||
}
|
||||
|
||||
pub fn msg_id_cstr(&self) -> &CStr {
|
||||
let msg_id = unsafe { call!(self.lib, notmuch_message_get_message_id)(self.message) };
|
||||
let msg_id =
|
||||
unsafe { call!(self.lib, notmuch_message_get_message_id)(self.message.as_ptr()) };
|
||||
unsafe { CStr::from_ptr(msg_id) }
|
||||
}
|
||||
|
||||
pub fn date(&self) -> crate::UnixTimestamp {
|
||||
(unsafe { call!(self.lib, notmuch_message_get_date)(self.message) }) as u64
|
||||
(unsafe { call!(self.lib, notmuch_message_get_date)(self.message.as_ptr()) }) as u64
|
||||
}
|
||||
|
||||
pub fn into_envelope(
|
||||
|
@ -173,23 +177,22 @@ impl<'m> Message<'m> {
|
|||
|
||||
pub fn replies_iter(&self) -> Option<MessageIterator> {
|
||||
if self.is_from_thread {
|
||||
let messages = unsafe { call!(self.lib, notmuch_message_get_replies)(self.message) };
|
||||
if messages.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(MessageIterator {
|
||||
lib: self.lib.clone(),
|
||||
messages,
|
||||
_ph: std::marker::PhantomData,
|
||||
is_from_thread: true,
|
||||
})
|
||||
}
|
||||
let messages = Some(NonNull::new(unsafe {
|
||||
call!(self.lib, notmuch_message_get_replies)(self.message.as_ptr())
|
||||
})?);
|
||||
Some(MessageIterator {
|
||||
lib: self.lib.clone(),
|
||||
messages,
|
||||
_ph: PhantomData,
|
||||
is_from_thread: true,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_thread_node(&self) -> (ThreadNodeHash, ThreadNode) {
|
||||
let (flags, _) = TagIterator::new(self).collect_flags_and_tags();
|
||||
(
|
||||
ThreadNodeHash::from(self.msg_id()),
|
||||
ThreadNode {
|
||||
|
@ -200,7 +203,7 @@ impl<'m> Message<'m> {
|
|||
date: self.date(),
|
||||
show_subject: true,
|
||||
group: ThreadHash::new(),
|
||||
unseen: false,
|
||||
unseen: !flags.intersects(Flag::SEEN),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -209,7 +212,7 @@ impl<'m> Message<'m> {
|
|||
if let Err(err) = unsafe {
|
||||
try_call!(
|
||||
self.lib,
|
||||
call!(self.lib, notmuch_message_add_tag)(self.message, tag.as_ptr())
|
||||
call!(self.lib, notmuch_message_add_tag)(self.message.as_ptr(), tag.as_ptr())
|
||||
)
|
||||
} {
|
||||
return Err(Error::new("Could not set tag.").set_source(Some(Arc::new(err))));
|
||||
|
@ -221,7 +224,7 @@ impl<'m> Message<'m> {
|
|||
if let Err(err) = unsafe {
|
||||
try_call!(
|
||||
self.lib,
|
||||
call!(self.lib, notmuch_message_remove_tag)(self.message, tag.as_ptr())
|
||||
call!(self.lib, notmuch_message_remove_tag)(self.message.as_ptr(), tag.as_ptr())
|
||||
)
|
||||
} {
|
||||
return Err(Error::new("Could not set tag.").set_source(Some(Arc::new(err))));
|
||||
|
@ -237,7 +240,7 @@ impl<'m> Message<'m> {
|
|||
if let Err(err) = unsafe {
|
||||
try_call!(
|
||||
self.lib,
|
||||
call!(self.lib, notmuch_message_tags_to_maildir_flags)(self.message)
|
||||
call!(self.lib, notmuch_message_tags_to_maildir_flags)(self.message.as_ptr())
|
||||
)
|
||||
} {
|
||||
return Err(Error::new("Could not set flags.").set_source(Some(Arc::new(err))));
|
||||
|
@ -246,7 +249,8 @@ impl<'m> Message<'m> {
|
|||
}
|
||||
|
||||
pub fn get_filename(&self) -> &OsStr {
|
||||
let fs_path = unsafe { call!(self.lib, notmuch_message_get_filename)(self.message) };
|
||||
let fs_path =
|
||||
unsafe { call!(self.lib, notmuch_message_get_filename)(self.message.as_ptr()) };
|
||||
let c_str = unsafe { CStr::from_ptr(fs_path) };
|
||||
OsStr::from_bytes(c_str.to_bytes())
|
||||
}
|
||||
|
@ -254,35 +258,35 @@ impl<'m> Message<'m> {
|
|||
|
||||
impl Drop for Message<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { call!(self.lib, notmuch_message_destroy)(self.message) };
|
||||
unsafe { call!(self.lib, notmuch_message_destroy)(self.message.as_ptr()) };
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MessageIterator<'query> {
|
||||
pub lib: Arc<libloading::Library>,
|
||||
pub messages: *mut ffi::notmuch_messages_t,
|
||||
pub lib: Arc<NotmuchLibrary>,
|
||||
pub messages: Option<NonNull<ffi::notmuch_messages_t>>,
|
||||
pub is_from_thread: bool,
|
||||
pub _ph: std::marker::PhantomData<*const Query<'query>>,
|
||||
pub _ph: PhantomData<*const Query<'query>>,
|
||||
}
|
||||
|
||||
impl<'q> Iterator for MessageIterator<'q> {
|
||||
type Item = Message<'q>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.messages.is_null() {
|
||||
None
|
||||
} else if unsafe { call!(self.lib, notmuch_messages_valid)(self.messages) } == 1 {
|
||||
let message = unsafe { call!(self.lib, notmuch_messages_get)(self.messages) };
|
||||
let messages = self.messages?;
|
||||
if unsafe { call!(self.lib, notmuch_messages_valid)(messages.as_ptr()) } == 1 {
|
||||
let message = unsafe { call!(self.lib, notmuch_messages_get)(messages.as_ptr()) };
|
||||
unsafe {
|
||||
call!(self.lib, notmuch_messages_move_to_next)(self.messages);
|
||||
call!(self.lib, notmuch_messages_move_to_next)(messages.as_ptr());
|
||||
}
|
||||
Some(Message {
|
||||
lib: self.lib.clone(),
|
||||
message,
|
||||
message: NonNull::new(message)?,
|
||||
is_from_thread: self.is_from_thread,
|
||||
_ph: std::marker::PhantomData,
|
||||
_ph: PhantomData,
|
||||
})
|
||||
} else {
|
||||
self.messages = std::ptr::null_mut();
|
||||
self.messages = None;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{hash_map::HashMap, BTreeMap, BTreeSet},
|
||||
ffi::{CStr, CString, OsStr},
|
||||
io::Read,
|
||||
|
@ -50,7 +51,7 @@ macro_rules! call {
|
|||
"{} must be a valid FFI symbol.",
|
||||
stringify!($func)
|
||||
);
|
||||
let func: libloading::Symbol<$func> = $lib.get(stringify!($func).as_bytes()).unwrap();
|
||||
let func: libloading::Symbol<$func> = $lib.inner.get(stringify!($func).as_bytes()).unwrap();
|
||||
func
|
||||
}};
|
||||
}
|
||||
|
@ -101,9 +102,15 @@ impl DbPointer {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NotmuchLibrary {
|
||||
pub inner: libloading::Library,
|
||||
pub dlpath: Cow<'static, str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DbConnection {
|
||||
pub lib: Arc<libloading::Library>,
|
||||
pub lib: Arc<NotmuchLibrary>,
|
||||
inner: Arc<Mutex<DbPointer>>,
|
||||
pub revision_uuid: Arc<RwLock<u64>>,
|
||||
}
|
||||
|
@ -201,10 +208,9 @@ impl DbConnection {
|
|||
);
|
||||
}
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
return false;
|
||||
}
|
||||
true
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
@ -249,7 +255,7 @@ impl Drop for DbConnection {
|
|||
#[derive(Debug)]
|
||||
pub struct NotmuchDb {
|
||||
#[allow(dead_code)]
|
||||
lib: Arc<libloading::Library>,
|
||||
lib: Arc<NotmuchLibrary>,
|
||||
revision_uuid: Arc<RwLock<u64>>,
|
||||
mailboxes: Arc<RwLock<HashMap<MailboxHash, NotmuchMailbox>>>,
|
||||
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
||||
|
@ -270,35 +276,38 @@ impl NotmuchDb {
|
|||
event_consumer: BackendEventConsumer,
|
||||
) -> Result<Box<dyn MailBackend>> {
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut dlpath = "libnotmuch.so.5";
|
||||
let mut dlpath = Cow::Borrowed("libnotmuch.so.5");
|
||||
#[cfg(target_os = "macos")]
|
||||
let mut dlpath = "libnotmuch.5.dylib";
|
||||
let mut dlpath = Cow::Borrowed("libnotmuch.5.dylib");
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
let mut dlpath = "libnotmuch.so";
|
||||
let mut dlpath = Cow::Borrowed("libnotmuch.so");
|
||||
let mut custom_dlpath = false;
|
||||
if let Some(lib_path) = s.extra.get("library_file_path") {
|
||||
dlpath = lib_path.as_str();
|
||||
dlpath = Cow::Owned(lib_path.to_string());
|
||||
custom_dlpath = true;
|
||||
}
|
||||
let lib = Arc::new(unsafe {
|
||||
match libloading::Library::new(dlpath) {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
if custom_dlpath {
|
||||
return Err(Error::new(format!(
|
||||
"Notmuch `library_file_path` setting value `{}` for account {} does \
|
||||
not exist or is a directory or not a valid library file.",
|
||||
dlpath, s.name
|
||||
))
|
||||
.set_kind(ErrorKind::Configuration)
|
||||
.set_source(Some(Arc::new(err))));
|
||||
} else {
|
||||
return Err(Error::new("Could not load libnotmuch!")
|
||||
.set_details(super::NOTMUCH_ERROR_DETAILS)
|
||||
let lib = Arc::new(NotmuchLibrary {
|
||||
inner: unsafe {
|
||||
match libloading::Library::new(dlpath.as_ref()) {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
if custom_dlpath {
|
||||
return Err(Error::new(format!(
|
||||
"Notmuch `library_file_path` setting value `{}` for account {} \
|
||||
does not exist or is a directory or not a valid library file.",
|
||||
dlpath, s.name
|
||||
))
|
||||
.set_kind(ErrorKind::Configuration)
|
||||
.set_source(Some(Arc::new(err))));
|
||||
} else {
|
||||
return Err(Error::new("Could not load libnotmuch!")
|
||||
.set_details(super::NOTMUCH_ERROR_DETAILS)
|
||||
.set_source(Some(Arc::new(err))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
dlpath,
|
||||
});
|
||||
let mut path = Path::new(s.root_mailbox.as_str()).expand();
|
||||
if !path.exists() {
|
||||
|
@ -486,7 +495,7 @@ impl NotmuchDb {
|
|||
fn new_connection(
|
||||
path: &Path,
|
||||
revision_uuid: Arc<RwLock<u64>>,
|
||||
lib: Arc<libloading::Library>,
|
||||
lib: Arc<NotmuchLibrary>,
|
||||
write: bool,
|
||||
) -> Result<DbConnection> {
|
||||
let path_c = CString::new(path.to_str().unwrap()).unwrap();
|
||||
|
@ -1030,7 +1039,7 @@ struct NotmuchOp {
|
|||
database: Arc<DbConnection>,
|
||||
bytes: Option<Vec<u8>>,
|
||||
#[allow(dead_code)]
|
||||
lib: Arc<libloading::Library>,
|
||||
lib: Arc<NotmuchLibrary>,
|
||||
}
|
||||
|
||||
impl BackendOp for NotmuchOp {
|
||||
|
|
|
@ -19,30 +19,31 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{ffi::CString, ptr::NonNull, sync::Arc};
|
||||
use std::{borrow::Cow, ffi::CString, ptr::NonNull, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
error::{Error, ErrorKind, Result},
|
||||
notmuch::{
|
||||
ffi::{
|
||||
self, notmuch_query_count_messages, notmuch_query_create, notmuch_query_destroy,
|
||||
notmuch_query_search_messages, notmuch_status_to_string,
|
||||
notmuch_messages_t, notmuch_query_count_messages, notmuch_query_create,
|
||||
notmuch_query_destroy, notmuch_query_search_messages, notmuch_query_t,
|
||||
notmuch_status_to_string,
|
||||
},
|
||||
DbConnection, MessageIterator,
|
||||
DbConnection, MessageIterator, NotmuchLibrary,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct Query<'s> {
|
||||
pub lib: Arc<libloading::Library>,
|
||||
pub ptr: NonNull<ffi::notmuch_query_t>,
|
||||
pub lib: Arc<NotmuchLibrary>,
|
||||
pub ptr: NonNull<notmuch_query_t>,
|
||||
pub query_str: &'s str,
|
||||
}
|
||||
|
||||
impl<'s> Query<'s> {
|
||||
pub fn new(database: &DbConnection, query_str: &'s str) -> Result<Self> {
|
||||
let lib: Arc<libloading::Library> = database.lib.clone();
|
||||
let lib: Arc<NotmuchLibrary> = database.lib.clone();
|
||||
let query_cstr = CString::new(query_str)?;
|
||||
let query: *mut ffi::notmuch_query_t = unsafe {
|
||||
let query: *mut notmuch_query_t = unsafe {
|
||||
call!(lib, notmuch_query_create)(
|
||||
database.inner.lock().unwrap().as_mut(),
|
||||
query_cstr.as_ptr(),
|
||||
|
@ -72,7 +73,7 @@ impl<'s> Query<'s> {
|
|||
}
|
||||
|
||||
pub fn search(&'s self) -> Result<MessageIterator<'s>> {
|
||||
let mut messages: *mut ffi::notmuch_messages_t = std::ptr::null_mut();
|
||||
let mut messages: *mut notmuch_messages_t = std::ptr::null_mut();
|
||||
let status = unsafe {
|
||||
call!(self.lib, notmuch_query_search_messages)(
|
||||
self.ptr.as_ptr(),
|
||||
|
@ -85,12 +86,25 @@ impl<'s> Query<'s> {
|
|||
self.query_str, status,
|
||||
)));
|
||||
}
|
||||
assert!(!messages.is_null());
|
||||
let messages = Some(NonNull::new(messages).ok_or_else(|| {
|
||||
Error::new(format!(
|
||||
"Search for {} failed because of an internal libnotmuch error.",
|
||||
self.query_str
|
||||
))
|
||||
.set_details(
|
||||
"notmuch_query_search_messages returned status == 0 but the passed `messages` \
|
||||
pointer argument is NULL.",
|
||||
)
|
||||
.set_kind(ErrorKind::LinkedLibrary(match self.lib.dlpath {
|
||||
Cow::Borrowed(v) => v,
|
||||
Cow::Owned(_) => "user configured path",
|
||||
}))
|
||||
})?);
|
||||
Ok(MessageIterator {
|
||||
messages,
|
||||
lib: self.lib.clone(),
|
||||
_ph: std::marker::PhantomData,
|
||||
is_from_thread: false,
|
||||
_ph: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,33 +19,40 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use super::*;
|
||||
use crate::notmuch::ffi::{
|
||||
notmuch_message_get_filename, notmuch_message_get_tags, notmuch_tags_destroy, notmuch_tags_get,
|
||||
notmuch_tags_move_to_next, notmuch_tags_valid,
|
||||
notmuch_tags_move_to_next, notmuch_tags_t, notmuch_tags_valid,
|
||||
};
|
||||
|
||||
pub struct TagIterator<'m> {
|
||||
pub tags: *mut ffi::notmuch_tags_t,
|
||||
pub tags: Option<NonNull<notmuch_tags_t>>,
|
||||
pub message: &'m Message<'m>,
|
||||
}
|
||||
|
||||
impl Drop for TagIterator<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { call!(self.message.lib, notmuch_tags_destroy)(self.tags) };
|
||||
if let Some(tags) = self.tags {
|
||||
unsafe { call!(self.message.lib, notmuch_tags_destroy)(tags.as_ptr()) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'m> TagIterator<'m> {
|
||||
pub fn new(message: &'m Message<'m>) -> TagIterator<'m> {
|
||||
TagIterator {
|
||||
tags: unsafe { call!(message.lib, notmuch_message_get_tags)(message.message) },
|
||||
tags: NonNull::new(unsafe {
|
||||
call!(message.lib, notmuch_message_get_tags)(message.message.as_ptr())
|
||||
}),
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_flags_and_tags(self) -> (Flag, Vec<String>) {
|
||||
fn flags(path: &CStr) -> Flag {
|
||||
fn flags(fs_path: NonNull<std::ffi::c_char>) -> Flag {
|
||||
let path = unsafe { CStr::from_ptr(fs_path.as_ptr()) };
|
||||
let mut flag = Flag::default();
|
||||
let mut ptr = path.to_bytes().len().saturating_sub(1);
|
||||
let mut is_valid = true;
|
||||
|
@ -75,9 +82,12 @@ impl<'m> TagIterator<'m> {
|
|||
|
||||
flag
|
||||
}
|
||||
let fs_path =
|
||||
unsafe { call!(self.message.lib, notmuch_message_get_filename)(self.message.message) };
|
||||
let c_str = unsafe { CStr::from_ptr(fs_path) };
|
||||
let fs_path = unsafe {
|
||||
// SAFETY;
|
||||
// all used pointers here are NonNull<wrapped>, and the cast to *mut _
|
||||
// afterwards is only to wrap the retval into a NonNull as well.
|
||||
call!(self.message.lib, notmuch_message_get_filename)(self.message.message.as_ptr())
|
||||
} as *mut std::ffi::c_char;
|
||||
|
||||
let tags = self.collect::<Vec<&CStr>>();
|
||||
let mut flag = Flag::default();
|
||||
|
@ -108,25 +118,28 @@ impl<'m> TagIterator<'m> {
|
|||
}
|
||||
}
|
||||
|
||||
(flag | flags(c_str), vec)
|
||||
(
|
||||
flag | NonNull::new(fs_path).map(flags).unwrap_or_default(),
|
||||
vec,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'m> Iterator for TagIterator<'m> {
|
||||
type Item = &'m CStr;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.tags.is_null() {
|
||||
None
|
||||
} else if unsafe { call!(self.message.lib, notmuch_tags_valid)(self.tags) } == 1 {
|
||||
let tags = self.tags?;
|
||||
if unsafe { call!(self.message.lib, notmuch_tags_valid)(tags.as_ptr()) } == 1 {
|
||||
let ret = Some(unsafe {
|
||||
CStr::from_ptr(call!(self.message.lib, notmuch_tags_get)(self.tags))
|
||||
CStr::from_ptr(call!(self.message.lib, notmuch_tags_get)(tags.as_ptr()))
|
||||
});
|
||||
unsafe {
|
||||
call!(self.message.lib, notmuch_tags_move_to_next)(self.tags);
|
||||
call!(self.message.lib, notmuch_tags_move_to_next)(tags.as_ptr());
|
||||
}
|
||||
ret
|
||||
} else {
|
||||
self.tags = std::ptr::null_mut();
|
||||
unsafe { call!(self.message.lib, notmuch_tags_destroy)(tags.as_ptr()) };
|
||||
self.tags = None;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,42 +22,63 @@ use super::*;
|
|||
use crate::{
|
||||
notmuch::ffi::{
|
||||
notmuch_thread_destroy, notmuch_thread_get_messages, notmuch_thread_get_newest_date,
|
||||
notmuch_thread_get_thread_id, notmuch_thread_get_total_messages, notmuch_threads_get,
|
||||
notmuch_threads_move_to_next, notmuch_threads_valid,
|
||||
notmuch_thread_get_thread_id, notmuch_thread_get_total_messages, notmuch_thread_t,
|
||||
notmuch_threads_get, notmuch_threads_move_to_next, notmuch_threads_t,
|
||||
notmuch_threads_valid,
|
||||
},
|
||||
thread::ThreadHash,
|
||||
};
|
||||
|
||||
pub struct Thread<'query> {
|
||||
pub lib: Arc<libloading::Library>,
|
||||
pub ptr: *mut ffi::notmuch_thread_t,
|
||||
pub lib: Arc<NotmuchLibrary>,
|
||||
pub inner: NonNull<notmuch_thread_t>,
|
||||
pub _ph: std::marker::PhantomData<*const Query<'query>>,
|
||||
}
|
||||
|
||||
impl<'q> Thread<'q> {
|
||||
#[inline]
|
||||
pub fn id(&self) -> ThreadHash {
|
||||
let thread_id = unsafe { call!(self.lib, notmuch_thread_get_thread_id)(self.ptr) };
|
||||
let thread_id = unsafe {
|
||||
// SAFETY:
|
||||
// All pointers used here are NonNull<_> wrapped.
|
||||
call!(self.lib, notmuch_thread_get_thread_id)(self.inner.as_ptr())
|
||||
};
|
||||
let c_str = unsafe { CStr::from_ptr(thread_id) };
|
||||
ThreadHash::from(c_str.to_bytes())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn date(&self) -> crate::UnixTimestamp {
|
||||
(unsafe { call!(self.lib, notmuch_thread_get_newest_date)(self.ptr) }) as u64
|
||||
(unsafe {
|
||||
// SAFETY:
|
||||
// All pointers used here are NonNull<_> wrapped.
|
||||
call!(self.lib, notmuch_thread_get_newest_date)(self.inner.as_ptr())
|
||||
}) as u64
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
(unsafe { call!(self.lib, notmuch_thread_get_total_messages)(self.ptr) }) as usize
|
||||
(unsafe {
|
||||
// SAFETY:
|
||||
// All pointers used here are NonNull<_> wrapped.
|
||||
call!(self.lib, notmuch_thread_get_total_messages)(self.inner.as_ptr())
|
||||
}) as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn iter(&'q self) -> MessageIterator<'q> {
|
||||
let ptr = unsafe { call!(self.lib, notmuch_thread_get_messages)(self.ptr) };
|
||||
let messages = NonNull::new(unsafe {
|
||||
// SAFETY:
|
||||
// All pointers used here are NonNull<_> wrapped.
|
||||
call!(self.lib, notmuch_thread_get_messages)(self.inner.as_ptr())
|
||||
});
|
||||
MessageIterator {
|
||||
lib: self.lib.clone(),
|
||||
messages: ptr,
|
||||
messages,
|
||||
is_from_thread: true,
|
||||
_ph: std::marker::PhantomData,
|
||||
}
|
||||
|
@ -66,33 +87,54 @@ impl<'q> Thread<'q> {
|
|||
|
||||
impl Drop for Thread<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { call!(self.lib, notmuch_thread_destroy)(self.ptr) }
|
||||
unsafe {
|
||||
// SAFETY:
|
||||
// All pointers used here are NonNull<_> wrapped.
|
||||
call!(self.lib, notmuch_thread_destroy)(self.inner.as_ptr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// notmuch threads iterator.
|
||||
///
|
||||
///
|
||||
/// Quoting the docs:
|
||||
///
|
||||
/// > Note that there's no explicit destructor needed for the
|
||||
/// > notmuch_threads_t object. (For consistency, we do provide a
|
||||
/// > notmuch_threads_destroy function, but there's no good reason
|
||||
/// > to call it if the query is about to be destroyed).
|
||||
///
|
||||
/// So there's no need to implement Drop for this type.
|
||||
pub struct ThreadsIterator<'query> {
|
||||
pub lib: Arc<libloading::Library>,
|
||||
pub threads: *mut ffi::notmuch_threads_t,
|
||||
pub lib: Arc<NotmuchLibrary>,
|
||||
pub inner: Option<NonNull<notmuch_threads_t>>,
|
||||
pub _ph: std::marker::PhantomData<*const Query<'query>>,
|
||||
}
|
||||
|
||||
impl<'q> Iterator for ThreadsIterator<'q> {
|
||||
type Item = Thread<'q>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.threads.is_null() {
|
||||
None
|
||||
} else if unsafe { call!(self.lib, notmuch_threads_valid)(self.threads) } == 1 {
|
||||
let thread = unsafe { call!(self.lib, notmuch_threads_get)(self.threads) };
|
||||
let inner = self.inner?;
|
||||
if unsafe { call!(self.lib, notmuch_threads_valid)(inner.as_ptr()) } == 1 {
|
||||
let Some(thread_inner) = NonNull::new(unsafe {
|
||||
// SAFETY:
|
||||
// All pointers used here are NonNull<_> wrapped.
|
||||
call!(self.lib, notmuch_threads_get)(inner.as_ptr())
|
||||
}) else {
|
||||
self.inner = None;
|
||||
return None;
|
||||
};
|
||||
unsafe {
|
||||
call!(self.lib, notmuch_threads_move_to_next)(self.threads);
|
||||
call!(self.lib, notmuch_threads_move_to_next)(inner.as_ptr());
|
||||
}
|
||||
Some(Thread {
|
||||
lib: self.lib.clone(),
|
||||
ptr: thread,
|
||||
inner: thread_inner,
|
||||
_ph: std::marker::PhantomData,
|
||||
})
|
||||
} else {
|
||||
self.threads = std::ptr::null_mut();
|
||||
self.inner = None;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue