From 7c01f88006effd22905ddfe2d635ac4f436d1eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Sun, 29 May 2022 12:36:10 +0200 Subject: [PATCH] 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. --- cli/Cargo.toml | 2 +- cli/src/backends/backend.rs | 8 +- cli/src/backends/imap/imap_backend.rs | 46 ++++-- cli/src/backends/imap/imap_mbox.rs | 154 -------------------- cli/src/backends/imap/imap_mbox_attr.rs | 119 --------------- cli/src/backends/maildir/maildir_backend.rs | 50 +++++-- cli/src/backends/maildir/maildir_mbox.rs | 144 ------------------ cli/src/backends/notmuch/notmuch_backend.rs | 32 ++-- cli/src/backends/notmuch/notmuch_mbox.rs | 83 ----------- cli/src/lib.rs | 15 +- cli/src/mbox/mbox.rs | 20 ++- cli/src/mbox/mbox_handlers.rs | 21 +-- cli/src/mbox/mboxes.rs | 16 ++ flake.nix | 1 + lib/Cargo.toml | 2 +- lib/src/lib.rs | 4 +- lib/src/mbox/mbox.rs | 19 +++ lib/src/mbox/mboxes.rs | 25 ++++ lib/src/mbox/mod.rs | 5 + rust-toolchain.toml | 2 +- 20 files changed, 193 insertions(+), 575 deletions(-) delete mode 100644 cli/src/backends/imap/imap_mbox.rs delete mode 100644 cli/src/backends/imap/imap_mbox_attr.rs delete mode 100644 cli/src/backends/maildir/maildir_mbox.rs delete mode 100644 cli/src/backends/notmuch/notmuch_mbox.rs create mode 100644 cli/src/mbox/mboxes.rs create mode 100644 lib/src/mbox/mbox.rs create mode 100644 lib/src/mbox/mboxes.rs create mode 100644 lib/src/mbox/mod.rs diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 50fea80..f974cc9 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -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" diff --git a/cli/src/backends/backend.rs b/cli/src/backends/backend.rs index d00ad1f..d6cb7b4 100644 --- a/cli/src/backends/backend.rs +++ b/cli/src/backends/backend.rs @@ -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>; + fn get_mboxes(&mut self) -> Result; fn del_mbox(&mut self, mbox: &str) -> Result<()>; fn get_envelopes( &mut self, diff --git a/cli/src/backends/imap/imap_backend.rs b/cli/src/backends/imap/imap_backend.rs index 9f9bc5c..f54d066 100644 --- a/cli/src/backends/imap/imap_backend.rs +++ b/cli/src/backends/imap/imap_backend.rs @@ -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> { - let mboxes: ImapMboxes = self + fn get_mboxes(&mut self) -> Result { + 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::>() + .join(", "), + }) + .collect(), + }; + + trace!("imap mailboxes: {:?}", mboxes); + trace!("<< get imap mailboxes"); + Ok(mboxes) } fn del_mbox(&mut self, mbox: &str) -> Result<()> { diff --git a/cli/src/backends/imap/imap_mbox.rs b/cli/src/backends/imap/imap_mbox.rs deleted file mode 100644 index 223ab6c..0000000 --- a/cli/src/backends/imap/imap_mbox.rs +++ /dev/null @@ -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, -} - -impl Deref for ImapMboxes { - type Target = Vec; - - 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>; - -impl<'a> From 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(), - } - } -} diff --git a/cli/src/backends/imap/imap_mbox_attr.rs b/cli/src/backends/imap/imap_mbox_attr.rs deleted file mode 100644 index 208d067..0000000 --- a/cli/src/backends/imap/imap_mbox_attr.rs +++ /dev/null @@ -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); - -impl Deref for ImapMboxAttrs { - type Target = Vec; - - 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()), - } - } -} diff --git a/cli/src/backends/maildir/maildir_backend.rs b/cli/src/backends/maildir/maildir_backend.rs index a83423d..1f6b61f 100644 --- a/cli/src/backends/maildir/maildir_backend.rs +++ b/cli/src/backends/maildir/maildir_backend.rs @@ -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> { - info!(">> get maildir dirs"); + fn get_mboxes(&mut self) -> Result { + 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<()> { diff --git a/cli/src/backends/maildir/maildir_mbox.rs b/cli/src/backends/maildir/maildir_mbox.rs deleted file mode 100644 index 3f2ec2f..0000000 --- a/cli/src/backends/maildir/maildir_mbox.rs +++ /dev/null @@ -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, -} - -impl Deref for MaildirMboxes { - type Target = Vec; - - 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 for MaildirMboxes { - type Error = Error; - - fn try_from(mail_entries: RawMaildirMboxes) -> Result { - 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 for MaildirMbox { - type Error = Error; - - fn try_from(mail_entry: RawMaildirMbox) -> Result { - 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(), - }) - } -} diff --git a/cli/src/backends/notmuch/notmuch_backend.rs b/cli/src/backends/notmuch/notmuch_backend.rs index 66a2c14..03cbd26 100644 --- a/cli/src/backends/notmuch/notmuch_backend.rs +++ b/cli/src/backends/notmuch/notmuch_backend.rs @@ -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> { - info!(">> get notmuch virtual mailboxes"); + fn get_mboxes(&mut self) -> Result { + 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<()> { diff --git a/cli/src/backends/notmuch/notmuch_mbox.rs b/cli/src/backends/notmuch/notmuch_mbox.rs deleted file mode 100644 index 2fe1262..0000000 --- a/cli/src/backends/notmuch/notmuch_mbox.rs +++ /dev/null @@ -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, -} - -impl Deref for NotmuchMboxes { - type Target = Vec; - - 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()) - } -} diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 2e0a581..144794f 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -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::*; } diff --git a/cli/src/mbox/mbox.rs b/cli/src/mbox/mbox.rs index 45f7713..e98e743 100644 --- a/cli/src/mbox/mbox.rs +++ b/cli/src/mbox/mbox.rs @@ -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()) + } } diff --git a/cli/src/mbox/mbox_handlers.rs b/cli/src/mbox/mbox_handlers.rs index 332db46..f8f5c5e 100644 --- a/cli/src/mbox/mbox_handlers.rs +++ b/cli/src/mbox/mbox_handlers.rs @@ -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> { - Ok(Box::new(ImapMboxes { + fn get_mboxes(&mut self) -> Result { + 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!(); diff --git a/cli/src/mbox/mboxes.rs b/cli/src/mbox/mboxes.rs new file mode 100644 index 0000000..9a032ff --- /dev/null +++ b/cli/src/mbox/mboxes.rs @@ -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(()) + } +} diff --git a/flake.nix b/flake.nix index 79b4819..c3c7af2 100644 --- a/flake.nix +++ b/flake.nix @@ -57,6 +57,7 @@ # nix develop devShell = pkgs.mkShell { + RUSTUP_TOOLCHAIN = "stable"; inputsFrom = builtins.attrValues self.packages.${system}; nativeBuildInputs = with pkgs; [ # Nix LSP + formatter diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 4bfae01..51d2f2c 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -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"] } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 9c8fa74..87eb169 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,2 +1,4 @@ -pub mod account; mod process; + +pub mod account; +pub mod mbox; diff --git a/lib/src/mbox/mbox.rs b/lib/src/mbox/mbox.rs new file mode 100644 index 0000000..b14606c --- /dev/null +++ b/lib/src/mbox/mbox.rs @@ -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) + } +} diff --git a/lib/src/mbox/mboxes.rs b/lib/src/mbox/mboxes.rs new file mode 100644 index 0000000..161479e --- /dev/null +++ b/lib/src/mbox/mboxes.rs @@ -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, +} + +impl Deref for Mboxes { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.mboxes + } +} + +impl DerefMut for Mboxes { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.mboxes + } +} diff --git a/lib/src/mbox/mod.rs b/lib/src/mbox/mod.rs new file mode 100644 index 0000000..5efcf73 --- /dev/null +++ b/lib/src/mbox/mod.rs @@ -0,0 +1,5 @@ +mod mbox; +pub use mbox::*; + +mod mboxes; +pub use mboxes::*; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 89378c5..292fe49 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.58.1" +channel = "stable"