diff --git a/src/app.rs b/src/app.rs deleted file mode 100644 index 73d1ef7..0000000 --- a/src/app.rs +++ /dev/null @@ -1,69 +0,0 @@ -use clap::{self, Arg}; -use error_chain::error_chain; -use std::env; - -use crate::{ - flag::cli::{flag_matches, flag_subcmds}, - imap::cli::{imap_matches, imap_subcmds}, - mbox::cli::{mbox_arg, mbox_matches, mbox_subcmds}, - msg::cli::{msg_matches, msg_subcmds}, - output::cli::output_args, -}; - -error_chain! { - links { - FlagCli(crate::flag::cli::Error, crate::flag::cli::ErrorKind); - ImapCli(crate::imap::cli::Error, crate::imap::cli::ErrorKind); - MboxCli(crate::mbox::cli::Error, crate::mbox::cli::ErrorKind); - MsgCli(crate::msg::cli::Error, crate::msg::cli::ErrorKind); - } -} - -pub struct App<'a>(pub clap::App<'a, 'a>); - -impl<'a> App<'a> { - pub fn new() -> Self { - let app = clap::App::new(env!("CARGO_PKG_NAME")) - .version(env!("CARGO_PKG_VERSION")) - .about(env!("CARGO_PKG_DESCRIPTION")) - .author(env!("CARGO_PKG_AUTHORS")) - .args(&output_args()) - .arg( - Arg::with_name("account") - .long("account") - .short("a") - .help("Selects a specific account") - .value_name("STRING"), - ) - .arg(mbox_arg()) - .subcommands(flag_subcmds()) - .subcommands(imap_subcmds()) - .subcommands(mbox_subcmds()) - .subcommands(msg_subcmds()); - - Self(app) - } - - pub fn run(self) -> Result<()> { - let matches = self.0.get_matches(); - - loop { - if mbox_matches(&matches)? { - break; - } - - if flag_matches(&matches)? { - break; - } - - if imap_matches(&matches)? { - break; - } - - msg_matches(&matches)?; - break; - } - - Ok(()) - } -} diff --git a/src/config/cli.rs b/src/config/cli.rs new file mode 100644 index 0000000..c7b797c --- /dev/null +++ b/src/config/cli.rs @@ -0,0 +1,9 @@ +use clap::Arg; + +pub fn account_arg<'a>() -> Arg<'a, 'a> { + Arg::with_name("account") + .long("account") + .short("a") + .help("Selects a specific account") + .value_name("STRING") +} diff --git a/src/config.rs b/src/config/model.rs similarity index 100% rename from src/config.rs rename to src/config/model.rs diff --git a/src/flag/cli.rs b/src/flag/cli.rs index f2cd420..09cc641 100644 --- a/src/flag/cli.rs +++ b/src/flag/cli.rs @@ -1,11 +1,11 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use error_chain::error_chain; -use crate::{config::Config, imap::model::ImapConnector}; +use crate::{config::model::Config, imap::model::ImapConnector}; error_chain! { links { - Config(crate::config::Error, crate::config::ErrorKind); + Config(crate::config::model::Error, crate::config::model::ErrorKind); Imap(crate::imap::model::Error, crate::imap::model::ErrorKind); } } diff --git a/src/imap/cli.rs b/src/imap/cli.rs index 60ed2c7..cd1d0a0 100644 --- a/src/imap/cli.rs +++ b/src/imap/cli.rs @@ -1,11 +1,11 @@ use clap::{self, App, ArgMatches, SubCommand}; use error_chain::error_chain; -use crate::{config::Config, imap::model::ImapConnector}; +use crate::{config::model::Config, imap::model::ImapConnector}; error_chain! { links { - Config(crate::config::Error, crate::config::ErrorKind); + Config(crate::config::model::Error, crate::config::model::ErrorKind); Imap(crate::imap::model::Error, crate::imap::model::ErrorKind); } } diff --git a/src/imap/model.rs b/src/imap/model.rs index 54c2f2f..8b993eb 100644 --- a/src/imap/model.rs +++ b/src/imap/model.rs @@ -4,14 +4,14 @@ use native_tls::{self, TlsConnector, TlsStream}; use std::net::TcpStream; use crate::{ - config::{self, Account, Config}, + config::model::{Account, Config}, mbox::model::{Mbox, Mboxes}, msg::model::Msg, }; error_chain! { links { - Config(config::Error, config::ErrorKind); + Config(crate::config::model::Error, crate::config::model::ErrorKind); } } diff --git a/src/main.rs b/src/main.rs index 81603e5..b1b9579 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ -mod app; -mod config; mod input; mod smtp; mod table; +mod config { + pub(crate) mod cli; + pub(crate) mod model; +} mod output { pub(crate) mod cli; pub(crate) mod utils; @@ -24,10 +26,64 @@ mod mbox { pub(crate) mod model; } -use crate::app::App; +use clap; +use error_chain::error_chain; +use std::env; + +use crate::{ + config::cli::account_arg, + flag::cli::{flag_matches, flag_subcmds}, + imap::cli::{imap_matches, imap_subcmds}, + mbox::cli::{mbox_arg, mbox_matches, mbox_subcmds}, + msg::cli::{msg_matches, msg_subcmds}, + output::cli::output_arg, +}; + +error_chain! { + links { + FlagCli(crate::flag::cli::Error, crate::flag::cli::ErrorKind); + ImapCli(crate::imap::cli::Error, crate::imap::cli::ErrorKind); + MboxCli(crate::mbox::cli::Error, crate::mbox::cli::ErrorKind); + MsgCli(crate::msg::cli::Error, crate::msg::cli::ErrorKind); + } +} + +fn run() -> Result<()> { + let matches = clap::App::new(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .about(env!("CARGO_PKG_DESCRIPTION")) + .author(env!("CARGO_PKG_AUTHORS")) + .arg(output_arg()) + .arg(account_arg()) + .arg(mbox_arg()) + .subcommands(flag_subcmds()) + .subcommands(imap_subcmds()) + .subcommands(mbox_subcmds()) + .subcommands(msg_subcmds()) + .get_matches(); + + loop { + if mbox_matches(&matches)? { + break; + } + + if flag_matches(&matches)? { + break; + } + + if imap_matches(&matches)? { + break; + } + + msg_matches(&matches)?; + break; + } + + Ok(()) +} fn main() { - if let Err(ref errs) = App::new().run() { + if let Err(ref errs) = run() { let mut errs = errs.iter(); match errs.next() { None => (), diff --git a/src/mbox.rs b/src/mbox.rs deleted file mode 100644 index 6ad92d5..0000000 --- a/src/mbox.rs +++ /dev/null @@ -1,63 +0,0 @@ -use imap; -use serde::Serialize; -use std::fmt; - -use crate::table::{self, DisplayRow, DisplayTable}; - -// Mbox - -#[derive(Debug, Serialize)] -pub struct Mbox { - pub delim: String, - pub name: String, - pub attributes: Vec, -} - -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 DisplayRow for Mbox { - fn to_row(&self) -> Vec { - use crate::table::*; - - vec![ - Cell::new(&[BLUE], &self.delim), - Cell::new(&[GREEN], &self.name), - FlexCell::new(&[YELLOW], &self.attributes.join(", ")), - ] - } -} - -// Mboxes - -#[derive(Debug, Serialize)] -pub struct Mboxes(pub Vec); - -impl<'a> DisplayTable<'a, Mbox> for Mboxes { - fn header_row() -> Vec { - use crate::table::*; - - vec![ - Cell::new(&[BOLD, UNDERLINE, WHITE], "DELIM"), - Cell::new(&[BOLD, UNDERLINE, WHITE], "NAME"), - FlexCell::new(&[BOLD, UNDERLINE, WHITE], "ATTRIBUTES"), - ] - } - - fn rows(&self) -> &Vec { - &self.0 - } -} - -impl fmt::Display for Mboxes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "\n{}", self.to_table()) - } -} diff --git a/src/mbox/cli.rs b/src/mbox/cli.rs index 6c711b4..0a59c07 100644 --- a/src/mbox/cli.rs +++ b/src/mbox/cli.rs @@ -1,11 +1,11 @@ use clap::{self, App, Arg, ArgMatches, SubCommand}; use error_chain::error_chain; -use crate::{config::Config, imap::model::ImapConnector, output::utils::print}; +use crate::{config::model::Config, imap::model::ImapConnector, output::utils::print}; error_chain! { links { - Config(crate::config::Error, crate::config::ErrorKind); + Config(crate::config::model::Error, crate::config::model::ErrorKind); Imap(crate::imap::model::Error, crate::imap::model::ErrorKind); MsgCli(crate::msg::cli::Error, crate::msg::cli::ErrorKind); OutputUtils(crate::output::utils::Error, crate::output::utils::ErrorKind); diff --git a/src/msg.rs b/src/msg.rs deleted file mode 100644 index 28e76fa..0000000 --- a/src/msg.rs +++ /dev/null @@ -1,578 +0,0 @@ -use error_chain::error_chain; -use lettre; -use mailparse::{self, MailHeaderMap}; -use rfc2047_decoder; -use serde::{ - ser::{self, SerializeStruct}, - Serialize, -}; -use std::{fmt, result}; -use uuid::Uuid; - -use crate::config::{Account, Config}; -use crate::flag::model::{Flag, Flags}; -use crate::table::{self, DisplayRow, DisplayTable}; - -error_chain! { - foreign_links { - Mailparse(mailparse::MailParseError); - Lettre(lettre::error::Error); - } -} - -// Template - -#[derive(Debug)] -pub struct Tpl(String); - -impl fmt::Display for Tpl { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Serialize for Tpl { - fn serialize(&self, serializer: S) -> result::Result - where - S: ser::Serializer, - { - let mut state = serializer.serialize_struct("Tpl", 1)?; - state.serialize_field("template", &self.0)?; - state.end() - } -} - -// Attachments - -#[derive(Debug)] -pub struct Attachment { - pub filename: String, - pub raw: Vec, -} - -impl<'a> Attachment { - // TODO: put in common with ReadableMsg - pub fn from_part(part: &'a mailparse::ParsedMail) -> Self { - Self { - filename: part - .get_content_disposition() - .params - .get("filename") - .unwrap_or(&Uuid::new_v4().to_simple().to_string()) - .to_owned(), - raw: part.get_body_raw().unwrap_or_default(), - } - } -} - -#[derive(Debug)] -pub struct Attachments(pub Vec); - -impl<'a> Attachments { - fn extract_from_part(&'a mut self, part: &'a mailparse::ParsedMail) { - if part.subparts.is_empty() { - let ctype = part - .get_headers() - .get_first_value("content-type") - .unwrap_or_default(); - - if !ctype.starts_with("text") { - self.0.push(Attachment::from_part(part)); - } - } else { - part.subparts - .iter() - .for_each(|part| self.extract_from_part(part)); - } - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - let msg = mailparse::parse_mail(bytes)?; - let mut attachments = Self(vec![]); - attachments.extract_from_part(&msg); - Ok(attachments) - } -} - -// Readable message - -#[derive(Debug)] -pub struct ReadableMsg { - pub content: String, - pub has_attachment: bool, -} - -impl Serialize for ReadableMsg { - fn serialize(&self, serializer: S) -> result::Result - where - S: ser::Serializer, - { - let mut state = serializer.serialize_struct("ReadableMsg", 2)?; - state.serialize_field("content", &self.content)?; - state.serialize_field("hasAttachment", if self.has_attachment { &1 } else { &0 })?; - state.end() - } -} - -impl fmt::Display for ReadableMsg { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.content) - } -} - -impl<'a> ReadableMsg { - fn flatten_parts(part: &'a mailparse::ParsedMail) -> Vec<&'a mailparse::ParsedMail<'a>> { - if part.subparts.is_empty() { - vec![part] - } else { - part.subparts - .iter() - .flat_map(Self::flatten_parts) - .collect::>() - } - } - - pub fn from_bytes(mime: &str, bytes: &[u8]) -> Result { - let msg = mailparse::parse_mail(bytes)?; - let (text_part, html_part, has_attachment) = Self::flatten_parts(&msg).into_iter().fold( - (None, None, false), - |(mut text_part, mut html_part, mut has_attachment), part| { - let ctype = part - .get_headers() - .get_first_value("content-type") - .unwrap_or_default(); - - if text_part.is_none() && ctype.starts_with("text/plain") { - text_part = part.get_body().ok(); - } else { - if html_part.is_none() && ctype.starts_with("text/html") { - html_part = part.get_body().ok(); - } else { - has_attachment = true - }; - }; - - (text_part, html_part, has_attachment) - }, - ); - - let content = if mime == "text/plain" { - text_part.or(html_part).unwrap_or_default() - } else { - html_part.or(text_part).unwrap_or_default() - }; - - Ok(Self { - content, - has_attachment, - }) - } -} - -// Message - -// #[derive(Debug, Serialize, PartialEq)] -// #[serde(rename_all = "lowercase")] -// pub enum Flag { -// Seen, -// Answered, -// Flagged, -// } - -// impl Flag { -// fn from_imap_flag(flag: &imap::types::Flag<'_>) -> Option { -// match flag { -// imap::types::Flag::Seen => Some(Self::Seen), -// imap::types::Flag::Answered => Some(Self::Answered), -// imap::types::Flag::Flagged => Some(Self::Flagged), -// _ => None, -// } -// } -// } - -#[derive(Debug, Serialize)] -pub struct Msg<'m> { - pub uid: u32, - pub flags: Flags<'m>, - pub subject: String, - pub sender: String, - pub date: String, - - #[serde(skip_serializing)] - raw: Vec, -} - -impl<'m> From> for Msg<'m> { - fn from(raw: Vec) -> Self { - Self { - uid: 0, - flags: Flags::new(&[]), - subject: String::from(""), - sender: String::from(""), - date: String::from(""), - raw, - } - } -} - -impl<'m> From for Msg<'m> { - fn from(raw: String) -> Self { - Self::from(raw.as_bytes().to_vec()) - } -} - -impl<'m> From<&'m imap::types::Fetch> for Msg<'m> { - fn from(fetch: &'m imap::types::Fetch) -> Self { - match fetch.envelope() { - None => Self::from(fetch.body().unwrap_or_default().to_vec()), - Some(envelope) => Self { - uid: fetch.uid.unwrap_or_default(), - flags: Flags::new(fetch.flags()), - subject: envelope - .subject - .and_then(|subj| rfc2047_decoder::decode(subj).ok()) - .unwrap_or_default(), - sender: envelope - .from - .as_ref() - .and_then(|addrs| addrs.first()?.name) - .and_then(|name| rfc2047_decoder::decode(name).ok()) - .unwrap_or_default(), - date: fetch - .internal_date() - .map(|date| date.naive_local().to_string()) - .unwrap_or_default(), - raw: fetch.body().unwrap_or_default().to_vec(), - }, - } - } -} - -impl<'m> Msg<'m> { - pub fn parse(&'m self) -> Result> { - Ok(mailparse::parse_mail(&self.raw)?) - } - - pub fn to_vec(&self) -> Result> { - let parsed = self.parse()?; - let headers = parsed.get_headers().get_raw_bytes().to_vec(); - let sep = "\r\n".as_bytes().to_vec(); - let body = parsed.get_body()?.as_bytes().to_vec(); - - Ok(vec![headers, sep, body].concat()) - } - - pub fn to_sendable_msg(&self) -> Result { - use lettre::message::header::{ContentTransferEncoding, ContentType}; - use lettre::message::{Message, SinglePart}; - - let parsed = self.parse()?; - let msg = parsed - .headers - .iter() - .fold(Message::builder(), |msg, h| { - let value = String::from_utf8(h.get_value_raw().to_vec()) - .unwrap() - .replace("\r", ""); - - match h.get_key().to_lowercase().as_str() { - "in-reply-to" => msg.in_reply_to(value.parse().unwrap()), - "from" => match value.parse() { - Ok(addr) => msg.from(addr), - Err(_) => msg, - }, - "to" => value - .split(",") - .fold(msg, |msg, addr| match addr.trim().parse() { - Ok(addr) => msg.to(addr), - Err(_) => msg, - }), - "cc" => value - .split(",") - .fold(msg, |msg, addr| match addr.trim().parse() { - Ok(addr) => msg.cc(addr), - Err(_) => msg, - }), - "bcc" => value - .split(",") - .fold(msg, |msg, addr| match addr.trim().parse() { - Ok(addr) => msg.bcc(addr), - Err(_) => msg, - }), - "subject" => msg.subject(value), - _ => msg, - } - }) - .singlepart( - SinglePart::builder() - .header(ContentType("text/plain; charset=utf-8".parse().unwrap())) - .header(ContentTransferEncoding::Base64) - .body(parsed.get_body_raw()?), - )?; - - Ok(msg) - } - - fn extract_text_bodies_into(part: &mailparse::ParsedMail, mime: &str, parts: &mut Vec) { - match part.subparts.len() { - 0 => { - let content_type = part - .get_headers() - .get_first_value("content-type") - .unwrap_or_default(); - - if content_type.starts_with(mime) { - parts.push(part.get_body().unwrap_or_default()) - } - } - _ => { - part.subparts - .iter() - .for_each(|part| Self::extract_text_bodies_into(part, mime, parts)); - } - } - } - - fn extract_text_bodies(&self, mime: &str) -> Result> { - let mut parts = vec![]; - Self::extract_text_bodies_into(&self.parse()?, mime, &mut parts); - Ok(parts) - } - - pub fn text_bodies(&self, mime: &str) -> Result { - let text_bodies = self.extract_text_bodies(mime)?; - Ok(text_bodies.join("\r\n")) - } - - pub fn build_new_tpl(config: &Config, account: &Account) -> Result { - let mut tpl = vec![]; - - // "Content" headers - tpl.push("Content-Type: text/plain; charset=utf-8".to_string()); - tpl.push("Content-Transfer-Encoding: 8bit".to_string()); - - // "From" header - tpl.push(format!("From: {}", config.address(account))); - - // "To" header - tpl.push("To: ".to_string()); - - // "Subject" header - tpl.push("Subject: ".to_string()); - - Ok(Tpl(tpl.join("\r\n"))) - } - - pub fn build_reply_tpl(&self, config: &Config, account: &Account) -> Result { - let msg = &self.parse()?; - let headers = msg.get_headers(); - let mut tpl = vec![]; - - // "Content" headers - tpl.push("Content-Type: text/plain; charset=utf-8".to_string()); - tpl.push("Content-Transfer-Encoding: 8bit".to_string()); - - // "From" header - tpl.push(format!("From: {}", config.address(account))); - - // "In-Reply-To" header - if let Some(msg_id) = headers.get_first_value("message-id") { - tpl.push(format!("In-Reply-To: {}", msg_id)); - } - - // "To" header - let to = headers - .get_first_value("reply-to") - .or(headers.get_first_value("from")) - .unwrap_or(String::new()); - tpl.push(format!("To: {}", to)); - - // "Subject" header - let subject = headers.get_first_value("subject").unwrap_or(String::new()); - tpl.push(format!("Subject: Re: {}", subject)); - - // Separator between headers and body - tpl.push(String::new()); - - // Original msg prepend with ">" - let thread = self - .text_bodies("text/plain")? - .replace("\r", "") - .split("\n") - .map(|line| format!(">{}", line)) - .collect::>() - .join("\r\n"); - tpl.push(thread); - - Ok(Tpl(tpl.join("\r\n"))) - } - - pub fn build_reply_all_tpl(&self, config: &Config, account: &Account) -> Result { - let msg = &self.parse()?; - let headers = msg.get_headers(); - let mut tpl = vec![]; - - // "Content" headers - tpl.push("Content-Type: text/plain; charset=utf-8".to_string()); - tpl.push("Content-Transfer-Encoding: 8bit".to_string()); - - // "From" header - tpl.push(format!("From: {}", config.address(account))); - - // "In-Reply-To" header - if let Some(msg_id) = headers.get_first_value("message-id") { - tpl.push(format!("In-Reply-To: {}", msg_id)); - } - - // "To" header - // All addresses coming from original "To" … - let email: lettre::Address = account.email.parse().unwrap(); - let to = headers - .get_all_values("to") - .iter() - .flat_map(|addrs| addrs.split(",")) - .fold(vec![], |mut mboxes, addr| { - match addr.trim().parse::() { - Err(_) => mboxes, - Ok(mbox) => { - // … except current user's one (from config) … - if mbox.email != email { - mboxes.push(mbox.to_string()); - } - mboxes - } - } - }); - // … and the ones coming from either "Reply-To" or "From" - let reply_to = headers - .get_all_values("reply-to") - .iter() - .flat_map(|addrs| addrs.split(",")) - .map(|addr| addr.trim().to_string()) - .collect::>(); - let reply_to = if reply_to.is_empty() { - headers - .get_all_values("from") - .iter() - .flat_map(|addrs| addrs.split(",")) - .map(|addr| addr.trim().to_string()) - .collect::>() - } else { - reply_to - }; - tpl.push(format!("To: {}", vec![reply_to, to].concat().join(", "))); - - // "Cc" header - let cc = headers - .get_all_values("cc") - .iter() - .flat_map(|addrs| addrs.split(",")) - .map(|addr| addr.trim().to_string()) - .collect::>(); - if !cc.is_empty() { - tpl.push(format!("Cc: {}", cc.join(", "))); - } - - // "Subject" header - let subject = headers.get_first_value("subject").unwrap_or(String::new()); - tpl.push(format!("Subject: Re: {}", subject)); - - // Separator between headers and body - tpl.push(String::new()); - - // Original msg prepend with ">" - let thread = self - .text_bodies("text/plain")? - .split("\r\n") - .map(|line| format!(">{}", line)) - .collect::>() - .join("\r\n"); - tpl.push(thread); - - Ok(Tpl(tpl.join("\r\n"))) - } - - pub fn build_forward_tpl(&self, config: &Config, account: &Account) -> Result { - let msg = &self.parse()?; - let headers = msg.get_headers(); - let mut tpl = vec![]; - - // "Content" headers - tpl.push("Content-Type: text/plain; charset=utf-8".to_string()); - tpl.push("Content-Transfer-Encoding: 8bit".to_string()); - - // "From" header - tpl.push(format!("From: {}", config.address(account))); - - // "To" header - tpl.push("To: ".to_string()); - - // "Subject" header - let subject = headers.get_first_value("subject").unwrap_or(String::new()); - tpl.push(format!("Subject: Fwd: {}", subject)); - - // Separator between headers and body - tpl.push(String::new()); - - // Original msg - tpl.push("-------- Forwarded Message --------".to_string()); - tpl.push(self.text_bodies("text/plain")?); - - Ok(Tpl(tpl.join("\r\n"))) - } -} - -impl<'m> DisplayRow for Msg<'m> { - fn to_row(&self) -> Vec { - use crate::table::*; - - let unseen = if self.flags.contains(&Flag::Seen) { - RESET - } else { - BOLD - }; - - vec![ - Cell::new(&[unseen.to_owned(), RED], &self.uid.to_string()), - Cell::new(&[unseen.to_owned(), WHITE], &self.flags.to_string()), - FlexCell::new(&[unseen.to_owned(), GREEN], &self.subject), - Cell::new(&[unseen.to_owned(), BLUE], &self.sender), - Cell::new(&[unseen.to_owned(), YELLOW], &self.date), - ] - } -} - -// Msgs - -#[derive(Debug, Serialize)] -pub struct Msgs<'m>(pub Vec>); - -impl<'m> DisplayTable<'m, Msg<'m>> for Msgs<'m> { - fn header_row() -> Vec { - use crate::table::*; - - vec![ - Cell::new(&[BOLD, UNDERLINE, WHITE], "UID"), - Cell::new(&[BOLD, UNDERLINE, WHITE], "FLAGS"), - FlexCell::new(&[BOLD, UNDERLINE, WHITE], "SUBJECT"), - Cell::new(&[BOLD, UNDERLINE, WHITE], "SENDER"), - Cell::new(&[BOLD, UNDERLINE, WHITE], "DATE"), - ] - } - - fn rows(&self) -> &Vec> { - &self.0 - } -} - -impl<'m> From<&'m imap::types::ZeroCopy>> for Msgs<'m> { - fn from(fetches: &'m imap::types::ZeroCopy>) -> Self { - Self(fetches.iter().map(Msg::from).collect::>()) - } -} - -impl<'m> fmt::Display for Msgs<'m> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "\n{}", self.to_table()) - } -} diff --git a/src/msg/cli.rs b/src/msg/cli.rs index cc1fa10..60413c1 100644 --- a/src/msg/cli.rs +++ b/src/msg/cli.rs @@ -3,7 +3,7 @@ use error_chain::error_chain; use std::fs; use crate::{ - config::Config, + config::model::Config, imap::model::ImapConnector, input, msg::model::{Attachments, Msg, Msgs, ReadableMsg}, @@ -13,7 +13,7 @@ use crate::{ error_chain! { links { - Config(crate::config::Error, crate::config::ErrorKind); + Config(crate::config::model::Error, crate::config::model::ErrorKind); Imap(crate::imap::model::Error, crate::imap::model::ErrorKind); Input(crate::input::Error, crate::input::ErrorKind); MsgModel(crate::msg::model::Error, crate::msg::model::ErrorKind); diff --git a/src/msg/model.rs b/src/msg/model.rs index 28e76fa..46888ee 100644 --- a/src/msg/model.rs +++ b/src/msg/model.rs @@ -9,7 +9,7 @@ use serde::{ use std::{fmt, result}; use uuid::Uuid; -use crate::config::{Account, Config}; +use crate::config::model::{Account, Config}; use crate::flag::model::{Flag, Flags}; use crate::table::{self, DisplayRow, DisplayTable}; diff --git a/src/output/cli.rs b/src/output/cli.rs index 3344f5e..c5a8e9c 100644 --- a/src/output/cli.rs +++ b/src/output/cli.rs @@ -1,11 +1,11 @@ use clap::Arg; -pub fn output_args<'a>() -> Vec> { - vec![Arg::with_name("output") +pub fn output_arg<'a>() -> Arg<'a, 'a> { + Arg::with_name("output") .long("output") .short("o") .help("Defines the output format") .value_name("STRING") .possible_values(&["plain", "json"]) - .default_value("plain")] + .default_value("plain") } diff --git a/src/smtp.rs b/src/smtp.rs index 810f0cf..6ef588a 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -1,11 +1,11 @@ use error_chain::error_chain; use lettre::{self, transport::smtp::SmtpTransport, Transport}; -use crate::config::{self, Account}; +use crate::config::model::Account; error_chain! { links { - Config(config::Error, config::ErrorKind); + Config(crate::config::model::Error, crate::config::model::ErrorKind); } foreign_links { Smtp(lettre::transport::smtp::Error);