forked from meli/meli
1
Fork 0

melib/notmuch: add support for virtual mailbox hierarchy

Add optional "parent" property to notmuch mailbox configuration.

Closes #167

https://git.meli.delivery/meli/meli/issues/167
sieve
Manos Pitsidianakis 2022-12-04 16:07:32 +02:00
parent 4f45b10974
commit 7606317f24
2 changed files with 79 additions and 6 deletions

View File

@ -187,21 +187,38 @@ Its format is described below in
notmuch is supported by loading the dynamic library libnotmuch.
If its location is missing from your library paths, you must add it yourself.
Alternatively, you can specify its path by using a setting.
.Bl -tag -width 36n
.It Ic root_mailbox
points to the directory which contains the
.Pa .notmuch/
subdirectory.
.Pp
notmuch mailboxes are virtual, since they are defined by user-given notmuch queries.
You must explicitly state the mailboxes you want in the
.Ic mailboxes
field and set the
.Ar query
property to each of them.
To create a tree hierarchy with virtual mailboxes, define the
.Ar parent
property to a mailbox as the name of the parent mailbox you have used in your configuration.
.Pp
Account properties:
.Bl -tag -width 36n
.It Ic root_mailbox
points to the directory which contains the
.Pa .notmuch/
subdirectory.
.It Ic library_file_path Ar Path
Use an arbitrary location of libnotmuch by specifying its full filesystem path.
.Pq Em optional
.El
Mailbox properties:
.Bl -tag -width 36n
.It Ic query Ar String
The notmuch query that defines what content this virtual mailbox has.
Refer to
.Xr notmuch-search-terms 7
for notmuch's search syntax.
.It Ic parent Ar String
If you wish to build a mailbox hierarchy, define the name of a parent mailbox you have used in your configuration.
.Pq Em optional
.El
Example:
.Bd -literal
[accounts.notmuch]
@ -212,6 +229,8 @@ format = "notmuch"
"INBOX" = { query="tag:inbox", subscribe = true }
"Drafts" = { query="tag:draft", subscribe = true }
"Sent" = { query="from:username@example.com from:username2@example.com", subscribe = true }
"Archives" = { query="tag:archived", subscribe = true }
"Archives/2019" = { query="tag:archived date:01-2019..12-2019", parent="Archives", subscribe = true }
.Ed
.Ss IMAP only
IMAP specific options are:

View File

@ -356,10 +356,14 @@ impl NotmuchDb {
}
path.pop();
let mut mailboxes = HashMap::default();
let mut mailboxes = HashMap::with_capacity(s.mailboxes.len());
let mut parents: Vec<(MailboxHash, &str)> = Vec::with_capacity(s.mailboxes.len());
for (k, f) in s.mailboxes.iter() {
if let Some(query_str) = f.extra.get("query") {
let hash = MailboxHash::from_bytes(k.as_bytes());
if let Some(parent) = f.extra.get("parent") {
parents.push((hash, parent));
}
mailboxes.insert(
hash,
NotmuchMailbox {
@ -383,6 +387,27 @@ impl NotmuchDb {
.set_kind(ErrorKind::Configuration));
}
}
for (hash, parent) in parents {
if let Some(&parent_hash) = mailboxes
.iter()
.find(|(_, v)| v.name == parent)
.map(|(k, _)| k)
{
mailboxes
.entry(parent_hash)
.or_default()
.children
.push(hash);
mailboxes.entry(hash).or_default().parent = Some(parent_hash);
} else {
return Err(Error::new(format!(
"Mailbox configuration for `{}` defines its parent mailbox as `{}` but no mailbox exists with this exact name.",
mailboxes[&hash].name(),
parent
))
.set_kind(ErrorKind::Configuration));
}
}
let account_hash = AccountHash::from_bytes(s.name().as_bytes());
Ok(Box::new(NotmuchDb {
@ -439,6 +464,7 @@ impl NotmuchDb {
)).set_kind(ErrorKind::Configuration));
}
}
let mut parents: Vec<(String, String)> = Vec::with_capacity(s.mailboxes.len());
for (k, f) in s.mailboxes.iter_mut() {
if f.extra.remove("query").is_none() {
return Err(Error::new(format!(
@ -448,6 +474,34 @@ impl NotmuchDb {
))
.set_kind(ErrorKind::Configuration));
}
if let Some(parent) = f.extra.remove("parent") {
parents.push((k.clone(), parent));
}
}
let mut path = Vec::with_capacity(8);
for (mbox, parent) in parents.iter() {
if !s.mailboxes.contains_key(parent) {
return Err(Error::new(format!(
"Mailbox configuration for `{}` defines its parent mailbox as `{}` but no mailbox exists with this exact name.",
mbox,
parent
))
.set_kind(ErrorKind::Configuration));
}
path.clear();
path.push(mbox.as_str());
let mut iter = parent.as_str();
while let Some((k, v)) = parents.iter().find(|(k, _v)| k == iter) {
if k == mbox {
return Err(Error::new(format!(
"Found cycle in mailbox hierarchy: {}",
path.join("->")
))
.set_kind(ErrorKind::Configuration));
}
path.push(k.as_str());
iter = v.as_str();
}
}
Ok(())
}