mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 17:15:12 +00:00
refactor template with clap derive api
This commit is contained in:
parent
b28f12c367
commit
fff11fbe20
11
src/cli.rs
11
src/cli.rs
|
@ -10,7 +10,10 @@ use crate::{
|
||||||
flag::command::FlagSubcommand,
|
flag::command::FlagSubcommand,
|
||||||
folder::command::FolderSubcommand,
|
folder::command::FolderSubcommand,
|
||||||
manual::command::ManualGenerateCommand,
|
manual::command::ManualGenerateCommand,
|
||||||
message::{attachment::command::AttachmentSubcommand, command::MessageSubcommand},
|
message::{
|
||||||
|
attachment::command::AttachmentSubcommand, command::MessageSubcommand,
|
||||||
|
template::command::TemplateSubcommand,
|
||||||
|
},
|
||||||
output::{ColorFmt, OutputFmt},
|
output::{ColorFmt, OutputFmt},
|
||||||
printer::Printer,
|
printer::Printer,
|
||||||
};
|
};
|
||||||
|
@ -105,6 +108,11 @@ pub enum HimalayaCommand {
|
||||||
#[command(alias = "messages", alias = "msgs", alias = "msg")]
|
#[command(alias = "messages", alias = "msgs", alias = "msg")]
|
||||||
Message(MessageSubcommand),
|
Message(MessageSubcommand),
|
||||||
|
|
||||||
|
/// Manage templates
|
||||||
|
#[command(subcommand)]
|
||||||
|
#[command(alias = "templates", alias = "tpls", alias = "tpl")]
|
||||||
|
Template(TemplateSubcommand),
|
||||||
|
|
||||||
/// Manage attachments
|
/// Manage attachments
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Attachment(AttachmentSubcommand),
|
Attachment(AttachmentSubcommand),
|
||||||
|
@ -128,6 +136,7 @@ impl HimalayaCommand {
|
||||||
Self::Envelope(cmd) => cmd.execute(printer, config).await,
|
Self::Envelope(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Flag(cmd) => cmd.execute(printer, config).await,
|
Self::Flag(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Message(cmd) => cmd.execute(printer, config).await,
|
Self::Message(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Template(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Attachment(cmd) => cmd.execute(printer, config).await,
|
Self::Attachment(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Manual(cmd) => cmd.execute(printer).await,
|
Self::Manual(cmd) => cmd.execute(printer).await,
|
||||||
Self::Completion(cmd) => cmd.execute(printer).await,
|
Self::Completion(cmd) => cmd.execute(printer).await,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// The raw message body argument parser
|
/// The raw message body argument parser
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod body;
|
pub mod body;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
|
pub mod reply;
|
||||||
|
|
9
src/email/message/arg/reply.rs
Normal file
9
src/email/message/arg/reply.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
/// The reply to all argument parser
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct MessageReplyAllArg {
|
||||||
|
/// Reply to all recipients
|
||||||
|
#[arg(long, short = 'A')]
|
||||||
|
pub all: bool,
|
||||||
|
}
|
|
@ -36,7 +36,7 @@ impl AttachmentDownloadCommand {
|
||||||
|
|
||||||
let (toml_account_config, account_config) =
|
let (toml_account_config, account_config) =
|
||||||
config.clone().into_account_configs(account, cache)?;
|
config.clone().into_account_configs(account, cache)?;
|
||||||
let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
let ids = &self.envelopes.ids;
|
let ids = &self.envelopes.ids;
|
||||||
let emails = backend.get_messages(&folder, ids).await?;
|
let emails = backend.get_messages(&folder, ids).await?;
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
||||||
config::TomlConfig,
|
config::TomlConfig,
|
||||||
envelope::arg::ids::EnvelopeIdArg,
|
envelope::arg::ids::EnvelopeIdArg,
|
||||||
folder::arg::name::FolderNameArg,
|
folder::arg::name::FolderNameArg,
|
||||||
message::arg::{body::BodyRawArg, header::HeaderRawArgs},
|
message::arg::{body::BodyRawArg, header::HeaderRawArgs, reply::MessageReplyAllArg},
|
||||||
printer::Printer,
|
printer::Printer,
|
||||||
ui::editor,
|
ui::editor,
|
||||||
};
|
};
|
||||||
|
@ -26,9 +26,8 @@ pub struct MessageReplyCommand {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub envelope: EnvelopeIdArg,
|
pub envelope: EnvelopeIdArg,
|
||||||
|
|
||||||
/// Reply to all recipients
|
#[command(flatten)]
|
||||||
#[arg(long, short = 'A')]
|
pub reply: MessageReplyAllArg,
|
||||||
pub all: bool,
|
|
||||||
|
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub headers: HeaderRawArgs,
|
pub headers: HeaderRawArgs,
|
||||||
|
@ -73,11 +72,11 @@ impl MessageReplyCommand {
|
||||||
.get_messages(folder, &[id])
|
.get_messages(folder, &[id])
|
||||||
.await?
|
.await?
|
||||||
.first()
|
.first()
|
||||||
.ok_or(anyhow!("cannot find message"))?
|
.ok_or(anyhow!("cannot find message {id}"))?
|
||||||
.to_reply_tpl_builder(&account_config)
|
.to_reply_tpl_builder(&account_config)
|
||||||
.with_headers(self.headers.raw)
|
.with_headers(self.headers.raw)
|
||||||
.with_body(body)
|
.with_body(body)
|
||||||
.with_reply_all(self.all)
|
.with_reply_all(self.reply.all)
|
||||||
.build()
|
.build()
|
||||||
.await?;
|
.await?;
|
||||||
editor::edit_tpl_with_editor(&account_config, printer, &backend, tpl).await?;
|
editor::edit_tpl_with_editor(&account_config, printer, &backend, tpl).await?;
|
||||||
|
|
|
@ -2,4 +2,4 @@ pub mod arg;
|
||||||
pub mod attachment;
|
pub mod attachment;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
// pub mod template;
|
pub mod template;
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
//! Module related to email template CLI.
|
|
||||||
//!
|
|
||||||
//! This module provides subcommands, arguments and a command matcher
|
|
||||||
//! related to email templating.
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
|
||||||
use log::warn;
|
|
||||||
|
|
||||||
use crate::message;
|
|
||||||
|
|
||||||
const ARG_BODY: &str = "body";
|
|
||||||
const ARG_HEADERS: &str = "headers";
|
|
||||||
const ARG_TPL: &str = "template";
|
|
||||||
const CMD_FORWARD: &str = "forward";
|
|
||||||
const CMD_REPLY: &str = "reply";
|
|
||||||
const CMD_SAVE: &str = "save";
|
|
||||||
const CMD_SEND: &str = "send";
|
|
||||||
const CMD_WRITE: &str = "write";
|
|
||||||
|
|
||||||
pub const CMD_TPL: &str = "template";
|
|
||||||
|
|
||||||
pub type RawTpl = String;
|
|
||||||
pub type Headers<'a> = Option<Vec<(&'a str, &'a str)>>;
|
|
||||||
pub type Body<'a> = Option<&'a str>;
|
|
||||||
|
|
||||||
/// Represents the template commands.
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum Cmd<'a> {
|
|
||||||
Forward(message::args::Id<'a>, Headers<'a>, Body<'a>),
|
|
||||||
Write(Headers<'a>, Body<'a>),
|
|
||||||
Reply(
|
|
||||||
message::args::Id<'a>,
|
|
||||||
message::args::All,
|
|
||||||
Headers<'a>,
|
|
||||||
Body<'a>,
|
|
||||||
),
|
|
||||||
Save(RawTpl),
|
|
||||||
Send(RawTpl),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the template command matcher.
|
|
||||||
pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Cmd<'a>>> {
|
|
||||||
let cmd = if let Some(m) = m.subcommand_matches(CMD_FORWARD) {
|
|
||||||
let id = message::args::parse_id_arg(m);
|
|
||||||
let headers = parse_headers_arg(m);
|
|
||||||
let body = parse_body_arg(m);
|
|
||||||
Some(Cmd::Forward(id, headers, body))
|
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_REPLY) {
|
|
||||||
let id = message::args::parse_id_arg(m);
|
|
||||||
let all = message::args::parse_reply_all_flag(m);
|
|
||||||
let headers = parse_headers_arg(m);
|
|
||||||
let body = parse_body_arg(m);
|
|
||||||
Some(Cmd::Reply(id, all, headers, body))
|
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_SAVE) {
|
|
||||||
let raw_tpl = parse_raw_arg(m);
|
|
||||||
Some(Cmd::Save(raw_tpl))
|
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_SEND) {
|
|
||||||
let raw_tpl = parse_raw_arg(m);
|
|
||||||
Some(Cmd::Send(raw_tpl))
|
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_WRITE) {
|
|
||||||
let headers = parse_headers_arg(m);
|
|
||||||
let body = parse_body_arg(m);
|
|
||||||
Some(Cmd::Write(headers, body))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the template subcommands.
|
|
||||||
pub fn subcmd() -> Command {
|
|
||||||
Command::new(CMD_TPL)
|
|
||||||
.alias("tpl")
|
|
||||||
.about("Subcommand to manage templates")
|
|
||||||
.long_about("Subcommand to manage templates like write, reply, send or save")
|
|
||||||
.subcommand_required(true)
|
|
||||||
.arg_required_else_help(true)
|
|
||||||
.subcommand(
|
|
||||||
Command::new(CMD_FORWARD)
|
|
||||||
.alias("fwd")
|
|
||||||
.about("Generate a template for forwarding an email")
|
|
||||||
.arg(message::args::id_arg())
|
|
||||||
.args(&args()),
|
|
||||||
)
|
|
||||||
.subcommand(
|
|
||||||
Command::new(CMD_REPLY)
|
|
||||||
.about("Generate a template for replying to an email")
|
|
||||||
.arg(message::args::id_arg())
|
|
||||||
.arg(message::args::reply_all_flag())
|
|
||||||
.args(&args()),
|
|
||||||
)
|
|
||||||
.subcommand(
|
|
||||||
Command::new(CMD_SAVE)
|
|
||||||
.about("Compile the template into a valid email then saves it")
|
|
||||||
.arg(Arg::new(ARG_TPL).raw(true)),
|
|
||||||
)
|
|
||||||
.subcommand(
|
|
||||||
Command::new(CMD_SEND)
|
|
||||||
.about("Compile the template into a valid email then sends it")
|
|
||||||
.arg(Arg::new(ARG_TPL).raw(true)),
|
|
||||||
)
|
|
||||||
.subcommand(
|
|
||||||
Command::new(CMD_WRITE)
|
|
||||||
.aliases(["new", "n"])
|
|
||||||
.about("Generate a template for writing a new email")
|
|
||||||
.args(&args()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the template arguments.
|
|
||||||
pub fn args() -> Vec<Arg> {
|
|
||||||
vec![
|
|
||||||
Arg::new(ARG_HEADERS)
|
|
||||||
.help("Override a specific header")
|
|
||||||
.short('H')
|
|
||||||
.long("header")
|
|
||||||
.value_name("KEY:VAL")
|
|
||||||
.action(ArgAction::Append),
|
|
||||||
Arg::new(ARG_BODY)
|
|
||||||
.help("Override the body")
|
|
||||||
.short('B')
|
|
||||||
.long("body")
|
|
||||||
.value_name("STRING"),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the template headers argument parser.
|
|
||||||
pub fn parse_headers_arg(m: &ArgMatches) -> Headers<'_> {
|
|
||||||
m.get_many::<String>(ARG_HEADERS).map(|h| {
|
|
||||||
h.filter_map(|h| match h.split_once(':') {
|
|
||||||
Some((key, val)) => Some((key, val.trim())),
|
|
||||||
None => {
|
|
||||||
warn!("invalid raw header {h:?}, skipping it");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the template body argument parser.
|
|
||||||
pub fn parse_body_arg(matches: &ArgMatches) -> Body<'_> {
|
|
||||||
matches.get_one::<String>(ARG_BODY).map(String::as_str)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the raw template argument parser.
|
|
||||||
pub fn parse_raw_arg(matches: &ArgMatches) -> RawTpl {
|
|
||||||
matches
|
|
||||||
.get_one::<String>(ARG_TPL)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
65
src/email/message/template/command/forward.rs
Normal file
65
src/email/message/template/command/forward.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use clap::Parser;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag,
|
||||||
|
backend::Backend,
|
||||||
|
cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig,
|
||||||
|
envelope::arg::ids::EnvelopeIdArg,
|
||||||
|
folder::arg::name::FolderNameArg,
|
||||||
|
message::arg::{body::BodyRawArg, header::HeaderRawArgs},
|
||||||
|
printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Generate a forward message template
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct TemplateForwardCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub envelope: EnvelopeIdArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub headers: HeaderRawArgs,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub body: BodyRawArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateForwardCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing template forward command");
|
||||||
|
|
||||||
|
let folder = &self.folder.name;
|
||||||
|
let account = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let cache = self.cache.disable;
|
||||||
|
|
||||||
|
let (toml_account_config, account_config) =
|
||||||
|
config.clone().into_account_configs(account, cache)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
|
let id = self.envelope.id;
|
||||||
|
let tpl: String = backend
|
||||||
|
.get_messages(folder, &[id])
|
||||||
|
.await?
|
||||||
|
.first()
|
||||||
|
.ok_or(anyhow!("cannot find message {id}"))?
|
||||||
|
.to_forward_tpl_builder(&account_config)
|
||||||
|
.with_headers(self.headers.raw)
|
||||||
|
.with_body(self.body.raw())
|
||||||
|
.build()
|
||||||
|
.await?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
printer.print(tpl)
|
||||||
|
}
|
||||||
|
}
|
52
src/email/message/template/command/mod.rs
Normal file
52
src/email/message/template/command/mod.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
pub mod forward;
|
||||||
|
pub mod reply;
|
||||||
|
pub mod save;
|
||||||
|
pub mod send;
|
||||||
|
pub mod write;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Subcommand;
|
||||||
|
|
||||||
|
use crate::{config::TomlConfig, printer::Printer};
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
forward::TemplateForwardCommand, reply::TemplateReplyCommand, save::TemplateSaveCommand,
|
||||||
|
send::TemplateSendCommand, write::TemplateWriteCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Subcommand to manage templates
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub enum TemplateSubcommand {
|
||||||
|
/// Write a new template
|
||||||
|
#[command(alias = "new", alias = "compose")]
|
||||||
|
Write(TemplateWriteCommand),
|
||||||
|
|
||||||
|
/// Reply to a template
|
||||||
|
#[command()]
|
||||||
|
Reply(TemplateReplyCommand),
|
||||||
|
|
||||||
|
/// Generate a template for forwarding an email
|
||||||
|
#[command(alias = "fwd")]
|
||||||
|
Forward(TemplateForwardCommand),
|
||||||
|
|
||||||
|
/// Save a template to a folder
|
||||||
|
#[command(arg_required_else_help = true)]
|
||||||
|
#[command(alias = "add", alias = "create")]
|
||||||
|
Save(TemplateSaveCommand),
|
||||||
|
|
||||||
|
/// Send a template
|
||||||
|
#[command(arg_required_else_help = true)]
|
||||||
|
Send(TemplateSendCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateSubcommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Write(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Reply(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Forward(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Save(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Send(cmd) => cmd.execute(printer, config).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
src/email/message/template/command/reply.rs
Normal file
69
src/email/message/template/command/reply.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use clap::Parser;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag,
|
||||||
|
backend::Backend,
|
||||||
|
cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig,
|
||||||
|
envelope::arg::ids::EnvelopeIdArg,
|
||||||
|
folder::arg::name::FolderNameArg,
|
||||||
|
message::arg::{body::BodyRawArg, header::HeaderRawArgs, reply::MessageReplyAllArg},
|
||||||
|
printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Generate a reply message template
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct TemplateReplyCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub envelope: EnvelopeIdArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub reply: MessageReplyAllArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub headers: HeaderRawArgs,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub body: BodyRawArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateReplyCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing template reply command");
|
||||||
|
|
||||||
|
let folder = &self.folder.name;
|
||||||
|
let id = self.envelope.id;
|
||||||
|
let account = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let cache = self.cache.disable;
|
||||||
|
|
||||||
|
let (toml_account_config, account_config) =
|
||||||
|
config.clone().into_account_configs(account, cache)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
|
let tpl: String = backend
|
||||||
|
.get_messages(folder, &[id])
|
||||||
|
.await?
|
||||||
|
.first()
|
||||||
|
.ok_or(anyhow!("cannot find message {id}"))?
|
||||||
|
.to_reply_tpl_builder(&account_config)
|
||||||
|
.with_headers(self.headers.raw)
|
||||||
|
.with_body(self.body.raw())
|
||||||
|
.with_reply_all(self.reply.all)
|
||||||
|
.build()
|
||||||
|
.await?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
printer.print(tpl)
|
||||||
|
}
|
||||||
|
}
|
66
src/email/message/template/command/save.rs
Normal file
66
src/email/message/template/command/save.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use atty::Stream;
|
||||||
|
use clap::Parser;
|
||||||
|
use log::info;
|
||||||
|
use mml::MmlCompilerBuilder;
|
||||||
|
use std::io::{self, BufRead};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Save a template to a folder
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct TemplateSaveCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameArg,
|
||||||
|
|
||||||
|
/// The raw template to save
|
||||||
|
#[arg(raw = true, value_delimiter = ' ')]
|
||||||
|
pub raw: Vec<String>,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateSaveCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing template save command");
|
||||||
|
|
||||||
|
let folder = &self.folder.name;
|
||||||
|
let account = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let cache = self.cache.disable;
|
||||||
|
|
||||||
|
let (toml_account_config, account_config) =
|
||||||
|
config.clone().into_account_configs(account, cache)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
|
let is_tty = atty::is(Stream::Stdin);
|
||||||
|
let is_json = printer.is_json();
|
||||||
|
let tpl = if is_tty || is_json {
|
||||||
|
self.raw.join(" ").replace("\r", "")
|
||||||
|
} else {
|
||||||
|
io::stdin()
|
||||||
|
.lock()
|
||||||
|
.lines()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut compiler = MmlCompilerBuilder::new();
|
||||||
|
|
||||||
|
#[cfg(feature = "pgp")]
|
||||||
|
compiler.set_some_pgp(config.pgp.clone());
|
||||||
|
|
||||||
|
let msg = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
|
||||||
|
backend.add_raw_message(folder, &msg).await?;
|
||||||
|
|
||||||
|
printer.print(format!("Template successfully saved to {folder}!"))
|
||||||
|
}
|
||||||
|
}
|
73
src/email/message/template/command/send.rs
Normal file
73
src/email/message/template/command/send.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use atty::Stream;
|
||||||
|
use clap::Parser;
|
||||||
|
use email::flag::Flag;
|
||||||
|
use log::info;
|
||||||
|
use mml::MmlCompilerBuilder;
|
||||||
|
use std::io::{self, BufRead};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig, printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Send a template
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct TemplateSendCommand {
|
||||||
|
/// The raw template to save
|
||||||
|
#[arg(raw = true, value_delimiter = ' ')]
|
||||||
|
pub raw: Vec<String>,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateSendCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing template send command");
|
||||||
|
|
||||||
|
let account = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let cache = self.cache.disable;
|
||||||
|
|
||||||
|
let (toml_account_config, account_config) =
|
||||||
|
config.clone().into_account_configs(account, cache)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
|
||||||
|
let folder = account_config.sent_folder_alias()?;
|
||||||
|
|
||||||
|
let is_tty = atty::is(Stream::Stdin);
|
||||||
|
let is_json = printer.is_json();
|
||||||
|
let tpl = if is_tty || is_json {
|
||||||
|
self.raw.join(" ").replace("\r", "")
|
||||||
|
} else {
|
||||||
|
io::stdin()
|
||||||
|
.lock()
|
||||||
|
.lines()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\r\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut compiler = MmlCompilerBuilder::new();
|
||||||
|
|
||||||
|
#[cfg(feature = "pgp")]
|
||||||
|
compiler.set_some_pgp(config.pgp.clone());
|
||||||
|
|
||||||
|
let msg = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
|
||||||
|
|
||||||
|
backend.send_raw_message(&msg).await?;
|
||||||
|
|
||||||
|
if account_config.email_sending_save_copy.unwrap_or_default() {
|
||||||
|
backend
|
||||||
|
.add_raw_message_with_flag(&folder, &msg, Flag::Seen)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
printer.print(format!("Template successfully sent and saved to {folder}!"))
|
||||||
|
} else {
|
||||||
|
printer.print("Template successfully sent!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
src/email/message/template/command/write.rs
Normal file
48
src/email/message/template/command/write.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use email::message::Message;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag,
|
||||||
|
cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig,
|
||||||
|
message::arg::{body::BodyRawArg, header::HeaderRawArgs},
|
||||||
|
printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Write a new template
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct TemplateWriteCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub headers: HeaderRawArgs,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub body: BodyRawArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateWriteCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing template write command");
|
||||||
|
|
||||||
|
let account = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let cache = self.cache.disable;
|
||||||
|
|
||||||
|
let (_, account_config) = config.clone().into_account_configs(account, cache)?;
|
||||||
|
|
||||||
|
let tpl: String = Message::new_tpl_builder(&account_config)
|
||||||
|
.with_headers(self.headers.raw)
|
||||||
|
.with_body(self.body.raw())
|
||||||
|
.build()
|
||||||
|
.await?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
printer.print(tpl)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,143 +0,0 @@
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use atty::Stream;
|
|
||||||
use email::{account::config::AccountConfig, flag::Flag, message::Message};
|
|
||||||
use mml::MmlCompilerBuilder;
|
|
||||||
use std::io::{stdin, BufRead};
|
|
||||||
|
|
||||||
use crate::{backend::Backend, printer::Printer};
|
|
||||||
|
|
||||||
pub async fn forward<P: Printer>(
|
|
||||||
config: &AccountConfig,
|
|
||||||
printer: &mut P,
|
|
||||||
backend: &Backend,
|
|
||||||
folder: &str,
|
|
||||||
id: &str,
|
|
||||||
headers: Option<Vec<(&str, &str)>>,
|
|
||||||
body: Option<&str>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let tpl: String = backend
|
|
||||||
.get_messages(folder, &[id])
|
|
||||||
.await?
|
|
||||||
.first()
|
|
||||||
.ok_or_else(|| anyhow!("cannot find email {}", id))?
|
|
||||||
.to_forward_tpl_builder(config)
|
|
||||||
.with_some_headers(headers)
|
|
||||||
.with_some_body(body)
|
|
||||||
.build()
|
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
|
|
||||||
printer.print(tpl)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn reply<P: Printer>(
|
|
||||||
config: &AccountConfig,
|
|
||||||
printer: &mut P,
|
|
||||||
backend: &Backend,
|
|
||||||
folder: &str,
|
|
||||||
id: &str,
|
|
||||||
all: bool,
|
|
||||||
headers: Option<Vec<(&str, &str)>>,
|
|
||||||
body: Option<&str>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let tpl: String = backend
|
|
||||||
.get_messages(folder, &[id])
|
|
||||||
.await?
|
|
||||||
.first()
|
|
||||||
.ok_or_else(|| anyhow!("cannot find email {}", id))?
|
|
||||||
.to_reply_tpl_builder(config)
|
|
||||||
.with_some_headers(headers)
|
|
||||||
.with_some_body(body)
|
|
||||||
.with_reply_all(all)
|
|
||||||
.build()
|
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
|
|
||||||
printer.print(tpl)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn save<P: Printer>(
|
|
||||||
#[allow(unused_variables)] config: &AccountConfig,
|
|
||||||
printer: &mut P,
|
|
||||||
backend: &Backend,
|
|
||||||
folder: &str,
|
|
||||||
tpl: String,
|
|
||||||
) -> Result<()> {
|
|
||||||
let tpl = if atty::is(Stream::Stdin) || printer.is_json() {
|
|
||||||
tpl.replace("\r", "")
|
|
||||||
} else {
|
|
||||||
stdin()
|
|
||||||
.lock()
|
|
||||||
.lines()
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n")
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
let mut compiler = MmlCompilerBuilder::new();
|
|
||||||
|
|
||||||
#[cfg(feature = "pgp")]
|
|
||||||
compiler.set_some_pgp(config.pgp.clone());
|
|
||||||
|
|
||||||
let email = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
|
|
||||||
|
|
||||||
backend.add_raw_message(folder, &email).await?;
|
|
||||||
|
|
||||||
printer.print("Template successfully saved!")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send<P: Printer>(
|
|
||||||
config: &AccountConfig,
|
|
||||||
printer: &mut P,
|
|
||||||
backend: &Backend,
|
|
||||||
tpl: String,
|
|
||||||
) -> Result<()> {
|
|
||||||
let folder = config.sent_folder_alias()?;
|
|
||||||
|
|
||||||
let tpl = if atty::is(Stream::Stdin) || printer.is_json() {
|
|
||||||
tpl.replace("\r", "")
|
|
||||||
} else {
|
|
||||||
stdin()
|
|
||||||
.lock()
|
|
||||||
.lines()
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n")
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
let mut compiler = MmlCompilerBuilder::new();
|
|
||||||
|
|
||||||
#[cfg(feature = "pgp")]
|
|
||||||
compiler.set_some_pgp(config.pgp.clone());
|
|
||||||
|
|
||||||
let email = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
|
|
||||||
|
|
||||||
backend.send_raw_message(&email).await?;
|
|
||||||
|
|
||||||
if config.email_sending_save_copy.unwrap_or_default() {
|
|
||||||
backend
|
|
||||||
.add_raw_message_with_flag(&folder, &email, Flag::Seen)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
printer.print("Template successfully sent!")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn write<P: Printer>(
|
|
||||||
config: &AccountConfig,
|
|
||||||
printer: &mut P,
|
|
||||||
headers: Option<Vec<(&str, &str)>>,
|
|
||||||
body: Option<&str>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let tpl: String = Message::new_tpl_builder(config)
|
|
||||||
.with_some_headers(headers)
|
|
||||||
.with_some_body(body)
|
|
||||||
.build()
|
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
|
|
||||||
printer.print(tpl)
|
|
||||||
}
|
|
|
@ -1,2 +1 @@
|
||||||
pub mod args;
|
pub mod command;
|
||||||
pub mod handlers;
|
|
||||||
|
|
|
@ -2,5 +2,7 @@ pub mod envelope;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
// pub use self::{envelope::flag, message::template};
|
pub use self::{
|
||||||
pub use self::envelope::flag;
|
envelope::flag,
|
||||||
|
message::{attachment, template},
|
||||||
|
};
|
||||||
|
|
93
src/main.rs
93
src/main.rs
|
@ -36,96 +36,3 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
cli.command.execute(&mut printer, &config).await
|
cli.command.execute(&mut printer, &config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn create_app() -> clap::Command {
|
|
||||||
// clap::Command::new(env!("CARGO_PKG_NAME"))
|
|
||||||
// .subcommand(message::args::subcmd())
|
|
||||||
// .subcommand(template::args::subcmd())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[tokio::main]
|
|
||||||
// async fn main() -> Result<()> {
|
|
||||||
// // check mailto command before app initialization
|
|
||||||
// let raw_args: Vec<String> = env::args().collect();
|
|
||||||
// if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
|
||||||
// let url = Url::parse(&raw_args[1])?;
|
|
||||||
// let (toml_account_config, account_config) = TomlConfig::from_default_paths()
|
|
||||||
// .await?
|
|
||||||
// .into_account_configs(None, false)?;
|
|
||||||
// let backend_builder =
|
|
||||||
// BackendBuilder::new(toml_account_config, account_config.clone(), true).await?;
|
|
||||||
// let backend = backend_builder.build().await?;
|
|
||||||
// let mut printer = StdoutPrinter::default();
|
|
||||||
|
|
||||||
// return message::handlers::mailto(&account_config, &backend, &mut printer, &url).await;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// match message::args::matches(&m)? {
|
|
||||||
// Some(message::args::Cmd::Attachments(ids)) => {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// return message::handlers::attachments(
|
|
||||||
// &account_config,
|
|
||||||
// &mut printer,
|
|
||||||
// &backend,
|
|
||||||
// &folder,
|
|
||||||
// ids,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// _ => (),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// match template::args::matches(&m)? {
|
|
||||||
// Some(template::args::Cmd::Forward(id, headers, body)) => {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// return template::handlers::forward(
|
|
||||||
// &account_config,
|
|
||||||
// &mut printer,
|
|
||||||
// &backend,
|
|
||||||
// &folder,
|
|
||||||
// id,
|
|
||||||
// headers,
|
|
||||||
// body,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// Some(template::args::Cmd::Write(headers, body)) => {
|
|
||||||
// return template::handlers::write(&account_config, &mut printer, headers, body).await;
|
|
||||||
// }
|
|
||||||
// Some(template::args::Cmd::Reply(id, all, headers, body)) => {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// return template::handlers::reply(
|
|
||||||
// &account_config,
|
|
||||||
// &mut printer,
|
|
||||||
// &backend,
|
|
||||||
// &folder,
|
|
||||||
// id,
|
|
||||||
// all,
|
|
||||||
// headers,
|
|
||||||
// body,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// Some(template::args::Cmd::Save(template)) => {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// return template::handlers::save(
|
|
||||||
// &account_config,
|
|
||||||
// &mut printer,
|
|
||||||
// &backend,
|
|
||||||
// &folder,
|
|
||||||
// template,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// Some(template::args::Cmd::Send(template)) => {
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
|
|
||||||
// return template::handlers::send(&account_config, &mut printer, &backend, template)
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// _ => (),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
Loading…
Reference in a new issue