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;
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?")?;

View file

@ -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<lettre::error::Error> for 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)]
pub struct Msg {
@ -228,7 +253,7 @@ impl<'a> Msg {
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![];
// "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<String> {
pub fn build_reply_tpl(&self, config: &Config, account: &Account) -> Result<Tpl> {
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::<Vec<String>>()
.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<String> {
pub fn build_reply_all_tpl(&self, config: &Config, account: &Account) -> Result<Tpl> {
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::<Vec<String>>()
.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<String> {
pub fn build_forward_tpl(&self, config: &Config, account: &Account) -> Result<Tpl> {
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")))
}
}