add id mapper to notmuch backend

This commit is contained in:
Clément DOUIN 2022-02-28 12:59:46 +01:00
parent ad1f97faed
commit 6606bd9f16
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
2 changed files with 92 additions and 58 deletions

View file

@ -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<Self> {
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<Box<dyn Envelopes>> {
// 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<Box<dyn Envelopes>> {
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<Box<dyn Envelopes>> {
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<Box<dyn ToString>> {
unimplemented!();
}
fn get_msg(&mut self, _mbox: &str, id: &str) -> Result<Msg> {
fn get_msg(&mut self, _mbox: &str, short_hash: &str) -> Result<Msg> {
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()

View file

@ -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<String>,
@ -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<RawNotmuchEnvelope> for NotmuchEnvelope {
fn try_from(raw_envelope: RawNotmuchEnvelope) -> Result<Self, Self::Error> {
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<RawNotmuchEnvelope> for NotmuchEnvelope {
let envelope = Self {
id,
hash,
flags: raw_envelope.tags().collect(),
subject,
sender,