add template commands

This commit is contained in:
Clément DOUIN 2021-03-07 20:37:37 +01:00
parent 3281c27d57
commit 2b5be91af9
No known key found for this signature in database
GPG key ID: 69C9B9CFFDEE2DEF
2 changed files with 119 additions and 31 deletions

View file

@ -8,6 +8,7 @@ mod smtp;
mod table; mod table;
use clap::{App, AppSettings, Arg, SubCommand}; use clap::{App, AppSettings, Arg, SubCommand};
use serde_json::json;
use std::{fmt, fs, process::exit, result}; use std::{fmt, fs, process::exit, result};
use crate::config::Config; use crate::config::Config;
@ -99,6 +100,13 @@ fn uid_arg() -> Arg<'static, 'static> {
.required(true) .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> { fn page_size_arg<'a>(default: &'a str) -> Arg<'a, 'a> {
Arg::with_name("size") Arg::with_name("size")
.help("Page size") .help("Page size")
@ -139,12 +147,12 @@ fn run() -> Result<()> {
Arg::with_name("account") Arg::with_name("account")
.long("account") .long("account")
.short("a") .short("a")
.help("Name of the config file to use") .help("Name of the account to use")
.value_name("STRING"), .value_name("STRING"),
) )
.subcommand( .subcommand(
SubCommand::with_name("mailboxes") SubCommand::with_name("mailboxes")
.aliases(&["mboxes", "mb", "m"]) .aliases(&["mboxes", "mbox", "mb", "m"])
.about("Lists all available mailboxes"), .about("Lists all available mailboxes"),
) )
.subcommand( .subcommand(
@ -188,7 +196,7 @@ fn run() -> Result<()> {
) )
.subcommand( .subcommand(
SubCommand::with_name("attachments") SubCommand::with_name("attachments")
.aliases(&["attach", "a"]) .aliases(&["attach", "att", "a"])
.about("Downloads all attachments from an email") .about("Downloads all attachments from an email")
.arg(uid_arg()) .arg(uid_arg())
.arg(mailbox_arg()), .arg(mailbox_arg()),
@ -200,12 +208,7 @@ fn run() -> Result<()> {
.about("Answers to an email") .about("Answers to an email")
.arg(uid_arg()) .arg(uid_arg())
.arg(mailbox_arg()) .arg(mailbox_arg())
.arg( .arg(reply_all_arg()),
Arg::with_name("reply-all")
.help("Includs all recipients")
.short("a")
.long("all"),
),
) )
.subcommand( .subcommand(
SubCommand::with_name("forward") SubCommand::with_name("forward")
@ -214,6 +217,32 @@ fn run() -> Result<()> {
.arg(uid_arg()) .arg(uid_arg())
.arg(mailbox_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(); .get_matches();
let account_name = matches.value_of("account"); 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 msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
let text_bodies = msg.text_bodies(&mime)?; let text_bodies = msg.text_bodies(&mime)?;
println!("{}", text_bodies); print(&output_type, json!({ "content": text_bodies }))?;
imap_conn.logout(); imap_conn.logout();
} }
@ -343,7 +372,7 @@ fn run() -> Result<()> {
let account = config.find_account_by_name(account_name)?; let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?; let mut imap_conn = ImapConnector::new(&account)?;
let tpl = Msg::build_new_tpl(&config, &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); let msg = Msg::from(content);
input::ask_for_confirmation("Send the message?")?; input::ask_for_confirmation("Send the message?")?;
@ -356,6 +385,41 @@ fn run() -> Result<()> {
imap_conn.logout(); 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") { if let Some(matches) = matches.subcommand_matches("reply") {
let config = Config::new_from_file()?; let config = Config::new_from_file()?;
let account = config.find_account_by_name(account_name)?; let account = config.find_account_by_name(account_name)?;
@ -371,7 +435,7 @@ fn run() -> Result<()> {
msg.build_reply_tpl(&config, &account)? 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); let msg = Msg::from(content);
input::ask_for_confirmation("Send the message?")?; 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 msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
let tpl = msg.build_forward_tpl(&config, &account)?; 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); let msg = Msg::from(content);
input::ask_for_confirmation("Send the message?")?; input::ask_for_confirmation("Send the message?")?;

View file

@ -1,7 +1,10 @@
use lettre; use lettre;
use mailparse::{self, MailHeaderMap}; use mailparse::{self, MailHeaderMap};
use rfc2047_decoder; use rfc2047_decoder;
use serde::Serialize; use serde::{
ser::{self, SerializeStruct},
Serialize,
};
use std::{fmt, result}; use std::{fmt, result};
use crate::config::{Account, Config}; use crate::config::{Account, Config};
@ -41,7 +44,29 @@ impl From<lettre::error::Error> for Error {
type Result<T> = result::Result<T, Error>; type Result<T> = result::Result<T, Error>;
// 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<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let mut state = serializer.serialize_struct("Tpl", 1)?;
state.serialize_field("template", &self.0)?;
state.end()
}
}
// Message
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct Msg { pub struct Msg {
@ -228,7 +253,7 @@ impl<'a> Msg {
Ok(parts) Ok(parts)
} }
pub fn build_new_tpl(config: &Config, account: &Account) -> Result<String> { pub fn build_new_tpl(config: &Config, account: &Account) -> Result<Tpl> {
let mut tpl = vec![]; let mut tpl = vec![];
// "From" header // "From" header
@ -240,10 +265,10 @@ impl<'a> Msg {
// "Subject" header // "Subject" header
tpl.push("Subject: ".to_string()); 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<String> { pub fn build_reply_tpl(&self, config: &Config, account: &Account) -> Result<Tpl> {
let msg = &self.parse()?; let msg = &self.parse()?;
let headers = msg.get_headers(); let headers = msg.get_headers();
let mut tpl = vec![]; let mut tpl = vec![];
@ -271,19 +296,19 @@ impl<'a> Msg {
tpl.push(String::new()); tpl.push(String::new());
// Original msg prepend with ">" // Original msg prepend with ">"
let thread = msg let thread = self
.get_body() .text_bodies("text/plain")?
.unwrap() .replace("\r", "")
.split("\r\n") .split("\n")
.map(|line| format!(">{}", line)) .map(|line| format!(">{}", line))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\r\n"); .join("\r\n");
tpl.push(thread); 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<String> { pub fn build_reply_all_tpl(&self, config: &Config, account: &Account) -> Result<Tpl> {
let msg = &self.parse()?; let msg = &self.parse()?;
let headers = msg.get_headers(); let headers = msg.get_headers();
let mut tpl = vec![]; let mut tpl = vec![];
@ -353,19 +378,18 @@ impl<'a> Msg {
tpl.push(String::new()); tpl.push(String::new());
// Original msg prepend with ">" // Original msg prepend with ">"
let thread = msg let thread = self
.get_body() .text_bodies("text/plain")?
.unwrap()
.split("\r\n") .split("\r\n")
.map(|line| format!(">{}", line)) .map(|line| format!(">{}", line))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\r\n"); .join("\r\n");
tpl.push(thread); 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<String> { pub fn build_forward_tpl(&self, config: &Config, account: &Account) -> Result<Tpl> {
let msg = &self.parse()?; let msg = &self.parse()?;
let headers = msg.get_headers(); let headers = msg.get_headers();
let mut tpl = vec![]; let mut tpl = vec![];
@ -385,9 +409,9 @@ impl<'a> Msg {
// Original msg // Original msg
tpl.push("-------- Forwarded Message --------".to_string()); 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")))
} }
} }