mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-20 07:01:12 +00:00
refactor tpl system (#80)
This commit is contained in:
parent
62ac9aef2c
commit
f71720e6b9
|
@ -84,6 +84,7 @@ all the options.*
|
||||||
[![paypal](https://img.shields.io/badge/-PayPal-0079c1?logo=PayPal&logoColor=ffffff)](https://www.paypal.com/paypalme/soywod)
|
[![paypal](https://img.shields.io/badge/-PayPal-0079c1?logo=PayPal&logoColor=ffffff)](https://www.paypal.com/paypalme/soywod)
|
||||||
[![ko-fi](https://img.shields.io/badge/-Ko--fi-ff5e5a?logo=Ko-fi&logoColor=ffffff)](https://ko-fi.com/soywod)
|
[![ko-fi](https://img.shields.io/badge/-Ko--fi-ff5e5a?logo=Ko-fi&logoColor=ffffff)](https://ko-fi.com/soywod)
|
||||||
[![buy-me-a-coffee](https://img.shields.io/badge/-Buy%20Me%20a%20Coffee-ffdd00?logo=Buy%20Me%20A%20Coffee&logoColor=000000)](https://www.buymeacoffee.com/soywod)
|
[![buy-me-a-coffee](https://img.shields.io/badge/-Buy%20Me%20a%20Coffee-ffdd00?logo=Buy%20Me%20A%20Coffee&logoColor=000000)](https://www.buymeacoffee.com/soywod)
|
||||||
|
[![liberapay](https://img.shields.io/badge/-Liberapay-f6c915?logo=Liberapay&logoColor=222222)](https://liberapay.com/soywod)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ const DEFAULT_PAGE_SIZE: usize = 10;
|
||||||
|
|
||||||
// Account
|
// Account
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
// Override
|
// Override
|
||||||
|
|
|
@ -20,25 +20,22 @@ fn flags_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||||
|
|
||||||
pub fn flag_subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
pub fn flag_subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
vec![clap::SubCommand::with_name("flags")
|
vec![clap::SubCommand::with_name("flags")
|
||||||
.aliases(&["flag"])
|
|
||||||
.about("Handles flags")
|
.about("Handles flags")
|
||||||
.subcommand(
|
.subcommand(
|
||||||
clap::SubCommand::with_name("set")
|
clap::SubCommand::with_name("set")
|
||||||
.aliases(&["s"])
|
|
||||||
.about("Replaces all message flags")
|
.about("Replaces all message flags")
|
||||||
.arg(uid_arg())
|
.arg(uid_arg())
|
||||||
.arg(flags_arg()),
|
.arg(flags_arg()),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
clap::SubCommand::with_name("add")
|
clap::SubCommand::with_name("add")
|
||||||
.aliases(&["a"])
|
|
||||||
.about("Appends flags to a message")
|
.about("Appends flags to a message")
|
||||||
.arg(uid_arg())
|
.arg(uid_arg())
|
||||||
.arg(flags_arg()),
|
.arg(flags_arg()),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
clap::SubCommand::with_name("remove")
|
clap::SubCommand::with_name("remove")
|
||||||
.aliases(&["rm", "r"])
|
.aliases(&["rm"])
|
||||||
.about("Removes flags from a message")
|
.about("Removes flags from a message")
|
||||||
.arg(uid_arg())
|
.arg(uid_arg())
|
||||||
.arg(flags_arg()),
|
.arg(flags_arg()),
|
||||||
|
|
|
@ -31,6 +31,7 @@ fn parse_args<'a>() -> clap::App<'a, 'a> {
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.about(env!("CARGO_PKG_DESCRIPTION"))
|
.about(env!("CARGO_PKG_DESCRIPTION"))
|
||||||
.author(env!("CARGO_PKG_AUTHORS"))
|
.author(env!("CARGO_PKG_AUTHORS"))
|
||||||
|
.setting(clap::AppSettings::InferSubcommands)
|
||||||
.args(&output_args())
|
.args(&output_args())
|
||||||
.args(&config_args())
|
.args(&config_args())
|
||||||
.arg(mbox_source_arg())
|
.arg(mbox_source_arg())
|
||||||
|
|
117
src/msg/cli.rs
117
src/msg/cli.rs
|
@ -13,7 +13,13 @@ use crate::{
|
||||||
imap::model::ImapConnector,
|
imap::model::ImapConnector,
|
||||||
input,
|
input,
|
||||||
mbox::cli::mbox_target_arg,
|
mbox::cli::mbox_target_arg,
|
||||||
msg::model::{Attachments, Msg, Msgs, ReadableMsg},
|
msg::{
|
||||||
|
model::{Attachments, Msg, Msgs, ReadableMsg},
|
||||||
|
tpl::{
|
||||||
|
cli::{tpl_matches, tpl_subcommand},
|
||||||
|
model::Tpl,
|
||||||
|
},
|
||||||
|
},
|
||||||
smtp,
|
smtp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,6 +28,7 @@ error_chain! {
|
||||||
Imap(crate::imap::model::Error, crate::imap::model::ErrorKind);
|
Imap(crate::imap::model::Error, crate::imap::model::ErrorKind);
|
||||||
Input(crate::input::Error, crate::input::ErrorKind);
|
Input(crate::input::Error, crate::input::ErrorKind);
|
||||||
MsgModel(crate::msg::model::Error, crate::msg::model::ErrorKind);
|
MsgModel(crate::msg::model::Error, crate::msg::model::ErrorKind);
|
||||||
|
TplCli(crate::msg::tpl::cli::Error, crate::msg::tpl::cli::ErrorKind);
|
||||||
Smtp(crate::smtp::Error, crate::smtp::ErrorKind);
|
Smtp(crate::smtp::Error, crate::smtp::ErrorKind);
|
||||||
}
|
}
|
||||||
foreign_links {
|
foreign_links {
|
||||||
|
@ -67,18 +74,17 @@ fn attachment_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||||
.long("attachment")
|
.long("attachment")
|
||||||
.value_name("PATH")
|
.value_name("PATH")
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.takes_value(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn msg_subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
pub fn msg_subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
vec![
|
vec![
|
||||||
clap::SubCommand::with_name("list")
|
clap::SubCommand::with_name("list")
|
||||||
.aliases(&["lst", "l"])
|
.aliases(&["lst"])
|
||||||
.about("Lists all messages")
|
.about("Lists all messages")
|
||||||
.arg(page_size_arg())
|
.arg(page_size_arg())
|
||||||
.arg(page_arg()),
|
.arg(page_arg()),
|
||||||
clap::SubCommand::with_name("search")
|
clap::SubCommand::with_name("search")
|
||||||
.aliases(&["query", "q", "s"])
|
.aliases(&["query", "q"])
|
||||||
.about("Lists messages matching the given IMAP query")
|
.about("Lists messages matching the given IMAP query")
|
||||||
.arg(page_size_arg())
|
.arg(page_size_arg())
|
||||||
.arg(page_arg())
|
.arg(page_arg())
|
||||||
|
@ -90,7 +96,6 @@ pub fn msg_subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
.required(true),
|
.required(true),
|
||||||
),
|
),
|
||||||
clap::SubCommand::with_name("write")
|
clap::SubCommand::with_name("write")
|
||||||
.aliases(&["w"])
|
|
||||||
.about("Writes a new message")
|
.about("Writes a new message")
|
||||||
.arg(attachment_arg()),
|
.arg(attachment_arg()),
|
||||||
clap::SubCommand::with_name("send")
|
clap::SubCommand::with_name("send")
|
||||||
|
@ -100,7 +105,6 @@ pub fn msg_subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
.about("Saves a raw message")
|
.about("Saves a raw message")
|
||||||
.arg(clap::Arg::with_name("message").raw(true)),
|
.arg(clap::Arg::with_name("message").raw(true)),
|
||||||
clap::SubCommand::with_name("read")
|
clap::SubCommand::with_name("read")
|
||||||
.aliases(&["r"])
|
|
||||||
.about("Reads text bodies of a message")
|
.about("Reads text bodies of a message")
|
||||||
.arg(uid_arg())
|
.arg(uid_arg())
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -119,55 +123,33 @@ pub fn msg_subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
.short("r"),
|
.short("r"),
|
||||||
),
|
),
|
||||||
clap::SubCommand::with_name("attachments")
|
clap::SubCommand::with_name("attachments")
|
||||||
.aliases(&["attach", "att", "a"])
|
|
||||||
.about("Downloads all message attachments")
|
.about("Downloads all message attachments")
|
||||||
.arg(uid_arg()),
|
.arg(uid_arg()),
|
||||||
clap::SubCommand::with_name("reply")
|
clap::SubCommand::with_name("reply")
|
||||||
.aliases(&["rep", "re"])
|
|
||||||
.about("Answers to a message")
|
.about("Answers to a message")
|
||||||
.arg(uid_arg())
|
.arg(uid_arg())
|
||||||
.arg(reply_all_arg())
|
.arg(reply_all_arg())
|
||||||
.arg(attachment_arg()),
|
.arg(attachment_arg()),
|
||||||
clap::SubCommand::with_name("forward")
|
clap::SubCommand::with_name("forward")
|
||||||
.aliases(&["fwd", "f"])
|
.aliases(&["fwd"])
|
||||||
.about("Forwards a message")
|
.about("Forwards a message")
|
||||||
.arg(uid_arg())
|
.arg(uid_arg())
|
||||||
.arg(attachment_arg()),
|
.arg(attachment_arg()),
|
||||||
clap::SubCommand::with_name("copy")
|
clap::SubCommand::with_name("copy")
|
||||||
.aliases(&["cp", "c"])
|
.aliases(&["cp"])
|
||||||
.about("Copies a message to the targetted mailbox")
|
.about("Copies a message to the targetted mailbox")
|
||||||
.arg(uid_arg())
|
.arg(uid_arg())
|
||||||
.arg(mbox_target_arg()),
|
.arg(mbox_target_arg()),
|
||||||
clap::SubCommand::with_name("move")
|
clap::SubCommand::with_name("move")
|
||||||
.aliases(&["mv", "m"])
|
.aliases(&["mv"])
|
||||||
.about("Moves a message to the targetted mailbox")
|
.about("Moves a message to the targetted mailbox")
|
||||||
.arg(uid_arg())
|
.arg(uid_arg())
|
||||||
.arg(mbox_target_arg()),
|
.arg(mbox_target_arg()),
|
||||||
clap::SubCommand::with_name("delete")
|
clap::SubCommand::with_name("delete")
|
||||||
.aliases(&["remove", "rm", "del", "d"])
|
.aliases(&["remove", "rm"])
|
||||||
.about("Deletes a message")
|
.about("Deletes a message")
|
||||||
.arg(uid_arg()),
|
.arg(uid_arg()),
|
||||||
clap::SubCommand::with_name("template")
|
tpl_subcommand(),
|
||||||
.aliases(&["tpl", "t"])
|
|
||||||
.about("Generates a message template")
|
|
||||||
.subcommand(
|
|
||||||
clap::SubCommand::with_name("new")
|
|
||||||
.aliases(&["n"])
|
|
||||||
.about("Generates a new message template"),
|
|
||||||
)
|
|
||||||
.subcommand(
|
|
||||||
clap::SubCommand::with_name("reply")
|
|
||||||
.aliases(&["rep", "r"])
|
|
||||||
.about("Generates a reply message template")
|
|
||||||
.arg(uid_arg())
|
|
||||||
.arg(reply_all_arg()),
|
|
||||||
)
|
|
||||||
.subcommand(
|
|
||||||
clap::SubCommand::with_name("forward")
|
|
||||||
.aliases(&["fwd", "fw", "f"])
|
|
||||||
.about("Generates a forward message template")
|
|
||||||
.arg(uid_arg()),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,9 +165,10 @@ pub fn msg_matches(app: &App) -> Result<bool> {
|
||||||
("save", Some(matches)) => msg_matches_save(app, matches),
|
("save", Some(matches)) => msg_matches_save(app, matches),
|
||||||
("search", Some(matches)) => msg_matches_search(app, matches),
|
("search", Some(matches)) => msg_matches_search(app, matches),
|
||||||
("send", Some(matches)) => msg_matches_send(app, matches),
|
("send", Some(matches)) => msg_matches_send(app, matches),
|
||||||
("template", Some(matches)) => msg_matches_template(app, matches),
|
|
||||||
("write", Some(matches)) => msg_matches_write(app, matches),
|
("write", Some(matches)) => msg_matches_write(app, matches),
|
||||||
|
|
||||||
|
("template", Some(matches)) => Ok(tpl_matches(app, matches)?),
|
||||||
|
|
||||||
("list", opt_matches) => msg_matches_list(app, opt_matches),
|
("list", opt_matches) => msg_matches_list(app, opt_matches),
|
||||||
(_other, opt_matches) => msg_matches_list(app, opt_matches),
|
(_other, opt_matches) => msg_matches_list(app, opt_matches),
|
||||||
}
|
}
|
||||||
|
@ -195,19 +178,11 @@ fn msg_matches_list(app: &App, opt_matches: Option<&clap::ArgMatches>) -> Result
|
||||||
debug!("list command matched");
|
debug!("list command matched");
|
||||||
|
|
||||||
let page_size: usize = opt_matches
|
let page_size: usize = opt_matches
|
||||||
.and_then(|matches| {
|
.and_then(|matches| matches.value_of("page-size").and_then(|s| s.parse().ok()))
|
||||||
matches.value_of("page-size")
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| app.config.default_page_size(&app.account));
|
.unwrap_or_else(|| app.config.default_page_size(&app.account));
|
||||||
debug!("page size: {:?}", page_size);
|
debug!("page size: {:?}", page_size);
|
||||||
let page: usize = opt_matches
|
let page: usize = opt_matches
|
||||||
.and_then(|matches| {
|
.and_then(|matches| matches.value_of("page").unwrap().parse().ok())
|
||||||
matches.value_of("page")
|
|
||||||
.unwrap()
|
|
||||||
.parse()
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
debug!("page: {}", &page);
|
debug!("page: {}", &page);
|
||||||
|
|
||||||
|
@ -294,8 +269,8 @@ fn msg_matches_read(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
let mut imap_conn = ImapConnector::new(&app.account)?;
|
let mut imap_conn = ImapConnector::new(&app.account)?;
|
||||||
let msg = imap_conn.read_msg(&app.mbox, &uid)?;
|
let msg = imap_conn.read_msg(&app.mbox, &uid)?;
|
||||||
if raw {
|
if raw {
|
||||||
let msg = String::from_utf8(msg)
|
let msg =
|
||||||
.chain_err(|| "Could not decode raw message as utf8 string")?;
|
String::from_utf8(msg).chain_err(|| "Could not decode raw message as utf8 string")?;
|
||||||
let msg = msg.trim_end_matches("\n");
|
let msg = msg.trim_end_matches("\n");
|
||||||
app.output.print(msg);
|
app.output.print(msg);
|
||||||
} else {
|
} else {
|
||||||
|
@ -352,7 +327,7 @@ fn msg_matches_write(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.map(String::from)
|
.map(String::from)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let tpl = Msg::build_new_tpl(&app.config, &app.account)?;
|
let tpl = Tpl::new(&app);
|
||||||
let content = input::open_editor_with_tpl(tpl.to_string().as_bytes())?;
|
let content = input::open_editor_with_tpl(tpl.to_string().as_bytes())?;
|
||||||
let mut msg = Msg::from(content);
|
let mut msg = Msg::from(content);
|
||||||
msg.attachments = attachments;
|
msg.attachments = attachments;
|
||||||
|
@ -513,53 +488,6 @@ fn msg_matches_forward(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_template(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
|
||||||
debug!("template command matched");
|
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("new") {
|
|
||||||
debug!("new command matched");
|
|
||||||
let tpl = Msg::build_new_tpl(&app.config, &app.account)?;
|
|
||||||
trace!("tpl: {:?}", tpl);
|
|
||||||
app.output.print(tpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("reply") {
|
|
||||||
debug!("reply command matched");
|
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
|
||||||
debug!("uid: {}", uid);
|
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&app.account)?;
|
|
||||||
let msg = Msg::from(imap_conn.read_msg(&app.mbox, &uid)?);
|
|
||||||
let tpl = if matches.is_present("reply-all") {
|
|
||||||
msg.build_reply_all_tpl(&app.config, &app.account)?
|
|
||||||
} else {
|
|
||||||
msg.build_reply_tpl(&app.config, &app.account)?
|
|
||||||
};
|
|
||||||
trace!("tpl: {:?}", tpl);
|
|
||||||
app.output.print(tpl);
|
|
||||||
|
|
||||||
imap_conn.logout();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("forward") {
|
|
||||||
debug!("forward command matched");
|
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
|
||||||
debug!("uid: {}", uid);
|
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&app.account)?;
|
|
||||||
let msg = Msg::from(imap_conn.read_msg(&app.mbox, &uid)?);
|
|
||||||
let tpl = msg.build_forward_tpl(&app.config, &app.account)?;
|
|
||||||
trace!("tpl: {:?}", tpl);
|
|
||||||
app.output.print(tpl);
|
|
||||||
|
|
||||||
imap_conn.logout();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn msg_matches_copy(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
fn msg_matches_copy(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
debug!("copy command matched");
|
debug!("copy command matched");
|
||||||
|
|
||||||
|
@ -665,4 +593,3 @@ fn msg_matches_save(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
|
pub mod tpl;
|
||||||
|
|
|
@ -100,23 +100,23 @@ impl<'a> Attachments {
|
||||||
|
|
||||||
// Readable message
|
// Readable message
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct ReadableMsg {
|
pub struct ReadableMsg {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub has_attachment: bool,
|
pub has_attachment: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for ReadableMsg {
|
// impl Serialize for ReadableMsg {
|
||||||
fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
|
// fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
|
||||||
where
|
// where
|
||||||
S: ser::Serializer,
|
// S: ser::Serializer,
|
||||||
{
|
// {
|
||||||
let mut state = serializer.serialize_struct("ReadableMsg", 2)?;
|
// let mut state = serializer.serialize_struct("ReadableMsg", 2)?;
|
||||||
state.serialize_field("content", &self.content)?;
|
// state.serialize_field("content", &self.content)?;
|
||||||
state.serialize_field("hasAttachment", if self.has_attachment { &1 } else { &0 })?;
|
// state.serialize_field("hasAttachment", if self.has_attachment { &1 } else { &0 })?;
|
||||||
state.end()
|
// state.end()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl fmt::Display for ReadableMsg {
|
impl fmt::Display for ReadableMsg {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
@ -374,7 +374,11 @@ impl<'m> Msg<'m> {
|
||||||
Ok(msg)
|
Ok(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_text_bodies_into(part: &mailparse::ParsedMail, mime: &str, parts: &mut Vec<String>) {
|
pub fn extract_text_bodies_into(
|
||||||
|
part: &mailparse::ParsedMail,
|
||||||
|
mime: &str,
|
||||||
|
parts: &mut Vec<String>,
|
||||||
|
) {
|
||||||
match part.subparts.len() {
|
match part.subparts.len() {
|
||||||
0 => {
|
0 => {
|
||||||
let content_type = part
|
let content_type = part
|
||||||
|
|
302
src/msg/tpl/cli.rs
Normal file
302
src/msg/tpl/cli.rs
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
use clap;
|
||||||
|
use error_chain::error_chain;
|
||||||
|
use log::{debug, trace};
|
||||||
|
use mailparse;
|
||||||
|
|
||||||
|
use crate::{app::App, imap::model::ImapConnector, msg::tpl::model::Tpl};
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
links {
|
||||||
|
Imap(crate::imap::model::Error, crate::imap::model::ErrorKind);
|
||||||
|
}
|
||||||
|
foreign_links {
|
||||||
|
Clap(clap::Error);
|
||||||
|
MailParse(mailparse::MailParseError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uid_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||||
|
clap::Arg::with_name("uid")
|
||||||
|
.help("Specifies the targetted message")
|
||||||
|
.value_name("UID")
|
||||||
|
.required(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reply_all_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||||
|
clap::Arg::with_name("reply-all")
|
||||||
|
.help("Includes all recipients")
|
||||||
|
.short("A")
|
||||||
|
.long("all")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tpl_subcommand<'a>() -> clap::App<'a, 'a> {
|
||||||
|
clap::SubCommand::with_name("template")
|
||||||
|
.aliases(&["tpl"])
|
||||||
|
.about("Generates a message template")
|
||||||
|
.subcommand(
|
||||||
|
clap::SubCommand::with_name("new")
|
||||||
|
.aliases(&["n"])
|
||||||
|
.about("Generates a new message template")
|
||||||
|
.args(&tpl_args()),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::SubCommand::with_name("reply")
|
||||||
|
.aliases(&["rep", "r"])
|
||||||
|
.about("Generates a reply message template")
|
||||||
|
.arg(uid_arg())
|
||||||
|
.arg(reply_all_arg())
|
||||||
|
.args(&tpl_args()),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::SubCommand::with_name("forward")
|
||||||
|
.aliases(&["fwd", "fw", "f"])
|
||||||
|
.about("Generates a forward message template")
|
||||||
|
.arg(uid_arg())
|
||||||
|
.args(&tpl_args()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tpl_args<'a>() -> Vec<clap::Arg<'a, 'a>> {
|
||||||
|
vec![
|
||||||
|
clap::Arg::with_name("subject")
|
||||||
|
.help("Overrides the Subject header")
|
||||||
|
.short("s")
|
||||||
|
.long("subject")
|
||||||
|
.value_name("STRING"),
|
||||||
|
clap::Arg::with_name("from")
|
||||||
|
.help("Overrides the From header")
|
||||||
|
.short("f")
|
||||||
|
.long("from")
|
||||||
|
.value_name("ADDR"),
|
||||||
|
clap::Arg::with_name("to")
|
||||||
|
.help("Overrides the To header")
|
||||||
|
.short("t")
|
||||||
|
.long("to")
|
||||||
|
.value_name("ADDR")
|
||||||
|
.multiple(true),
|
||||||
|
clap::Arg::with_name("cc")
|
||||||
|
.help("Overrides the Cc header")
|
||||||
|
.short("c")
|
||||||
|
.long("cc")
|
||||||
|
.value_name("ADDR")
|
||||||
|
.multiple(true),
|
||||||
|
clap::Arg::with_name("bcc")
|
||||||
|
.help("Overrides the Bcc header")
|
||||||
|
.short("b")
|
||||||
|
.long("bcc")
|
||||||
|
.value_name("ADDR")
|
||||||
|
.multiple(true),
|
||||||
|
clap::Arg::with_name("header")
|
||||||
|
.help("Overrides a specific header")
|
||||||
|
.short("h")
|
||||||
|
.long("header")
|
||||||
|
.value_name("KEY: VAL")
|
||||||
|
.multiple(true),
|
||||||
|
clap::Arg::with_name("body")
|
||||||
|
.help("Overrides the body")
|
||||||
|
.short("B")
|
||||||
|
.long("body")
|
||||||
|
.value_name("STRING"),
|
||||||
|
clap::Arg::with_name("signature")
|
||||||
|
.help("Overrides the signature")
|
||||||
|
.short("S")
|
||||||
|
.long("signature")
|
||||||
|
.value_name("STRING"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tpl_matches(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
|
match matches.subcommand() {
|
||||||
|
("new", Some(matches)) => tpl_matches_new(app, matches),
|
||||||
|
("reply", Some(matches)) => tpl_matches_reply(app, matches),
|
||||||
|
("forward", Some(matches)) => tpl_matches_forward(app, matches),
|
||||||
|
|
||||||
|
// TODO: find a way to show the help message for template subcommand
|
||||||
|
_ => Err("Subcommand not found".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tpl_matches_new(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
|
debug!("new command matched");
|
||||||
|
let mut tpl = Tpl::new(&app);
|
||||||
|
|
||||||
|
if let Some(from) = matches.value_of("from") {
|
||||||
|
debug!("overriden from: {:?}", from);
|
||||||
|
tpl.header("From", from);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(subject) = matches.value_of("subject") {
|
||||||
|
debug!("overriden subject: {:?}", subject);
|
||||||
|
tpl.header("Subject", subject);
|
||||||
|
};
|
||||||
|
|
||||||
|
let addrs = matches.values_of("to").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden to: {:?}", addrs);
|
||||||
|
tpl.header("To", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
let addrs = matches.values_of("cc").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden cc: {:?}", addrs);
|
||||||
|
tpl.header("Cc", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
let addrs = matches.values_of("bcc").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden bcc: {:?}", addrs);
|
||||||
|
tpl.header("Bcc", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
for header in matches.values_of("header").unwrap_or_default() {
|
||||||
|
let mut header = header.split(":");
|
||||||
|
let key = header.next().unwrap_or_default();
|
||||||
|
let val = header.next().unwrap_or_default().trim_start();
|
||||||
|
debug!("overriden header: {}={}", key, val);
|
||||||
|
tpl.header(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(body) = matches.value_of("body") {
|
||||||
|
debug!("overriden body: {:?}", body);
|
||||||
|
tpl.body(body);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(signature) = matches.value_of("signature") {
|
||||||
|
debug!("overriden signature: {:?}", signature);
|
||||||
|
tpl.signature(signature);
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("tpl: {:?}", tpl);
|
||||||
|
app.output.print(tpl);
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tpl_matches_reply(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
|
debug!("reply command matched");
|
||||||
|
|
||||||
|
let uid = matches.value_of("uid").unwrap();
|
||||||
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
|
let mut imap_conn = ImapConnector::new(&app.account)?;
|
||||||
|
let msg = &imap_conn.read_msg(&app.mbox, &uid)?;
|
||||||
|
let msg = mailparse::parse_mail(&msg)?;
|
||||||
|
let mut tpl = if matches.is_present("reply-all") {
|
||||||
|
Tpl::reply(&app, &msg)
|
||||||
|
} else {
|
||||||
|
Tpl::reply_all(&app, &msg)
|
||||||
|
};
|
||||||
|
if let Some(from) = matches.value_of("from") {
|
||||||
|
debug!("overriden from: {:?}", from);
|
||||||
|
tpl.header("From", from);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(subject) = matches.value_of("subject") {
|
||||||
|
debug!("overriden subject: {:?}", subject);
|
||||||
|
tpl.header("Subject", subject);
|
||||||
|
};
|
||||||
|
|
||||||
|
let addrs = matches.values_of("to").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden to: {:?}", addrs);
|
||||||
|
tpl.header("To", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
let addrs = matches.values_of("cc").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden cc: {:?}", addrs);
|
||||||
|
tpl.header("Cc", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
let addrs = matches.values_of("bcc").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden bcc: {:?}", addrs);
|
||||||
|
tpl.header("Bcc", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
for header in matches.values_of("header").unwrap_or_default() {
|
||||||
|
let mut header = header.split(":");
|
||||||
|
let key = header.next().unwrap_or_default();
|
||||||
|
let val = header.next().unwrap_or_default().trim_start();
|
||||||
|
debug!("overriden header: {}={}", key, val);
|
||||||
|
tpl.header(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(body) = matches.value_of("body") {
|
||||||
|
debug!("overriden body: {:?}", body);
|
||||||
|
tpl.body(body);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(signature) = matches.value_of("signature") {
|
||||||
|
debug!("overriden signature: {:?}", signature);
|
||||||
|
tpl.signature(signature);
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("tpl: {:?}", tpl);
|
||||||
|
app.output.print(tpl);
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tpl_matches_forward(app: &App, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
|
debug!("forward command matched");
|
||||||
|
|
||||||
|
let uid = matches.value_of("uid").unwrap();
|
||||||
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
|
let mut imap_conn = ImapConnector::new(&app.account)?;
|
||||||
|
let msg = &imap_conn.read_msg(&app.mbox, &uid)?;
|
||||||
|
let msg = mailparse::parse_mail(&msg)?;
|
||||||
|
let mut tpl = Tpl::forward(&app, &msg);
|
||||||
|
|
||||||
|
if let Some(from) = matches.value_of("from") {
|
||||||
|
debug!("overriden from: {:?}", from);
|
||||||
|
tpl.header("From", from);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(subject) = matches.value_of("subject") {
|
||||||
|
debug!("overriden subject: {:?}", subject);
|
||||||
|
tpl.header("Subject", subject);
|
||||||
|
};
|
||||||
|
|
||||||
|
let addrs = matches.values_of("to").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden to: {:?}", addrs);
|
||||||
|
tpl.header("To", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
let addrs = matches.values_of("cc").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden cc: {:?}", addrs);
|
||||||
|
tpl.header("Cc", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
let addrs = matches.values_of("bcc").unwrap_or_default();
|
||||||
|
if addrs.len() > 0 {
|
||||||
|
debug!("overriden bcc: {:?}", addrs);
|
||||||
|
tpl.header("Bcc", addrs.collect::<Vec<_>>().join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
for header in matches.values_of("header").unwrap_or_default() {
|
||||||
|
let mut header = header.split(":");
|
||||||
|
let key = header.next().unwrap_or_default();
|
||||||
|
let val = header.next().unwrap_or_default().trim_start();
|
||||||
|
debug!("overriden header: {}={}", key, val);
|
||||||
|
tpl.header(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(body) = matches.value_of("body") {
|
||||||
|
debug!("overriden body: {:?}", body);
|
||||||
|
tpl.body(body);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(signature) = matches.value_of("signature") {
|
||||||
|
debug!("overriden signature: {:?}", signature);
|
||||||
|
tpl.signature(signature);
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("tpl: {:?}", tpl);
|
||||||
|
app.output.print(tpl);
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
2
src/msg/tpl/mod.rs
Normal file
2
src/msg/tpl/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod cli;
|
||||||
|
pub mod model;
|
636
src/msg/tpl/model.rs
Normal file
636
src/msg/tpl/model.rs
Normal file
|
@ -0,0 +1,636 @@
|
||||||
|
use error_chain::error_chain;
|
||||||
|
use mailparse::{self, MailHeaderMap};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
|
use crate::{app::App, msg::model::Msg};
|
||||||
|
|
||||||
|
error_chain! {}
|
||||||
|
|
||||||
|
const TPL_HEADERS: &[&str] = &["From", "To", "In-Reply-To", "Cc", "Bcc", "Subject"];
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct Tpl {
|
||||||
|
headers: HashMap<String, String>,
|
||||||
|
body: Option<String>,
|
||||||
|
signature: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tpl {
|
||||||
|
pub fn new(app: &App) -> Self {
|
||||||
|
let mut headers = HashMap::new();
|
||||||
|
headers.insert("From".to_string(), app.config.address(app.account));
|
||||||
|
headers.insert("To".to_string(), String::new());
|
||||||
|
headers.insert("Subject".to_string(), String::new());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
headers,
|
||||||
|
body: None,
|
||||||
|
signature: app.config.signature(app.account),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply(app: &App, msg: &mailparse::ParsedMail) -> Self {
|
||||||
|
let parsed_headers = msg.get_headers();
|
||||||
|
let mut headers = HashMap::new();
|
||||||
|
|
||||||
|
headers.insert("From".to_string(), app.config.address(app.account));
|
||||||
|
|
||||||
|
let to = parsed_headers
|
||||||
|
.get_first_value("reply-to")
|
||||||
|
.or(parsed_headers.get_first_value("from"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
headers.insert("To".to_string(), to);
|
||||||
|
|
||||||
|
if let Some(in_reply_to) = parsed_headers.get_first_value("message-id") {
|
||||||
|
headers.insert("In-Reply-To".to_string(), in_reply_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
let subject = parsed_headers
|
||||||
|
.get_first_value("subject")
|
||||||
|
.unwrap_or_default();
|
||||||
|
headers.insert("Subject".to_string(), format!("Re: {}", subject));
|
||||||
|
|
||||||
|
let mut parts = vec![];
|
||||||
|
Msg::extract_text_bodies_into(&msg, "text/plain", &mut parts);
|
||||||
|
if parts.is_empty() {
|
||||||
|
Msg::extract_text_bodies_into(&msg, "text/html", &mut parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = parts
|
||||||
|
.join("\r\n\r\n")
|
||||||
|
.replace("\r", "")
|
||||||
|
.split("\n")
|
||||||
|
.map(|line| format!(">{}", line))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
headers,
|
||||||
|
body: Some(body),
|
||||||
|
signature: app.config.signature(&app.account),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply_all(app: &App, msg: &mailparse::ParsedMail) -> Self {
|
||||||
|
let parsed_headers = msg.get_headers();
|
||||||
|
let mut headers = HashMap::new();
|
||||||
|
|
||||||
|
let from: lettre::message::Mailbox = app.config.address(app.account).parse().unwrap();
|
||||||
|
headers.insert("From".to_string(), from.to_string());
|
||||||
|
|
||||||
|
let to = parsed_headers
|
||||||
|
.get_all_values("to")
|
||||||
|
.iter()
|
||||||
|
.flat_map(|addrs| addrs.split(","))
|
||||||
|
.fold(vec![], |mut mboxes, addr| {
|
||||||
|
match addr.trim().parse::<lettre::message::Mailbox>() {
|
||||||
|
Err(_) => mboxes,
|
||||||
|
Ok(mbox) => {
|
||||||
|
if mbox != from {
|
||||||
|
mboxes.push(mbox.to_string());
|
||||||
|
}
|
||||||
|
mboxes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let reply_to = parsed_headers
|
||||||
|
.get_all_values("reply-to")
|
||||||
|
.iter()
|
||||||
|
.flat_map(|addrs| addrs.split(","))
|
||||||
|
.map(|addr| addr.trim().to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
let reply_to = if reply_to.is_empty() {
|
||||||
|
parsed_headers
|
||||||
|
.get_all_values("from")
|
||||||
|
.iter()
|
||||||
|
.flat_map(|addrs| addrs.split(","))
|
||||||
|
.map(|addr| addr.trim().to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
} else {
|
||||||
|
reply_to
|
||||||
|
};
|
||||||
|
headers.insert("To".to_string(), [reply_to, to].concat().join(", "));
|
||||||
|
|
||||||
|
if let Some(in_reply_to) = parsed_headers.get_first_value("message-id") {
|
||||||
|
headers.insert("In-Reply-To".to_string(), in_reply_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cc = parsed_headers.get_all_values("cc");
|
||||||
|
if !cc.is_empty() {
|
||||||
|
headers.insert("Cc".to_string(), cc.join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
let subject = parsed_headers
|
||||||
|
.get_first_value("subject")
|
||||||
|
.unwrap_or_default();
|
||||||
|
headers.insert("Subject".to_string(), format!("Re: {}", subject));
|
||||||
|
|
||||||
|
let mut parts = vec![];
|
||||||
|
Msg::extract_text_bodies_into(&msg, "text/plain", &mut parts);
|
||||||
|
if parts.is_empty() {
|
||||||
|
Msg::extract_text_bodies_into(&msg, "text/html", &mut parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = parts
|
||||||
|
.join("\r\n\r\n")
|
||||||
|
.replace("\r", "")
|
||||||
|
.split("\n")
|
||||||
|
.map(|line| format!(">{}", line))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
headers,
|
||||||
|
body: Some(body),
|
||||||
|
signature: app.config.signature(&app.account),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forward(app: &App, msg: &mailparse::ParsedMail) -> Self {
|
||||||
|
let parsed_headers = msg.get_headers();
|
||||||
|
let mut headers = HashMap::new();
|
||||||
|
|
||||||
|
headers.insert("From".to_string(), app.config.address(app.account));
|
||||||
|
headers.insert("To".to_string(), String::new());
|
||||||
|
let subject = parsed_headers
|
||||||
|
.get_first_value("subject")
|
||||||
|
.unwrap_or_default();
|
||||||
|
headers.insert("Subject".to_string(), format!("Fwd: {}", subject));
|
||||||
|
|
||||||
|
let mut parts = vec![];
|
||||||
|
Msg::extract_text_bodies_into(&msg, "text/plain", &mut parts);
|
||||||
|
if parts.is_empty() {
|
||||||
|
Msg::extract_text_bodies_into(&msg, "text/html", &mut parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body = String::from("-------- Forwarded Message --------\n");
|
||||||
|
body.push_str(&parts.join("\r\n\r\n").replace("\r", ""));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
headers,
|
||||||
|
body: Some(body),
|
||||||
|
signature: app.config.signature(&app.account),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header<K: ToString, V: ToString>(&mut self, key: K, val: V) -> &Self {
|
||||||
|
self.headers.insert(key.to_string(), val.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body<T: ToString>(&mut self, body: T) -> &Self {
|
||||||
|
self.body = Some(body.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature<T: ToString>(&mut self, signature: T) -> &Self {
|
||||||
|
self.signature = Some(signature.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Tpl {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let mut tpl = TPL_HEADERS.iter().fold(String::new(), |mut tpl, &key| {
|
||||||
|
if let Some(val) = self.headers.get(key) {
|
||||||
|
tpl.push_str(&format!("{}: {}\n", key, val));
|
||||||
|
};
|
||||||
|
tpl
|
||||||
|
});
|
||||||
|
|
||||||
|
for (key, val) in self.headers.iter() {
|
||||||
|
if !TPL_HEADERS.contains(&key.as_str()) {
|
||||||
|
tpl.push_str(&format!("{}: {}\n", key, val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl.push_str("\n");
|
||||||
|
|
||||||
|
if let Some(body) = self.body.as_ref() {
|
||||||
|
tpl.push_str(&body);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(signature) = self.signature.as_ref() {
|
||||||
|
tpl.push_str("\n\n");
|
||||||
|
tpl.push_str(&signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", tpl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
config::model::{Account, Config},
|
||||||
|
msg::tpl::model::Tpl,
|
||||||
|
output::model::Output,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_tpl() {
|
||||||
|
let account = Account {
|
||||||
|
name: Some(String::from("Test")),
|
||||||
|
downloads_dir: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
default: Some(true),
|
||||||
|
email: String::from("test@localhost"),
|
||||||
|
watch_cmds: None,
|
||||||
|
imap_host: String::new(),
|
||||||
|
imap_port: 0,
|
||||||
|
imap_starttls: None,
|
||||||
|
imap_insecure: None,
|
||||||
|
imap_login: String::new(),
|
||||||
|
imap_passwd_cmd: String::new(),
|
||||||
|
smtp_host: String::new(),
|
||||||
|
smtp_port: 0,
|
||||||
|
smtp_starttls: None,
|
||||||
|
smtp_insecure: None,
|
||||||
|
smtp_login: String::new(),
|
||||||
|
smtp_passwd_cmd: String::new(),
|
||||||
|
};
|
||||||
|
let config = Config {
|
||||||
|
name: String::new(),
|
||||||
|
downloads_dir: None,
|
||||||
|
notify_cmd: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
watch_cmds: None,
|
||||||
|
accounts: vec![(String::from("account"), account.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let output = Output::new("plain");
|
||||||
|
let mbox = String::new();
|
||||||
|
let arg_matches = clap::ArgMatches::new();
|
||||||
|
let app = App::new(&config, &account, &output, &mbox, &arg_matches);
|
||||||
|
let tpl = Tpl::new(&app);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"From: Test <test@localhost>\nTo: \nSubject: \n\n",
|
||||||
|
tpl.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_tpl_with_signature() {
|
||||||
|
let account = Account {
|
||||||
|
name: Some(String::from("Test")),
|
||||||
|
downloads_dir: None,
|
||||||
|
signature: Some(String::from("-- \nCordialement,")),
|
||||||
|
default_page_size: None,
|
||||||
|
default: Some(true),
|
||||||
|
email: String::from("test@localhost"),
|
||||||
|
watch_cmds: None,
|
||||||
|
imap_host: String::new(),
|
||||||
|
imap_port: 0,
|
||||||
|
imap_starttls: None,
|
||||||
|
imap_insecure: None,
|
||||||
|
imap_login: String::new(),
|
||||||
|
imap_passwd_cmd: String::new(),
|
||||||
|
smtp_host: String::new(),
|
||||||
|
smtp_port: 0,
|
||||||
|
smtp_starttls: None,
|
||||||
|
smtp_insecure: None,
|
||||||
|
smtp_login: String::new(),
|
||||||
|
smtp_passwd_cmd: String::new(),
|
||||||
|
};
|
||||||
|
let config = Config {
|
||||||
|
name: String::new(),
|
||||||
|
downloads_dir: None,
|
||||||
|
notify_cmd: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
watch_cmds: None,
|
||||||
|
accounts: vec![(String::from("account"), account.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let output = Output::new("plain");
|
||||||
|
let mbox = String::new();
|
||||||
|
let arg_matches = clap::ArgMatches::new();
|
||||||
|
let app = App::new(&config, &account, &output, &mbox, &arg_matches);
|
||||||
|
let tpl = Tpl::new(&app);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"From: Test <test@localhost>\nTo: \nSubject: \n\n\n\n-- \nCordialement,",
|
||||||
|
tpl.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reply_tpl() {
|
||||||
|
let account = Account {
|
||||||
|
name: Some(String::from("Test")),
|
||||||
|
downloads_dir: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
default: Some(true),
|
||||||
|
email: String::from("test@localhost"),
|
||||||
|
watch_cmds: None,
|
||||||
|
imap_host: String::new(),
|
||||||
|
imap_port: 0,
|
||||||
|
imap_starttls: None,
|
||||||
|
imap_insecure: None,
|
||||||
|
imap_login: String::new(),
|
||||||
|
imap_passwd_cmd: String::new(),
|
||||||
|
smtp_host: String::new(),
|
||||||
|
smtp_port: 0,
|
||||||
|
smtp_starttls: None,
|
||||||
|
smtp_insecure: None,
|
||||||
|
smtp_login: String::new(),
|
||||||
|
smtp_passwd_cmd: String::new(),
|
||||||
|
};
|
||||||
|
let config = Config {
|
||||||
|
name: String::new(),
|
||||||
|
downloads_dir: None,
|
||||||
|
notify_cmd: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
watch_cmds: None,
|
||||||
|
accounts: vec![(String::from("account"), account.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let output = Output::new("plain");
|
||||||
|
let mbox = String::new();
|
||||||
|
let arg_matches = clap::ArgMatches::new();
|
||||||
|
let app = App::new(&config, &account, &output, &mbox, &arg_matches);
|
||||||
|
let parsed_mail = mailparse::parse_mail(
|
||||||
|
b"Content-Type: text/plain\r\nFrom: Sender <sender@localhost>\r\nSubject: Test\r\n\r\nHello, world!",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let tpl = Tpl::reply(&app, &parsed_mail);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"From: Test <test@localhost>\nTo: Sender <sender@localhost>\nSubject: Re: Test\n\n>Hello, world!",
|
||||||
|
tpl.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reply_tpl_with_signature() {
|
||||||
|
let account = Account {
|
||||||
|
name: Some(String::from("Test")),
|
||||||
|
downloads_dir: None,
|
||||||
|
signature: Some(String::from("-- \nCordialement,")),
|
||||||
|
default_page_size: None,
|
||||||
|
default: Some(true),
|
||||||
|
email: String::from("test@localhost"),
|
||||||
|
watch_cmds: None,
|
||||||
|
imap_host: String::new(),
|
||||||
|
imap_port: 0,
|
||||||
|
imap_starttls: None,
|
||||||
|
imap_insecure: None,
|
||||||
|
imap_login: String::new(),
|
||||||
|
imap_passwd_cmd: String::new(),
|
||||||
|
smtp_host: String::new(),
|
||||||
|
smtp_port: 0,
|
||||||
|
smtp_starttls: None,
|
||||||
|
smtp_insecure: None,
|
||||||
|
smtp_login: String::new(),
|
||||||
|
smtp_passwd_cmd: String::new(),
|
||||||
|
};
|
||||||
|
let config = Config {
|
||||||
|
name: String::new(),
|
||||||
|
downloads_dir: None,
|
||||||
|
notify_cmd: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
watch_cmds: None,
|
||||||
|
accounts: vec![(String::from("account"), account.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let output = Output::new("plain");
|
||||||
|
let mbox = String::new();
|
||||||
|
let arg_matches = clap::ArgMatches::new();
|
||||||
|
let app = App::new(&config, &account, &output, &mbox, &arg_matches);
|
||||||
|
let parsed_mail = mailparse::parse_mail(
|
||||||
|
b"Content-Type: text/plain\r\nFrom: Sender <sender@localhost>\r\nSubject: Test\r\n\r\nHello, world!",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let tpl = Tpl::reply(&app, &parsed_mail);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"From: Test <test@localhost>\nTo: Sender <sender@localhost>\nSubject: Re: Test\n\n>Hello, world!\n\n-- \nCordialement,",
|
||||||
|
tpl.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reply_all_tpl() {
|
||||||
|
let account = Account {
|
||||||
|
name: Some(String::from("To")),
|
||||||
|
downloads_dir: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
default: Some(true),
|
||||||
|
email: String::from("to@localhost"),
|
||||||
|
watch_cmds: None,
|
||||||
|
imap_host: String::new(),
|
||||||
|
imap_port: 0,
|
||||||
|
imap_starttls: None,
|
||||||
|
imap_insecure: None,
|
||||||
|
imap_login: String::new(),
|
||||||
|
imap_passwd_cmd: String::new(),
|
||||||
|
smtp_host: String::new(),
|
||||||
|
smtp_port: 0,
|
||||||
|
smtp_starttls: None,
|
||||||
|
smtp_insecure: None,
|
||||||
|
smtp_login: String::new(),
|
||||||
|
smtp_passwd_cmd: String::new(),
|
||||||
|
};
|
||||||
|
let config = Config {
|
||||||
|
name: String::new(),
|
||||||
|
downloads_dir: None,
|
||||||
|
notify_cmd: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
watch_cmds: None,
|
||||||
|
accounts: vec![(String::from("account"), account.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let output = Output::new("plain");
|
||||||
|
let mbox = String::new();
|
||||||
|
let arg_matches = clap::ArgMatches::new();
|
||||||
|
let app = App::new(&config, &account, &output, &mbox, &arg_matches);
|
||||||
|
let parsed_mail = mailparse::parse_mail(
|
||||||
|
b"Message-Id: 1\r
|
||||||
|
Content-Type: text/plain\r
|
||||||
|
From: From <from@localhost>\r
|
||||||
|
To: To <to@localhost>,to_bis@localhost\r
|
||||||
|
Cc: Cc <cc@localhost>, cc_bis@localhost\r
|
||||||
|
Subject: Test\r
|
||||||
|
\r
|
||||||
|
Hello, world!",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let tpl = Tpl::reply_all(&app, &parsed_mail);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"From: To <to@localhost>
|
||||||
|
To: From <from@localhost>, to_bis@localhost
|
||||||
|
In-Reply-To: 1
|
||||||
|
Cc: Cc <cc@localhost>, cc_bis@localhost
|
||||||
|
Subject: Re: Test
|
||||||
|
|
||||||
|
>Hello, world!",
|
||||||
|
tpl.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reply_all_tpl_with_signature() {
|
||||||
|
let account = Account {
|
||||||
|
name: Some(String::from("Test")),
|
||||||
|
downloads_dir: None,
|
||||||
|
signature: Some(String::from("-- \nCordialement,")),
|
||||||
|
default_page_size: None,
|
||||||
|
default: Some(true),
|
||||||
|
email: String::from("test@localhost"),
|
||||||
|
watch_cmds: None,
|
||||||
|
imap_host: String::new(),
|
||||||
|
imap_port: 0,
|
||||||
|
imap_starttls: None,
|
||||||
|
imap_insecure: None,
|
||||||
|
imap_login: String::new(),
|
||||||
|
imap_passwd_cmd: String::new(),
|
||||||
|
smtp_host: String::new(),
|
||||||
|
smtp_port: 0,
|
||||||
|
smtp_starttls: None,
|
||||||
|
smtp_insecure: None,
|
||||||
|
smtp_login: String::new(),
|
||||||
|
smtp_passwd_cmd: String::new(),
|
||||||
|
};
|
||||||
|
let config = Config {
|
||||||
|
name: String::new(),
|
||||||
|
downloads_dir: None,
|
||||||
|
notify_cmd: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
watch_cmds: None,
|
||||||
|
accounts: vec![(String::from("account"), account.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let output = Output::new("plain");
|
||||||
|
let mbox = String::new();
|
||||||
|
let arg_matches = clap::ArgMatches::new();
|
||||||
|
let app = App::new(&config, &account, &output, &mbox, &arg_matches);
|
||||||
|
let parsed_mail = mailparse::parse_mail(
|
||||||
|
b"Content-Type: text/plain\r\nFrom: Sender <sender@localhost>\r\nSubject: Test\r\n\r\nHello, world!",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let tpl = Tpl::reply(&app, &parsed_mail);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"From: Test <test@localhost>\nTo: Sender <sender@localhost>\nSubject: Re: Test\n\n>Hello, world!\n\n-- \nCordialement,",
|
||||||
|
tpl.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forward_tpl() {
|
||||||
|
let account = Account {
|
||||||
|
name: Some(String::from("Test")),
|
||||||
|
downloads_dir: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
default: Some(true),
|
||||||
|
email: String::from("test@localhost"),
|
||||||
|
watch_cmds: None,
|
||||||
|
imap_host: String::new(),
|
||||||
|
imap_port: 0,
|
||||||
|
imap_starttls: None,
|
||||||
|
imap_insecure: None,
|
||||||
|
imap_login: String::new(),
|
||||||
|
imap_passwd_cmd: String::new(),
|
||||||
|
smtp_host: String::new(),
|
||||||
|
smtp_port: 0,
|
||||||
|
smtp_starttls: None,
|
||||||
|
smtp_insecure: None,
|
||||||
|
smtp_login: String::new(),
|
||||||
|
smtp_passwd_cmd: String::new(),
|
||||||
|
};
|
||||||
|
let config = Config {
|
||||||
|
name: String::new(),
|
||||||
|
downloads_dir: None,
|
||||||
|
notify_cmd: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
watch_cmds: None,
|
||||||
|
accounts: vec![(String::from("account"), account.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let output = Output::new("plain");
|
||||||
|
let mbox = String::new();
|
||||||
|
let arg_matches = clap::ArgMatches::new();
|
||||||
|
let app = App::new(&config, &account, &output, &mbox, &arg_matches);
|
||||||
|
let parsed_mail = mailparse::parse_mail(
|
||||||
|
b"Content-Type: text/plain\r\nFrom: Sender <sender@localhost>\r\nSubject: Test\r\n\r\nHello, world!",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let tpl = Tpl::forward(&app, &parsed_mail);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"From: Test <test@localhost>\nTo: \nSubject: Fwd: Test\n\n-------- Forwarded Message --------\nHello, world!",
|
||||||
|
tpl.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forward_tpl_with_signature() {
|
||||||
|
let account = Account {
|
||||||
|
name: Some(String::from("Test")),
|
||||||
|
downloads_dir: None,
|
||||||
|
signature: Some(String::from("-- \nCordialement,")),
|
||||||
|
default_page_size: None,
|
||||||
|
default: Some(true),
|
||||||
|
email: String::from("test@localhost"),
|
||||||
|
watch_cmds: None,
|
||||||
|
imap_host: String::new(),
|
||||||
|
imap_port: 0,
|
||||||
|
imap_starttls: None,
|
||||||
|
imap_insecure: None,
|
||||||
|
imap_login: String::new(),
|
||||||
|
imap_passwd_cmd: String::new(),
|
||||||
|
smtp_host: String::new(),
|
||||||
|
smtp_port: 0,
|
||||||
|
smtp_starttls: None,
|
||||||
|
smtp_insecure: None,
|
||||||
|
smtp_login: String::new(),
|
||||||
|
smtp_passwd_cmd: String::new(),
|
||||||
|
};
|
||||||
|
let config = Config {
|
||||||
|
name: String::new(),
|
||||||
|
downloads_dir: None,
|
||||||
|
notify_cmd: None,
|
||||||
|
signature: None,
|
||||||
|
default_page_size: None,
|
||||||
|
watch_cmds: None,
|
||||||
|
accounts: vec![(String::from("account"), account.clone())]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let output = Output::new("plain");
|
||||||
|
let mbox = String::new();
|
||||||
|
let arg_matches = clap::ArgMatches::new();
|
||||||
|
let app = App::new(&config, &account, &output, &mbox, &arg_matches);
|
||||||
|
let parsed_mail = mailparse::parse_mail(
|
||||||
|
b"Content-Type: text/plain\r\nFrom: Sender <sender@localhost>\r\nSubject: Test\r\n\r\nHello, world!",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let tpl = Tpl::forward(&app, &parsed_mail);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"From: Test <test@localhost>\nTo: \nSubject: Fwd: Test\n\n-------- Forwarded Message --------\nHello, world!\n\n-- \nCordialement,",
|
||||||
|
tpl.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue