mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-22 02:50:19 +00:00
make Backend::get_mboxes return struct instead of trait (#340)
This step was necessary to move logic from CLI to lib. Indeed, the trait returned by get_mboxes needed to implement Table, which is related to the CLI module only.
This commit is contained in:
parent
a0461d84ba
commit
7c01f88006
20 changed files with 193 additions and 575 deletions
|
@ -20,7 +20,7 @@ section = "mail"
|
|||
imap-backend = ["imap", "imap-proto"]
|
||||
maildir-backend = ["maildir", "md5"]
|
||||
notmuch-backend = ["notmuch", "maildir-backend"]
|
||||
default = ["imap-backend", "maildir-backend"]
|
||||
default = ["imap-backend", "maildir-backend", "notmuch-backend"]
|
||||
|
||||
[dependencies]
|
||||
ammonia = "3.1.2"
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
//! custom backend implementations.
|
||||
|
||||
use anyhow::Result;
|
||||
use himalaya_lib::mbox::Mboxes;
|
||||
|
||||
use crate::{
|
||||
mbox::Mboxes,
|
||||
msg::{Envelopes, Msg},
|
||||
};
|
||||
use crate::msg::{Envelopes, Msg};
|
||||
|
||||
pub trait Backend<'a> {
|
||||
fn connect(&mut self) -> Result<()> {
|
||||
|
@ -16,7 +14,7 @@ pub trait Backend<'a> {
|
|||
}
|
||||
|
||||
fn add_mbox(&mut self, mbox: &str) -> Result<()>;
|
||||
fn get_mboxes(&mut self) -> Result<Box<dyn Mboxes>>;
|
||||
fn get_mboxes(&mut self) -> Result<Mboxes>;
|
||||
fn del_mbox(&mut self, mbox: &str) -> Result<()>;
|
||||
fn get_envelopes(
|
||||
&mut self,
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
//! This module contains the definition of the IMAP backend.
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use himalaya_lib::account::{AccountConfig, ImapBackendConfig};
|
||||
use himalaya_lib::{
|
||||
account::{AccountConfig, ImapBackendConfig},
|
||||
mbox::{Mbox, Mboxes},
|
||||
};
|
||||
use imap::types::NameAttribute;
|
||||
use log::{debug, log_enabled, trace, Level};
|
||||
use native_tls::{TlsConnector, TlsStream};
|
||||
use std::{
|
||||
|
@ -14,10 +18,7 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
backends::{
|
||||
imap::msg_sort_criterion::SortCriteria, Backend, ImapEnvelope, ImapEnvelopes, ImapMboxes,
|
||||
},
|
||||
mbox::Mboxes,
|
||||
backends::{imap::msg_sort_criterion::SortCriteria, Backend, ImapEnvelope, ImapEnvelopes},
|
||||
msg::{Envelopes, Msg},
|
||||
output::run_cmd,
|
||||
};
|
||||
|
@ -211,13 +212,38 @@ impl<'a> Backend<'a> for ImapBackend<'a> {
|
|||
.context(format!("cannot create imap mailbox {:?}", mbox))
|
||||
}
|
||||
|
||||
fn get_mboxes(&mut self) -> Result<Box<dyn Mboxes>> {
|
||||
let mboxes: ImapMboxes = self
|
||||
fn get_mboxes(&mut self) -> Result<Mboxes> {
|
||||
trace!(">> get imap mailboxes");
|
||||
|
||||
let imap_mboxes = self
|
||||
.sess()?
|
||||
.list(Some(""), Some("*"))
|
||||
.context("cannot list mailboxes")?
|
||||
.into();
|
||||
Ok(Box::new(mboxes))
|
||||
.context("cannot list mailboxes")?;
|
||||
let mboxes = Mboxes {
|
||||
mboxes: imap_mboxes
|
||||
.iter()
|
||||
.map(|imap_mbox| Mbox {
|
||||
delim: imap_mbox.delimiter().unwrap_or_default().into(),
|
||||
name: imap_mbox.name().into(),
|
||||
desc: imap_mbox
|
||||
.attributes()
|
||||
.iter()
|
||||
.map(|attr| match attr {
|
||||
NameAttribute::Marked => "Marked",
|
||||
NameAttribute::Unmarked => "Unmarked",
|
||||
NameAttribute::NoSelect => "NoSelect",
|
||||
NameAttribute::NoInferiors => "NoInferiors",
|
||||
NameAttribute::Custom(custom) => custom.trim_start_matches('\\'),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
trace!("imap mailboxes: {:?}", mboxes);
|
||||
trace!("<< get imap mailboxes");
|
||||
Ok(mboxes)
|
||||
}
|
||||
|
||||
fn del_mbox(&mut self, mbox: &str) -> Result<()> {
|
||||
|
|
|
@ -1,154 +0,0 @@
|
|||
//! IMAP mailbox module.
|
||||
//!
|
||||
//! This module provides IMAP types and conversion utilities related
|
||||
//! to the mailbox.
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
use std::fmt::{self, Display};
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::mbox::Mboxes;
|
||||
use crate::{
|
||||
output::{PrintTable, PrintTableOpts, WriteColor},
|
||||
ui::{Cell, Row, Table},
|
||||
};
|
||||
|
||||
use super::ImapMboxAttrs;
|
||||
|
||||
/// Represents a list of IMAP mailboxes.
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
pub struct ImapMboxes {
|
||||
#[serde(rename = "response")]
|
||||
pub mboxes: Vec<ImapMbox>,
|
||||
}
|
||||
|
||||
impl Deref for ImapMboxes {
|
||||
type Target = Vec<ImapMbox>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.mboxes
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ImapMboxes {
|
||||
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||
writeln!(writer)?;
|
||||
Table::print(writer, self, opts)?;
|
||||
writeln!(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mboxes for ImapMboxes {
|
||||
//
|
||||
}
|
||||
|
||||
/// Represents the IMAP mailbox.
|
||||
#[derive(Debug, Default, PartialEq, Eq, serde::Serialize)]
|
||||
pub struct ImapMbox {
|
||||
/// Represents the mailbox hierarchie delimiter.
|
||||
pub delim: String,
|
||||
|
||||
/// Represents the mailbox name.
|
||||
pub name: String,
|
||||
|
||||
/// Represents the mailbox attributes.
|
||||
pub attrs: ImapMboxAttrs,
|
||||
}
|
||||
|
||||
impl ImapMbox {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ImapMbox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Table for ImapMbox {
|
||||
fn head() -> Row {
|
||||
Row::new()
|
||||
.cell(Cell::new("DELIM").bold().underline().white())
|
||||
.cell(Cell::new("NAME").bold().underline().white())
|
||||
.cell(
|
||||
Cell::new("ATTRIBUTES")
|
||||
.shrinkable()
|
||||
.bold()
|
||||
.underline()
|
||||
.white(),
|
||||
)
|
||||
}
|
||||
|
||||
fn row(&self) -> Row {
|
||||
Row::new()
|
||||
.cell(Cell::new(&self.delim).white())
|
||||
.cell(Cell::new(&self.name).green())
|
||||
.cell(Cell::new(&self.attrs.to_string()).shrinkable().blue())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::backends::ImapMboxAttr;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_should_create_new_mbox() {
|
||||
assert_eq!(ImapMbox::default(), ImapMbox::new(""));
|
||||
assert_eq!(
|
||||
ImapMbox {
|
||||
name: "INBOX".into(),
|
||||
..ImapMbox::default()
|
||||
},
|
||||
ImapMbox::new("INBOX")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_display_mbox() {
|
||||
let default_mbox = ImapMbox::default();
|
||||
assert_eq!("", default_mbox.to_string());
|
||||
|
||||
let new_mbox = ImapMbox::new("INBOX");
|
||||
assert_eq!("INBOX", new_mbox.to_string());
|
||||
|
||||
let full_mbox = ImapMbox {
|
||||
delim: ".".into(),
|
||||
name: "Sent".into(),
|
||||
attrs: ImapMboxAttrs(vec![ImapMboxAttr::NoSelect]),
|
||||
};
|
||||
assert_eq!("Sent", full_mbox.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a list of raw mailboxes returned by the `imap` crate.
|
||||
pub type RawImapMboxes = imap::types::ZeroCopy<Vec<RawImapMbox>>;
|
||||
|
||||
impl<'a> From<RawImapMboxes> for ImapMboxes {
|
||||
fn from(raw_mboxes: RawImapMboxes) -> Self {
|
||||
Self {
|
||||
mboxes: raw_mboxes.iter().map(ImapMbox::from).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the raw mailbox returned by the `imap` crate.
|
||||
pub type RawImapMbox = imap::types::Name;
|
||||
|
||||
impl<'a> From<&'a RawImapMbox> for ImapMbox {
|
||||
fn from(raw_mbox: &'a RawImapMbox) -> Self {
|
||||
Self {
|
||||
delim: raw_mbox.delimiter().unwrap_or_default().into(),
|
||||
name: raw_mbox.name().into(),
|
||||
attrs: raw_mbox.attributes().into(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
//! IMAP mailbox attribute module.
|
||||
//!
|
||||
//! This module provides IMAP types and conversion utilities related
|
||||
//! to the mailbox attribute.
|
||||
|
||||
/// Represents the raw mailbox attribute returned by the `imap` crate.
|
||||
pub use imap::types::NameAttribute as RawImapMboxAttr;
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
/// Represents the attributes of the mailbox.
|
||||
#[derive(Debug, Default, PartialEq, Eq, serde::Serialize)]
|
||||
pub struct ImapMboxAttrs(pub Vec<ImapMboxAttr>);
|
||||
|
||||
impl Deref for ImapMboxAttrs {
|
||||
type Target = Vec<ImapMboxAttr>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ImapMboxAttrs {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut glue = "";
|
||||
for attr in self.iter() {
|
||||
write!(f, "{}{}", glue, attr)?;
|
||||
glue = ", ";
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
|
||||
pub enum ImapMboxAttr {
|
||||
NoInferiors,
|
||||
NoSelect,
|
||||
Marked,
|
||||
Unmarked,
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// Makes the attribute displayable.
|
||||
impl Display for ImapMboxAttr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ImapMboxAttr::NoInferiors => write!(f, "NoInferiors"),
|
||||
ImapMboxAttr::NoSelect => write!(f, "NoSelect"),
|
||||
ImapMboxAttr::Marked => write!(f, "Marked"),
|
||||
ImapMboxAttr::Unmarked => write!(f, "Unmarked"),
|
||||
ImapMboxAttr::Custom(custom) => write!(f, "{}", custom),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_should_display_attrs() {
|
||||
macro_rules! attrs_from {
|
||||
($($attr:expr),*) => {
|
||||
ImapMboxAttrs(vec![$($attr,)*]).to_string()
|
||||
};
|
||||
}
|
||||
|
||||
let empty_attr = attrs_from![];
|
||||
let single_attr = attrs_from![ImapMboxAttr::NoInferiors];
|
||||
let multiple_attrs = attrs_from![
|
||||
ImapMboxAttr::Custom("AttrCustom".into()),
|
||||
ImapMboxAttr::NoInferiors
|
||||
];
|
||||
|
||||
assert_eq!("", empty_attr);
|
||||
assert_eq!("NoInferiors", single_attr);
|
||||
assert!(multiple_attrs.contains("NoInferiors"));
|
||||
assert!(multiple_attrs.contains("AttrCustom"));
|
||||
assert!(multiple_attrs.contains(","));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_display_attr() {
|
||||
macro_rules! attr_from {
|
||||
($attr:ident) => {
|
||||
ImapMboxAttr::$attr.to_string()
|
||||
};
|
||||
($custom:literal) => {
|
||||
ImapMboxAttr::Custom($custom.into()).to_string()
|
||||
};
|
||||
}
|
||||
|
||||
assert_eq!("NoInferiors", attr_from![NoInferiors]);
|
||||
assert_eq!("NoSelect", attr_from![NoSelect]);
|
||||
assert_eq!("Marked", attr_from![Marked]);
|
||||
assert_eq!("Unmarked", attr_from![Unmarked]);
|
||||
assert_eq!("CustomAttr", attr_from!["CustomAttr"]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [RawImapMboxAttr<'a>]> for ImapMboxAttrs {
|
||||
fn from(raw_attrs: &'a [RawImapMboxAttr<'a>]) -> Self {
|
||||
Self(raw_attrs.iter().map(ImapMboxAttr::from).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a RawImapMboxAttr<'a>> for ImapMboxAttr {
|
||||
fn from(attr: &'a RawImapMboxAttr<'a>) -> Self {
|
||||
match attr {
|
||||
RawImapMboxAttr::NoInferiors => Self::NoInferiors,
|
||||
RawImapMboxAttr::NoSelect => Self::NoSelect,
|
||||
RawImapMboxAttr::Marked => Self::Marked,
|
||||
RawImapMboxAttr::Unmarked => Self::Unmarked,
|
||||
RawImapMboxAttr::Custom(cow) => Self::Custom(cow.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,13 +4,15 @@
|
|||
//! traits implementation.
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use himalaya_lib::account::{AccountConfig, MaildirBackendConfig};
|
||||
use himalaya_lib::{
|
||||
account::{AccountConfig, MaildirBackendConfig},
|
||||
mbox::{Mbox, Mboxes},
|
||||
};
|
||||
use log::{debug, info, trace};
|
||||
use std::{convert::TryInto, env, fs, path::PathBuf};
|
||||
use std::{convert::TryInto, env, ffi::OsStr, fs, path::PathBuf};
|
||||
|
||||
use crate::{
|
||||
backends::{Backend, IdMapper, MaildirEnvelopes, MaildirFlags, MaildirMboxes},
|
||||
mbox::Mboxes,
|
||||
backends::{Backend, IdMapper, MaildirEnvelopes, MaildirFlags},
|
||||
msg::{Envelopes, Msg},
|
||||
};
|
||||
|
||||
|
@ -85,17 +87,39 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_mboxes(&mut self) -> Result<Box<dyn Mboxes>> {
|
||||
info!(">> get maildir dirs");
|
||||
fn get_mboxes(&mut self) -> Result<Mboxes> {
|
||||
trace!(">> get maildir mailboxes");
|
||||
|
||||
let dirs: MaildirMboxes =
|
||||
self.mdir.list_subdirs().try_into().with_context(|| {
|
||||
format!("cannot parse maildir dirs from {:?}", self.mdir.path())
|
||||
})?;
|
||||
trace!("dirs: {:?}", dirs);
|
||||
let mut mboxes = Mboxes::default();
|
||||
for (name, desc) in &self.account_config.mailboxes {
|
||||
mboxes.push(Mbox {
|
||||
delim: String::from("/"),
|
||||
name: name.into(),
|
||||
desc: desc.into(),
|
||||
})
|
||||
}
|
||||
for entry in self.mdir.list_subdirs() {
|
||||
let dir = entry?;
|
||||
let dirname = dir.path().file_name();
|
||||
mboxes.push(Mbox {
|
||||
delim: String::from("/"),
|
||||
name: dirname
|
||||
.and_then(OsStr::to_str)
|
||||
.and_then(|s| if s.len() < 2 { None } else { Some(&s[1..]) })
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"cannot parse maildir subdirectory name from path {:?}",
|
||||
dirname
|
||||
)
|
||||
})?
|
||||
.into(),
|
||||
..Mbox::default()
|
||||
});
|
||||
}
|
||||
|
||||
info!("<< get maildir dirs");
|
||||
Ok(Box::new(dirs))
|
||||
trace!("maildir mailboxes: {:?}", mboxes);
|
||||
trace!("<< get maildir mailboxes");
|
||||
Ok(mboxes)
|
||||
}
|
||||
|
||||
fn del_mbox(&mut self, dir: &str) -> Result<()> {
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
//! Maildir mailbox module.
|
||||
//!
|
||||
//! This module provides Maildir types and conversion utilities
|
||||
//! related to the mailbox
|
||||
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
ffi::OsStr,
|
||||
fmt::{self, Display},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
mbox::Mboxes,
|
||||
output::{PrintTable, PrintTableOpts, WriteColor},
|
||||
ui::{Cell, Row, Table},
|
||||
};
|
||||
|
||||
/// Represents a list of Maildir mailboxes.
|
||||
#[derive(Debug, Default, serde::Serialize)]
|
||||
pub struct MaildirMboxes {
|
||||
#[serde(rename = "response")]
|
||||
pub mboxes: Vec<MaildirMbox>,
|
||||
}
|
||||
|
||||
impl Deref for MaildirMboxes {
|
||||
type Target = Vec<MaildirMbox>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.mboxes
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for MaildirMboxes {
|
||||
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||
writeln!(writer)?;
|
||||
Table::print(writer, self, opts)?;
|
||||
writeln!(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mboxes for MaildirMboxes {
|
||||
//
|
||||
}
|
||||
|
||||
/// Represents the mailbox.
|
||||
#[derive(Debug, Default, PartialEq, Eq, serde::Serialize)]
|
||||
pub struct MaildirMbox {
|
||||
/// Represents the mailbox name.
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl MaildirMbox {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self { name: name.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MaildirMbox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Table for MaildirMbox {
|
||||
fn head() -> Row {
|
||||
Row::new().cell(Cell::new("SUBDIR").bold().underline().white())
|
||||
}
|
||||
|
||||
fn row(&self) -> Row {
|
||||
Row::new().cell(Cell::new(&self.name).green())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_should_create_new_mbox() {
|
||||
assert_eq!(MaildirMbox::default(), MaildirMbox::new(""));
|
||||
assert_eq!(
|
||||
MaildirMbox {
|
||||
name: "INBOX".into(),
|
||||
..MaildirMbox::default()
|
||||
},
|
||||
MaildirMbox::new("INBOX")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_display_mbox() {
|
||||
let default_mbox = MaildirMbox::default();
|
||||
assert_eq!("", default_mbox.to_string());
|
||||
|
||||
let new_mbox = MaildirMbox::new("INBOX");
|
||||
assert_eq!("INBOX", new_mbox.to_string());
|
||||
|
||||
let full_mbox = MaildirMbox {
|
||||
name: "Sent".into(),
|
||||
};
|
||||
assert_eq!("Sent", full_mbox.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a list of raw mailboxes returned by the `maildir` crate.
|
||||
pub type RawMaildirMboxes = maildir::MaildirEntries;
|
||||
|
||||
impl TryFrom<RawMaildirMboxes> for MaildirMboxes {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(mail_entries: RawMaildirMboxes) -> Result<Self, Self::Error> {
|
||||
let mut mboxes = vec![];
|
||||
for entry in mail_entries {
|
||||
mboxes.push(entry?.try_into()?);
|
||||
}
|
||||
Ok(MaildirMboxes { mboxes })
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the raw mailbox returned by the `maildir` crate.
|
||||
pub type RawMaildirMbox = maildir::Maildir;
|
||||
|
||||
impl TryFrom<RawMaildirMbox> for MaildirMbox {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(mail_entry: RawMaildirMbox) -> Result<Self, Self::Error> {
|
||||
let subdir_name = mail_entry.path().file_name();
|
||||
Ok(Self {
|
||||
name: subdir_name
|
||||
.and_then(OsStr::to_str)
|
||||
.and_then(|s| if s.len() < 2 { None } else { Some(&s[1..]) })
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"cannot parse maildir subdirectory name from path {:?}",
|
||||
subdir_name,
|
||||
)
|
||||
})?
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
use std::{convert::TryInto, fs};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use himalaya_lib::account::{AccountConfig, NotmuchBackendConfig};
|
||||
use himalaya_lib::{
|
||||
account::{AccountConfig, NotmuchBackendConfig},
|
||||
mbox::{Mbox, Mboxes},
|
||||
};
|
||||
use log::{debug, info, trace};
|
||||
|
||||
use crate::{
|
||||
backends::{Backend, IdMapper, MaildirBackend, NotmuchEnvelopes, NotmuchMbox, NotmuchMboxes},
|
||||
mbox::Mboxes,
|
||||
backends::{Backend, IdMapper, MaildirBackend, NotmuchEnvelopes},
|
||||
msg::{Envelopes, Msg},
|
||||
};
|
||||
|
||||
|
@ -115,20 +117,22 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> {
|
|||
))
|
||||
}
|
||||
|
||||
fn get_mboxes(&mut self) -> Result<Box<dyn Mboxes>> {
|
||||
info!(">> get notmuch virtual mailboxes");
|
||||
fn get_mboxes(&mut self) -> Result<Mboxes> {
|
||||
trace!(">> get notmuch virtual mailboxes");
|
||||
|
||||
let mut mboxes: Vec<_> = self
|
||||
.account_config
|
||||
.mailboxes
|
||||
.iter()
|
||||
.map(|(k, v)| NotmuchMbox::new(k, v))
|
||||
.collect();
|
||||
trace!("virtual mailboxes: {:?}", mboxes);
|
||||
let mut mboxes = Mboxes::default();
|
||||
for (name, desc) in &self.account_config.mailboxes {
|
||||
mboxes.push(Mbox {
|
||||
name: name.into(),
|
||||
desc: desc.into(),
|
||||
..Mbox::default()
|
||||
})
|
||||
}
|
||||
mboxes.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap());
|
||||
|
||||
info!("<< get notmuch virtual mailboxes");
|
||||
Ok(Box::new(NotmuchMboxes { mboxes }))
|
||||
trace!("notmuch virtual mailboxes: {:?}", mboxes);
|
||||
trace!("<< get notmuch virtual mailboxes");
|
||||
Ok(mboxes)
|
||||
}
|
||||
|
||||
fn del_mbox(&mut self, _mbox: &str) -> Result<()> {
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
//! Notmuch mailbox module.
|
||||
//!
|
||||
//! This module provides Notmuch types and conversion utilities
|
||||
//! related to the mailbox
|
||||
|
||||
use anyhow::Result;
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
mbox::Mboxes,
|
||||
output::{PrintTable, PrintTableOpts, WriteColor},
|
||||
ui::{Cell, Row, Table},
|
||||
};
|
||||
|
||||
/// Represents a list of Notmuch mailboxes.
|
||||
#[derive(Debug, Default, serde::Serialize)]
|
||||
pub struct NotmuchMboxes {
|
||||
#[serde(rename = "response")]
|
||||
pub mboxes: Vec<NotmuchMbox>,
|
||||
}
|
||||
|
||||
impl Deref for NotmuchMboxes {
|
||||
type Target = Vec<NotmuchMbox>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.mboxes
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for NotmuchMboxes {
|
||||
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||
writeln!(writer)?;
|
||||
Table::print(writer, self, opts)?;
|
||||
writeln!(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mboxes for NotmuchMboxes {
|
||||
//
|
||||
}
|
||||
|
||||
/// Represents the notmuch virtual mailbox.
|
||||
#[derive(Debug, Default, PartialEq, Eq, serde::Serialize)]
|
||||
pub struct NotmuchMbox {
|
||||
/// Represents the virtual mailbox name.
|
||||
pub name: String,
|
||||
|
||||
/// Represents the query associated to the virtual mailbox name.
|
||||
pub query: String,
|
||||
}
|
||||
|
||||
impl NotmuchMbox {
|
||||
pub fn new(name: &str, query: &str) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
query: query.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NotmuchMbox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Table for NotmuchMbox {
|
||||
fn head() -> Row {
|
||||
Row::new()
|
||||
.cell(Cell::new("NAME").bold().underline().white())
|
||||
.cell(Cell::new("QUERY").bold().underline().white())
|
||||
}
|
||||
|
||||
fn row(&self) -> Row {
|
||||
Row::new()
|
||||
.cell(Cell::new(&self.name).white())
|
||||
.cell(Cell::new(&self.query).green())
|
||||
}
|
||||
}
|
|
@ -2,6 +2,9 @@ pub mod mbox {
|
|||
pub mod mbox;
|
||||
pub use mbox::*;
|
||||
|
||||
pub mod mboxes;
|
||||
pub use mboxes::*;
|
||||
|
||||
pub mod mbox_args;
|
||||
pub mod mbox_handlers;
|
||||
}
|
||||
|
@ -49,12 +52,6 @@ pub mod backends {
|
|||
|
||||
pub mod imap_handlers;
|
||||
|
||||
pub mod imap_mbox;
|
||||
pub use imap_mbox::*;
|
||||
|
||||
pub mod imap_mbox_attr;
|
||||
pub use imap_mbox_attr::*;
|
||||
|
||||
pub mod imap_envelope;
|
||||
pub use imap_envelope::*;
|
||||
|
||||
|
@ -72,9 +69,6 @@ pub mod backends {
|
|||
pub mod maildir_backend;
|
||||
pub use maildir_backend::*;
|
||||
|
||||
pub mod maildir_mbox;
|
||||
pub use maildir_mbox::*;
|
||||
|
||||
pub mod maildir_envelope;
|
||||
pub use maildir_envelope::*;
|
||||
|
||||
|
@ -90,9 +84,6 @@ pub mod backends {
|
|||
pub mod notmuch_backend;
|
||||
pub use notmuch_backend::*;
|
||||
|
||||
pub mod notmuch_mbox;
|
||||
pub use notmuch_mbox::*;
|
||||
|
||||
pub mod notmuch_envelope;
|
||||
pub use notmuch_envelope::*;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
use std::fmt;
|
||||
use himalaya_lib::mbox::Mbox;
|
||||
|
||||
use crate::output::PrintTable;
|
||||
use crate::ui::{Cell, Row, Table};
|
||||
|
||||
pub trait Mboxes: fmt::Debug + erased_serde::Serialize + PrintTable {
|
||||
//
|
||||
impl Table for Mbox {
|
||||
fn head() -> Row {
|
||||
Row::new()
|
||||
.cell(Cell::new("DELIM").bold().underline().white())
|
||||
.cell(Cell::new("NAME").bold().underline().white())
|
||||
.cell(Cell::new("DESC").bold().underline().white())
|
||||
}
|
||||
|
||||
fn row(&self) -> Row {
|
||||
Row::new()
|
||||
.cell(Cell::new(&self.delim).white())
|
||||
.cell(Cell::new(&self.name).blue())
|
||||
.cell(Cell::new(&self.desc).green())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ pub fn list<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
|||
let mboxes = backend.get_mboxes()?;
|
||||
trace!("mailboxes: {:?}", mboxes);
|
||||
printer.print_table(
|
||||
mboxes,
|
||||
// TODO: remove Box
|
||||
Box::new(mboxes),
|
||||
PrintTableOpts {
|
||||
format: &config.format,
|
||||
max_width,
|
||||
|
@ -32,12 +33,11 @@ pub fn list<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use himalaya_lib::mbox::{Mbox, Mboxes};
|
||||
use std::{fmt::Debug, io};
|
||||
use termcolor::ColorSpec;
|
||||
|
||||
use crate::{
|
||||
backends::{ImapMbox, ImapMboxAttr, ImapMboxAttrs, ImapMboxes},
|
||||
mbox::Mboxes,
|
||||
msg::{Envelopes, Msg},
|
||||
output::{Print, PrintTable, WriteColor},
|
||||
};
|
||||
|
@ -114,24 +114,19 @@ mod tests {
|
|||
fn add_mbox(&mut self, _: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn get_mboxes(&mut self) -> Result<Box<dyn Mboxes>> {
|
||||
Ok(Box::new(ImapMboxes {
|
||||
fn get_mboxes(&mut self) -> Result<Mboxes> {
|
||||
Ok(Mboxes {
|
||||
mboxes: vec![
|
||||
ImapMbox {
|
||||
Mbox {
|
||||
delim: "/".into(),
|
||||
name: "INBOX".into(),
|
||||
attrs: ImapMboxAttrs(vec![ImapMboxAttr::NoSelect]),
|
||||
},
|
||||
ImapMbox {
|
||||
Mbox {
|
||||
delim: "/".into(),
|
||||
name: "Sent".into(),
|
||||
attrs: ImapMboxAttrs(vec![
|
||||
ImapMboxAttr::NoInferiors,
|
||||
ImapMboxAttr::Custom("HasNoChildren".into()),
|
||||
]),
|
||||
},
|
||||
],
|
||||
}))
|
||||
})
|
||||
}
|
||||
fn del_mbox(&mut self, _: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
|
|
16
cli/src/mbox/mboxes.rs
Normal file
16
cli/src/mbox/mboxes.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use anyhow::Result;
|
||||
use himalaya_lib::mbox::Mboxes;
|
||||
|
||||
use crate::{
|
||||
output::{PrintTable, PrintTableOpts, WriteColor},
|
||||
ui::Table,
|
||||
};
|
||||
|
||||
impl PrintTable for Mboxes {
|
||||
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||
writeln!(writer)?;
|
||||
Table::print(writer, self, opts)?;
|
||||
writeln!(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -57,6 +57,7 @@
|
|||
|
||||
# nix develop
|
||||
devShell = pkgs.mkShell {
|
||||
RUSTUP_TOOLCHAIN = "stable";
|
||||
inputsFrom = builtins.attrValues self.packages.${system};
|
||||
nativeBuildInputs = with pkgs; [
|
||||
# Nix LSP + formatter
|
||||
|
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
imap-backend = ["imap", "imap-proto"]
|
||||
maildir-backend = ["maildir", "md5"]
|
||||
notmuch-backend = ["notmuch", "maildir-backend"]
|
||||
default = ["imap-backend", "maildir-backend"]
|
||||
default = ["imap-backend", "maildir-backend", "notmuch-backend"]
|
||||
|
||||
[dependencies]
|
||||
lettre = { version = "0.10.0-rc.1", features = ["serde"] }
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
pub mod account;
|
||||
mod process;
|
||||
|
||||
pub mod account;
|
||||
pub mod mbox;
|
||||
|
|
19
lib/src/mbox/mbox.rs
Normal file
19
lib/src/mbox/mbox.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
/// Represents the mailbox.
|
||||
#[derive(Debug, Default, PartialEq, Eq, Serialize)]
|
||||
pub struct Mbox {
|
||||
/// Represents the mailbox hierarchie delimiter.
|
||||
pub delim: String,
|
||||
/// Represents the mailbox name.
|
||||
pub name: String,
|
||||
/// Represents the mailbox description.
|
||||
pub desc: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for Mbox {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
25
lib/src/mbox/mboxes.rs
Normal file
25
lib/src/mbox/mboxes.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use serde::Serialize;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::Mbox;
|
||||
|
||||
/// Represents the list of mailboxes.
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
pub struct Mboxes {
|
||||
#[serde(rename = "response")]
|
||||
pub mboxes: Vec<Mbox>,
|
||||
}
|
||||
|
||||
impl Deref for Mboxes {
|
||||
type Target = Vec<Mbox>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.mboxes
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Mboxes {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.mboxes
|
||||
}
|
||||
}
|
5
lib/src/mbox/mod.rs
Normal file
5
lib/src/mbox/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod mbox;
|
||||
pub use mbox::*;
|
||||
|
||||
mod mboxes;
|
||||
pub use mboxes::*;
|
|
@ -1,2 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "1.58.1"
|
||||
channel = "stable"
|
||||
|
|
Loading…
Reference in a new issue