diff --git a/CHANGELOG.md b/CHANGELOG.md index 977b28f..d6b76f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Mailbox attributes [#134] + ### Changed - Get signature from file [#135] @@ -243,6 +247,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#131]: https://github.com/soywod/himalaya/issues/131 [#132]: https://github.com/soywod/himalaya/issues/132 [#133]: https://github.com/soywod/himalaya/issues/133 +[#134]: https://github.com/soywod/himalaya/issues/134 [#135]: https://github.com/soywod/himalaya/issues/135 [#136]: https://github.com/soywod/himalaya/issues/136 [#138]: https://github.com/soywod/himalaya/issues/138 diff --git a/src/imap/model.rs b/src/imap/model.rs index 1841434..75006ba 100644 --- a/src/imap/model.rs +++ b/src/imap/model.rs @@ -7,7 +7,6 @@ use std::{collections::HashSet, iter::FromIterator, net::TcpStream}; use crate::{ config::model::{Account, Config}, flag::model::Flag, - mbox::model::{Mbox, Mboxes}, msg::model::Msg, }; @@ -167,16 +166,13 @@ impl<'ic> ImapConnector<'ic> { } } - pub fn list_mboxes(&mut self) -> Result { - let mboxes = self + pub fn list_mboxes(&mut self) -> Result>> { + let names = self .sess .list(Some(""), Some("*")) - .chain_err(|| "Cannot list mailboxes")? - .iter() - .map(Mbox::from_name) - .collect::>(); + .chain_err(|| "Cannot list mailboxes")?; - Ok(Mboxes(mboxes)) + Ok(names) } pub fn list_msgs( diff --git a/src/mbox/cli.rs b/src/mbox/cli.rs index 0d06662..80571ec 100644 --- a/src/mbox/cli.rs +++ b/src/mbox/cli.rs @@ -2,7 +2,7 @@ use clap; use error_chain::error_chain; use log::{debug, trace}; -use crate::{app::App, imap::model::ImapConnector}; +use crate::{app::App, imap::model::ImapConnector, mbox::model::Mboxes}; error_chain! { links { @@ -36,7 +36,8 @@ pub fn mbox_matches(app: &App) -> Result { debug!("mailboxes command matched"); let mut imap_conn = ImapConnector::new(&app.account)?; - let mboxes = imap_conn.list_mboxes()?; + let names = imap_conn.list_mboxes()?; + let mboxes = Mboxes::from(&names); debug!("found {} mailboxes", mboxes.0.len()); trace!("mailboxes: {:?}", mboxes); app.output.print(mboxes); diff --git a/src/mbox/model.rs b/src/mbox/model.rs index 6f5707c..a14e341 100644 --- a/src/mbox/model.rs +++ b/src/mbox/model.rs @@ -1,29 +1,100 @@ use imap; -use serde::Serialize; +use serde::{ + ser::{self, SerializeSeq}, + Serialize, +}; use std::fmt; use crate::table::{Cell, Row, Table}; -// Mbox +// Attribute -#[derive(Debug, Serialize)] -pub struct Mbox { - pub delim: String, - pub name: String, - pub attributes: Vec, -} +#[derive(Debug, PartialEq)] +struct SerializableAttribute<'a>(&'a imap::types::NameAttribute<'a>); -impl Mbox { - pub fn from_name(name: &imap::types::Name) -> Self { - Self { - delim: name.delimiter().unwrap_or_default().to_owned(), - name: name.name().to_owned(), - attributes: vec![], // TODO: set attributes +impl<'a> Into<&'a str> for &'a SerializableAttribute<'a> { + fn into(self) -> &'a str { + match &self.0 { + imap::types::NameAttribute::NoInferiors => "\\NoInferiors", + imap::types::NameAttribute::NoSelect => "\\NoSelect", + imap::types::NameAttribute::Marked => "\\Marked", + imap::types::NameAttribute::Unmarked => "\\Unmarked", + imap::types::NameAttribute::Custom(cow) => cow, } } } -impl Table for Mbox { +impl<'a> ser::Serialize for SerializableAttribute<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(self.into()) + } +} + +#[derive(Debug, PartialEq)] +pub struct Attributes<'a>(&'a [imap::types::NameAttribute<'a>]); + +impl<'a> From<&'a [imap::types::NameAttribute<'a>]> for Attributes<'a> { + fn from(attrs: &'a [imap::types::NameAttribute<'a>]) -> Self { + Self(attrs) + } +} + +impl<'a> ToString for Attributes<'a> { + fn to_string(&self) -> String { + match self.0.len() { + 0 => String::new(), + 1 => { + let attr = &SerializableAttribute(&self.0[0]); + let attr: &str = attr.into(); + attr.to_owned() + } + _ => { + let attr = &SerializableAttribute(&self.0[0]); + let attr: &str = attr.into(); + format!("{}, {}", attr, Attributes(&self.0[1..]).to_string()) + } + } + } +} + +impl<'a> ser::Serialize for Attributes<'a> { + fn serialize(&self, serializer: T) -> Result + where + T: ser::Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.0.len()))?; + + for attr in self.0 { + seq.serialize_element(&SerializableAttribute(attr))?; + } + + seq.end() + } +} + +// Mailbox + +#[derive(Debug, Serialize)] +pub struct Mbox<'a> { + pub delim: String, + pub name: String, + pub attributes: Attributes<'a>, +} + +impl<'a> From<&'a imap::types::Name> for Mbox<'a> { + fn from(name: &'a imap::types::Name) -> Self { + Self { + delim: name.delimiter().unwrap_or_default().to_owned(), + name: name.name().to_owned(), + attributes: Attributes::from(name.attributes()), + } + } +} + +impl<'a> Table for Mbox<'a> { fn head() -> Row { Row::new() .cell(Cell::new("DELIM").bold().underline().white()) @@ -41,16 +112,22 @@ impl Table for Mbox { Row::new() .cell(Cell::new(&self.delim).white()) .cell(Cell::new(&self.name).green()) - .cell(Cell::new(&self.attributes.join(", ")).shrinkable().yellow()) + .cell(Cell::new(&self.attributes.to_string()).shrinkable().blue()) } } // Mboxes #[derive(Debug, Serialize)] -pub struct Mboxes(pub Vec); +pub struct Mboxes<'a>(pub Vec>); -impl fmt::Display for Mboxes { +impl<'a> From<&'a imap::types::ZeroCopy>> for Mboxes<'a> { + fn from(names: &'a imap::types::ZeroCopy>) -> Self { + Self(names.iter().map(Mbox::from).collect::>()) + } +} + +impl fmt::Display for Mboxes<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "\n{}", Table::render(&self.0)) }