mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 17:15:12 +00:00
refactor message with clap derive api (part 2)
This commit is contained in:
parent
a47902af7d
commit
b8ef771614
|
@ -20,7 +20,7 @@ use email::{
|
||||||
add::{imap::AddFlagsImap, maildir::AddFlagsMaildir},
|
add::{imap::AddFlagsImap, maildir::AddFlagsMaildir},
|
||||||
remove::{imap::RemoveFlagsImap, maildir::RemoveFlagsMaildir},
|
remove::{imap::RemoveFlagsImap, maildir::RemoveFlagsMaildir},
|
||||||
set::{imap::SetFlagsImap, maildir::SetFlagsMaildir},
|
set::{imap::SetFlagsImap, maildir::SetFlagsMaildir},
|
||||||
Flags,
|
Flag, Flags,
|
||||||
},
|
},
|
||||||
folder::{
|
folder::{
|
||||||
add::{imap::AddFolderImap, maildir::AddFolderMaildir},
|
add::{imap::AddFolderImap, maildir::AddFolderMaildir},
|
||||||
|
@ -800,6 +800,27 @@ impl Backend {
|
||||||
id_mapper.create_alias(&*id)?;
|
id_mapper.create_alias(&*id)?;
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn add_flag(&self, folder: &str, ids: &[usize], flag: Flag) -> Result<()> {
|
||||||
|
let backend_kind = self.toml_account_config.add_flags_kind();
|
||||||
|
let id_mapper = self.build_id_mapper(folder, backend_kind)?;
|
||||||
|
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||||
|
self.backend.add_flag(folder, &ids, flag).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_flag(&self, folder: &str, ids: &[usize], flag: Flag) -> Result<()> {
|
||||||
|
let backend_kind = self.toml_account_config.set_flags_kind();
|
||||||
|
let id_mapper = self.build_id_mapper(folder, backend_kind)?;
|
||||||
|
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||||
|
self.backend.set_flag(folder, &ids, flag).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_flag(&self, folder: &str, ids: &[usize], flag: Flag) -> Result<()> {
|
||||||
|
let backend_kind = self.toml_account_config.remove_flags_kind();
|
||||||
|
let id_mapper = self.build_id_mapper(folder, backend_kind)?;
|
||||||
|
let ids = Id::multiple(id_mapper.get_ids(ids)?);
|
||||||
|
self.backend.remove_flag(folder, &ids, flag).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Backend {
|
impl Deref for Backend {
|
||||||
|
|
10
src/cli.rs
10
src/cli.rs
|
@ -80,27 +80,27 @@ pub struct Cli {
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
pub enum HimalayaCommand {
|
pub enum HimalayaCommand {
|
||||||
/// Subcommand to manage accounts
|
/// Manage accounts
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
#[command(alias = "accounts")]
|
#[command(alias = "accounts")]
|
||||||
Account(AccountSubcommand),
|
Account(AccountSubcommand),
|
||||||
|
|
||||||
/// Subcommand to manage folders
|
/// Manage folders
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
#[command(alias = "folders")]
|
#[command(alias = "folders")]
|
||||||
Folder(FolderSubcommand),
|
Folder(FolderSubcommand),
|
||||||
|
|
||||||
/// Subcommand to manage envelopes
|
/// Manage envelopes
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
#[command(alias = "envelopes")]
|
#[command(alias = "envelopes")]
|
||||||
Envelope(EnvelopeSubcommand),
|
Envelope(EnvelopeSubcommand),
|
||||||
|
|
||||||
/// Subcommand to manage flags
|
/// Manage flags
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
#[command(alias = "flags")]
|
#[command(alias = "flags")]
|
||||||
Flag(FlagSubcommand),
|
Flag(FlagSubcommand),
|
||||||
|
|
||||||
/// Subcommand to manage messages
|
/// Manage messages
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
#[command(alias = "messages", alias = "msgs", alias = "msg")]
|
#[command(alias = "messages", alias = "msgs", alias = "msg")]
|
||||||
Message(MessageSubcommand),
|
Message(MessageSubcommand),
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
|
/// The envelope id argument parser
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct EnvelopeIdArg {
|
||||||
|
/// The envelope id
|
||||||
|
#[arg(value_name = "ID", required = true)]
|
||||||
|
pub id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// The envelopes ids arguments parser
|
/// The envelopes ids arguments parser
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct EnvelopeIdsArgs {
|
pub struct EnvelopeIdsArgs {
|
||||||
|
|
26
src/email/message/arg/body.rs
Normal file
26
src/email/message/arg/body.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
/// The raw message body argument parser
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct BodyRawArg {
|
||||||
|
/// Prefill the template with a custom body
|
||||||
|
#[arg(raw = true, required = false)]
|
||||||
|
#[arg(name = "body-raw", value_delimiter = ' ')]
|
||||||
|
pub raw: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BodyRawArg {
|
||||||
|
pub fn raw(self) -> String {
|
||||||
|
self.raw.join(" ").replace("\r", "").replace("\n", "\r\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for BodyRawArg {
|
||||||
|
type Target = Vec<String>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.raw
|
||||||
|
}
|
||||||
|
}
|
20
src/email/message/arg/header.rs
Normal file
20
src/email/message/arg/header.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
/// The envelope id argument parser
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct HeaderRawArgs {
|
||||||
|
/// Prefill the template with custom headers
|
||||||
|
///
|
||||||
|
/// A raw header should follow the pattern KEY:VAL.
|
||||||
|
#[arg(long = "header", short = 'H', required = false)]
|
||||||
|
#[arg(name = "header-raw", value_name = "KEY:VAL", value_parser = raw_header_parser)]
|
||||||
|
pub raw: Vec<(String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_header_parser(raw_header: &str) -> Result<(String, String), String> {
|
||||||
|
if let Some((key, val)) = raw_header.split_once(":") {
|
||||||
|
Ok((key.trim().to_owned(), val.trim().to_owned()))
|
||||||
|
} else {
|
||||||
|
Err(format!("cannot parse raw header {raw_header:?}"))
|
||||||
|
}
|
||||||
|
}
|
2
src/email/message/arg/mod.rs
Normal file
2
src/email/message/arg/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod body;
|
||||||
|
pub mod header;
|
|
@ -47,6 +47,8 @@ impl MessageCopyCommand {
|
||||||
let ids = &self.envelopes.ids;
|
let ids = &self.envelopes.ids;
|
||||||
backend.copy_messages(from_folder, to_folder, ids).await?;
|
backend.copy_messages(from_folder, to_folder, ids).await?;
|
||||||
|
|
||||||
printer.print("Message(s) successfully copied from {from_folder} to {to_folder}!")
|
printer.print(format!(
|
||||||
|
"Message(s) successfully copied from {from_folder} to {to_folder}!"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
83
src/email/message/command/forward.rs
Normal file
83
src/email/message/command/forward.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use atty::Stream;
|
||||||
|
use clap::Parser;
|
||||||
|
use log::info;
|
||||||
|
use std::io::{self, BufRead};
|
||||||
|
|
||||||
|
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,
|
||||||
|
ui::editor,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Forward a new message
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct MessageForwardCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub envelope: EnvelopeIdArg,
|
||||||
|
|
||||||
|
/// Forward to all recipients
|
||||||
|
#[arg(long, short = 'A')]
|
||||||
|
pub all: bool,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub headers: HeaderRawArgs,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub body: BodyRawArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageForwardCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing message 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(), true).await?;
|
||||||
|
|
||||||
|
let is_tty = atty::is(Stream::Stdin);
|
||||||
|
let is_json = printer.is_json();
|
||||||
|
let body = if !self.body.is_empty() && (is_tty || is_json) {
|
||||||
|
self.body.raw()
|
||||||
|
} else {
|
||||||
|
io::stdin()
|
||||||
|
.lock()
|
||||||
|
.lines()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\r\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = self.envelope.id;
|
||||||
|
let tpl = backend
|
||||||
|
.get_messages(folder, &[id])
|
||||||
|
.await?
|
||||||
|
.first()
|
||||||
|
.ok_or(anyhow!("cannot find message"))?
|
||||||
|
.to_forward_tpl_builder(&account_config)
|
||||||
|
.with_headers(self.headers.raw)
|
||||||
|
.with_body(body)
|
||||||
|
.build()
|
||||||
|
.await?;
|
||||||
|
editor::edit_tpl_with_editor(&account_config, printer, &backend, tpl).await
|
||||||
|
}
|
||||||
|
}
|
46
src/email/message/command/mailto.rs
Normal file
46
src/email/message/command/mailto.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use log::info;
|
||||||
|
use mail_builder::MessageBuilder;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::{backend::Backend, config::TomlConfig, printer::Printer, ui::editor};
|
||||||
|
|
||||||
|
/// Parse and edit a message from a mailto URL string
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct MessageMailtoCommand {
|
||||||
|
/// The mailto url
|
||||||
|
#[arg()]
|
||||||
|
pub url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageMailtoCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing message mailto command");
|
||||||
|
|
||||||
|
let (toml_account_config, account_config) =
|
||||||
|
config.clone().into_account_configs(None, false)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
|
||||||
|
|
||||||
|
let mut builder = MessageBuilder::new().to(self.url.path());
|
||||||
|
|
||||||
|
for (key, val) in self.url.query_pairs() {
|
||||||
|
match key.to_lowercase().as_bytes() {
|
||||||
|
b"cc" => builder = builder.cc(val.to_string()),
|
||||||
|
b"bcc" => builder = builder.bcc(val.to_string()),
|
||||||
|
b"subject" => builder = builder.subject(val),
|
||||||
|
b"body" => builder = builder.text_body(val),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tpl = account_config
|
||||||
|
.generate_tpl_interpreter()
|
||||||
|
.with_show_only_headers(account_config.email_writing_headers())
|
||||||
|
.build()
|
||||||
|
.from_msg_builder(builder)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
editor::edit_tpl_with_editor(&account_config, printer, &backend, tpl).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,13 @@
|
||||||
pub mod copy;
|
pub mod copy;
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
|
pub mod forward;
|
||||||
|
pub mod mailto;
|
||||||
pub mod move_;
|
pub mod move_;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
|
pub mod reply;
|
||||||
pub mod save;
|
pub mod save;
|
||||||
pub mod send;
|
pub mod send;
|
||||||
|
pub mod write;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
@ -11,8 +15,10 @@ use clap::Subcommand;
|
||||||
use crate::{config::TomlConfig, printer::Printer};
|
use crate::{config::TomlConfig, printer::Printer};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
copy::MessageCopyCommand, delete::MessageDeleteCommand, move_::MessageMoveCommand,
|
copy::MessageCopyCommand, delete::MessageDeleteCommand, forward::MessageForwardCommand,
|
||||||
read::MessageReadCommand, save::MessageSaveCommand, send::MessageSendCommand,
|
mailto::MessageMailtoCommand, move_::MessageMoveCommand, read::MessageReadCommand,
|
||||||
|
reply::MessageReplyCommand, save::MessageSaveCommand, send::MessageSendCommand,
|
||||||
|
write::MessageWriteCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Subcommand to manage messages
|
/// Subcommand to manage messages
|
||||||
|
@ -22,6 +28,22 @@ pub enum MessageSubcommand {
|
||||||
#[command(arg_required_else_help = true)]
|
#[command(arg_required_else_help = true)]
|
||||||
Read(MessageReadCommand),
|
Read(MessageReadCommand),
|
||||||
|
|
||||||
|
/// Write a new message
|
||||||
|
#[command(alias = "new", alias = "compose")]
|
||||||
|
Write(MessageWriteCommand),
|
||||||
|
|
||||||
|
/// Reply to a message
|
||||||
|
#[command()]
|
||||||
|
Reply(MessageReplyCommand),
|
||||||
|
|
||||||
|
/// Forward a message
|
||||||
|
#[command(alias = "fwd")]
|
||||||
|
Forward(MessageForwardCommand),
|
||||||
|
|
||||||
|
/// Parse and edit a message from a mailto URL string
|
||||||
|
#[command()]
|
||||||
|
Mailto(MessageMailtoCommand),
|
||||||
|
|
||||||
/// Save a message to a folder
|
/// Save a message to a folder
|
||||||
#[command(arg_required_else_help = true)]
|
#[command(arg_required_else_help = true)]
|
||||||
#[command(alias = "add", alias = "create")]
|
#[command(alias = "add", alias = "create")]
|
||||||
|
@ -48,6 +70,10 @@ impl MessageSubcommand {
|
||||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Read(cmd) => cmd.execute(printer, config).await,
|
Self::Read(cmd) => cmd.execute(printer, config).await,
|
||||||
|
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::Mailto(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Save(cmd) => cmd.execute(printer, config).await,
|
Self::Save(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Send(cmd) => cmd.execute(printer, config).await,
|
Self::Send(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Copy(cmd) => cmd.execute(printer, config).await,
|
Self::Copy(cmd) => cmd.execute(printer, config).await,
|
||||||
|
|
|
@ -47,6 +47,8 @@ impl MessageMoveCommand {
|
||||||
let ids = &self.envelopes.ids;
|
let ids = &self.envelopes.ids;
|
||||||
backend.move_messages(from_folder, to_folder, ids).await?;
|
backend.move_messages(from_folder, to_folder, ids).await?;
|
||||||
|
|
||||||
printer.print("Message(s) successfully moved from {from_folder} to {to_folder}!")
|
printer.print(format!(
|
||||||
|
"Message(s) successfully moved from {from_folder} to {to_folder}!"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
86
src/email/message/command/reply.rs
Normal file
86
src/email/message/command/reply.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use atty::Stream;
|
||||||
|
use clap::Parser;
|
||||||
|
use email::flag::Flag;
|
||||||
|
use log::info;
|
||||||
|
use std::io::{self, BufRead};
|
||||||
|
|
||||||
|
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,
|
||||||
|
ui::editor,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Reply a new message
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct MessageReplyCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub envelope: EnvelopeIdArg,
|
||||||
|
|
||||||
|
/// Reply to all recipients
|
||||||
|
#[arg(long, short = 'A')]
|
||||||
|
pub all: bool,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub headers: HeaderRawArgs,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub body: BodyRawArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageReplyCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing message reply 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(), true).await?;
|
||||||
|
|
||||||
|
let is_tty = atty::is(Stream::Stdin);
|
||||||
|
let is_json = printer.is_json();
|
||||||
|
let body = if !self.body.is_empty() && (is_tty || is_json) {
|
||||||
|
self.body.raw()
|
||||||
|
} else {
|
||||||
|
io::stdin()
|
||||||
|
.lock()
|
||||||
|
.lines()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\r\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = self.envelope.id;
|
||||||
|
let tpl = backend
|
||||||
|
.get_messages(folder, &[id])
|
||||||
|
.await?
|
||||||
|
.first()
|
||||||
|
.ok_or(anyhow!("cannot find message"))?
|
||||||
|
.to_reply_tpl_builder(&account_config)
|
||||||
|
.with_headers(self.headers.raw)
|
||||||
|
.with_body(body)
|
||||||
|
.with_reply_all(self.all)
|
||||||
|
.build()
|
||||||
|
.await?;
|
||||||
|
editor::edit_tpl_with_editor(&account_config, printer, &backend, tpl).await?;
|
||||||
|
backend.add_flag(&folder, &[id], Flag::Answered).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,6 +56,6 @@ impl MessageSaveCommand {
|
||||||
.add_raw_message(folder, raw_email.as_bytes())
|
.add_raw_message(folder, raw_email.as_bytes())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
printer.print("Message successfully saved to {folder}!")
|
printer.print(format!("Message successfully saved to {folder}!"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl MessageSendCommand {
|
||||||
.add_raw_message_with_flag(&folder, raw_email.as_bytes(), Flag::Seen)
|
.add_raw_message_with_flag(&folder, raw_email.as_bytes(), Flag::Seen)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
printer.print("Message successfully sent and saved to {folder}!")
|
printer.print(format!("Message successfully sent and saved to {folder}!"))
|
||||||
} else {
|
} else {
|
||||||
printer.print("Message successfully sent!")
|
printer.print("Message successfully sent!")
|
||||||
}
|
}
|
||||||
|
|
66
src/email/message/command/write.rs
Normal file
66
src/email/message/command/write.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use atty::Stream;
|
||||||
|
use clap::Parser;
|
||||||
|
use email::message::Message;
|
||||||
|
use log::info;
|
||||||
|
use std::io::{self, BufRead};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag,
|
||||||
|
backend::Backend,
|
||||||
|
cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig,
|
||||||
|
message::arg::{body::BodyRawArg, header::HeaderRawArgs},
|
||||||
|
printer::Printer,
|
||||||
|
ui::editor,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Write a new message
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct MessageWriteCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub headers: HeaderRawArgs,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub body: BodyRawArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageWriteCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing message write 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 is_tty = atty::is(Stream::Stdin);
|
||||||
|
let is_json = printer.is_json();
|
||||||
|
let body = if !self.body.is_empty() && (is_tty || is_json) {
|
||||||
|
self.body.raw()
|
||||||
|
} else {
|
||||||
|
io::stdin()
|
||||||
|
.lock()
|
||||||
|
.lines()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\r\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
let tpl = Message::new_tpl_builder(&account_config)
|
||||||
|
.with_headers(self.headers.raw)
|
||||||
|
.with_body(body)
|
||||||
|
.build()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
editor::edit_tpl_with_editor(&account_config, printer, &backend, tpl).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,30 +69,6 @@ pub async fn attachments<P: 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 = 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?;
|
|
||||||
trace!("initial template: {tpl}");
|
|
||||||
editor::edit_tpl_with_editor(config, printer, backend, tpl).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses and edits a message from a [mailto] URL string.
|
/// Parses and edits a message from a [mailto] URL string.
|
||||||
///
|
///
|
||||||
/// [mailto]: https://en.wikipedia.org/wiki/Mailto
|
/// [mailto]: https://en.wikipedia.org/wiki/Mailto
|
||||||
|
@ -123,49 +99,3 @@ pub async fn mailto<P: Printer>(
|
||||||
|
|
||||||
editor::edit_tpl_with_editor(config, printer, backend, tpl).await
|
editor::edit_tpl_with_editor(config, printer, backend, tpl).await
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = 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?;
|
|
||||||
trace!("initial template: {tpl}");
|
|
||||||
editor::edit_tpl_with_editor(config, printer, backend, tpl).await?;
|
|
||||||
backend
|
|
||||||
.add_flag(&folder, &Id::single(id), Flag::Answered)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn write<P: Printer>(
|
|
||||||
config: &AccountConfig,
|
|
||||||
printer: &mut P,
|
|
||||||
backend: &Backend,
|
|
||||||
headers: Option<Vec<(&str, &str)>>,
|
|
||||||
body: Option<&str>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let tpl = Message::new_tpl_builder(config)
|
|
||||||
.with_some_headers(headers)
|
|
||||||
.with_some_body(body)
|
|
||||||
.build()
|
|
||||||
.await?;
|
|
||||||
trace!("initial template: {tpl}");
|
|
||||||
editor::edit_tpl_with_editor(config, printer, backend, tpl).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// pub mod args;
|
// pub mod args;
|
||||||
|
pub mod arg;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
// pub mod handlers;
|
// pub mod handlers;
|
||||||
|
|
62
src/main.rs
62
src/main.rs
|
@ -3,6 +3,7 @@ use clap::Parser;
|
||||||
use env_logger::{Builder as LoggerBuilder, Env, DEFAULT_FILTER_ENV};
|
use env_logger::{Builder as LoggerBuilder, Env, DEFAULT_FILTER_ENV};
|
||||||
use himalaya::{cli::Cli, config::TomlConfig, printer::StdoutPrinter};
|
use himalaya::{cli::Cli, config::TomlConfig, printer::StdoutPrinter};
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
@ -17,6 +18,17 @@ async fn main() -> Result<()> {
|
||||||
.format_timestamp(None)
|
.format_timestamp(None)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
let raw_args: Vec<String> = env::args().collect();
|
||||||
|
if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
||||||
|
// TODO
|
||||||
|
// let cmd = MessageMailtoCommand::command()
|
||||||
|
// .no_binary_name(true)
|
||||||
|
// .try_get_matches_from([&raw_args[1]]);
|
||||||
|
// match cmd {
|
||||||
|
// Ok(m) => m.exec
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
let mut printer = StdoutPrinter::new(cli.output, cli.color);
|
let mut printer = StdoutPrinter::new(cli.output, cli.color);
|
||||||
|
@ -61,56 +73,6 @@ async fn main() -> Result<()> {
|
||||||
// )
|
// )
|
||||||
// .await;
|
// .await;
|
||||||
// }
|
// }
|
||||||
// Some(message::args::Cmd::Forward(id, headers, body)) => {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
|
|
||||||
// return message::handlers::forward(
|
|
||||||
// &account_config,
|
|
||||||
// &mut printer,
|
|
||||||
// &backend,
|
|
||||||
// &folder,
|
|
||||||
// id,
|
|
||||||
// headers,
|
|
||||||
// body,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// Some(message::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(), true).await?;
|
|
||||||
// return message::handlers::reply(
|
|
||||||
// &account_config,
|
|
||||||
// &mut printer,
|
|
||||||
// &backend,
|
|
||||||
// &folder,
|
|
||||||
// id,
|
|
||||||
// all,
|
|
||||||
// headers,
|
|
||||||
// body,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// Some(message::args::Cmd::Save(raw_email)) => {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// return message::handlers::save(&mut printer, &backend, &folder, raw_email).await;
|
|
||||||
// }
|
|
||||||
// Some(message::args::Cmd::Send(raw_email)) => {
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
|
|
||||||
// return message::handlers::send(&account_config, &mut printer, &backend, raw_email)
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// Some(message::args::Cmd::Write(headers, body)) => {
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
|
|
||||||
// return message::handlers::write(
|
|
||||||
// &account_config,
|
|
||||||
// &mut printer,
|
|
||||||
// &backend,
|
|
||||||
// headers,
|
|
||||||
// body,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// _ => (),
|
// _ => (),
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue