diff --git a/README.md b/README.md index f1122f9..39fe437 100644 --- a/README.md +++ b/README.md @@ -31,13 +31,20 @@ extern crate notmuch; ```rust extern crate notmuch; - fn main() { - let mut mail_path = std::env::home_dir().unwrap(); mail_path.push(".mail"); - let db = notmuch::Database::open(&mail_path, notmuch::DatabaseMode::ReadOnly).unwrap(); + let mut config_path = std::env::home_dir().unwrap(); + config_path.push(".config/custom-notmuch-config-path"); + + let db = notmuch::Database::open_with_config( + &mail_path, + notmuch::DatabaseMode::ReadOnly, + &config_path, + None, + ) + .unwrap(); let query = db.create_query("").unwrap(); let mut threads = query.search_threads().unwrap(); @@ -45,7 +52,6 @@ fn main() { println!("thread {:?} {:?}", thread.subject(), thread.authors()); } } - ``` ## Concurrency diff --git a/src/database.rs b/src/database.rs index d287c1f..4ee0296 100644 --- a/src/database.rs +++ b/src/database.rs @@ -72,6 +72,7 @@ impl Database { }) } + #[deprecated = "Replaced with `open_with_config`"] pub fn open

(path: P, mode: DatabaseMode) -> Result where P: AsRef, @@ -87,6 +88,55 @@ impl Database { }) } + pub fn open_with_config( + database_path: Option, + mode: DatabaseMode, + config_path: Option, + profile: Option<&str>, + ) -> Result + where + DP: AsRef, + CP: AsRef, + { + let database_path_str = + database_path.map(|p| CString::new(p.as_ref().to_str().unwrap()).unwrap()); + let database_path_ptr = database_path_str + .as_ref() + .map(|p| p.as_ptr()) + .unwrap_or_else(|| ptr::null()); + + let config_path_str = + config_path.map(|p| CString::new(p.as_ref().to_str().unwrap()).unwrap()); + let config_path_ptr = config_path_str + .as_ref() + .map(|p| p.as_ptr()) + .unwrap_or_else(|| ptr::null()); + + let profile_str = profile.map(|p| CString::new(p).unwrap()); + let profile_ptr = profile_str + .as_ref() + .map(|p| p.as_ptr()) + .unwrap_or_else(|| ptr::null()); + + let mut db = ptr::null_mut(); + let mut error_message = ptr::null_mut(); + unsafe { + ffi::notmuch_database_open_with_config( + database_path_ptr, + mode.into(), + config_path_ptr, + profile_ptr, + &mut db, + &mut error_message, + ) + } + .as_verbose_result(error_message)?; + + Ok(Database { + ptr: Rc::new(DatabasePtr(db)), + }) + } + pub fn close(&self) -> Result<()> { unsafe { ffi::notmuch_database_close(self.ptr.0) }.as_result()?; @@ -360,7 +410,9 @@ pub struct AtomicOperation { impl AtomicOperation { pub fn new(database: &Database) -> Result { database.begin_atomic()?; - Ok(AtomicOperation { database: database.clone() }) + Ok(AtomicOperation { + database: database.clone(), + }) } } diff --git a/src/error.rs b/src/error.rs index 74045b1..dc9746b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ pub type Result = result::Result; pub enum Error { IoError(io::Error), NotmuchError(ffi::Status), + NotmuchVerboseError(ffi::Status, String), UnspecifiedError, } @@ -17,6 +18,7 @@ impl fmt::Display for Error { match self { Error::IoError(e) => e.fmt(f), Error::NotmuchError(e) => e.fmt(f), + Error::NotmuchVerboseError(e, msg) => write!(f, "{} {}", e, msg), Error::UnspecifiedError => write!(f, "Generic notmuch error"), } } @@ -27,6 +29,7 @@ impl error::Error for Error { match &self { Error::IoError(e) => Some(e), Error::NotmuchError(e) => Some(e), + Error::NotmuchVerboseError(e, _) => Some(e), Error::UnspecifiedError => None, } } diff --git a/src/ffi.rs b/src/ffi.rs index ab462b2..a5afac4 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -5,8 +5,9 @@ use libc::{c_char, c_double, c_int, c_uint, c_ulong, c_void, time_t}; use error::{Error, Result}; -use std::{error, fmt, str}; use std::borrow::Cow; +use std::ffi::CStr; +use std::{error, fmt, ptr, str}; use utils::ToStr; notmuch_enum! { @@ -60,6 +61,30 @@ impl notmuch_status_t { Err(Error::NotmuchError(Status::from(self))) } } + + pub fn as_verbose_result(self, error_message_cstr: *mut c_char) -> Result<()> { + let error_message = if error_message_cstr == ptr::null_mut() { + None + } else { + unsafe { + let msg = Some( + CStr::from_ptr(error_message_cstr) + .to_string_lossy() + .to_string(), + ); + libc::free(error_message_cstr as *mut c_void); + msg + } + }; + if self.is_ok() { + Ok(()) + } else { + Err(Error::NotmuchVerboseError( + Status::from(self), + error_message.unwrap(), + )) + } + } } impl ToStr for Status { @@ -272,6 +297,108 @@ extern "C" { error_message: *mut *mut c_char, ) -> notmuch_status_t; + /// Open an existing notmuch database located at `database_path`, using + /// configuration in `config_path`. + /// + /// * `database_path`: Path to existing database. + /// + /// A notmuch database is a Xapian database containing appropriate + /// metadata. + /// + /// The database should have been created at some time in the past, (not + /// necessarily by this process), by calling `notmuch_database_create`. + /// + /// If `database_path` is `NULL`, use the location specified + /// + /// * in the environment variable `NOTMUCH_DATABASE`, if non-empty + /// + /// * in a configuration file, located as described under 'config_path' + /// + /// * by `$XDG_DATA_HOME`/notmuch/`$PROFILE` where `XDG_DATA_HOME` + /// defaults to "$HOME/.local/share" and `PROFILE` as as discussed in + /// 'profile' + /// + /// If `database_path` is non-`NULL`, but does not appear to be a Xapian + /// database, check for a directory '.notmuch/xapian' below + /// `database_path` (this is the behavior of + /// `notmuch_database_open_verbose` pre-0.32). + /// + /// * `mode`: Mode to open database. Use one of + /// `notmuch_database_mode_t::READ_WRITE` or + /// `notmuch_database_mode_t::READ_ONLY`. + /// + /// * `config_path`: Path to config file. + /// + /// Config file is key-value, with mandatory sections. See + /// `notmuch-config(5)` for more information. The key-value pair + /// overrides the corresponding configuration data stored in the + /// database (see `notmuch_database_get_config`). + /// + /// If `config_path` is `NULL` use the path specified + /// + /// * in environment variable `NOTMUCH_CONFIG`, if non-empty + /// + /// * by `XDG_CONFIG_HOME`/notmuch/ where `XDG_CONFIG_HOME` defaults to + /// "`$HOME`/.config". + /// + /// * by `$HOME`/.notmuch-config + /// + /// If `config_path` is `""` (empty string) then do not open any + /// configuration file. + /// + /// * `profile`: Name of profile (configuration/database variant). + /// + /// If non-`NULL`, append to the directory / file path determined for + /// `config_path` and `database_path`. + /// + /// If `NULL` then use + /// + /// * environment variable `NOTMUCH_PROFILE` if defined, + /// + /// * otherwise `"default"` for directories and `""` (empty string) for + /// paths. + /// + /// * `database`: Pointer to database object. May not be `NULL`. + /// + /// The caller should call `notmuch_database_destroy` when finished with + /// this database. + /// + /// In case of any failure, this function returns an error status and + /// sets `*database` to `NULL`. + /// + /// * `error_message`: If non-`NULL`, store error message from opening the + /// database. + /// + /// Any such message is allocated by `malloc(3)` and should be freed by + /// the caller. + /// + /// Return Value: + /// + /// * `notmuch_status_t::SUCCESS`: Successfully opened the database. + /// + /// * `notmuch_status_t::NULL_POINTER`: The given `database` argument is + /// `NULL`. + /// + /// * `notmuch_status_t::NO_CONFIG`: No config file was found. Fatal. + /// + /// * `notmuch_status_t::OUT_OF_MEMORY`: Out of memory. + /// + /// * `notmuch_status_t::FILE_ERROR`: An error occurred trying to open the + /// database or config file (such as permission denied, or file not + /// found, etc.), or the database version is unknown. + /// + /// * `notmuch_status_t::XAPIAN_EXCEPTION`: A Xapian exception occurred. + /// + /// Since libnotmuch 5.4 (notmuch 0.32) + pub fn notmuch_database_open_with_config( + database_path: *const c_char, + mode: notmuch_database_mode_t, + config_path: *const c_char, + profile: *const c_char, + database: *mut *mut notmuch_database_t, + error_message: *mut *mut c_char, + ) -> notmuch_status_t; + /// Retrieve last status string for given database. pub fn notmuch_database_status_string(notmuch: *mut notmuch_database_t) -> *const c_char;