diff --git a/src/backends/maildir/maildir_backend.rs b/src/backends/maildir/maildir_backend.rs index e0e9153e..b9c842ab 100644 --- a/src/backends/maildir/maildir_backend.rs +++ b/src/backends/maildir/maildir_backend.rs @@ -141,10 +141,10 @@ impl<'a> Backend<'a> for MaildirBackend<'a> { let page_begin = page * page_size; debug!("page begin: {:?}", page_begin); if page_begin > envelopes.len() { - return Err(anyhow!(format!( + return Err(anyhow!( "cannot get maildir envelopes at page {:?} (out of bounds)", page_begin + 1, - ))); + )); } let page_end = envelopes.len().min(page_begin + page_size); debug!("page end: {:?}", page_end); diff --git a/src/backends/notmuch/notmuch_backend.rs b/src/backends/notmuch/notmuch_backend.rs index 50a53b6c..8db629b2 100644 --- a/src/backends/notmuch/notmuch_backend.rs +++ b/src/backends/notmuch/notmuch_backend.rs @@ -1,6 +1,7 @@ use std::{convert::TryInto, fs}; use anyhow::{anyhow, Context, Result}; +use log::{debug, info, trace}; use crate::{ backends::{Backend, IdMapper, NotmuchEnvelopes, NotmuchMbox, NotmuchMboxes}, @@ -9,6 +10,8 @@ use crate::{ msg::{Envelopes, Msg}, }; +/// Represents the Notmuch backend. +#[derive(Debug)] pub struct NotmuchBackend<'a> { account_config: &'a AccountConfig, notmuch_config: &'a NotmuchBackendConfig, @@ -20,24 +23,31 @@ impl<'a> NotmuchBackend<'a> { account_config: &'a AccountConfig, notmuch_config: &'a NotmuchBackendConfig, ) -> Result { - Ok(Self { + info!(">> create new notmuch backend"); + + let backend = Self { account_config, notmuch_config, db: notmuch::Database::open( notmuch_config.notmuch_database_dir.clone(), notmuch::DatabaseMode::ReadWrite, ) - .context(format!( - "cannot open notmuch database at {:?}", - notmuch_config.notmuch_database_dir - ))?, - }) + .with_context(|| { + format!( + "cannot open notmuch database at {:?}", + notmuch_config.notmuch_database_dir + ) + })?, + }; + trace!("backend: {:?}", backend); + + info!("<< create new notmuch backend"); + Ok(backend) } fn _search_envelopes( &mut self, query: &str, - virt_mbox: &str, page_size: usize, page: usize, ) -> Result> { @@ -45,28 +55,26 @@ impl<'a> NotmuchBackend<'a> { let query_builder = self .db .create_query(query) - .context(format!("cannot create notmuch query from {:?}", query))?; + .with_context(|| format!("cannot create notmuch query from {:?}", query))?; let mut envelopes: NotmuchEnvelopes = query_builder .search_messages() - .context(format!( - "cannot find notmuch envelopes from query {:?}", - query - ))? + .with_context(|| format!("cannot find notmuch envelopes from query {:?}", query))? .try_into() - .context(format!( - "cannot parse notmuch envelopes from query {:?}", - query - ))?; + .with_context(|| format!("cannot parse notmuch envelopes from query {:?}", query))?; + debug!("envelopes len: {:?}", envelopes.len()); + trace!("envelopes: {:?}", envelopes); // Calculates pagination boundaries. let page_begin = page * page_size; + debug!("page begin: {:?}", page_begin); if page_begin > envelopes.len() { return Err(anyhow!(format!( - "cannot find notmuch envelopes at page {:?} (out of bounds)", + "cannot get notmuch envelopes at page {:?} (out of bounds)", page_begin + 1, ))); } let page_end = envelopes.len().min(page_begin + page_size); + debug!("page end: {:?}", page_end); // Sorts envelopes by most recent date. envelopes.sort_by(|a, b| b.date.partial_cmp(&a.date).unwrap()); @@ -74,7 +82,10 @@ impl<'a> NotmuchBackend<'a> { // Applies pagination boundaries. envelopes.0 = envelopes[page_begin..page_end].to_owned(); - // Appends id <=> hash entries to the id mapper cache file. + // Appends envelopes hash to the id mapper cache file and + // calculates the new short hash length. The short hash length + // represents the minimum hash length possible to avoid + // conflicts. let short_hash_len = { let mut mapper = IdMapper::new(&self.notmuch_config.notmuch_database_dir)?; let entries = envelopes @@ -83,6 +94,7 @@ impl<'a> NotmuchBackend<'a> { .collect(); mapper.append(entries)? }; + debug!("short hash length: {:?}", short_hash_len); // Shorten envelopes hash. envelopes @@ -95,22 +107,35 @@ impl<'a> NotmuchBackend<'a> { impl<'a> Backend<'a> for NotmuchBackend<'a> { fn add_mbox(&mut self, _mbox: &str) -> Result<()> { - unimplemented!(); + info!(">> add notmuch mailbox"); + info!("<< add notmuch mailbox"); + Err(anyhow!( + "cannot add notmuch mailbox: feature not implemented" + )) } fn get_mboxes(&mut self) -> Result> { - let mut mboxes: Vec<_> = self + info!(">> get notmuch virtual mailboxes"); + + let mut virt_mboxes: Vec<_> = self .account_config .mailboxes .iter() .map(|(k, v)| NotmuchMbox::new(k, v)) .collect(); - mboxes.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap()); - Ok(Box::new(NotmuchMboxes(mboxes))) + trace!("virtual mailboxes: {:?}", virt_mboxes); + virt_mboxes.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap()); + + info!("<< get notmuch virtual mailboxes"); + Ok(Box::new(NotmuchMboxes(virt_mboxes))) } fn del_mbox(&mut self, _mbox: &str) -> Result<()> { - unimplemented!(); + info!(">> delete notmuch mailbox"); + info!("<< delete notmuch mailbox"); + Err(anyhow!( + "cannot delete notmuch mailbox: feature not implemented" + )) } fn get_envelopes( @@ -119,13 +144,22 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> { page_size: usize, page: usize, ) -> Result> { + info!(">> get notmuch envelopes"); + debug!("virtual mailbox: {:?}", virt_mbox); + debug!("page size: {:?}", page_size); + debug!("page: {:?}", page); + let query = self .account_config .mailboxes .get(virt_mbox) .map(|s| s.as_str()) .unwrap_or("all"); - self._search_envelopes(query, virt_mbox, page_size, page) + debug!("query: {:?}", query); + let envelopes = self._search_envelopes(query, page_size, page)?; + + info!("<< get notmuch envelopes"); + Ok(envelopes) } fn search_envelopes( @@ -136,69 +170,133 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> { page_size: usize, page: usize, ) -> Result> { - self._search_envelopes(query, virt_mbox, page_size, page) + info!(">> search notmuch envelopes"); + debug!("virtual mailbox: {:?}", virt_mbox); + debug!("query: {:?}", query); + debug!("page size: {:?}", page_size); + debug!("page: {:?}", page); + + let query = if query.is_empty() { + self.account_config + .mailboxes + .get(virt_mbox) + .map(|s| s.as_str()) + .unwrap_or("all") + } else { + query + }; + debug!("final query: {:?}", query); + let envelopes = self._search_envelopes(query, page_size, page)?; + + info!("<< search notmuch envelopes"); + Ok(envelopes) } - fn add_msg(&mut self, _mbox: &str, _msg: &[u8], _flags: &str) -> Result> { - unimplemented!(); + fn add_msg( + &mut self, + _virt_mbox: &str, + _msg: &[u8], + _flags: &str, + ) -> Result> { + info!(">> add notmuch envelopes"); + info!("<< add notmuch envelopes"); + Err(anyhow!( + "cannot add notmuch envelopes: feature not implemented" + )) } - fn get_msg(&mut self, _mbox: &str, short_hash: &str) -> Result { - let id = IdMapper::new(&self.notmuch_config.notmuch_database_dir)? + fn get_msg(&mut self, _virt_mbox: &str, short_hash: &str) -> Result { + info!(">> add notmuch envelopes"); + debug!("short hash: {:?}", short_hash); + + let dir = &self.notmuch_config.notmuch_database_dir; + let id = IdMapper::new(dir) + .with_context(|| format!("cannot create id mapper instance for {:?}", dir))? .find(short_hash) - .context(format!( - "cannot get notmuch message from short hash {:?}", - short_hash - ))?; - let msg_filepath = self + .with_context(|| { + format!( + "cannot find notmuch message from short hash {:?}", + short_hash + ) + })?; + debug!("id: {:?}", id); + let msg_file_path = self .db .find_message(&id) - .context(format!("cannot find notmuch message {:?}", id))? + .with_context(|| format!("cannot find notmuch message {:?}", id))? .ok_or_else(|| anyhow!("cannot find notmuch message {:?}", id))? .filename() .to_owned(); - let raw_msg = fs::read(&msg_filepath) - .context(format!("cannot read message from file {:?}", msg_filepath))?; - let msg = Msg::from_parsed_mail(mailparse::parse_mail(&raw_msg)?, &self.account_config)?; + debug!("message file path: {:?}", msg_file_path); + let raw_msg = fs::read(&msg_file_path).with_context(|| { + format!("cannot read notmuch message from file {:?}", msg_file_path) + })?; + let msg = mailparse::parse_mail(&raw_msg) + .with_context(|| format!("cannot parse raw notmuch message {:?}", id))?; + let msg = Msg::from_parsed_mail(msg, &self.account_config) + .with_context(|| format!("cannot parse notmuch message {:?}", id))?; + trace!("message: {:?}", msg); + + info!("<< get notmuch message"); Ok(msg) } fn copy_msg(&mut self, _mbox_src: &str, _mbox_dst: &str, _id: &str) -> Result<()> { - unimplemented!(); + info!(">> copy notmuch message"); + info!("<< copy notmuch message"); + Err(anyhow!( + "cannot copy notmuch message: feature not implemented" + )) } - fn move_msg(&mut self, _mbox_src: &str, _mbox_dst: &str, _id: &str) -> Result<()> { - unimplemented!(); + fn move_msg(&mut self, _src: &str, _dst: &str, _id: &str) -> Result<()> { + info!(">> move notmuch message"); + info!("<< move notmuch message"); + Err(anyhow!( + "cannot move notmuch message: feature not implemented" + )) } - fn del_msg(&mut self, _mbox: &str, short_hash: &str) -> Result<()> { - let id = IdMapper::new(&self.notmuch_config.notmuch_database_dir)? + fn del_msg(&mut self, _virt_mbox: &str, short_hash: &str) -> Result<()> { + info!(">> delete notmuch message"); + debug!("short hash: {:?}", short_hash); + + let dir = &self.notmuch_config.notmuch_database_dir; + let id = IdMapper::new(dir) + .with_context(|| format!("cannot create id mapper instance for {:?}", dir))? .find(short_hash) - .context(format!( - "cannot get notmuch message from short hash {:?}", - short_hash - ))?; - let msg_filepath = self + .with_context(|| { + format!( + "cannot find notmuch message from short hash {:?}", + short_hash + ) + })?; + debug!("id: {:?}", id); + let msg_file_path = self .db .find_message(&id) - .context(format!("cannot find notmuch message {:?}", id))? + .with_context(|| format!("cannot find notmuch message {:?}", id))? .ok_or_else(|| anyhow!("cannot find notmuch message {:?}", id))? .filename() .to_owned(); + debug!("message file path: {:?}", msg_file_path); self.db - .remove_message(msg_filepath) - .context(format!("cannot delete notmuch message {:?}", id)) + .remove_message(msg_file_path) + .with_context(|| format!("cannot delete notmuch message {:?}", id))?; + + info!("<< delete notmuch message"); + Ok(()) } - fn add_flags(&mut self, _mbox: &str, _id: &str, _flags_str: &str) -> Result<()> { + fn add_flags(&mut self, _virt_mbox: &str, _id: &str, _flags: &str) -> Result<()> { unimplemented!(); } - fn set_flags(&mut self, _mbox: &str, _id: &str, _flags_str: &str) -> Result<()> { + fn set_flags(&mut self, _virt_mbox: &str, _id: &str, _flags: &str) -> Result<()> { unimplemented!(); } - fn del_flags(&mut self, _mbox: &str, _id: &str, _flags_str: &str) -> Result<()> { + fn del_flags(&mut self, _virt_mbox: &str, _id: &str, _flags: &str) -> Result<()> { unimplemented!(); } }