From 2b5be91af971374a890a24f7944b1add7cfe72ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Sun, 7 Mar 2021 20:37:37 +0100 Subject: [PATCH] add template commands --- src/main.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++-------- src/msg.rs | 60 ++++++++++++++++++++++++----------- 2 files changed, 119 insertions(+), 31 deletions(-) diff --git a/src/main.rs b/src/main.rs index 07925d8..ccc9fa1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ mod smtp; mod table; use clap::{App, AppSettings, Arg, SubCommand}; +use serde_json::json; use std::{fmt, fs, process::exit, result}; use crate::config::Config; @@ -99,6 +100,13 @@ fn uid_arg() -> Arg<'static, 'static> { .required(true) } +fn reply_all_arg() -> Arg<'static, 'static> { + Arg::with_name("reply-all") + .help("Includes all recipients") + .short("a") + .long("all") +} + fn page_size_arg<'a>(default: &'a str) -> Arg<'a, 'a> { Arg::with_name("size") .help("Page size") @@ -139,12 +147,12 @@ fn run() -> Result<()> { Arg::with_name("account") .long("account") .short("a") - .help("Name of the config file to use") + .help("Name of the account to use") .value_name("STRING"), ) .subcommand( SubCommand::with_name("mailboxes") - .aliases(&["mboxes", "mb", "m"]) + .aliases(&["mboxes", "mbox", "mb", "m"]) .about("Lists all available mailboxes"), ) .subcommand( @@ -188,7 +196,7 @@ fn run() -> Result<()> { ) .subcommand( SubCommand::with_name("attachments") - .aliases(&["attach", "a"]) + .aliases(&["attach", "att", "a"]) .about("Downloads all attachments from an email") .arg(uid_arg()) .arg(mailbox_arg()), @@ -200,12 +208,7 @@ fn run() -> Result<()> { .about("Answers to an email") .arg(uid_arg()) .arg(mailbox_arg()) - .arg( - Arg::with_name("reply-all") - .help("Includs all recipients") - .short("a") - .long("all"), - ), + .arg(reply_all_arg()), ) .subcommand( SubCommand::with_name("forward") @@ -214,6 +217,32 @@ fn run() -> Result<()> { .arg(uid_arg()) .arg(mailbox_arg()), ) + .subcommand( + SubCommand::with_name("template") + .aliases(&["tpl", "t"]) + .about("Generates a message template") + .subcommand( + SubCommand::with_name("new") + .aliases(&["n"]) + .about("Generates a new message template") + .arg(mailbox_arg()), + ) + .subcommand( + SubCommand::with_name("reply") + .aliases(&["rep", "r"]) + .about("Generates a reply message template") + .arg(uid_arg()) + .arg(mailbox_arg()) + .arg(reply_all_arg()), + ) + .subcommand( + SubCommand::with_name("forward") + .aliases(&["fwd", "fw", "f"]) + .about("Generates a forward message template") + .arg(uid_arg()) + .arg(mailbox_arg()), + ), + ) .get_matches(); let account_name = matches.value_of("account"); @@ -309,7 +338,7 @@ fn run() -> Result<()> { let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?); let text_bodies = msg.text_bodies(&mime)?; - println!("{}", text_bodies); + print(&output_type, json!({ "content": text_bodies }))?; imap_conn.logout(); } @@ -343,7 +372,7 @@ fn run() -> Result<()> { let account = config.find_account_by_name(account_name)?; let mut imap_conn = ImapConnector::new(&account)?; let tpl = Msg::build_new_tpl(&config, &account)?; - let content = input::open_editor_with_tpl(&tpl.as_bytes())?; + let content = input::open_editor_with_tpl(tpl.to_string().as_bytes())?; let msg = Msg::from(content); input::ask_for_confirmation("Send the message?")?; @@ -356,6 +385,41 @@ fn run() -> Result<()> { imap_conn.logout(); } + if let Some(matches) = matches.subcommand_matches("template") { + let config = Config::new_from_file()?; + let account = config.find_account_by_name(account_name)?; + let mut imap_conn = ImapConnector::new(&account)?; + + if let Some(_) = matches.subcommand_matches("new") { + let tpl = Msg::build_new_tpl(&config, &account)?; + print(&output_type, &tpl)?; + } + + if let Some(matches) = matches.subcommand_matches("reply") { + let uid = matches.value_of("uid").unwrap(); + let mbox = matches.value_of("mailbox").unwrap(); + + let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?); + let tpl = if matches.is_present("reply-all") { + msg.build_reply_all_tpl(&config, &account)? + } else { + msg.build_reply_tpl(&config, &account)? + }; + + print(&output_type, &tpl)?; + } + + if let Some(matches) = matches.subcommand_matches("forward") { + let uid = matches.value_of("uid").unwrap(); + let mbox = matches.value_of("mailbox").unwrap(); + + let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?); + let tpl = msg.build_forward_tpl(&config, &account)?; + + print(&output_type, &tpl)?; + } + } + if let Some(matches) = matches.subcommand_matches("reply") { let config = Config::new_from_file()?; let account = config.find_account_by_name(account_name)?; @@ -371,7 +435,7 @@ fn run() -> Result<()> { msg.build_reply_tpl(&config, &account)? }; - let content = input::open_editor_with_tpl(&tpl.as_bytes())?; + let content = input::open_editor_with_tpl(&tpl.to_string().as_bytes())?; let msg = Msg::from(content); input::ask_for_confirmation("Send the message?")?; @@ -394,7 +458,7 @@ fn run() -> Result<()> { let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?); let tpl = msg.build_forward_tpl(&config, &account)?; - let content = input::open_editor_with_tpl(&tpl.as_bytes())?; + let content = input::open_editor_with_tpl(&tpl.to_string().as_bytes())?; let msg = Msg::from(content); input::ask_for_confirmation("Send the message?")?; diff --git a/src/msg.rs b/src/msg.rs index 31e6917..b93c76b 100644 --- a/src/msg.rs +++ b/src/msg.rs @@ -1,7 +1,10 @@ use lettre; use mailparse::{self, MailHeaderMap}; use rfc2047_decoder; -use serde::Serialize; +use serde::{ + ser::{self, SerializeStruct}, + Serialize, +}; use std::{fmt, result}; use crate::config::{Account, Config}; @@ -41,7 +44,29 @@ impl From for Error { type Result = result::Result; -// Msg +// 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() + } +} + +// Message #[derive(Debug, Serialize)] pub struct Msg { @@ -228,7 +253,7 @@ impl<'a> Msg { Ok(parts) } - pub fn build_new_tpl(config: &Config, account: &Account) -> Result { + pub fn build_new_tpl(config: &Config, account: &Account) -> Result { let mut tpl = vec![]; // "From" header @@ -240,10 +265,10 @@ impl<'a> Msg { // "Subject" header tpl.push("Subject: ".to_string()); - Ok(tpl.join("\r\n")) + Ok(Tpl(tpl.join("\r\n"))) } - pub fn build_reply_tpl(&self, config: &Config, account: &Account) -> Result { + pub fn build_reply_tpl(&self, config: &Config, account: &Account) -> Result { let msg = &self.parse()?; let headers = msg.get_headers(); let mut tpl = vec![]; @@ -271,19 +296,19 @@ impl<'a> Msg { tpl.push(String::new()); // Original msg prepend with ">" - let thread = msg - .get_body() - .unwrap() - .split("\r\n") + let thread = self + .text_bodies("text/plain")? + .replace("\r", "") + .split("\n") .map(|line| format!(">{}", line)) .collect::>() .join("\r\n"); tpl.push(thread); - Ok(tpl.join("\r\n")) + Ok(Tpl(tpl.join("\r\n"))) } - pub fn build_reply_all_tpl(&self, config: &Config, account: &Account) -> Result { + 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![]; @@ -353,19 +378,18 @@ impl<'a> Msg { tpl.push(String::new()); // Original msg prepend with ">" - let thread = msg - .get_body() - .unwrap() + let thread = self + .text_bodies("text/plain")? .split("\r\n") .map(|line| format!(">{}", line)) .collect::>() .join("\r\n"); tpl.push(thread); - Ok(tpl.join("\r\n")) + Ok(Tpl(tpl.join("\r\n"))) } - pub fn build_forward_tpl(&self, config: &Config, account: &Account) -> Result { + pub fn build_forward_tpl(&self, config: &Config, account: &Account) -> Result { let msg = &self.parse()?; let headers = msg.get_headers(); let mut tpl = vec![]; @@ -385,9 +409,9 @@ impl<'a> Msg { // Original msg tpl.push("-------- Forwarded Message --------".to_string()); - tpl.push(msg.get_body().unwrap_or(String::new())); + tpl.push(self.text_bodies("text/plain")?); - Ok(tpl.join("\r\n")) + Ok(Tpl(tpl.join("\r\n"))) } }