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:
Clément DOUIN 2022-05-29 12:36:10 +02:00
parent a0461d84ba
commit 7c01f88006
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
20 changed files with 193 additions and 575 deletions

View file

@ -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"

View file

@ -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,

View file

@ -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<()> {

View file

@ -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(),
}
}
}

View file

@ -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()),
}
}
}

View file

@ -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<()> {

View file

@ -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(),
})
}
}

View file

@ -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<()> {

View file

@ -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())
}
}

View file

@ -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::*;
}

View file

@ -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())
}
}

View file

@ -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
View 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(())
}
}

View file

@ -57,6 +57,7 @@
# nix develop
devShell = pkgs.mkShell {
RUSTUP_TOOLCHAIN = "stable";
inputsFrom = builtins.attrValues self.packages.${system};
nativeBuildInputs = with pkgs; [
# Nix LSP + formatter

View file

@ -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"] }

View file

@ -1,2 +1,4 @@
pub mod account;
mod process;
pub mod account;
pub mod mbox;

19
lib/src/mbox/mbox.rs Normal file
View 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
View 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
View file

@ -0,0 +1,5 @@
mod mbox;
pub use mbox::*;
mod mboxes;
pub use mboxes::*;

View file

@ -1,2 +1,2 @@
[toolchain]
channel = "1.58.1"
channel = "stable"