mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 17:15:12 +00:00
improve args management
This commit is contained in:
parent
3feccc3225
commit
329af51534
|
@ -56,7 +56,7 @@ for all the options.*
|
|||
|
||||
## Features
|
||||
|
||||
- Mailbox listing
|
||||
- Folder listing
|
||||
- Email listing and searching
|
||||
- Email composition based on `$EDITOR`
|
||||
- Email manipulation (copy/move/delete)
|
||||
|
|
|
@ -1,316 +1,352 @@
|
|||
//! Module related to message CLI.
|
||||
//! Module related to email CLI.
|
||||
//!
|
||||
//! This module provides subcommands, arguments and a command matcher related to message.
|
||||
//! This module provides subcommands, arguments and a command matcher related to email.
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{self, App, Arg, ArgMatches, SubCommand};
|
||||
use himalaya_lib::email::TplOverride;
|
||||
use log::{debug, info, trace};
|
||||
use log::{debug, trace};
|
||||
|
||||
use crate::{email, flag, folder, tpl, ui::table};
|
||||
|
||||
type Seq<'a> = &'a str;
|
||||
type PageSize = usize;
|
||||
type Page = usize;
|
||||
type Mbox<'a> = &'a str;
|
||||
type TextMime<'a> = &'a str;
|
||||
type Raw = bool;
|
||||
type All = bool;
|
||||
type RawMsg<'a> = &'a str;
|
||||
type Query = String;
|
||||
type AttachmentPaths<'a> = Vec<&'a str>;
|
||||
type MaxTableWidth = Option<usize>;
|
||||
type Encrypt = bool;
|
||||
type Criteria = String;
|
||||
type Headers<'a> = Vec<&'a str>;
|
||||
const ARG_ATTACHMENTS: &str = "attachment";
|
||||
const ARG_CRITERIA: &str = "criterion";
|
||||
const ARG_ENCRYPT: &str = "encrypt";
|
||||
const ARG_HEADERS: &str = "header";
|
||||
const ARG_ID: &str = "id";
|
||||
const ARG_IDS: &str = "ids";
|
||||
const ARG_MIME_TYPE: &str = "mime-type";
|
||||
const ARG_PAGE: &str = "page";
|
||||
const ARG_PAGE_SIZE: &str = "page-size";
|
||||
const ARG_QUERY: &str = "query";
|
||||
const ARG_RAW: &str = "raw";
|
||||
const ARG_REPLY_ALL: &str = "reply-all";
|
||||
const CMD_ATTACHMENTS: &str = "attachments";
|
||||
const CMD_COPY: &str = "copy";
|
||||
const CMD_DELETE: &str = "delete";
|
||||
const CMD_FORWARD: &str = "forward";
|
||||
const CMD_LIST: &str = "list";
|
||||
const CMD_MOVE: &str = "move";
|
||||
const CMD_READ: &str = "read";
|
||||
const CMD_REPLY: &str = "reply";
|
||||
const CMD_SAVE: &str = "save";
|
||||
const CMD_SEARCH: &str = "search";
|
||||
const CMD_SEND: &str = "send";
|
||||
const CMD_SORT: &str = "sort";
|
||||
const CMD_WRITE: &str = "write";
|
||||
|
||||
/// Message commands.
|
||||
type Criteria = String;
|
||||
type Encrypt = bool;
|
||||
type Folder<'a> = &'a str;
|
||||
type Page = usize;
|
||||
type PageSize = usize;
|
||||
type Query = String;
|
||||
type Raw = bool;
|
||||
type RawEmail<'a> = &'a str;
|
||||
type TextMime<'a> = &'a str;
|
||||
|
||||
pub(crate) type All = bool;
|
||||
pub(crate) type Attachments<'a> = Vec<&'a str>;
|
||||
pub(crate) type Headers<'a> = Vec<&'a str>;
|
||||
pub(crate) type Id<'a> = &'a str;
|
||||
pub(crate) type Ids<'a> = &'a str;
|
||||
|
||||
/// Represents the email commands.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Cmd<'a> {
|
||||
Attachments(Seq<'a>),
|
||||
Copy(Seq<'a>, Mbox<'a>),
|
||||
Delete(Seq<'a>),
|
||||
Forward(Seq<'a>, AttachmentPaths<'a>, Encrypt),
|
||||
List(MaxTableWidth, Option<PageSize>, Page),
|
||||
Move(Seq<'a>, Mbox<'a>),
|
||||
Read(Seq<'a>, TextMime<'a>, Raw, Headers<'a>),
|
||||
Reply(Seq<'a>, All, AttachmentPaths<'a>, Encrypt),
|
||||
Save(RawMsg<'a>),
|
||||
Search(Query, MaxTableWidth, Option<PageSize>, Page),
|
||||
Sort(Criteria, Query, MaxTableWidth, Option<PageSize>, Page),
|
||||
Send(RawMsg<'a>),
|
||||
Write(TplOverride<'a>, AttachmentPaths<'a>, Encrypt),
|
||||
Attachments(Id<'a>),
|
||||
Copy(Id<'a>, Folder<'a>),
|
||||
Delete(Id<'a>),
|
||||
Forward(Id<'a>, Attachments<'a>, Encrypt),
|
||||
List(table::args::MaxTableWidth, Option<PageSize>, Page),
|
||||
Move(Id<'a>, Folder<'a>),
|
||||
Read(Id<'a>, TextMime<'a>, Raw, Headers<'a>),
|
||||
Reply(Id<'a>, All, Attachments<'a>, Encrypt),
|
||||
Save(RawEmail<'a>),
|
||||
Search(Query, table::args::MaxTableWidth, Option<PageSize>, Page),
|
||||
Send(RawEmail<'a>),
|
||||
Sort(
|
||||
Criteria,
|
||||
Query,
|
||||
table::args::MaxTableWidth,
|
||||
Option<PageSize>,
|
||||
Page,
|
||||
),
|
||||
Write(TplOverride<'a>, Attachments<'a>, Encrypt),
|
||||
|
||||
Flag(Option<flag::args::Cmd<'a>>),
|
||||
Tpl(Option<tpl::args::Cmd<'a>>),
|
||||
}
|
||||
|
||||
/// Message command matcher.
|
||||
/// Email command matcher.
|
||||
pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Cmd<'a>>> {
|
||||
info!("entering message command matcher");
|
||||
trace!("matches: {:?}", m);
|
||||
|
||||
if let Some(m) = m.subcommand_matches("attachments") {
|
||||
info!("attachments command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("seq: {}", seq);
|
||||
return Ok(Some(Cmd::Attachments(seq)));
|
||||
}
|
||||
let cmd = if let Some(m) = m.subcommand_matches(CMD_ATTACHMENTS) {
|
||||
debug!("attachments command matched");
|
||||
let id = parse_id_arg(m);
|
||||
Cmd::Attachments(id)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_COPY) {
|
||||
debug!("copy command matched");
|
||||
let id = parse_id_arg(m);
|
||||
let folder = folder::args::parse_target_arg(m);
|
||||
Cmd::Copy(id, folder)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_DELETE) {
|
||||
debug!("delete command matched");
|
||||
let id = parse_id_arg(m);
|
||||
Cmd::Delete(id)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_FORWARD) {
|
||||
debug!("forward command matched");
|
||||
let id = parse_id_arg(m);
|
||||
let attachments = parse_attachments_arg(m);
|
||||
let encrypt = parse_encrypt_flag(m);
|
||||
Cmd::Forward(id, attachments, encrypt)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_LIST) {
|
||||
debug!("list command matched");
|
||||
let max_table_width = table::args::parse_max_width(m);
|
||||
let page_size = parse_page_size_arg(m);
|
||||
let page = parse_page_arg(m);
|
||||
Cmd::List(max_table_width, page_size, page)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_MOVE) {
|
||||
debug!("move command matched");
|
||||
let id = parse_id_arg(m);
|
||||
let folder = folder::args::parse_target_arg(m);
|
||||
Cmd::Move(id, folder)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_READ) {
|
||||
debug!("read command matched");
|
||||
let id = parse_id_arg(m);
|
||||
let mime = parse_mime_type_arg(m);
|
||||
let raw = parse_raw_flag(m);
|
||||
let headers = parse_headers_arg(m);
|
||||
Cmd::Read(id, mime, raw, headers)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_REPLY) {
|
||||
debug!("reply command matched");
|
||||
let id = parse_id_arg(m);
|
||||
let all = parse_reply_all_flag(m);
|
||||
let attachments = parse_attachments_arg(m);
|
||||
let encrypt = parse_encrypt_flag(m);
|
||||
Cmd::Reply(id, all, attachments, encrypt)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_SAVE) {
|
||||
debug!("save command matched");
|
||||
let email = parse_raw_arg(m);
|
||||
Cmd::Save(email)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_SEARCH) {
|
||||
debug!("search command matched");
|
||||
let max_table_width = table::args::parse_max_width(m);
|
||||
let page_size = parse_page_size_arg(m);
|
||||
let page = parse_page_arg(m);
|
||||
let query = parse_query_arg(m);
|
||||
Cmd::Search(query, max_table_width, page_size, page)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_SORT) {
|
||||
debug!("sort command matched");
|
||||
let max_table_width = table::args::parse_max_width(m);
|
||||
let page_size = parse_page_size_arg(m);
|
||||
let page = parse_page_arg(m);
|
||||
let criteria = parse_criteria_arg(m);
|
||||
let query = parse_query_arg(m);
|
||||
Cmd::Sort(criteria, query, max_table_width, page_size, page)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_SEND) {
|
||||
debug!("send command matched");
|
||||
let email = parse_raw_arg(m);
|
||||
Cmd::Send(email)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_WRITE) {
|
||||
debug!("write command matched");
|
||||
let attachments = parse_attachments_arg(m);
|
||||
let encrypt = parse_encrypt_flag(m);
|
||||
let tpl = tpl::args::parse_override_arg(m);
|
||||
Cmd::Write(tpl, attachments, encrypt)
|
||||
} else if let Some(m) = m.subcommand_matches(tpl::args::CMD_TPL) {
|
||||
Cmd::Tpl(tpl::args::matches(m)?)
|
||||
} else if let Some(m) = m.subcommand_matches(flag::args::CMD_FLAG) {
|
||||
Cmd::Flag(flag::args::matches(m)?)
|
||||
} else {
|
||||
debug!("default list command matched");
|
||||
Cmd::List(None, None, 0)
|
||||
};
|
||||
|
||||
if let Some(m) = m.subcommand_matches("copy") {
|
||||
info!("copy command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("seq: {}", seq);
|
||||
let mbox = m.value_of("folder-target").unwrap();
|
||||
debug!(r#"target mailbox: "{:?}""#, mbox);
|
||||
return Ok(Some(Cmd::Copy(seq, mbox)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("delete") {
|
||||
info!("copy command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("seq: {}", seq);
|
||||
return Ok(Some(Cmd::Delete(seq)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("forward") {
|
||||
info!("forward command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("seq: {}", seq);
|
||||
let paths: Vec<&str> = m.values_of("attachments").unwrap_or_default().collect();
|
||||
debug!("attachments paths: {:?}", paths);
|
||||
let encrypt = m.is_present("encrypt");
|
||||
debug!("encrypt: {}", encrypt);
|
||||
return Ok(Some(Cmd::Forward(seq, paths, encrypt)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("list") {
|
||||
info!("list command matched");
|
||||
let max_table_width = m
|
||||
.value_of("max-table-width")
|
||||
.and_then(|width| width.parse::<usize>().ok());
|
||||
debug!("max table width: {:?}", max_table_width);
|
||||
let page_size = m.value_of("page-size").and_then(|s| s.parse().ok());
|
||||
debug!("page size: {:?}", page_size);
|
||||
let page = m
|
||||
.value_of("page")
|
||||
.unwrap_or("1")
|
||||
.parse()
|
||||
.ok()
|
||||
.map(|page| 1.max(page) - 1)
|
||||
.unwrap_or_default();
|
||||
debug!("page: {}", page);
|
||||
return Ok(Some(Cmd::List(max_table_width, page_size, page)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("move") {
|
||||
info!("move command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("seq: {}", seq);
|
||||
let mbox = m.value_of("folder-target").unwrap();
|
||||
debug!("target mailbox: {:?}", mbox);
|
||||
return Ok(Some(Cmd::Move(seq, mbox)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("read") {
|
||||
info!("read command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("seq: {}", seq);
|
||||
let mime = m.value_of("mime-type").unwrap();
|
||||
debug!("text mime: {}", mime);
|
||||
let raw = m.is_present("raw");
|
||||
debug!("raw: {}", raw);
|
||||
let headers: Vec<&str> = m.values_of("headers").unwrap_or_default().collect();
|
||||
debug!("headers: {:?}", headers);
|
||||
return Ok(Some(Cmd::Read(seq, mime, raw, headers)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("reply") {
|
||||
info!("reply command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("seq: {}", seq);
|
||||
let all = m.is_present("reply-all");
|
||||
debug!("reply all: {}", all);
|
||||
let paths: Vec<&str> = m.values_of("attachments").unwrap_or_default().collect();
|
||||
debug!("attachments paths: {:?}", paths);
|
||||
let encrypt = m.is_present("encrypt");
|
||||
debug!("encrypt: {}", encrypt);
|
||||
|
||||
return Ok(Some(Cmd::Reply(seq, all, paths, encrypt)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("save") {
|
||||
info!("save command matched");
|
||||
let msg = m.value_of("message").unwrap_or_default();
|
||||
trace!("message: {}", msg);
|
||||
return Ok(Some(Cmd::Save(msg)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("search") {
|
||||
info!("search command matched");
|
||||
let max_table_width = m
|
||||
.value_of("max-table-width")
|
||||
.and_then(|width| width.parse::<usize>().ok());
|
||||
debug!("max table width: {:?}", max_table_width);
|
||||
let page_size = m.value_of("page-size").and_then(|s| s.parse().ok());
|
||||
debug!("page size: {:?}", page_size);
|
||||
let page = m
|
||||
.value_of("page")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.ok()
|
||||
.map(|page| 1.max(page) - 1)
|
||||
.unwrap_or_default();
|
||||
debug!("page: {}", page);
|
||||
let query = m
|
||||
.values_of("query")
|
||||
.unwrap_or_default()
|
||||
.fold((false, vec![]), |(escape, mut cmds), cmd| {
|
||||
match (cmd, escape) {
|
||||
// Next command is an arg and needs to be escaped
|
||||
("subject", _) | ("body", _) | ("text", _) => {
|
||||
cmds.push(cmd.to_string());
|
||||
(true, cmds)
|
||||
}
|
||||
// Escaped arg commands
|
||||
(_, true) => {
|
||||
cmds.push(format!("\"{}\"", cmd));
|
||||
(false, cmds)
|
||||
}
|
||||
// Regular commands
|
||||
(_, false) => {
|
||||
cmds.push(cmd.to_string());
|
||||
(false, cmds)
|
||||
}
|
||||
}
|
||||
})
|
||||
.1
|
||||
.join(" ");
|
||||
debug!("query: {}", query);
|
||||
return Ok(Some(Cmd::Search(query, max_table_width, page_size, page)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("sort") {
|
||||
info!("sort command matched");
|
||||
let max_table_width = m
|
||||
.value_of("max-table-width")
|
||||
.and_then(|width| width.parse::<usize>().ok());
|
||||
debug!("max table width: {:?}", max_table_width);
|
||||
let page_size = m.value_of("page-size").and_then(|s| s.parse().ok());
|
||||
debug!("page size: {:?}", page_size);
|
||||
let page = m
|
||||
.value_of("page")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.ok()
|
||||
.map(|page| 1.max(page) - 1)
|
||||
.unwrap_or_default();
|
||||
debug!("page: {:?}", page);
|
||||
let criteria = m
|
||||
.values_of("criterion")
|
||||
.unwrap_or_default()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
debug!("criteria: {:?}", criteria);
|
||||
let query = m
|
||||
.values_of("query")
|
||||
.unwrap_or_default()
|
||||
.fold((false, vec![]), |(escape, mut cmds), cmd| {
|
||||
match (cmd, escape) {
|
||||
// Next command is an arg and needs to be escaped
|
||||
("subject", _) | ("body", _) | ("text", _) => {
|
||||
cmds.push(cmd.to_string());
|
||||
(true, cmds)
|
||||
}
|
||||
// Escaped arg commands
|
||||
(_, true) => {
|
||||
cmds.push(format!("\"{}\"", cmd));
|
||||
(false, cmds)
|
||||
}
|
||||
// Regular commands
|
||||
(_, false) => {
|
||||
cmds.push(cmd.to_string());
|
||||
(false, cmds)
|
||||
}
|
||||
}
|
||||
})
|
||||
.1
|
||||
.join(" ");
|
||||
debug!("query: {:?}", query);
|
||||
return Ok(Some(Cmd::Sort(
|
||||
criteria,
|
||||
query,
|
||||
max_table_width,
|
||||
page_size,
|
||||
page,
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("send") {
|
||||
info!("send command matched");
|
||||
let msg = m.value_of("message").unwrap_or_default();
|
||||
trace!("message: {}", msg);
|
||||
return Ok(Some(Cmd::Send(msg)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("write") {
|
||||
info!("write command matched");
|
||||
let attachment_paths: Vec<&str> = m.values_of("attachments").unwrap_or_default().collect();
|
||||
debug!("attachments paths: {:?}", attachment_paths);
|
||||
let encrypt = m.is_present("encrypt");
|
||||
debug!("encrypt: {}", encrypt);
|
||||
let tpl = tpl::args::from_args(m);
|
||||
return Ok(Some(Cmd::Write(tpl, attachment_paths, encrypt)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("template") {
|
||||
return Ok(Some(Cmd::Tpl(tpl::args::matches(m)?)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("flag") {
|
||||
return Ok(Some(Cmd::Flag(flag::args::matches(m)?)));
|
||||
}
|
||||
|
||||
info!("default list command matched");
|
||||
Ok(Some(Cmd::List(None, None, 0)))
|
||||
Ok(Some(cmd))
|
||||
}
|
||||
|
||||
/// Message sequence number argument.
|
||||
pub fn seq_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("seq")
|
||||
.help("Specifies the targetted message")
|
||||
.value_name("SEQ")
|
||||
/// Represents the email subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![
|
||||
flag::args::subcmds(),
|
||||
tpl::args::subcmds(),
|
||||
vec![
|
||||
SubCommand::with_name(CMD_ATTACHMENTS)
|
||||
.aliases(&["attachment", "attach", "att", "at", "a"])
|
||||
.about("Downloads all attachments of the targeted email")
|
||||
.arg(email::args::id_arg()),
|
||||
SubCommand::with_name(CMD_LIST)
|
||||
.aliases(&["lst", "l"])
|
||||
.about("Lists all emails")
|
||||
.arg(page_size_arg())
|
||||
.arg(page_arg())
|
||||
.arg(table::args::max_width()),
|
||||
SubCommand::with_name(CMD_SEARCH)
|
||||
.aliases(&["s", "query", "q"])
|
||||
.about("Lists emails matching the given IMAP query")
|
||||
.arg(page_size_arg())
|
||||
.arg(page_arg())
|
||||
.arg(table::args::max_width())
|
||||
.arg(query_arg()),
|
||||
SubCommand::with_name(CMD_SORT)
|
||||
.about("Sorts emails by the given criteria and matching the given IMAP query")
|
||||
.arg(page_size_arg())
|
||||
.arg(page_arg())
|
||||
.arg(table::args::max_width())
|
||||
.arg(criteria_arg())
|
||||
.arg(query_arg()),
|
||||
SubCommand::with_name(CMD_WRITE)
|
||||
.about("Writes a new email")
|
||||
.args(&tpl::args::args())
|
||||
.arg(attachments_arg())
|
||||
.arg(encrypt_flag()),
|
||||
SubCommand::with_name(CMD_SEND)
|
||||
.about("Sends a raw email")
|
||||
.arg(raw_arg()),
|
||||
SubCommand::with_name(CMD_SAVE)
|
||||
.about("Saves a raw email")
|
||||
.arg(raw_arg()),
|
||||
SubCommand::with_name(CMD_READ)
|
||||
.about("Reads text bodies of a email")
|
||||
.arg(id_arg())
|
||||
.arg(mime_type_arg())
|
||||
.arg(raw_flag())
|
||||
.arg(headers_arg()),
|
||||
SubCommand::with_name(CMD_REPLY)
|
||||
.aliases(&["rep", "r"])
|
||||
.about("Answers to an email")
|
||||
.arg(id_arg())
|
||||
.arg(reply_all_flag())
|
||||
.arg(attachments_arg())
|
||||
.arg(encrypt_flag()),
|
||||
SubCommand::with_name(CMD_FORWARD)
|
||||
.aliases(&["fwd", "f"])
|
||||
.about("Forwards an email")
|
||||
.arg(id_arg())
|
||||
.arg(attachments_arg())
|
||||
.arg(encrypt_flag()),
|
||||
SubCommand::with_name(CMD_COPY)
|
||||
.aliases(&["cp", "c"])
|
||||
.about("Copies an email to the targeted folder")
|
||||
.arg(id_arg())
|
||||
.arg(folder::args::target_arg()),
|
||||
SubCommand::with_name(CMD_MOVE)
|
||||
.aliases(&["mv"])
|
||||
.about("Moves an email to the targeted folder")
|
||||
.arg(id_arg())
|
||||
.arg(folder::args::target_arg()),
|
||||
SubCommand::with_name(CMD_DELETE)
|
||||
.aliases(&["del", "d", "remove", "rm"])
|
||||
.about("Deletes an email")
|
||||
.arg(id_arg()),
|
||||
],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
|
||||
/// Represents the email id argument.
|
||||
pub fn id_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_ID)
|
||||
.help("Specifies the targeted email")
|
||||
.value_name("ID")
|
||||
.required(true)
|
||||
}
|
||||
|
||||
/// Message sequence range argument.
|
||||
pub fn seq_range_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("seq-range")
|
||||
.help("Specifies targetted message(s)")
|
||||
.long_help("Specifies a range of targetted messages. The range follows the [RFC3501](https://datatracker.ietf.org/doc/html/rfc3501#section-9) format: `1:5` matches messages with sequence number between 1 and 5, `1,5` matches messages with sequence number 1 or 5, * matches all messages.")
|
||||
.value_name("SEQ")
|
||||
/// Represents the email id argument parser.
|
||||
pub fn parse_id_arg<'a>(matches: &'a ArgMatches<'a>) -> &'a str {
|
||||
matches.value_of(ARG_ID).unwrap()
|
||||
}
|
||||
|
||||
/// Represents the email sort criteria argument.
|
||||
pub fn criteria_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_CRITERIA)
|
||||
.long("criterion")
|
||||
.short("c")
|
||||
.help("Email sorting preferences")
|
||||
.value_name("CRITERION:ORDER")
|
||||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.required(true)
|
||||
.possible_values(&[
|
||||
"arrival",
|
||||
"arrival:asc",
|
||||
"arrival:desc",
|
||||
"cc",
|
||||
"cc:asc",
|
||||
"cc:desc",
|
||||
"date",
|
||||
"date:asc",
|
||||
"date:desc",
|
||||
"from",
|
||||
"from:asc",
|
||||
"from:desc",
|
||||
"size",
|
||||
"size:asc",
|
||||
"size:desc",
|
||||
"subject",
|
||||
"subject:asc",
|
||||
"subject:desc",
|
||||
"to",
|
||||
"to:asc",
|
||||
"to:desc",
|
||||
])
|
||||
}
|
||||
|
||||
/// Represents the email sort criteria argument parser.
|
||||
pub fn parse_criteria_arg<'a>(matches: &'a ArgMatches<'a>) -> String {
|
||||
matches
|
||||
.values_of(ARG_CRITERIA)
|
||||
.unwrap_or_default()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
/// Represents the email ids argument.
|
||||
pub fn id_range_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_IDS)
|
||||
.help("Specifies targeted email(s)")
|
||||
.long_help("Specifies a range of targeted emails. The range follows the RFC3501 format.")
|
||||
.value_name("RANGE")
|
||||
.required(true)
|
||||
}
|
||||
|
||||
/// Message reply all argument.
|
||||
pub fn reply_all_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("reply-all")
|
||||
/// Represents the email ids argument parser.
|
||||
pub fn parse_ids_arg<'a>(matches: &'a ArgMatches<'a>) -> &'a str {
|
||||
matches.value_of(email::args::ARG_IDS).unwrap()
|
||||
}
|
||||
|
||||
/// Represents the email reply all argument.
|
||||
pub fn reply_all_flag<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_REPLY_ALL)
|
||||
.help("Includes all recipients")
|
||||
.short("A")
|
||||
.long("all")
|
||||
}
|
||||
|
||||
/// Message page size argument.
|
||||
/// Represents the email reply all argument parser.
|
||||
pub fn parse_reply_all_flag<'a>(matches: &'a ArgMatches<'a>) -> bool {
|
||||
matches.is_present(ARG_REPLY_ALL)
|
||||
}
|
||||
|
||||
/// Represents the page size argument.
|
||||
fn page_size_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("page-size")
|
||||
Arg::with_name(ARG_PAGE_SIZE)
|
||||
.help("Page size")
|
||||
.short("s")
|
||||
.long("size")
|
||||
.value_name("INT")
|
||||
}
|
||||
|
||||
/// Message page argument.
|
||||
/// Represents the page size argument parser.
|
||||
fn parse_page_size_arg<'a>(matches: &'a ArgMatches<'a>) -> Option<usize> {
|
||||
matches.value_of(ARG_PAGE_SIZE).and_then(|s| s.parse().ok())
|
||||
}
|
||||
|
||||
/// Represents the page argument.
|
||||
fn page_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("page")
|
||||
Arg::with_name(ARG_PAGE)
|
||||
.help("Page number")
|
||||
.short("p")
|
||||
.long("page")
|
||||
|
@ -318,154 +354,136 @@ fn page_arg<'a>() -> Arg<'a, 'a> {
|
|||
.default_value("0")
|
||||
}
|
||||
|
||||
/// Message attachment argument.
|
||||
/// Represents the page argument parser.
|
||||
fn parse_page_arg<'a>(matches: &'a ArgMatches<'a>) -> usize {
|
||||
matches
|
||||
.value_of(ARG_PAGE)
|
||||
.unwrap_or("1")
|
||||
.parse()
|
||||
.ok()
|
||||
.map(|page| 1.max(page) - 1)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Represents the email attachments argument.
|
||||
pub fn attachments_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("attachments")
|
||||
.help("Adds attachment to the message")
|
||||
Arg::with_name(ARG_ATTACHMENTS)
|
||||
.help("Adds attachment to the email")
|
||||
.short("a")
|
||||
.long("attachment")
|
||||
.value_name("PATH")
|
||||
.multiple(true)
|
||||
}
|
||||
|
||||
/// Represents the message headers argument.
|
||||
/// Represents the email attachments argument parser.
|
||||
pub fn parse_attachments_arg<'a>(matches: &'a ArgMatches<'a>) -> Vec<&'a str> {
|
||||
matches
|
||||
.values_of(ARG_ATTACHMENTS)
|
||||
.unwrap_or_default()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Represents the email headers argument.
|
||||
pub fn headers_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("headers")
|
||||
.help("Shows additional headers with the message")
|
||||
Arg::with_name(ARG_HEADERS)
|
||||
.help("Shows additional headers with the email")
|
||||
.short("h")
|
||||
.long("header")
|
||||
.value_name("STR")
|
||||
.value_name("STRING")
|
||||
.multiple(true)
|
||||
}
|
||||
|
||||
/// Message encrypt argument.
|
||||
pub fn encrypt_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("encrypt")
|
||||
.help("Encrypts the message")
|
||||
/// Represents the email headers argument parser.
|
||||
pub fn parse_headers_arg<'a>(matches: &'a ArgMatches<'a>) -> Vec<&'a str> {
|
||||
matches.values_of(ARG_HEADERS).unwrap_or_default().collect()
|
||||
}
|
||||
|
||||
/// Represents the raw flag.
|
||||
pub fn raw_flag<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_RAW)
|
||||
.help("Reads a raw email")
|
||||
.long("raw")
|
||||
.short("r")
|
||||
}
|
||||
|
||||
/// Represents the raw flag parser.
|
||||
pub fn parse_raw_flag<'a>(matches: &'a ArgMatches<'a>) -> bool {
|
||||
matches.is_present(ARG_RAW)
|
||||
}
|
||||
|
||||
/// Represents the email raw argument.
|
||||
pub fn raw_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_RAW).raw(true)
|
||||
}
|
||||
|
||||
/// Represents the email raw argument parser.
|
||||
pub fn parse_raw_arg<'a>(matches: &'a ArgMatches<'a>) -> &'a str {
|
||||
matches.value_of(ARG_RAW).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Represents the email encrypt flag.
|
||||
pub fn encrypt_flag<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_ENCRYPT)
|
||||
.help("Encrypts the email")
|
||||
.short("e")
|
||||
.long("encrypt")
|
||||
}
|
||||
|
||||
/// Message subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![
|
||||
flag::args::subcmds(),
|
||||
tpl::args::subcmds(),
|
||||
vec![
|
||||
SubCommand::with_name("attachments")
|
||||
.aliases(&["attachment", "att", "a"])
|
||||
.about("Downloads all message attachments")
|
||||
.arg(email::args::seq_arg()),
|
||||
SubCommand::with_name("list")
|
||||
.aliases(&["lst", "l"])
|
||||
.about("Lists all messages")
|
||||
.arg(page_size_arg())
|
||||
.arg(page_arg())
|
||||
.arg(table::args::max_width()),
|
||||
SubCommand::with_name("search")
|
||||
.aliases(&["s", "query", "q"])
|
||||
.about("Lists messages matching the given IMAP query")
|
||||
.arg(page_size_arg())
|
||||
.arg(page_arg())
|
||||
.arg(table::args::max_width())
|
||||
.arg(
|
||||
Arg::with_name("query")
|
||||
.help("IMAP query")
|
||||
.long_help("The IMAP query format follows the [RFC3501](https://tools.ietf.org/html/rfc3501#section-6.4.4). The query is case-insensitive.")
|
||||
.value_name("QUERY")
|
||||
.multiple(true)
|
||||
.required(true),
|
||||
),
|
||||
SubCommand::with_name("sort")
|
||||
.about("Sorts messages by the given criteria and matching the given IMAP query")
|
||||
.arg(page_size_arg())
|
||||
.arg(page_arg())
|
||||
.arg(table::args::max_width())
|
||||
.arg(
|
||||
Arg::with_name("criterion")
|
||||
.long("criterion")
|
||||
.short("c")
|
||||
.help("Defines the message sorting preferences")
|
||||
.value_name("CRITERION:ORDER")
|
||||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.required(true)
|
||||
.possible_values(&[
|
||||
"arrival", "arrival:asc", "arrival:desc",
|
||||
"cc", "cc:asc", "cc:desc",
|
||||
"date", "date:asc", "date:desc",
|
||||
"from", "from:asc", "from:desc",
|
||||
"size", "size:asc", "size:desc",
|
||||
"subject", "subject:asc", "subject:desc",
|
||||
"to", "to:asc", "to:desc",
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("query")
|
||||
.help("IMAP query")
|
||||
.long_help("The IMAP query format follows the [RFC3501](https://tools.ietf.org/html/rfc3501#section-6.4.4). The query is case-insensitive.")
|
||||
.value_name("QUERY")
|
||||
.default_value("ALL")
|
||||
.raw(true),
|
||||
),
|
||||
SubCommand::with_name("write")
|
||||
.about("Writes a new message")
|
||||
.args(&tpl::args::tpl_args())
|
||||
.arg(attachments_arg())
|
||||
.arg(encrypt_arg()),
|
||||
SubCommand::with_name("send")
|
||||
.about("Sends a raw message")
|
||||
.arg(Arg::with_name("message").raw(true)),
|
||||
SubCommand::with_name("save")
|
||||
.about("Saves a raw message")
|
||||
.arg(Arg::with_name("message").raw(true)),
|
||||
SubCommand::with_name("read")
|
||||
.about("Reads text bodies of a message")
|
||||
.arg(seq_arg())
|
||||
.arg(
|
||||
Arg::with_name("mime-type")
|
||||
.help("MIME type to use")
|
||||
.short("t")
|
||||
.long("mime-type")
|
||||
.value_name("MIME")
|
||||
.possible_values(&["plain", "html"])
|
||||
.default_value("plain"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("raw")
|
||||
.help("Reads raw message")
|
||||
.long("raw")
|
||||
.short("r"),
|
||||
)
|
||||
.arg(headers_arg()),
|
||||
SubCommand::with_name("reply")
|
||||
.aliases(&["rep", "r"])
|
||||
.about("Answers to a message")
|
||||
.arg(seq_arg())
|
||||
.arg(reply_all_arg())
|
||||
.arg(attachments_arg())
|
||||
.arg(encrypt_arg()),
|
||||
SubCommand::with_name("forward")
|
||||
.aliases(&["fwd", "f"])
|
||||
.about("Forwards a message")
|
||||
.arg(seq_arg())
|
||||
.arg(attachments_arg())
|
||||
.arg(encrypt_arg()),
|
||||
SubCommand::with_name("copy")
|
||||
.aliases(&["cp", "c"])
|
||||
.about("Copies a message to the targetted mailbox")
|
||||
.arg(seq_arg())
|
||||
.arg(folder::args::target_arg()),
|
||||
SubCommand::with_name("move")
|
||||
.aliases(&["mv"])
|
||||
.about("Moves a message to the targetted mailbox")
|
||||
.arg(seq_arg())
|
||||
.arg(folder::args::target_arg()),
|
||||
SubCommand::with_name("delete")
|
||||
.aliases(&["del", "d", "remove", "rm"])
|
||||
.about("Deletes a message")
|
||||
.arg(seq_arg()),
|
||||
],
|
||||
]
|
||||
.concat()
|
||||
/// Represents the email encrypt flag parser.
|
||||
pub fn parse_encrypt_flag<'a>(matches: &'a ArgMatches<'a>) -> bool {
|
||||
matches.is_present(ARG_ENCRYPT)
|
||||
}
|
||||
|
||||
/// Represents the email MIME type argument.
|
||||
pub fn mime_type_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_MIME_TYPE)
|
||||
.help("MIME type to use")
|
||||
.short("t")
|
||||
.long("mime-type")
|
||||
.value_name("MIME")
|
||||
.possible_values(&["plain", "html"])
|
||||
.default_value("plain")
|
||||
}
|
||||
|
||||
/// Represents the email MIME type argument parser.
|
||||
pub fn parse_mime_type_arg<'a>(matches: &'a ArgMatches<'a>) -> &'a str {
|
||||
matches.value_of(ARG_MIME_TYPE).unwrap()
|
||||
}
|
||||
|
||||
/// Represents the email query argument.
|
||||
pub fn query_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_QUERY)
|
||||
.help("IMAP query")
|
||||
.long_help("The IMAP query format follows the RFC3501. The query is case-insensitive.")
|
||||
.value_name("QUERY")
|
||||
.multiple(true)
|
||||
.required(true)
|
||||
}
|
||||
|
||||
/// Represents the email query argument parser.
|
||||
pub fn parse_query_arg<'a>(matches: &'a ArgMatches<'a>) -> String {
|
||||
matches
|
||||
.values_of(ARG_QUERY)
|
||||
.unwrap_or_default()
|
||||
.fold((false, vec![]), |(escape, mut cmds), cmd| {
|
||||
match (cmd, escape) {
|
||||
// Next command is an arg and needs to be escaped
|
||||
("subject", _) | ("body", _) | ("text", _) => {
|
||||
cmds.push(cmd.to_string());
|
||||
(true, cmds)
|
||||
}
|
||||
// Escaped arg commands
|
||||
(_, true) => {
|
||||
cmds.push(format!("\"{}\"", cmd));
|
||||
(false, cmds)
|
||||
}
|
||||
// Regular commands
|
||||
(_, false) => {
|
||||
cmds.push(cmd.to_string());
|
||||
(false, cmds)
|
||||
}
|
||||
}
|
||||
})
|
||||
.1
|
||||
.join(" ")
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ pub fn attachments<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
|||
printer.print_struct("Done!")
|
||||
}
|
||||
|
||||
/// Copy a message from a mailbox to another.
|
||||
/// Copy a message from a folder to another.
|
||||
pub fn copy<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
||||
seq: &str,
|
||||
mbox_src: &str,
|
||||
|
@ -77,7 +77,7 @@ pub fn delete<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
|||
printer.print_struct(format!("Message(s) {} successfully deleted", seq))
|
||||
}
|
||||
|
||||
/// Forward the given message UID from the selected mailbox.
|
||||
/// Forward the given message UID from the selected folder.
|
||||
pub fn forward<'a, P: Printer, B: Backend<'a> + ?Sized, S: Sender + ?Sized>(
|
||||
seq: &str,
|
||||
attachments_paths: Vec<&str>,
|
||||
|
@ -104,7 +104,7 @@ pub fn forward<'a, P: Printer, B: Backend<'a> + ?Sized, S: Sender + ?Sized>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// List paginated messages from the selected mailbox.
|
||||
/// List paginated messages from the selected folder.
|
||||
pub fn list<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
||||
max_width: Option<usize>,
|
||||
page_size: Option<usize>,
|
||||
|
@ -195,7 +195,7 @@ pub fn mailto<'a, P: Printer, B: Backend<'a> + ?Sized, S: Sender + ?Sized>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Move a message from a mailbox to another.
|
||||
/// Move a message from a folder to another.
|
||||
pub fn move_<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
||||
seq: &str,
|
||||
mbox_src: &str,
|
||||
|
@ -260,16 +260,14 @@ pub fn reply<'a, P: Printer, B: Backend<'a> + ?Sized, S: Sender + ?Sized>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Saves a raw message to the targetted mailbox.
|
||||
/// Saves a raw message to the targetted folder.
|
||||
pub fn save<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
||||
mbox: &str,
|
||||
raw_msg: &str,
|
||||
printer: &mut P,
|
||||
backend: &mut B,
|
||||
) -> Result<()> {
|
||||
info!("entering save message handler");
|
||||
|
||||
debug!("mailbox: {}", mbox);
|
||||
debug!("folder: {}", mbox);
|
||||
|
||||
let is_tty = atty::is(Stream::Stdin);
|
||||
debug!("is tty: {}", is_tty);
|
||||
|
@ -290,7 +288,8 @@ pub fn save<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Paginate messages from the selected mailbox matching the specified query.
|
||||
/// Paginate messages from the selected folder matching the specified
|
||||
/// query.
|
||||
pub fn search<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
||||
query: String,
|
||||
max_width: Option<usize>,
|
||||
|
@ -314,7 +313,8 @@ pub fn search<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
|||
)
|
||||
}
|
||||
|
||||
/// Paginates messages from the selected mailbox matching the specified query, sorted by the given criteria.
|
||||
/// Paginates messages from the selected folder matching the specified
|
||||
/// query, sorted by the given criteria.
|
||||
pub fn sort<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
||||
sort: String,
|
||||
query: String,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Message flag CLI module.
|
||||
//! Email flag CLI module.
|
||||
//!
|
||||
//! This module provides subcommands, arguments and a command matcher related to the message flag
|
||||
//! domain.
|
||||
//! This module provides subcommands, arguments and a command matcher
|
||||
//! related to the email flag domain.
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{self, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
|
@ -9,101 +9,91 @@ use log::{debug, info};
|
|||
|
||||
use crate::email;
|
||||
|
||||
type SeqRange<'a> = &'a str;
|
||||
const ARG_FLAGS: &str = "flag";
|
||||
|
||||
const CMD_ADD: &str = "add";
|
||||
const CMD_DEL: &str = "remove";
|
||||
const CMD_SET: &str = "set";
|
||||
|
||||
pub(crate) const CMD_FLAG: &str = "flag";
|
||||
|
||||
type Flags = String;
|
||||
|
||||
/// Represents the flag commands.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Cmd<'a> {
|
||||
/// Represents the add flags command.
|
||||
Add(SeqRange<'a>, Flags),
|
||||
/// Represents the set flags command.
|
||||
Set(SeqRange<'a>, Flags),
|
||||
/// Represents the remove flags command.
|
||||
Remove(SeqRange<'a>, Flags),
|
||||
Add(email::args::Ids<'a>, Flags),
|
||||
Set(email::args::Ids<'a>, Flags),
|
||||
Del(email::args::Ids<'a>, Flags),
|
||||
}
|
||||
|
||||
/// Defines the flag command matcher.
|
||||
/// Represents the flag command matcher.
|
||||
pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Cmd<'a>>> {
|
||||
info!("entering message flag command matcher");
|
||||
|
||||
if let Some(m) = m.subcommand_matches("add") {
|
||||
info!("add subcommand matched");
|
||||
let seq_range = m.value_of("seq-range").unwrap();
|
||||
debug!("seq range: {}", seq_range);
|
||||
let flags: String = m
|
||||
.values_of("flags")
|
||||
.unwrap_or_default()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
debug!("flags: {:?}", flags);
|
||||
return Ok(Some(Cmd::Add(seq_range, flags)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("set") {
|
||||
info!("set subcommand matched");
|
||||
let seq_range = m.value_of("seq-range").unwrap();
|
||||
debug!("seq range: {}", seq_range);
|
||||
let flags: String = m
|
||||
.values_of("flags")
|
||||
.unwrap_or_default()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
debug!("flags: {:?}", flags);
|
||||
return Ok(Some(Cmd::Set(seq_range, flags)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("remove") {
|
||||
let cmd = if let Some(m) = m.subcommand_matches(CMD_ADD) {
|
||||
debug!("add subcommand matched");
|
||||
let ids = email::args::parse_ids_arg(m);
|
||||
let flags: String = parse_flags_arg(m);
|
||||
Some(Cmd::Add(ids, flags))
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_SET) {
|
||||
debug!("set subcommand matched");
|
||||
let ids = email::args::parse_ids_arg(m);
|
||||
let flags: String = parse_flags_arg(m);
|
||||
Some(Cmd::Set(ids, flags))
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_DEL) {
|
||||
info!("remove subcommand matched");
|
||||
let seq_range = m.value_of("seq-range").unwrap();
|
||||
debug!("seq range: {}", seq_range);
|
||||
let flags: String = m
|
||||
.values_of("flags")
|
||||
.unwrap_or_default()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
debug!("flags: {:?}", flags);
|
||||
return Ok(Some(Cmd::Remove(seq_range, flags)));
|
||||
}
|
||||
let ids = email::args::parse_ids_arg(m);
|
||||
let flags: String = parse_flags_arg(m);
|
||||
Some(Cmd::Del(ids, flags))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
/// Defines the flags argument.
|
||||
fn flags_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("flags")
|
||||
.help("IMAP flags")
|
||||
.long_help("IMAP flags. Flags are case-insensitive, and they do not need to be prefixed with `\\`.")
|
||||
/// Represents the flag subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![SubCommand::with_name(CMD_FLAG)
|
||||
.aliases(&["flags", "flg"])
|
||||
.about("Handles email flags")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name(CMD_ADD)
|
||||
.aliases(&["a"])
|
||||
.about("Adds email flags")
|
||||
.arg(email::args::id_range_arg())
|
||||
.arg(flags_arg()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name(CMD_SET)
|
||||
.aliases(&["s", "change", "c"])
|
||||
.about("Sets email flags")
|
||||
.arg(email::args::id_range_arg())
|
||||
.arg(flags_arg()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name(CMD_DEL)
|
||||
.aliases(&["rem", "rm", "r", "delete", "del", "d"])
|
||||
.about("Removes email flags")
|
||||
.arg(email::args::id_range_arg())
|
||||
.arg(flags_arg()),
|
||||
)]
|
||||
}
|
||||
|
||||
/// Represents the flags argument.
|
||||
pub fn flags_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_FLAGS)
|
||||
.long_help("Flags are case-insensitive, and they do not need to be prefixed with `\\`.")
|
||||
.value_name("FLAGS…")
|
||||
.multiple(true)
|
||||
.required(true)
|
||||
}
|
||||
|
||||
/// Contains flag subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![SubCommand::with_name("flag")
|
||||
.aliases(&["flags", "flg"])
|
||||
.about("Handles flags")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("add")
|
||||
.aliases(&["a"])
|
||||
.about("Adds flags to a message")
|
||||
.arg(email::args::seq_range_arg())
|
||||
.arg(flags_arg()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("set")
|
||||
.aliases(&["s", "change", "c"])
|
||||
.about("Replaces all message flags")
|
||||
.arg(email::args::seq_range_arg())
|
||||
.arg(flags_arg()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("remove")
|
||||
.aliases(&["rem", "rm", "r", "delete", "del", "d"])
|
||||
.about("Removes flags from a message")
|
||||
.arg(email::args::seq_range_arg())
|
||||
.arg(flags_arg()),
|
||||
)]
|
||||
/// Represents the flags argument parser.
|
||||
pub fn parse_flags_arg<'a>(matches: &'a ArgMatches<'a>) -> String {
|
||||
matches
|
||||
.values_of(ARG_FLAGS)
|
||||
.unwrap_or_default()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
|
|
@ -1,80 +1,98 @@
|
|||
//! Mailbox CLI module.
|
||||
//! Folder CLI module.
|
||||
//!
|
||||
//! This module provides subcommands, arguments and a command matcher related to the mailbox
|
||||
//! domain.
|
||||
//! This module provides subcommands, arguments and a command matcher
|
||||
//! related to the folder domain.
|
||||
|
||||
use anyhow::Result;
|
||||
use clap;
|
||||
use log::{debug, info};
|
||||
use clap::{self, App, Arg, ArgMatches, SubCommand};
|
||||
use log::debug;
|
||||
|
||||
use crate::ui::table;
|
||||
|
||||
type MaxTableWidth = Option<usize>;
|
||||
const ARG_SOURCE: &str = "source";
|
||||
const ARG_TARGET: &str = "target";
|
||||
const CMD_FOLDERS: &str = "folders";
|
||||
|
||||
/// Represents the mailbox commands.
|
||||
/// Represents the folder commands.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Cmd {
|
||||
/// Represents the list mailboxes command.
|
||||
List(MaxTableWidth),
|
||||
List(table::args::MaxTableWidth),
|
||||
}
|
||||
|
||||
/// Defines the mailbox command matcher.
|
||||
pub fn matches(m: &clap::ArgMatches) -> Result<Option<Cmd>> {
|
||||
info!("entering mailbox command matcher");
|
||||
/// Represents the folder command matcher.
|
||||
pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
||||
let cmd = if let Some(m) = m.subcommand_matches(CMD_FOLDERS) {
|
||||
debug!("folders command matched");
|
||||
let max_table_width = table::args::parse_max_width(m);
|
||||
Some(Cmd::List(max_table_width))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(m) = m.subcommand_matches("mailboxes") {
|
||||
info!("mailboxes command matched");
|
||||
let max_table_width = m
|
||||
.value_of("max-table-width")
|
||||
.and_then(|width| width.parse::<usize>().ok());
|
||||
debug!("max table width: {:?}", max_table_width);
|
||||
return Ok(Some(Cmd::List(max_table_width)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
/// Contains mailbox subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||
vec![clap::SubCommand::with_name("mailboxes")
|
||||
/// Represents folder subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![SubCommand::with_name(CMD_FOLDERS)
|
||||
.aliases(&[
|
||||
"mailbox", "mboxes", "mbox", "mb", "m", "folders", "fold", "fo",
|
||||
"folder",
|
||||
"fold",
|
||||
"fo",
|
||||
"mailboxes",
|
||||
"mailbox",
|
||||
"mboxes",
|
||||
"mbox",
|
||||
"mb",
|
||||
"m",
|
||||
])
|
||||
.about("Lists folders")
|
||||
.arg(table::args::max_width())]
|
||||
}
|
||||
|
||||
/// Defines the source mailbox argument.
|
||||
pub fn source_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||
clap::Arg::with_name("folder-source")
|
||||
/// Represents the source folder argument.
|
||||
pub fn source_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_SOURCE)
|
||||
.short("f")
|
||||
.long("folder")
|
||||
.help("Specifies the folder source")
|
||||
.value_name("SOURCE")
|
||||
}
|
||||
|
||||
/// Defines the target mailbox argument.
|
||||
pub fn target_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||
clap::Arg::with_name("folder-target")
|
||||
/// Represents the source folder argument parser.
|
||||
pub fn parse_source_arg<'a>(matches: &'a ArgMatches<'a>) -> &'a str {
|
||||
matches.value_of(ARG_SOURCE).unwrap()
|
||||
}
|
||||
|
||||
/// Represents the target folder argument.
|
||||
pub fn target_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name(ARG_TARGET)
|
||||
.help("Specifies the folder target")
|
||||
.value_name("TARGET")
|
||||
.required(true)
|
||||
}
|
||||
|
||||
/// Represents the target folder argument parser.
|
||||
pub fn parse_target_arg<'a>(matches: &'a ArgMatches<'a>) -> &'a str {
|
||||
matches.value_of(ARG_TARGET).unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use clap::{App, ErrorKind};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_should_match_cmds() {
|
||||
let arg = clap::App::new("himalaya")
|
||||
let arg = App::new("himalaya")
|
||||
.subcommands(subcmds())
|
||||
.get_matches_from(&["himalaya", "mailboxes"]);
|
||||
.get_matches_from(&["himalaya", "folders"]);
|
||||
assert_eq!(Some(Cmd::List(None)), matches(&arg).unwrap());
|
||||
|
||||
let arg = clap::App::new("himalaya")
|
||||
let arg = App::new("himalaya")
|
||||
.subcommands(subcmds())
|
||||
.get_matches_from(&["himalaya", "mailboxes", "--max-width", "20"]);
|
||||
.get_matches_from(&["himalaya", "folders", "--max-width", "20"]);
|
||||
assert_eq!(Some(Cmd::List(Some(20))), matches(&arg).unwrap());
|
||||
}
|
||||
|
||||
|
@ -82,57 +100,53 @@ mod tests {
|
|||
fn it_should_match_aliases() {
|
||||
macro_rules! get_matches_from {
|
||||
($alias:expr) => {
|
||||
clap::App::new("himalaya")
|
||||
App::new("himalaya")
|
||||
.subcommands(subcmds())
|
||||
.get_matches_from(&["himalaya", $alias])
|
||||
.subcommand_name()
|
||||
};
|
||||
}
|
||||
|
||||
assert_eq!(Some("mailboxes"), get_matches_from!["mailboxes"]);
|
||||
assert_eq!(Some("mailboxes"), get_matches_from!["mboxes"]);
|
||||
assert_eq!(Some("mailboxes"), get_matches_from!["mbox"]);
|
||||
assert_eq!(Some("mailboxes"), get_matches_from!["mb"]);
|
||||
assert_eq!(Some("mailboxes"), get_matches_from!["m"]);
|
||||
assert_eq!(Some("folders"), get_matches_from!["folders"]);
|
||||
assert_eq!(Some("folders"), get_matches_from!["folder"]);
|
||||
assert_eq!(Some("folders"), get_matches_from!["fold"]);
|
||||
assert_eq!(Some("folders"), get_matches_from!["fo"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_match_source_arg() {
|
||||
macro_rules! get_matches_from {
|
||||
($($arg:expr),*) => {
|
||||
clap::App::new("himalaya")
|
||||
App::new("himalaya")
|
||||
.arg(source_arg())
|
||||
.get_matches_from(&["himalaya", $($arg,)*])
|
||||
};
|
||||
}
|
||||
|
||||
let app = get_matches_from![];
|
||||
assert_eq!(None, app.value_of("folder-source"));
|
||||
assert_eq!(None, app.value_of("source"));
|
||||
|
||||
let app = get_matches_from!["-m", "SOURCE"];
|
||||
assert_eq!(Some("SOURCE"), app.value_of("folder-source"));
|
||||
let app = get_matches_from!["-f", "SOURCE"];
|
||||
assert_eq!(Some("SOURCE"), app.value_of("source"));
|
||||
|
||||
let app = get_matches_from!["--mailbox", "SOURCE"];
|
||||
assert_eq!(Some("SOURCE"), app.value_of("folder-source"));
|
||||
let app = get_matches_from!["--folder", "SOURCE"];
|
||||
assert_eq!(Some("SOURCE"), app.value_of("source"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_match_target_arg() {
|
||||
macro_rules! get_matches_from {
|
||||
($($arg:expr),*) => {
|
||||
clap::App::new("himalaya")
|
||||
App::new("himalaya")
|
||||
.arg(target_arg())
|
||||
.get_matches_from_safe(&["himalaya", $($arg,)*])
|
||||
};
|
||||
}
|
||||
|
||||
let app = get_matches_from![];
|
||||
assert_eq!(
|
||||
clap::ErrorKind::MissingRequiredArgument,
|
||||
app.unwrap_err().kind
|
||||
);
|
||||
assert_eq!(ErrorKind::MissingRequiredArgument, app.unwrap_err().kind);
|
||||
|
||||
let app = get_matches_from!["TARGET"];
|
||||
assert_eq!(Some("TARGET"), app.unwrap().value_of("folder-target"));
|
||||
assert_eq!(Some("TARGET"), app.unwrap().value_of("target"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
//! Mailbox handling module.
|
||||
//! Folder handling module.
|
||||
//!
|
||||
//! This module gathers all mailbox actions triggered by the CLI.
|
||||
//! This module gathers all folder actions triggered by the CLI.
|
||||
|
||||
use anyhow::Result;
|
||||
use himalaya_lib::{AccountConfig, Backend};
|
||||
use log::{info, trace};
|
||||
use log::trace;
|
||||
|
||||
use crate::printer::{PrintTableOpts, Printer};
|
||||
|
||||
/// Lists all mailboxes.
|
||||
/// Lists all folders.
|
||||
pub fn list<'a, P: Printer, B: Backend<'a> + ?Sized>(
|
||||
max_width: Option<usize>,
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &mut B,
|
||||
) -> Result<()> {
|
||||
info!("entering list mailbox handler");
|
||||
let mboxes = backend.folder_list()?;
|
||||
trace!("mailboxes: {:?}", mboxes);
|
||||
let folders = backend.folder_list()?;
|
||||
trace!("folders: {:?}", folders);
|
||||
printer.print_table(
|
||||
// TODO: remove Box
|
||||
Box::new(mboxes),
|
||||
Box::new(folders),
|
||||
PrintTableOpts {
|
||||
format: &config.email_reading_format,
|
||||
max_width,
|
||||
|
@ -109,20 +108,18 @@ mod tests {
|
|||
unimplemented!();
|
||||
}
|
||||
fn folder_list(&mut self) -> backend::Result<Folders> {
|
||||
Ok(Folders {
|
||||
folders: vec![
|
||||
Folder {
|
||||
delim: "/".into(),
|
||||
name: "INBOX".into(),
|
||||
desc: "desc".into(),
|
||||
},
|
||||
Folder {
|
||||
delim: "/".into(),
|
||||
name: "Sent".into(),
|
||||
desc: "desc".into(),
|
||||
},
|
||||
],
|
||||
})
|
||||
Ok(Folders(vec![
|
||||
Folder {
|
||||
delim: "/".into(),
|
||||
name: "INBOX".into(),
|
||||
desc: "desc".into(),
|
||||
},
|
||||
Folder {
|
||||
delim: "/".into(),
|
||||
name: "Sent".into(),
|
||||
desc: "desc".into(),
|
||||
},
|
||||
]))
|
||||
}
|
||||
fn folder_delete(&mut self, _: &str) -> backend::Result<()> {
|
||||
unimplemented!();
|
||||
|
@ -143,9 +140,6 @@ mod tests {
|
|||
fn email_add(&mut self, _: &str, _: &[u8], _: &str) -> backend::Result<String> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn email_list(&mut self, _: &str, _: &str) -> backend::Result<Email> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn email_get(&mut self, _: &str, _: &str) -> backend::Result<Email> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -167,6 +161,9 @@ mod tests {
|
|||
fn flags_delete(&mut self, _: &str, _: &str, _: &str) -> backend::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
let account_config = AccountConfig::default();
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Command>> {
|
|||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![
|
||||
clap::SubCommand::with_name("notify")
|
||||
.about("Notifies when new messages arrive in the given mailbox")
|
||||
.about("Notifies when new messages arrive in the given folder")
|
||||
.aliases(&["idle"])
|
||||
.arg(
|
||||
clap::Arg::with_name("keepalive")
|
||||
|
|
|
@ -1,138 +1,163 @@
|
|||
//! Module related to message template CLI.
|
||||
//! Module related to email template CLI.
|
||||
//!
|
||||
//! This module provides subcommands, arguments and a command matcher related to message template.
|
||||
//! This module provides subcommands, arguments and a command matcher
|
||||
//! related to email templating.
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{self, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use himalaya_lib::email::TplOverride;
|
||||
use log::{debug, info, trace};
|
||||
use log::debug;
|
||||
|
||||
use crate::email;
|
||||
|
||||
type Seq<'a> = &'a str;
|
||||
type ReplyAll = bool;
|
||||
type AttachmentPaths<'a> = Vec<&'a str>;
|
||||
const ARG_BCC: &str = "bcc";
|
||||
const ARG_BODY: &str = "body";
|
||||
const ARG_CC: &str = "cc";
|
||||
const ARG_FROM: &str = "from";
|
||||
const ARG_HEADERS: &str = "header";
|
||||
const ARG_SIGNATURE: &str = "signature";
|
||||
const ARG_SUBJECT: &str = "subject";
|
||||
const ARG_TO: &str = "to";
|
||||
const ARG_TPL: &str = "template";
|
||||
const CMD_FORWARD: &str = "forward";
|
||||
const CMD_NEW: &str = "new";
|
||||
const CMD_REPLY: &str = "reply";
|
||||
const CMD_SAVE: &str = "save";
|
||||
const CMD_SEND: &str = "send";
|
||||
|
||||
pub(crate) const CMD_TPL: &str = "template";
|
||||
|
||||
type Tpl<'a> = &'a str;
|
||||
|
||||
pub fn from_args<'a>(matches: &'a ArgMatches<'a>) -> TplOverride {
|
||||
TplOverride {
|
||||
subject: matches.value_of("subject"),
|
||||
from: matches.values_of("from").map(|v| v.collect()),
|
||||
to: matches.values_of("to").map(|v| v.collect()),
|
||||
cc: matches.values_of("cc").map(|v| v.collect()),
|
||||
bcc: matches.values_of("bcc").map(|v| v.collect()),
|
||||
headers: matches.values_of("headers").map(|v| v.collect()),
|
||||
body: matches.value_of("body"),
|
||||
sig: matches.value_of("signature"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Message template commands.
|
||||
/// Represents the template commands.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Cmd<'a> {
|
||||
Forward(email::args::Id<'a>, TplOverride<'a>),
|
||||
New(TplOverride<'a>),
|
||||
Reply(Seq<'a>, ReplyAll, TplOverride<'a>),
|
||||
Forward(Seq<'a>, TplOverride<'a>),
|
||||
Save(AttachmentPaths<'a>, Tpl<'a>),
|
||||
Send(AttachmentPaths<'a>, Tpl<'a>),
|
||||
Reply(email::args::Id<'a>, email::args::All, TplOverride<'a>),
|
||||
Save(email::args::Attachments<'a>, Tpl<'a>),
|
||||
Send(email::args::Attachments<'a>, Tpl<'a>),
|
||||
}
|
||||
|
||||
/// Message template command matcher.
|
||||
/// Represents the template command matcher.
|
||||
pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Cmd<'a>>> {
|
||||
info!("entering message template command matcher");
|
||||
let cmd = if let Some(m) = m.subcommand_matches(CMD_FORWARD) {
|
||||
debug!("forward subcommand matched");
|
||||
let id = email::args::parse_id_arg(m);
|
||||
let tpl = parse_override_arg(m);
|
||||
Some(Cmd::Forward(id, tpl))
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_NEW) {
|
||||
debug!("new subcommand matched");
|
||||
let tpl = parse_override_arg(m);
|
||||
Some(Cmd::New(tpl))
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_REPLY) {
|
||||
debug!("reply subcommand matched");
|
||||
let id = email::args::parse_id_arg(m);
|
||||
let all = email::args::parse_reply_all_flag(m);
|
||||
let tpl = parse_override_arg(m);
|
||||
Some(Cmd::Reply(id, all, tpl))
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_SAVE) {
|
||||
debug!("save subcommand matched");
|
||||
let attachments = email::args::parse_attachments_arg(m);
|
||||
let tpl = parse_raw_arg(m);
|
||||
Some(Cmd::Save(attachments, tpl))
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_SEND) {
|
||||
debug!("send subcommand matched");
|
||||
let attachments = email::args::parse_attachments_arg(m);
|
||||
let tpl = parse_raw_arg(m);
|
||||
Some(Cmd::Send(attachments, tpl))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(m) = m.subcommand_matches("new") {
|
||||
info!("new subcommand matched");
|
||||
let tpl = from_args(m);
|
||||
trace!("template override: {:?}", tpl);
|
||||
return Ok(Some(Cmd::New(tpl)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("reply") {
|
||||
info!("reply subcommand matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("sequence: {}", seq);
|
||||
let all = m.is_present("reply-all");
|
||||
debug!("reply all: {}", all);
|
||||
let tpl = from_args(m);
|
||||
trace!("template override: {:?}", tpl);
|
||||
return Ok(Some(Cmd::Reply(seq, all, tpl)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("forward") {
|
||||
info!("forward subcommand matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
debug!("sequence: {}", seq);
|
||||
let tpl = from_args(m);
|
||||
trace!("template args: {:?}", tpl);
|
||||
return Ok(Some(Cmd::Forward(seq, tpl)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("save") {
|
||||
info!("save subcommand matched");
|
||||
let attachment_paths: Vec<&str> = m.values_of("attachments").unwrap_or_default().collect();
|
||||
trace!("attachments paths: {:?}", attachment_paths);
|
||||
let tpl = m.value_of("template").unwrap_or_default();
|
||||
trace!("template: {}", tpl);
|
||||
return Ok(Some(Cmd::Save(attachment_paths, tpl)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("send") {
|
||||
info!("send subcommand matched");
|
||||
let attachment_paths: Vec<&str> = m.values_of("attachments").unwrap_or_default().collect();
|
||||
trace!("attachments paths: {:?}", attachment_paths);
|
||||
let tpl = m.value_of("template").unwrap_or_default();
|
||||
trace!("template: {}", tpl);
|
||||
return Ok(Some(Cmd::Send(attachment_paths, tpl)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
/// Message template args.
|
||||
pub fn tpl_args<'a>() -> Vec<Arg<'a, 'a>> {
|
||||
/// Represents the template subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![SubCommand::with_name(CMD_TPL)
|
||||
.aliases(&["tpl"])
|
||||
.about("Handles email templates")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name(CMD_NEW)
|
||||
.aliases(&["n"])
|
||||
.about("Generates a template for a new email")
|
||||
.args(&args()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name(CMD_REPLY)
|
||||
.aliases(&["rep", "re", "r"])
|
||||
.about("Generates a template for replying to an email")
|
||||
.arg(email::args::id_arg())
|
||||
.arg(email::args::reply_all_flag())
|
||||
.args(&args()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name(CMD_FORWARD)
|
||||
.aliases(&["fwd", "fw", "f"])
|
||||
.about("Generates a template for forwarding an email")
|
||||
.arg(email::args::id_arg())
|
||||
.args(&args()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name(CMD_SAVE)
|
||||
.about("Saves an email based on the given template")
|
||||
.arg(&email::args::attachments_arg())
|
||||
.arg(Arg::with_name(ARG_TPL).raw(true)),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name(CMD_SEND)
|
||||
.about("Sends an email based on the given template")
|
||||
.arg(&email::args::attachments_arg())
|
||||
.arg(Arg::with_name(ARG_TPL).raw(true)),
|
||||
)]
|
||||
}
|
||||
|
||||
/// Represents the template arguments.
|
||||
pub fn args<'a>() -> Vec<Arg<'a, 'a>> {
|
||||
vec![
|
||||
Arg::with_name("subject")
|
||||
Arg::with_name(ARG_SUBJECT)
|
||||
.help("Overrides the Subject header")
|
||||
.short("s")
|
||||
.long("subject")
|
||||
.value_name("STRING"),
|
||||
Arg::with_name("from")
|
||||
Arg::with_name(ARG_FROM)
|
||||
.help("Overrides the From header")
|
||||
.short("f")
|
||||
.long("from")
|
||||
.value_name("ADDR")
|
||||
.multiple(true),
|
||||
Arg::with_name("to")
|
||||
Arg::with_name(ARG_TO)
|
||||
.help("Overrides the To header")
|
||||
.short("t")
|
||||
.long("to")
|
||||
.value_name("ADDR")
|
||||
.multiple(true),
|
||||
Arg::with_name("cc")
|
||||
Arg::with_name(ARG_CC)
|
||||
.help("Overrides the Cc header")
|
||||
.short("c")
|
||||
.long("cc")
|
||||
.value_name("ADDR")
|
||||
.multiple(true),
|
||||
Arg::with_name("bcc")
|
||||
Arg::with_name(ARG_BCC)
|
||||
.help("Overrides the Bcc header")
|
||||
.short("b")
|
||||
.long("bcc")
|
||||
.value_name("ADDR")
|
||||
.multiple(true),
|
||||
Arg::with_name("header")
|
||||
Arg::with_name(ARG_HEADERS)
|
||||
.help("Overrides a specific header")
|
||||
.short("h")
|
||||
.long("header")
|
||||
.value_name("KEY: VAL")
|
||||
.value_name("KEY:VAL")
|
||||
.multiple(true),
|
||||
Arg::with_name("body")
|
||||
Arg::with_name(ARG_BODY)
|
||||
.help("Overrides the body")
|
||||
.short("B")
|
||||
.long("body")
|
||||
.value_name("STRING"),
|
||||
Arg::with_name("signature")
|
||||
Arg::with_name(ARG_SIGNATURE)
|
||||
.help("Overrides the signature")
|
||||
.short("S")
|
||||
.long("signature")
|
||||
|
@ -140,43 +165,21 @@ pub fn tpl_args<'a>() -> Vec<Arg<'a, 'a>> {
|
|||
]
|
||||
}
|
||||
|
||||
/// Message template subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![SubCommand::with_name("template")
|
||||
.aliases(&["tpl"])
|
||||
.about("Generates a message template")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("new")
|
||||
.aliases(&["n"])
|
||||
.about("Generates a new message template")
|
||||
.args(&tpl_args()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("reply")
|
||||
.aliases(&["rep", "re", "r"])
|
||||
.about("Generates a reply message template")
|
||||
.arg(email::args::seq_arg())
|
||||
.arg(email::args::reply_all_arg())
|
||||
.args(&tpl_args()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("forward")
|
||||
.aliases(&["fwd", "fw", "f"])
|
||||
.about("Generates a forward message template")
|
||||
.arg(email::args::seq_arg())
|
||||
.args(&tpl_args()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("save")
|
||||
.about("Saves a message based on the given template")
|
||||
.arg(&email::args::attachments_arg())
|
||||
.arg(Arg::with_name("template").raw(true)),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("send")
|
||||
.about("Sends a message based on the given template")
|
||||
.arg(&email::args::attachments_arg())
|
||||
.arg(Arg::with_name("template").raw(true)),
|
||||
)]
|
||||
/// Represents the template override argument parser.
|
||||
pub fn parse_override_arg<'a>(matches: &'a ArgMatches<'a>) -> TplOverride {
|
||||
TplOverride {
|
||||
subject: matches.value_of(ARG_SUBJECT),
|
||||
from: matches.values_of(ARG_FROM).map(Iterator::collect),
|
||||
to: matches.values_of(ARG_TO).map(Iterator::collect),
|
||||
cc: matches.values_of(ARG_CC).map(Iterator::collect),
|
||||
bcc: matches.values_of(ARG_BCC).map(Iterator::collect),
|
||||
headers: matches.values_of(ARG_HEADERS).map(Iterator::collect),
|
||||
body: matches.value_of(ARG_BODY),
|
||||
signature: matches.value_of(ARG_SIGNATURE),
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the raw template argument parser.
|
||||
pub fn parse_raw_arg<'a>(matches: &'a ArgMatches<'a>) -> &'a str {
|
||||
matches.value_of(ARG_TPL).unwrap_or_default()
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ fn main() -> Result<()> {
|
|||
_ => (),
|
||||
}
|
||||
|
||||
// Check mailbox commands.
|
||||
// Check folder commands.
|
||||
match folder::args::matches(&m)? {
|
||||
Some(folder::args::Cmd::List(max_width)) => {
|
||||
return folder::handlers::list(
|
||||
|
@ -254,7 +254,7 @@ fn main() -> Result<()> {
|
|||
backend.as_mut(),
|
||||
);
|
||||
}
|
||||
Some(flag::args::Cmd::Remove(seq_range, ref flags)) => {
|
||||
Some(flag::args::Cmd::Del(seq_range, ref flags)) => {
|
||||
return flag::handlers::remove(
|
||||
seq_range,
|
||||
flags,
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
use clap::Arg;
|
||||
use clap::{Arg, ArgMatches};
|
||||
|
||||
/// Defines the max table width argument.
|
||||
const ARG_MAX_TABLE_WIDTH: &str = "max-table-width";
|
||||
|
||||
pub(crate) type MaxTableWidth = Option<usize>;
|
||||
|
||||
/// Represents the max table width argument.
|
||||
pub fn max_width<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("max-table-width")
|
||||
Arg::with_name(ARG_MAX_TABLE_WIDTH)
|
||||
.help("Defines a maximum width for the table")
|
||||
.short("w")
|
||||
.long("max-width")
|
||||
.value_name("INT")
|
||||
}
|
||||
|
||||
/// Represents the max table width argument parser.
|
||||
pub fn parse_max_width<'a>(matches: &'a ArgMatches<'a>) -> Option<usize> {
|
||||
matches
|
||||
.value_of(ARG_MAX_TABLE_WIDTH)
|
||||
.and_then(|width| width.parse::<usize>().ok())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue