From 6606bd9f160d5d6f6f4acbfc2bed15e989545743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Mon, 28 Feb 2022 12:59:46 +0100 Subject: [PATCH] add id mapper to notmuch backend --- src/backends/notmuch/notmuch_backend.rs | 137 ++++++++++++++--------- src/backends/notmuch/notmuch_envelope.rs | 13 ++- 2 files changed, 92 insertions(+), 58 deletions(-) diff --git a/src/backends/notmuch/notmuch_backend.rs b/src/backends/notmuch/notmuch_backend.rs index dbfd4c2f..50a53b6c 100644 --- a/src/backends/notmuch/notmuch_backend.rs +++ b/src/backends/notmuch/notmuch_backend.rs @@ -3,7 +3,7 @@ use std::{convert::TryInto, fs}; use anyhow::{anyhow, Context, Result}; use crate::{ - backends::{Backend, NotmuchEnvelopes, NotmuchMbox, NotmuchMboxes}, + backends::{Backend, IdMapper, NotmuchEnvelopes, NotmuchMbox, NotmuchMboxes}, config::{AccountConfig, NotmuchBackendConfig}, mbox::Mboxes, msg::{Envelopes, Msg}, @@ -11,6 +11,7 @@ use crate::{ pub struct NotmuchBackend<'a> { account_config: &'a AccountConfig, + notmuch_config: &'a NotmuchBackendConfig, db: notmuch::Database, } @@ -21,6 +22,7 @@ impl<'a> NotmuchBackend<'a> { ) -> Result { Ok(Self { account_config, + notmuch_config, db: notmuch::Database::open( notmuch_config.notmuch_database_dir.clone(), notmuch::DatabaseMode::ReadWrite, @@ -31,6 +33,64 @@ impl<'a> NotmuchBackend<'a> { ))?, }) } + + fn _search_envelopes( + &mut self, + query: &str, + virt_mbox: &str, + page_size: usize, + page: usize, + ) -> Result> { + // Gets envelopes matching the given Notmuch query. + let query_builder = self + .db + .create_query(query) + .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 + ))? + .try_into() + .context(format!( + "cannot parse notmuch envelopes from query {:?}", + query + ))?; + + // Calculates pagination boundaries. + let page_begin = page * page_size; + if page_begin > envelopes.len() { + return Err(anyhow!(format!( + "cannot find notmuch envelopes at page {:?} (out of bounds)", + page_begin + 1, + ))); + } + let page_end = envelopes.len().min(page_begin + page_size); + + // Sorts envelopes by most recent date. + envelopes.sort_by(|a, b| b.date.partial_cmp(&a.date).unwrap()); + + // Applies pagination boundaries. + envelopes.0 = envelopes[page_begin..page_end].to_owned(); + + // Appends id <=> hash entries to the id mapper cache file. + let short_hash_len = { + let mut mapper = IdMapper::new(&self.notmuch_config.notmuch_database_dir)?; + let entries = envelopes + .iter() + .map(|env| (env.hash.to_owned(), env.id.to_owned())) + .collect(); + mapper.append(entries)? + }; + + // Shorten envelopes hash. + envelopes + .iter_mut() + .for_each(|env| env.hash = env.hash[0..short_hash_len].to_owned()); + + Ok(Box::new(envelopes)) + } } impl<'a> Backend<'a> for NotmuchBackend<'a> { @@ -55,81 +115,44 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> { fn get_envelopes( &mut self, - mbox: &str, + virt_mbox: &str, page_size: usize, page: usize, ) -> Result> { let query = self .account_config .mailboxes - .get(mbox) + .get(virt_mbox) .map(|s| s.as_str()) .unwrap_or("all"); - let query_builder = self - .db - .create_query(query) - .context("cannot create notmuch query")?; - let mut envelopes: NotmuchEnvelopes = query_builder - .search_messages() - .context(format!( - "cannot find notmuch envelopes with query {:?}", - query - ))? - .try_into()?; - envelopes.sort_by(|a, b| b.date.partial_cmp(&a.date).unwrap()); - let page_begin = page * page_size; - if page_begin > envelopes.len() { - return Err(anyhow!(format!( - "cannot find notmuch envelopes at page {:?} (out of bounds)", - page_begin + 1, - ))); - } - let page_end = envelopes.len().min(page_begin + page_size); - envelopes.0 = envelopes[page_begin..page_end].to_owned(); - Ok(Box::new(envelopes)) + self._search_envelopes(query, virt_mbox, page_size, page) } - fn find_envelopes( + fn search_envelopes( &mut self, - _mbox: &str, + virt_mbox: &str, query: &str, _sort: &str, page_size: usize, page: usize, ) -> Result> { - let query_builder = self - .db - .create_query(query) - .context("cannot create notmuch query")?; - let mut envelopes: NotmuchEnvelopes = query_builder - .search_messages() - .context(format!( - "cannot find notmuch envelopes with query {:?}", - query - ))? - .try_into()?; - // TODO: use sort from parameters instead - envelopes.sort_by(|a, b| b.date.partial_cmp(&a.date).unwrap()); - let page_begin = page * page_size; - if page_begin > envelopes.len() { - return Err(anyhow!(format!( - "cannot find notmuch envelopes at page {:?} (out of bounds)", - page_begin + 1, - ))); - } - let page_end = envelopes.len().min(page_begin + page_size); - envelopes.0 = envelopes[page_begin..page_end].to_owned(); - Ok(Box::new(envelopes)) + self._search_envelopes(query, virt_mbox, page_size, page) } fn add_msg(&mut self, _mbox: &str, _msg: &[u8], _flags: &str) -> Result> { unimplemented!(); } - fn get_msg(&mut self, _mbox: &str, id: &str) -> Result { + fn get_msg(&mut self, _mbox: &str, short_hash: &str) -> Result { + let id = IdMapper::new(&self.notmuch_config.notmuch_database_dir)? + .find(short_hash) + .context(format!( + "cannot get notmuch message from short hash {:?}", + short_hash + ))?; let msg_filepath = self .db - .find_message(id) + .find_message(&id) .context(format!("cannot find notmuch message {:?}", id))? .ok_or_else(|| anyhow!("cannot find notmuch message {:?}", id))? .filename() @@ -148,10 +171,16 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> { unimplemented!(); } - fn del_msg(&mut self, _mbox: &str, id: &str) -> Result<()> { + fn del_msg(&mut self, _mbox: &str, short_hash: &str) -> Result<()> { + let id = IdMapper::new(&self.notmuch_config.notmuch_database_dir)? + .find(short_hash) + .context(format!( + "cannot get notmuch message from short hash {:?}", + short_hash + ))?; let msg_filepath = self .db - .find_message(id) + .find_message(&id) .context(format!("cannot find notmuch message {:?}", id))? .ok_or_else(|| anyhow!("cannot find notmuch message {:?}", id))? .filename() diff --git a/src/backends/notmuch/notmuch_envelope.rs b/src/backends/notmuch/notmuch_envelope.rs index 559191c6..297535f1 100644 --- a/src/backends/notmuch/notmuch_envelope.rs +++ b/src/backends/notmuch/notmuch_envelope.rs @@ -51,6 +51,9 @@ pub struct NotmuchEnvelope { /// Represents the id of the message. pub id: String, + /// Represents the MD5 hash of the message id. + pub hash: String, + /// Represents the tags of the message. pub flags: Vec, @@ -67,7 +70,7 @@ pub struct NotmuchEnvelope { impl Table for NotmuchEnvelope { fn head() -> Row { Row::new() - .cell(Cell::new("ID").bold().underline().white()) + .cell(Cell::new("HASH").bold().underline().white()) .cell(Cell::new("FLAGS").bold().underline().white()) .cell(Cell::new("SUBJECT").shrinkable().bold().underline().white()) .cell(Cell::new("SENDER").bold().underline().white()) @@ -75,14 +78,14 @@ impl Table for NotmuchEnvelope { } fn row(&self) -> Row { - let id = self.id.to_string(); + let hash = self.hash.to_string(); let unseen = !self.flags.contains(&String::from("unread")); let flags = String::new(); let subject = &self.subject; let sender = &self.sender; let date = &self.date; Row::new() - .cell(Cell::new(id).bold_if(unseen).red()) + .cell(Cell::new(hash).bold_if(unseen).red()) .cell(Cell::new(flags).bold_if(unseen).white()) .cell(Cell::new(subject).shrinkable().bold_if(unseen).green()) .cell(Cell::new(sender).bold_if(unseen).blue()) @@ -117,7 +120,8 @@ impl<'a> TryFrom for NotmuchEnvelope { fn try_from(raw_envelope: RawNotmuchEnvelope) -> Result { info!("begin: try building envelope from notmuch parsed mail"); - let id = raw_envelope.id().trim().to_string(); + let id = raw_envelope.id().to_string(); + let hash = format!("{:x}", md5::compute(&id)); let subject = raw_envelope .header("subject") .context("cannot get header \"Subject\" from notmuch message")? @@ -159,6 +163,7 @@ impl<'a> TryFrom for NotmuchEnvelope { let envelope = Self { id, + hash, flags: raw_envelope.tags().collect(), subject, sender,