From ef3214f36fa833fd72552193cbc7603d18cd2556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Fri, 8 Dec 2023 12:18:18 +0100 Subject: [PATCH] clean doc --- src/account/arg/name.rs | 26 ++++++------- src/account/command/configure.rs | 29 ++++++++------- src/account/command/list.rs | 13 ++++--- src/account/command/mod.rs | 9 +++-- src/account/command/sync.rs | 37 ++++++++++++++----- src/cache/arg/disable.rs | 18 ++++----- src/cli.rs | 28 ++++---------- src/completion/command.rs | 8 +++- src/config/mod.rs | 5 +-- src/email/envelope/arg/ids.rs | 8 ++-- src/email/envelope/command/list.rs | 26 ++++++++----- src/email/envelope/command/mod.rs | 8 +++- src/email/envelope/flag/arg/ids_and_flags.rs | 4 +- src/email/envelope/flag/command/add.rs | 11 ++++-- src/email/envelope/flag/command/mod.rs | 10 +++-- src/email/envelope/flag/command/remove.rs | 11 ++++-- src/email/envelope/flag/command/set.rs | 11 ++++-- src/email/message/arg/body.rs | 14 +++---- src/email/message/arg/header.rs | 4 +- src/email/message/arg/reply.rs | 7 +++- .../message/attachment/command/download.rs | 9 +++-- src/email/message/attachment/command/mod.rs | 7 +++- src/email/message/command/copy.rs | 6 +-- src/email/message/command/delete.rs | 11 ++++-- src/email/message/command/forward.rs | 19 +++++----- src/email/message/command/mailto.rs | 28 ++++++++++++-- src/email/message/command/mod.rs | 19 ++++------ src/email/message/command/move_.rs | 6 +-- src/email/message/command/read.rs | 22 ++++++----- src/email/message/command/reply.rs | 15 +++++--- src/email/message/command/save.rs | 25 ++++++------- src/email/message/command/send.rs | 25 +++++++------ src/email/message/command/write.rs | 15 +++++--- src/email/message/template/arg/body.rs | 25 +++++++++++++ src/email/message/template/arg/mod.rs | 1 + src/email/message/template/command/forward.rs | 14 ++++--- src/email/message/template/command/mod.rs | 25 +++++++------ src/email/message/template/command/reply.rs | 15 +++++--- src/email/message/template/command/save.rs | 21 +++++++---- src/email/message/template/command/send.rs | 20 ++++++---- src/email/message/template/command/write.rs | 15 ++++---- src/email/message/template/mod.rs | 1 + src/folder/arg/name.rs | 20 +++++----- src/folder/command/create.rs | 11 ++++-- src/folder/command/delete.rs | 12 +++--- src/folder/command/expunge.rs | 10 ++--- src/folder/command/list.rs | 16 ++++---- src/folder/command/mod.rs | 12 +++--- src/folder/command/purge.rs | 12 +++--- src/main.rs | 25 +++++++------ src/manual/command.rs | 9 ++++- src/ui/table/arg/max_width.rs | 12 ++++-- 52 files changed, 452 insertions(+), 318 deletions(-) create mode 100644 src/email/message/template/arg/body.rs create mode 100644 src/email/message/template/arg/mod.rs diff --git a/src/account/arg/name.rs b/src/account/arg/name.rs index 4456e14..189d5ba 100644 --- a/src/account/arg/name.rs +++ b/src/account/arg/name.rs @@ -1,26 +1,24 @@ use clap::Parser; -/// The account name argument parser +/// The account name argument parser. #[derive(Debug, Parser)] pub struct AccountNameArg { - /// The name of the account + /// The name of the account. /// - /// The account names are taken from the table at the root level - /// of your TOML configuration file. - #[arg(value_name = "ACCOUNT")] + /// An account name corresponds to an entry in the table at the + /// root level of your TOML configuration file. + #[arg(name = "account_name", value_name = "ACCOUNT")] pub name: String, } -/// The account name flag parser +/// The account name flag parser. #[derive(Debug, Parser)] pub struct AccountNameFlag { - /// Override the default account - #[arg( - long = "account", - short = 'a', - name = "account-name", - value_name = "NAME", - global = true - )] + /// Override the default account. + /// + /// An account name corresponds to an entry in the table at the + /// root level of your TOML configuration file. + #[arg(long = "account", short = 'a', global = true)] + #[arg(name = "account_name", value_name = "NAME")] pub name: Option, } diff --git a/src/account/command/configure.rs b/src/account/command/configure.rs index bcfbda1..616e97c 100644 --- a/src/account/command/configure.rs +++ b/src/account/command/configure.rs @@ -15,26 +15,32 @@ use crate::{ printer::Printer, }; -/// Configure the given account +/// Configure an account. +/// +/// This command is mostly used to define or reset passwords managed +/// by your global keyring. If you do not use the keyring system, you +/// can skip this command. #[derive(Debug, Parser)] pub struct AccountConfigureCommand { #[command(flatten)] pub account: AccountNameArg, - /// Force the account to reconfigure, even if it has already been - /// configured + /// Reset keyring passwords. + /// + /// This argument will force passwords to be prompted again, then + /// saved to your global keyring. #[arg(long, short)] - pub force: bool, + pub reset: bool, } impl AccountConfigureCommand { pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { info!("executing account configure command"); - let (_, account_config) = - config.into_toml_account_config(Some(self.account.name.as_str()))?; + let account = &self.account.name; + let (_, account_config) = config.into_toml_account_config(Some(account))?; - if self.force { + if self.reset { #[cfg(feature = "imap")] if let Some(ref config) = account_config.imap { let reset = match &config.auth { @@ -102,11 +108,8 @@ impl AccountConfigureCommand { } printer.print(format!( - "Account {} successfully {}configured!", - self.account.name, - if self.force { "re" } else { "" } - ))?; - - Ok(()) + "Account {account} successfully {}configured!", + if self.reset { "re" } else { "" } + )) } } diff --git a/src/account/command/list.rs b/src/account/command/list.rs index ee70b04..5b6a72a 100644 --- a/src/account/command/list.rs +++ b/src/account/command/list.rs @@ -6,14 +6,17 @@ use crate::{ account::Accounts, config::TomlConfig, printer::{PrintTableOpts, Printer}, - ui::arg::max_width::MaxTableWidthFlag, + ui::arg::max_width::TableMaxWidthFlag, }; -/// List all accounts +/// List all accounts. +/// +/// This command lists all accounts defined in your TOML configuration +/// file. #[derive(Debug, Parser)] pub struct AccountListCommand { #[command(flatten)] - pub table: MaxTableWidthFlag, + pub table: TableMaxWidthFlag, } impl AccountListCommand { @@ -31,9 +34,7 @@ impl AccountListCommand { .unwrap_or(&Default::default()), max_width: self.table.max_width, }, - )?; - - Ok(()) + ) } } diff --git a/src/account/command/mod.rs b/src/account/command/mod.rs index 2677935..76c8147 100644 --- a/src/account/command/mod.rs +++ b/src/account/command/mod.rs @@ -11,18 +11,19 @@ use self::{ configure::AccountConfigureCommand, list::AccountListCommand, sync::AccountSyncCommand, }; -/// Subcommand to manage accounts +/// Manage accounts. +/// +/// An account is a set of settings, identified by an account +/// name. Settings are directly taken from your TOML configuration +/// file. #[derive(Debug, Subcommand)] pub enum AccountSubcommand { - /// Configure an account #[command(alias = "cfg")] Configure(AccountConfigureCommand), - /// List all accounts #[command(alias = "lst")] List(AccountListCommand), - /// Synchronize an account locally #[command()] Sync(AccountSyncCommand), } diff --git a/src/account/command/sync.rs b/src/account/command/sync.rs index 286d05f..7db35bc 100644 --- a/src/account/command/sync.rs +++ b/src/account/command/sync.rs @@ -32,30 +32,49 @@ const SUB_PROGRESS_DONE_STYLE: Lazy = Lazy::new(|| { ProgressStyle::with_template(" {prefix:.bold} \n {wide_bar:.green} {percent}% ").unwrap() }); +/// Synchronize an account. +/// +/// This command allows you to synchronize all folders and emails +/// (including envelopes and messages) of an account into a local +/// Maildir folder. #[derive(Debug, Parser)] pub struct AccountSyncCommand { #[command(flatten)] pub account: AccountNameArg, - /// Run the synchronization without applying changes + /// Run the synchronization without applying any changes. /// /// Instead, a report will be printed to stdout containing all the /// changes the synchronization plan to do. #[arg(long, short)] pub dry_run: bool, - #[arg(long, short = 'f', value_name = "FOLDER", action = ArgAction::Append, conflicts_with = "exclude_folder", conflicts_with = "all_folders")] + /// Synchronize only specific folders. + /// + /// Only the given folders will be synchronized (including + /// associated envelopes and messages). Useful when you need to + /// speed up the synchronization process. A good usecase is to + /// synchronize only the INBOX in order to quickly check for new + /// messages. + #[arg(long, short = 'f')] + #[arg(value_name = "FOLDER", action = ArgAction::Append)] + #[arg(conflicts_with = "exclude_folder", conflicts_with = "all_folders")] pub include_folder: Vec, - #[arg(long, short = 'x', value_name = "FOLDER", action = ArgAction::Append, conflicts_with = "include_folder", conflicts_with = "all_folders")] + /// Omit specific folders from the synchronization. + /// + /// The given folders will be excluded from the synchronization + /// (including associated envelopes and messages). Useful when you + /// have heavy folders that you do not want to take care of, or to + /// speed up the synchronization process. + #[arg(long, short = 'x')] + #[arg(value_name = "FOLDER", action = ArgAction::Append)] + #[arg(conflicts_with = "include_folder", conflicts_with = "all_folders")] pub exclude_folder: Vec, - #[arg( - long, - short = 'A', - conflicts_with = "include_folder", - conflicts_with = "exclude_folder" - )] + /// Synchronize all exsting folders. + #[arg(long, short = 'A')] + #[arg(conflicts_with = "include_folder", conflicts_with = "exclude_folder")] pub all_folders: bool, } diff --git a/src/cache/arg/disable.rs b/src/cache/arg/disable.rs index 54d5b8e..b1049f2 100644 --- a/src/cache/arg/disable.rs +++ b/src/cache/arg/disable.rs @@ -1,19 +1,15 @@ use clap::Parser; -/// The disable cache flag parser +/// The disable cache flag parser. #[derive(Debug, Parser)] -pub struct DisableCacheFlag { - /// Disable any sort of cache +pub struct CacheDisableFlag { + /// Disable any sort of cache. /// /// The action depends on commands it apply on. For example, when /// listing envelopes using the IMAP backend, this flag will - /// ensure that envelopes are fetched from the IMAP server and not - /// from the synchronized local Maildir. - #[arg( - long = "disable-cache", - alias = "no-cache", - name = "disable-cache", - global = true - )] + /// ensure that envelopes are fetched from the IMAP server rather + /// than the synchronized local Maildir. + #[arg(long = "disable-cache", alias = "no-cache", global = true)] + #[arg(name = "cache_disable")] pub disable: bool, } diff --git a/src/cli.rs b/src/cli.rs index a66fc68..d8f9025 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -19,14 +19,8 @@ use crate::{ }; #[derive(Parser, Debug)] -#[command( - name = "himalaya", - author, - version, - about, - propagate_version = true, - infer_subcommands = true -)] +#[command(name = "himalaya", author, version, about)] +#[command(propagate_version = true, infer_subcommands = true)] pub struct Cli { #[command(subcommand)] pub command: HimalayaCommand, @@ -83,46 +77,38 @@ pub struct Cli { #[derive(Subcommand, Debug)] pub enum HimalayaCommand { - /// Manage accounts #[command(subcommand)] #[command(alias = "accounts")] Account(AccountSubcommand), - /// Manage folders #[command(subcommand)] #[command(alias = "folders")] Folder(FolderSubcommand), - /// Manage envelopes #[command(subcommand)] #[command(alias = "envelopes")] Envelope(EnvelopeSubcommand), - /// Manage flags #[command(subcommand)] #[command(alias = "flags")] Flag(FlagSubcommand), - /// Manage messages #[command(subcommand)] #[command(alias = "messages", alias = "msgs", alias = "msg")] Message(MessageSubcommand), - /// Manage templates + #[command(subcommand)] + #[command(alias = "attachments")] + Attachment(AttachmentSubcommand), + #[command(subcommand)] #[command(alias = "templates", alias = "tpls", alias = "tpl")] Template(TemplateSubcommand), - /// Manage attachments - #[command(subcommand)] - Attachment(AttachmentSubcommand), - - /// Generate manual pages to a directory #[command(arg_required_else_help = true)] #[command(alias = "manuals", alias = "mans")] Manual(ManualGenerateCommand), - /// Print completion script for a shell to stdout #[command(arg_required_else_help = true)] #[command(alias = "completions")] Completion(CompletionGenerateCommand), @@ -136,8 +122,8 @@ impl HimalayaCommand { Self::Envelope(cmd) => cmd.execute(printer, config).await, Self::Flag(cmd) => cmd.execute(printer, config).await, Self::Message(cmd) => cmd.execute(printer, config).await, - Self::Template(cmd) => cmd.execute(printer, config).await, Self::Attachment(cmd) => cmd.execute(printer, config).await, + Self::Template(cmd) => cmd.execute(printer, config).await, Self::Manual(cmd) => cmd.execute(printer).await, Self::Completion(cmd) => cmd.execute(printer).await, } diff --git a/src/completion/command.rs b/src/completion/command.rs index 67a3a7f..32b45b0 100644 --- a/src/completion/command.rs +++ b/src/completion/command.rs @@ -6,10 +6,14 @@ use std::io; use crate::{cli::Cli, printer::Printer}; -/// Print completion script for a shell to stdout +/// Print completion script for a shell to stdout. +/// +/// This command allows you to generate completion script for a given +/// shell. The script is printed to the standard output. If you want +/// to write it to a file, just use unix redirection. #[derive(Debug, Parser)] pub struct CompletionGenerateCommand { - /// Shell for which completion script should be generated for + /// Shell for which completion script should be generated for. #[arg(value_parser = value_parser!(Shell))] pub shell: Shell, } diff --git a/src/config/mod.rs b/src/config/mod.rs index 25559c1..3d96fb1 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -121,10 +121,7 @@ impl TomlConfig { match path.map(Into::into) { Some(ref path) if path.exists() => Self::from_path(path), Some(path) => Self::from_wizard(path).await, - None => match Self::first_valid_default_path() { - Some(path) => Self::from_path(&path), - None => Self::from_wizard(Self::default_path()?).await, - }, + None => Self::from_default_paths().await, } } diff --git a/src/email/envelope/arg/ids.rs b/src/email/envelope/arg/ids.rs index 6a29438..2ed73d9 100644 --- a/src/email/envelope/arg/ids.rs +++ b/src/email/envelope/arg/ids.rs @@ -1,17 +1,17 @@ use clap::Parser; -/// The envelope id argument parser +/// The envelope id argument parser. #[derive(Debug, Parser)] pub struct EnvelopeIdArg { - /// The envelope id + /// 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)] pub struct EnvelopeIdsArgs { - /// The list of envelopes ids + /// The list of envelopes ids. #[arg(value_name = "ID", required = true)] pub ids: Vec, } diff --git a/src/email/envelope/command/list.rs b/src/email/envelope/command/list.rs index 6e9ba1b..5528e00 100644 --- a/src/email/envelope/command/list.rs +++ b/src/email/envelope/command/list.rs @@ -5,35 +5,43 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, folder::arg::name::FolderNameOptionalArg, printer::{PrintTableOpts, Printer}, - ui::arg::max_width::MaxTableWidthFlag, + ui::arg::max_width::TableMaxWidthFlag, }; -/// List all envelopes from a folder +/// List all envelopes. +/// +/// This command allows you to list all envelopes included in the +/// given folder. #[derive(Debug, Parser)] pub struct EnvelopeListCommand { #[command(flatten)] pub folder: FolderNameOptionalArg, - /// The page number + /// The page number. + /// + /// The page number starts from 1 (which is the default). Giving a + /// page number to big will result in a out of bound error. #[arg(long, short, value_name = "NUMBER", default_value = "1")] pub page: usize, - /// The page size + /// The page size. + /// + /// Determine the amount of envelopes a page should contain. #[arg(long, short = 's', value_name = "NUMBER")] pub page_size: Option, #[command(flatten)] - pub table: MaxTableWidthFlag, + pub table: TableMaxWidthFlag, + + #[command(flatten)] + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, - - #[command(flatten)] - pub cache: DisableCacheFlag, } impl EnvelopeListCommand { diff --git a/src/email/envelope/command/mod.rs b/src/email/envelope/command/mod.rs index 8bda915..1ebf707 100644 --- a/src/email/envelope/command/mod.rs +++ b/src/email/envelope/command/mod.rs @@ -7,10 +7,14 @@ use crate::{config::TomlConfig, printer::Printer}; use self::list::EnvelopeListCommand; -/// Subcommand to manage envelopes +/// Manage envelopes. +/// +/// An envelope is a small representation of a message. It contains an +/// identifier (given by the backend), some flags as well as few +/// headers from the message itself. This subcommand allows you to +/// manage them. #[derive(Debug, Subcommand)] pub enum EnvelopeSubcommand { - /// List all envelopes from a folder #[command(alias = "lst")] List(EnvelopeListCommand), } diff --git a/src/email/envelope/flag/arg/ids_and_flags.rs b/src/email/envelope/flag/arg/ids_and_flags.rs index fe0e146..a8f9e70 100644 --- a/src/email/envelope/flag/arg/ids_and_flags.rs +++ b/src/email/envelope/flag/arg/ids_and_flags.rs @@ -2,10 +2,10 @@ use clap::Parser; use email::flag::{Flag, Flags}; use log::debug; -/// The ids and/or flags arguments parser +/// The ids and/or flags arguments parser. #[derive(Debug, Parser)] pub struct IdsAndFlagsArgs { - /// The list of ids and/or flags + /// The list of ids and/or flags. /// /// Every argument that can be parsed as an integer is considered /// an id, otherwise it is considered as a flag. diff --git a/src/email/envelope/flag/command/add.rs b/src/email/envelope/flag/command/add.rs index 63d7dd3..c979993 100644 --- a/src/email/envelope/flag/command/add.rs +++ b/src/email/envelope/flag/command/add.rs @@ -5,14 +5,17 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs}, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Add flag(s) to an envelope +/// Add flag(s) to an envelope. +/// +/// This command allows you to attach the given flag(s) to the given +/// envelope(s). #[derive(Debug, Parser)] pub struct FlagAddCommand { #[command(flatten)] @@ -22,10 +25,10 @@ pub struct FlagAddCommand { pub args: IdsAndFlagsArgs, #[command(flatten)] - pub account: AccountNameFlag, + pub cache: CacheDisableFlag, #[command(flatten)] - pub cache: DisableCacheFlag, + pub account: AccountNameFlag, } impl FlagAddCommand { diff --git a/src/email/envelope/flag/command/mod.rs b/src/email/envelope/flag/command/mod.rs index 05c7a2f..87d1633 100644 --- a/src/email/envelope/flag/command/mod.rs +++ b/src/email/envelope/flag/command/mod.rs @@ -9,20 +9,22 @@ use crate::{config::TomlConfig, printer::Printer}; use self::{add::FlagAddCommand, remove::FlagRemoveCommand, set::FlagSetCommand}; -/// Subcommand to manage flags +/// Manage flags. +/// +/// A flag is a tag associated to an envelope. Existing flags are +/// seen, answered, flagged, deleted, draft. Other flags are +/// considered custom, which are not always supported (the +/// synchronization does not take care of them yet). #[derive(Debug, Subcommand)] pub enum FlagSubcommand { - /// Add flag(s) to an envelope #[command(arg_required_else_help = true)] #[command(alias = "create")] Add(FlagAddCommand), - /// Replace flag(s) of an envelope #[command(arg_required_else_help = true)] #[command(aliases = ["update", "change", "replace"])] Set(FlagSetCommand), - /// Remove flag(s) from an envelope #[command(arg_required_else_help = true)] #[command(aliases = ["rm", "delete", "del"])] Remove(FlagRemoveCommand), diff --git a/src/email/envelope/flag/command/remove.rs b/src/email/envelope/flag/command/remove.rs index 59583f5..807811d 100644 --- a/src/email/envelope/flag/command/remove.rs +++ b/src/email/envelope/flag/command/remove.rs @@ -5,14 +5,17 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs}, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Remove flag(s) from an envelope +/// Remove flag(s) from an envelope. +/// +/// This command allows you to remove the given flag(s) from the given +/// envelope(s). #[derive(Debug, Parser)] pub struct FlagRemoveCommand { #[command(flatten)] @@ -22,10 +25,10 @@ pub struct FlagRemoveCommand { pub args: IdsAndFlagsArgs, #[command(flatten)] - pub account: AccountNameFlag, + pub cache: CacheDisableFlag, #[command(flatten)] - pub cache: DisableCacheFlag, + pub account: AccountNameFlag, } impl FlagRemoveCommand { diff --git a/src/email/envelope/flag/command/set.rs b/src/email/envelope/flag/command/set.rs index cb04482..8c7343a 100644 --- a/src/email/envelope/flag/command/set.rs +++ b/src/email/envelope/flag/command/set.rs @@ -5,14 +5,17 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, flag::arg::ids_and_flags::{into_tuple, IdsAndFlagsArgs}, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Replace flag(s) of an envelope +/// Replace flag(s) of an envelope. +/// +/// This command allows you to replace existing flags of the given +/// envelope(s) with the given flag(s). #[derive(Debug, Parser)] pub struct FlagSetCommand { #[command(flatten)] @@ -22,10 +25,10 @@ pub struct FlagSetCommand { pub args: IdsAndFlagsArgs, #[command(flatten)] - pub account: AccountNameFlag, + pub cache: CacheDisableFlag, #[command(flatten)] - pub cache: DisableCacheFlag, + pub account: AccountNameFlag, } impl FlagSetCommand { diff --git a/src/email/message/arg/body.rs b/src/email/message/arg/body.rs index 36ca6e0..f7ec4a1 100644 --- a/src/email/message/arg/body.rs +++ b/src/email/message/arg/body.rs @@ -1,22 +1,22 @@ use clap::Parser; use std::ops::Deref; -/// The raw message body argument 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 struct MessageRawBodyArg { + /// Prefill the template with a custom body. + #[arg(trailing_var_arg = true)] + #[arg(name = "body-raw")] pub raw: Vec, } -impl BodyRawArg { +impl MessageRawBodyArg { pub fn raw(self) -> String { self.raw.join(" ").replace("\r", "").replace("\n", "\r\n") } } -impl Deref for BodyRawArg { +impl Deref for MessageRawBodyArg { type Target = Vec; fn deref(&self) -> &Self::Target { diff --git a/src/email/message/arg/header.rs b/src/email/message/arg/header.rs index 0f4b34d..29e4336 100644 --- a/src/email/message/arg/header.rs +++ b/src/email/message/arg/header.rs @@ -1,9 +1,9 @@ use clap::Parser; -/// The envelope id argument parser +/// The envelope id argument parser. #[derive(Debug, Parser)] pub struct HeaderRawArgs { - /// Prefill the template with custom headers + /// Prefill the template with custom headers. /// /// A raw header should follow the pattern KEY:VAL. #[arg(long = "header", short = 'H', required = false)] diff --git a/src/email/message/arg/reply.rs b/src/email/message/arg/reply.rs index 5855001..20491dc 100644 --- a/src/email/message/arg/reply.rs +++ b/src/email/message/arg/reply.rs @@ -1,9 +1,12 @@ use clap::Parser; -/// The reply to all argument parser +/// The reply to all argument parser. #[derive(Debug, Parser)] pub struct MessageReplyAllArg { - /// Reply to all recipients + /// Reply to all recipients. + /// + /// This argument will add all recipients for the To and Cc + /// headers. #[arg(long, short = 'A')] pub all: bool, } diff --git a/src/email/message/attachment/command/download.rs b/src/email/message/attachment/command/download.rs index e0b7a1d..c2dad39 100644 --- a/src/email/message/attachment/command/download.rs +++ b/src/email/message/attachment/command/download.rs @@ -5,12 +5,15 @@ use std::fs; use uuid::Uuid; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Download attachments of a message +/// Download all attachments for the given message. +/// +/// This command allows you to download all attachments found for the +/// given message to your downloads directory. #[derive(Debug, Parser)] pub struct AttachmentDownloadCommand { #[command(flatten)] @@ -20,7 +23,7 @@ pub struct AttachmentDownloadCommand { pub envelopes: EnvelopeIdsArgs, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/attachment/command/mod.rs b/src/email/message/attachment/command/mod.rs index 3770ae0..5ae2c61 100644 --- a/src/email/message/attachment/command/mod.rs +++ b/src/email/message/attachment/command/mod.rs @@ -7,10 +7,13 @@ use crate::{config::TomlConfig, printer::Printer}; use self::download::AttachmentDownloadCommand; -/// Subcommand dedicated to attachments +/// Manage attachments. +/// +/// A message body can be composed of multiple MIME parts. An +/// attachment is the representation of a binary part of a message +/// body. #[derive(Debug, Subcommand)] pub enum AttachmentSubcommand { - /// Download all attachments of one or more messages #[command(arg_required_else_help = true)] Download(AttachmentDownloadCommand), } diff --git a/src/email/message/command/copy.rs b/src/email/message/command/copy.rs index 07d4b00..6ffb2ae 100644 --- a/src/email/message/command/copy.rs +++ b/src/email/message/command/copy.rs @@ -5,14 +5,14 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::{SourceFolderNameArg, TargetFolderNameArg}, printer::Printer, }; -/// Copy a message from a source folder to a target folder +/// Copy a message from a source folder to a target folder. #[derive(Debug, Parser)] pub struct MessageCopyCommand { #[command(flatten)] @@ -25,7 +25,7 @@ pub struct MessageCopyCommand { pub envelopes: EnvelopeIdsArgs, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/command/delete.rs b/src/email/message/command/delete.rs index 55dece0..8b67b91 100644 --- a/src/email/message/command/delete.rs +++ b/src/email/message/command/delete.rs @@ -3,12 +3,17 @@ use clap::Parser; use log::info; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Delete a message from a folder +/// Mark as deleted a message from a folder. +/// +/// This command does not really delete the message: if the given +/// folder points to the trash folder, it adds the "deleted" flag to +/// its envelope, otherwise it moves it to the trash folder. Only the +/// expunge folder command truly deletes messages. #[derive(Debug, Parser)] pub struct MessageDeleteCommand { #[command(flatten)] @@ -18,7 +23,7 @@ pub struct MessageDeleteCommand { pub envelopes: EnvelopeIdsArgs, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/command/forward.rs b/src/email/message/command/forward.rs index f6199cc..4dbf61f 100644 --- a/src/email/message/command/forward.rs +++ b/src/email/message/command/forward.rs @@ -7,16 +7,21 @@ use std::io::{self, BufRead}; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdArg, folder::arg::name::FolderNameArg, - message::arg::{body::BodyRawArg, header::HeaderRawArgs}, + message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs}, printer::Printer, ui::editor, }; -/// Forward a new message +/// Forward a message. +/// +/// This command allows you to forward the given message using the +/// editor defined in your environment variable $EDITOR. When the +/// edition process finishes, you can choose between saving or sending +/// the final message. #[derive(Debug, Parser)] pub struct MessageForwardCommand { #[command(flatten)] @@ -25,18 +30,14 @@ pub struct MessageForwardCommand { #[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, + pub body: MessageRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/command/mailto.rs b/src/email/message/command/mailto.rs index 4041317..c2daed4 100644 --- a/src/email/message/command/mailto.rs +++ b/src/email/message/command/mailto.rs @@ -1,20 +1,30 @@ use anyhow::Result; use clap::Parser; -use log::info; +use log::{debug, 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 +/// Parse and edit a message from a mailto URL string. +/// +/// This command allows you to edit a message from the mailto format +/// using the editor defined in your environment variable +/// $EDITOR. When the edition process finishes, you can choose between +/// saving or sending the final message. #[derive(Debug, Parser)] pub struct MessageMailtoCommand { - /// The mailto url + /// The mailto url. #[arg()] pub url: Url, } impl MessageMailtoCommand { + pub fn new(url: &str) -> Result { + let url = Url::parse(url)?; + Ok(Self { url }) + } + pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> { info!("executing message mailto command"); @@ -23,17 +33,27 @@ impl MessageMailtoCommand { let backend = Backend::new(toml_account_config, account_config.clone(), true).await?; let mut builder = MessageBuilder::new().to(self.url.path()); + let mut body = String::new(); 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), + b"body" => body += &val, _ => (), } } + match account_config.signature() { + Ok(Some(ref signature)) => builder = builder.text_body(body + "\n\n" + signature), + Ok(None) => builder = builder.text_body(body), + Err(err) => { + debug!("cannot add signature to mailto message, skipping it: {err}"); + debug!("{err:?}"); + } + } + let tpl = account_config .generate_tpl_interpreter() .with_show_only_headers(account_config.email_writing_headers()) diff --git a/src/email/message/command/mod.rs b/src/email/message/command/mod.rs index dc2c5d5..53606b2 100644 --- a/src/email/message/command/mod.rs +++ b/src/email/message/command/mod.rs @@ -21,47 +21,42 @@ use self::{ write::MessageWriteCommand, }; -/// Subcommand to manage messages +/// Manage messages. +/// +/// A message is the content of an email. It is composed of headers +/// (located at the top of the message) and a body (located at the +/// bottom of the message). Both are separated by two new lines. This +/// subcommand allows you to manage them. #[derive(Debug, Subcommand)] pub enum MessageSubcommand { - /// Read a message #[command(arg_required_else_help = true)] Read(MessageReadCommand), - /// Write a new message - #[command(alias = "new", alias = "compose")] + #[command(alias = "add", alias = "create", 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 #[command(arg_required_else_help = true)] #[command(alias = "add", alias = "create")] Save(MessageSaveCommand), - /// Send a message #[command(arg_required_else_help = true)] Send(MessageSendCommand), - /// Copy a message from a source folder to a target folder #[command(arg_required_else_help = true)] Copy(MessageCopyCommand), - /// Move a message from a source folder to a target folder #[command(arg_required_else_help = true)] Move(MessageMoveCommand), - /// Delete a message from a folder #[command(arg_required_else_help = true)] Delete(MessageDeleteCommand), } diff --git a/src/email/message/command/move_.rs b/src/email/message/command/move_.rs index 76d2744..728116a 100644 --- a/src/email/message/command/move_.rs +++ b/src/email/message/command/move_.rs @@ -5,14 +5,14 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::{SourceFolderNameArg, TargetFolderNameArg}, printer::Printer, }; -/// Move a message from a source folder to a target folder +/// Move a message from a source folder to a target folder. #[derive(Debug, Parser)] pub struct MessageMoveCommand { #[command(flatten)] @@ -25,7 +25,7 @@ pub struct MessageMoveCommand { pub envelopes: EnvelopeIdsArgs, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/command/read.rs b/src/email/message/command/read.rs index 373547a..53d1515 100644 --- a/src/email/message/command/read.rs +++ b/src/email/message/command/read.rs @@ -4,12 +4,14 @@ use log::info; use mml::message::FilterParts; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Read a message from a folder +/// Read a message. +/// +/// This command allows you to read a message. #[derive(Debug, Parser)] pub struct MessageReadCommand { #[command(flatten)] @@ -18,18 +20,18 @@ pub struct MessageReadCommand { #[command(flatten)] pub envelopes: EnvelopeIdsArgs, - /// Read the raw version of the message + /// Read the raw version of the given message. /// - /// The raw message represents the message as it is on the - /// backend, unedited: not decoded nor decrypted. This is useful - /// for debugging faulty messages, but also for + /// The raw message represents the headers and the body as it is + /// on the backend, unedited: not decoded nor decrypted. This is + /// useful for debugging faulty messages, but also for /// saving/sending/transfering messages. #[arg(long, short)] #[arg(conflicts_with = "no_headers")] #[arg(conflicts_with = "headers")] pub raw: bool, - /// Read only body of text/html parts + /// Read only body of text/html parts. /// /// This argument is useful when you need to read the HTML version /// of a message. Combined with --no-headers, you can write it to @@ -38,7 +40,7 @@ pub struct MessageReadCommand { #[arg(conflicts_with = "raw")] pub html: bool, - /// Read only the body of the message + /// Read only the body of the message. /// /// All headers will be removed from the message. #[arg(long)] @@ -47,7 +49,7 @@ pub struct MessageReadCommand { pub no_headers: bool, /// List of headers that should be visible at the top of the - /// message + /// message. /// /// If a given header is not found in the message, it will not be /// visible. If no header is given, defaults to the one set up in @@ -58,7 +60,7 @@ pub struct MessageReadCommand { pub headers: Vec, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/command/reply.rs b/src/email/message/command/reply.rs index c95c537..dfcbbbe 100644 --- a/src/email/message/command/reply.rs +++ b/src/email/message/command/reply.rs @@ -8,16 +8,21 @@ use std::io::{self, BufRead}; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdArg, folder::arg::name::FolderNameArg, - message::arg::{body::BodyRawArg, header::HeaderRawArgs, reply::MessageReplyAllArg}, + message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs, reply::MessageReplyAllArg}, printer::Printer, ui::editor, }; -/// Reply a new message +/// Reply to a message. +/// +/// This command allows you to reply to the given message using the +/// editor defined in your environment variable $EDITOR. When the +/// edition process finishes, you can choose between saving or sending +/// the final message. #[derive(Debug, Parser)] pub struct MessageReplyCommand { #[command(flatten)] @@ -33,10 +38,10 @@ pub struct MessageReplyCommand { pub headers: HeaderRawArgs, #[command(flatten)] - pub body: BodyRawArg, + pub body: MessageRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/command/save.rs b/src/email/message/command/save.rs index afd3813..637ddeb 100644 --- a/src/email/message/command/save.rs +++ b/src/email/message/command/save.rs @@ -5,22 +5,24 @@ use log::info; use std::io::{self, BufRead}; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, - config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, + config::TomlConfig, folder::arg::name::FolderNameArg, message::arg::body::MessageRawBodyArg, + printer::Printer, }; -/// Save a message to a folder +/// Save a message to a folder. +/// +/// This command allows you to add a raw message to the given folder. #[derive(Debug, Parser)] pub struct MessageSaveCommand { #[command(flatten)] pub folder: FolderNameArg, - /// The raw message to save - #[arg(value_name = "MESSAGE", raw = true)] - pub raw: String, + #[command(flatten)] + pub body: MessageRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, @@ -33,7 +35,6 @@ impl MessageSaveCommand { let folder = &self.folder.name; let account = self.account.name.as_ref().map(String::as_str); let cache = self.cache.disable; - let raw_msg = &self.raw; let (toml_account_config, account_config) = config.clone().into_account_configs(account, cache)?; @@ -41,8 +42,8 @@ impl MessageSaveCommand { let is_tty = atty::is(Stream::Stdin); let is_json = printer.is_json(); - let raw_email = if is_tty || is_json { - raw_msg.replace("\r", "").replace("\n", "\r\n") + let msg = if is_tty || is_json { + self.body.raw() } else { io::stdin() .lock() @@ -52,9 +53,7 @@ impl MessageSaveCommand { .join("\r\n") }; - backend - .add_raw_message(folder, raw_email.as_bytes()) - .await?; + backend.add_raw_message(folder, msg.as_bytes()).await?; printer.print(format!("Message successfully saved to {folder}!")) } diff --git a/src/email/message/command/send.rs b/src/email/message/command/send.rs index d83e6e5..9f176b1 100644 --- a/src/email/message/command/send.rs +++ b/src/email/message/command/send.rs @@ -6,19 +6,21 @@ use log::info; use std::io::{self, BufRead}; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, - config::TomlConfig, printer::Printer, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, + config::TomlConfig, message::arg::body::MessageRawBodyArg, printer::Printer, }; -/// Send a message from a folder +/// Send a message. +/// +/// This command allows you to send a raw message and to save a copy +/// to your send folder. #[derive(Debug, Parser)] pub struct MessageSendCommand { - /// The raw message to send - #[arg(value_name = "MESSAGE", raw = true)] - pub raw: String, + #[command(flatten)] + pub body: MessageRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, @@ -30,7 +32,6 @@ impl MessageSendCommand { let account = self.account.name.as_ref().map(String::as_str); let cache = self.cache.disable; - let raw_msg = &self.raw; let (toml_account_config, account_config) = config.clone().into_account_configs(account, cache)?; @@ -39,8 +40,8 @@ impl MessageSendCommand { let is_tty = atty::is(Stream::Stdin); let is_json = printer.is_json(); - let raw_email = if is_tty || is_json { - raw_msg.replace("\r", "").replace("\n", "\r\n") + let msg = if is_tty || is_json { + self.body.raw() } else { io::stdin() .lock() @@ -50,11 +51,11 @@ impl MessageSendCommand { .join("\r\n") }; - backend.send_raw_message(raw_email.as_bytes()).await?; + backend.send_raw_message(msg.as_bytes()).await?; if account_config.email_sending_save_copy.unwrap_or_default() { backend - .add_raw_message_with_flag(&folder, raw_email.as_bytes(), Flag::Seen) + .add_raw_message_with_flag(&folder, msg.as_bytes(), Flag::Seen) .await?; printer.print(format!("Message successfully sent and saved to {folder}!")) diff --git a/src/email/message/command/write.rs b/src/email/message/command/write.rs index c0d5aca..38b1c28 100644 --- a/src/email/message/command/write.rs +++ b/src/email/message/command/write.rs @@ -8,24 +8,29 @@ use std::io::{self, BufRead}; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, - message::arg::{body::BodyRawArg, header::HeaderRawArgs}, + message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs}, printer::Printer, ui::editor, }; -/// Write a new message +/// Write a new message. +/// +/// This command allows you to write a new message using the editor +/// defined in your environment variable $EDITOR. When the edition +/// process finishes, you can choose between saving or sending the +/// final message. #[derive(Debug, Parser)] pub struct MessageWriteCommand { #[command(flatten)] pub headers: HeaderRawArgs, #[command(flatten)] - pub body: BodyRawArg, + pub body: MessageRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/template/arg/body.rs b/src/email/message/template/arg/body.rs new file mode 100644 index 0000000..fafd455 --- /dev/null +++ b/src/email/message/template/arg/body.rs @@ -0,0 +1,25 @@ +use clap::Parser; +use std::ops::Deref; + +/// The raw template body argument parser. +#[derive(Debug, Parser)] +pub struct TemplateRawBodyArg { + /// Prefill the template with a custom body. + #[arg(trailing_var_arg = true)] + #[arg(name = "body-raw")] + pub raw: Vec, +} + +impl TemplateRawBodyArg { + pub fn raw(self) -> String { + self.raw.join(" ").replace("\r", "") + } +} + +impl Deref for TemplateRawBodyArg { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.raw + } +} diff --git a/src/email/message/template/arg/mod.rs b/src/email/message/template/arg/mod.rs new file mode 100644 index 0000000..81f6efd --- /dev/null +++ b/src/email/message/template/arg/mod.rs @@ -0,0 +1 @@ +pub mod body; diff --git a/src/email/message/template/command/forward.rs b/src/email/message/template/command/forward.rs index b5950d1..c09529d 100644 --- a/src/email/message/template/command/forward.rs +++ b/src/email/message/template/command/forward.rs @@ -5,15 +5,19 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdArg, folder::arg::name::FolderNameArg, - message::arg::{body::BodyRawArg, header::HeaderRawArgs}, + message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs}, printer::Printer, }; -/// Generate a forward message template +/// Generate a template for forwarding a message. +/// +/// The generated template is prefilled with your email in a From +/// header as well as your signature. The forwarded message is also +/// prefilled in the body of the template, prefixed by a separator. #[derive(Debug, Parser)] pub struct TemplateForwardCommand { #[command(flatten)] @@ -26,10 +30,10 @@ pub struct TemplateForwardCommand { pub headers: HeaderRawArgs, #[command(flatten)] - pub body: BodyRawArg, + pub body: MessageRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/template/command/mod.rs b/src/email/message/template/command/mod.rs index 202fdf5..eed6ca7 100644 --- a/src/email/message/template/command/mod.rs +++ b/src/email/message/template/command/mod.rs @@ -14,28 +14,31 @@ use self::{ send::TemplateSendCommand, write::TemplateWriteCommand, }; -/// Subcommand to manage templates +/// Manage templates. +/// +/// A template is an editable version of a message (headers + +/// body). It uses a specific language called MML that allows you to +/// attach file or encrypt content. This subcommand allows you manage +/// them. +/// +/// You can learn more about MML at +/// . #[derive(Debug, Subcommand)] pub enum TemplateSubcommand { - /// Write a new template - #[command(alias = "new", alias = "compose")] + #[command(alias = "create", alias = "new", alias = "compose")] Write(TemplateWriteCommand), - /// Reply to a template - #[command()] + #[command(arg_required_else_help = true)] Reply(TemplateReplyCommand), - /// Generate a template for forwarding an email + #[command(arg_required_else_help = true)] #[command(alias = "fwd")] Forward(TemplateForwardCommand), - /// Save a template to a folder - #[command(arg_required_else_help = true)] - #[command(alias = "add", alias = "create")] + #[command(alias = "add")] Save(TemplateSaveCommand), - /// Send a template - #[command(arg_required_else_help = true)] + #[command()] Send(TemplateSendCommand), } diff --git a/src/email/message/template/command/reply.rs b/src/email/message/template/command/reply.rs index 80f75b4..fe42af5 100644 --- a/src/email/message/template/command/reply.rs +++ b/src/email/message/template/command/reply.rs @@ -5,15 +5,20 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, envelope::arg::ids::EnvelopeIdArg, folder::arg::name::FolderNameArg, - message::arg::{body::BodyRawArg, header::HeaderRawArgs, reply::MessageReplyAllArg}, + message::arg::{body::MessageRawBodyArg, header::HeaderRawArgs, reply::MessageReplyAllArg}, printer::Printer, }; -/// Generate a reply message template +/// Generate a template for replying to a message. +/// +/// The generated template is prefilled with your email in a From +/// header as well as your signature. The replied message is also +/// prefilled in the body of the template, with all lines prefixed by +/// the symbol greater than ">". #[derive(Debug, Parser)] pub struct TemplateReplyCommand { #[command(flatten)] @@ -29,10 +34,10 @@ pub struct TemplateReplyCommand { pub headers: HeaderRawArgs, #[command(flatten)] - pub body: BodyRawArg, + pub body: MessageRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/template/command/save.rs b/src/email/message/template/command/save.rs index c99fc52..4253468 100644 --- a/src/email/message/template/command/save.rs +++ b/src/email/message/template/command/save.rs @@ -6,22 +6,27 @@ use mml::MmlCompilerBuilder; use std::io::{self, BufRead}; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, - config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, + config::TomlConfig, email::template::arg::body::TemplateRawBodyArg, + folder::arg::name::FolderNameArg, printer::Printer, }; -/// Save a template to a folder +/// Save a template to a folder. +/// +/// This command allows you to save a template to the given +/// folder. The template is compiled into a MIME message before being +/// saved to the folder. If you want to save a raw message, use the +/// message save command instead. #[derive(Debug, Parser)] pub struct TemplateSaveCommand { #[command(flatten)] pub folder: FolderNameArg, - /// The raw template to save - #[arg(raw = true, value_delimiter = ' ')] - pub raw: Vec, + #[command(flatten)] + pub body: TemplateRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, @@ -42,7 +47,7 @@ impl TemplateSaveCommand { let is_tty = atty::is(Stream::Stdin); let is_json = printer.is_json(); let tpl = if is_tty || is_json { - self.raw.join(" ").replace("\r", "") + self.body.raw() } else { io::stdin() .lock() diff --git a/src/email/message/template/command/send.rs b/src/email/message/template/command/send.rs index ff36745..4b74b95 100644 --- a/src/email/message/template/command/send.rs +++ b/src/email/message/template/command/send.rs @@ -7,19 +7,23 @@ use mml::MmlCompilerBuilder; use std::io::{self, BufRead}; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, - config::TomlConfig, printer::Printer, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, + config::TomlConfig, email::template::arg::body::TemplateRawBodyArg, printer::Printer, }; -/// Send a template +/// Send a template. +/// +/// This command allows you to send a template and save a copy to the +/// sent folder. The template is compiled into a MIME message before +/// being sent. If you want to send a raw message, use the message +/// send command instead. #[derive(Debug, Parser)] pub struct TemplateSendCommand { - /// The raw template to save - #[arg(raw = true, value_delimiter = ' ')] - pub raw: Vec, + #[command(flatten)] + pub body: TemplateRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, @@ -40,7 +44,7 @@ impl TemplateSendCommand { let is_tty = atty::is(Stream::Stdin); let is_json = printer.is_json(); let tpl = if is_tty || is_json { - self.raw.join(" ").replace("\r", "") + self.body.raw() } else { io::stdin() .lock() diff --git a/src/email/message/template/command/write.rs b/src/email/message/template/command/write.rs index 72b3168..cf22bd4 100644 --- a/src/email/message/template/command/write.rs +++ b/src/email/message/template/command/write.rs @@ -4,24 +4,25 @@ use email::message::Message; use log::info; use crate::{ - account::arg::name::AccountNameFlag, - cache::arg::disable::DisableCacheFlag, - config::TomlConfig, - message::arg::{body::BodyRawArg, header::HeaderRawArgs}, + account::arg::name::AccountNameFlag, cache::arg::disable::CacheDisableFlag, config::TomlConfig, + email::template::arg::body::TemplateRawBodyArg, message::arg::header::HeaderRawArgs, printer::Printer, }; -/// Write a new template +/// Generate a template for writing a new message from scratch. +/// +/// The generated template is prefilled with your email in a From +/// header as well as your signature. #[derive(Debug, Parser)] pub struct TemplateWriteCommand { #[command(flatten)] pub headers: HeaderRawArgs, #[command(flatten)] - pub body: BodyRawArg, + pub body: TemplateRawBodyArg, #[command(flatten)] - pub cache: DisableCacheFlag, + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, diff --git a/src/email/message/template/mod.rs b/src/email/message/template/mod.rs index 9fe7961..1a3a73a 100644 --- a/src/email/message/template/mod.rs +++ b/src/email/message/template/mod.rs @@ -1 +1,2 @@ +pub mod arg; pub mod command; diff --git a/src/folder/arg/name.rs b/src/folder/arg/name.rs index 730c1a3..15307b8 100644 --- a/src/folder/arg/name.rs +++ b/src/folder/arg/name.rs @@ -1,34 +1,34 @@ use clap::Parser; use email::account::config::DEFAULT_INBOX_FOLDER; -/// The folder name argument parser +/// The folder name argument parser. #[derive(Debug, Parser)] pub struct FolderNameArg { - /// The name of the folder - #[arg(name = "folder-name", value_name = "FOLDER")] + /// The name of the folder. + #[arg(name = "folder_name", value_name = "FOLDER")] pub name: String, } -/// The optional folder name argument parser +/// The optional folder name argument parser. #[derive(Debug, Parser)] pub struct FolderNameOptionalArg { - /// The name of the folder - #[arg(name = "folder-name", value_name = "FOLDER", default_value = DEFAULT_INBOX_FOLDER)] + /// The name of the folder. + #[arg(name = "folder_name", value_name = "FOLDER", default_value = DEFAULT_INBOX_FOLDER)] pub name: String, } -/// The source folder name argument parser +/// The source folder name argument parser. #[derive(Debug, Parser)] pub struct SourceFolderNameArg { - /// The name of the source folder + /// The name of the source folder. #[arg(name = "from-folder-name", value_name = "FROM")] pub name: String, } -/// The target folder name argument parser +/// The target folder name argument parser. #[derive(Debug, Parser)] pub struct TargetFolderNameArg { - /// The name of the target folder + /// The name of the target folder. #[arg(name = "to-folder-name", value_name = "TO")] pub name: String, } diff --git a/src/folder/command/create.rs b/src/folder/command/create.rs index 02c409e..3e27138 100644 --- a/src/folder/command/create.rs +++ b/src/folder/command/create.rs @@ -3,21 +3,24 @@ use clap::Parser; use log::info; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Create a new folder +/// Create a new folder. +/// +/// This command allows you to create a new folder using the given +/// name. #[derive(Debug, Parser)] pub struct FolderCreateCommand { #[command(flatten)] pub folder: FolderNameArg, #[command(flatten)] - pub account: AccountNameFlag, + pub cache: CacheDisableFlag, #[command(flatten)] - pub cache: DisableCacheFlag, + pub account: AccountNameFlag, } impl FolderCreateCommand { diff --git a/src/folder/command/delete.rs b/src/folder/command/delete.rs index e0786db..14a500a 100644 --- a/src/folder/command/delete.rs +++ b/src/folder/command/delete.rs @@ -5,24 +5,24 @@ use log::info; use std::process; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Delete a folder +/// Delete a folder. /// -/// All emails from a given folder are definitely deleted. The folder -/// is also deleted after execution of the command. +/// All emails from the given folder are definitely deleted. The +/// folder is also deleted after execution of the command. #[derive(Debug, Parser)] pub struct FolderDeleteCommand { #[command(flatten)] pub folder: FolderNameArg, #[command(flatten)] - pub account: AccountNameFlag, + pub cache: CacheDisableFlag, #[command(flatten)] - pub cache: DisableCacheFlag, + pub account: AccountNameFlag, } impl FolderDeleteCommand { diff --git a/src/folder/command/expunge.rs b/src/folder/command/expunge.rs index ac2c0b3..a6df0ca 100644 --- a/src/folder/command/expunge.rs +++ b/src/folder/command/expunge.rs @@ -3,14 +3,14 @@ use clap::Parser; use log::info; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Expunge a folder +/// Expunge a folder. /// /// The concept of expunging is similar to the IMAP one: it definitely -/// deletes emails from a given folder that contain the "deleted" +/// deletes emails from the given folder that contain the "deleted" /// flag. #[derive(Debug, Parser)] pub struct FolderExpungeCommand { @@ -18,10 +18,10 @@ pub struct FolderExpungeCommand { pub folder: FolderNameArg, #[command(flatten)] - pub account: AccountNameFlag, + pub cache: CacheDisableFlag, #[command(flatten)] - pub cache: DisableCacheFlag, + pub account: AccountNameFlag, } impl FolderExpungeCommand { diff --git a/src/folder/command/list.rs b/src/folder/command/list.rs index 03e0a65..8809b92 100644 --- a/src/folder/command/list.rs +++ b/src/folder/command/list.rs @@ -5,24 +5,26 @@ use log::info; use crate::{ account::arg::name::AccountNameFlag, backend::Backend, - cache::arg::disable::DisableCacheFlag, + cache::arg::disable::CacheDisableFlag, config::TomlConfig, folder::Folders, printer::{PrintTableOpts, Printer}, - ui::arg::max_width::MaxTableWidthFlag, + ui::arg::max_width::TableMaxWidthFlag, }; -/// List all folders +/// List all folders. +/// +/// This command allows you to list all exsting folders. #[derive(Debug, Parser)] pub struct FolderListCommand { #[command(flatten)] - pub table: MaxTableWidthFlag, + pub table: TableMaxWidthFlag, + + #[command(flatten)] + pub cache: CacheDisableFlag, #[command(flatten)] pub account: AccountNameFlag, - - #[command(flatten)] - pub cache: DisableCacheFlag, } impl FolderListCommand { diff --git a/src/folder/command/mod.rs b/src/folder/command/mod.rs index 54868ae..29959aa 100644 --- a/src/folder/command/mod.rs +++ b/src/folder/command/mod.rs @@ -14,26 +14,24 @@ use self::{ list::FolderListCommand, purge::FolderPurgeCommand, }; -/// Subcommand to manage accounts +/// Manage folders. +/// +/// A folder (AKA mailbox, or directory) contains envelopes and +/// messages. This subcommand allows you to manage them. #[derive(Debug, Subcommand)] pub enum FolderSubcommand { - /// Create a new folder - #[command(alias = "add")] + #[command(alias = "add", alias = "new")] Create(FolderCreateCommand), - /// List all folders #[command(alias = "lst")] List(FolderListCommand), - /// Expunge a folder #[command()] Expunge(FolderExpungeCommand), - /// Purge a folder #[command()] Purge(FolderPurgeCommand), - /// Delete a folder #[command(alias = "remove", alias = "rm")] Delete(FolderDeleteCommand), } diff --git a/src/folder/command/purge.rs b/src/folder/command/purge.rs index 3d870f7..9f97648 100644 --- a/src/folder/command/purge.rs +++ b/src/folder/command/purge.rs @@ -5,24 +5,24 @@ use log::info; use std::process; use crate::{ - account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag, + account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::CacheDisableFlag, config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer, }; -/// Purge a folder +/// Purge a folder. /// -/// All emails from a given folder are definitely deleted. The purged -/// folder will remain empty after executing of the command. +/// All emails from the given folder are definitely deleted. The +/// purged folder will remain empty after execution of the command. #[derive(Debug, Parser)] pub struct FolderPurgeCommand { #[command(flatten)] pub folder: FolderNameArg, #[command(flatten)] - pub account: AccountNameFlag, + pub cache: CacheDisableFlag, #[command(flatten)] - pub cache: DisableCacheFlag, + pub account: AccountNameFlag, } impl FolderPurgeCommand { diff --git a/src/main.rs b/src/main.rs index ea150eb..71291b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ use anyhow::Result; use clap::Parser; use env_logger::{Builder as LoggerBuilder, Env, DEFAULT_FILTER_ENV}; -use himalaya::{cli::Cli, config::TomlConfig, printer::StdoutPrinter}; +use himalaya::{ + cli::Cli, config::TomlConfig, message::command::mailto::MessageMailtoCommand, + printer::StdoutPrinter, +}; use log::{debug, warn}; use std::env; @@ -9,7 +12,7 @@ use std::env; async fn main() -> Result<()> { #[cfg(not(target_os = "windows"))] if let Err((_, err)) = coredump::register_panic_handler() { - warn!("cannot register custom panic handler: {err}"); + warn!("cannot register coredump panic handler: {err}"); debug!("{err:?}"); } @@ -18,15 +21,15 @@ async fn main() -> Result<()> { .format_timestamp(None) .init(); - let raw_args: Vec = 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 - // } + // if the first argument starts by "mailto:", execute straight the + // mailto message command + if let Some(ref url) = env::args().nth(1).filter(|arg| arg.starts_with("mailto:")) { + let mut printer = StdoutPrinter::default(); + let config = TomlConfig::from_default_paths().await?; + + return MessageMailtoCommand::new(url)? + .execute(&mut printer, &config) + .await; } let cli = Cli::parse(); diff --git a/src/manual/command.rs b/src/manual/command.rs index f55ca93..383bb2c 100644 --- a/src/manual/command.rs +++ b/src/manual/command.rs @@ -7,10 +7,15 @@ use std::{fs, path::PathBuf}; use crate::{cli::Cli, printer::Printer}; -/// Generate manual pages to a directory +/// Generate manual pages to a directory. +/// +/// This command allows you to generate manual pages (following the +/// man page format) to the given directory. If the directory does not +/// exist, it will be created. Any existing man pages will be +/// overriden. #[derive(Debug, Parser)] pub struct ManualGenerateCommand { - /// Directory where man files should be generated in + /// Directory where man files should be generated in. #[arg(value_parser = dir_parser)] pub dir: PathBuf, } diff --git a/src/ui/table/arg/max_width.rs b/src/ui/table/arg/max_width.rs index bbcd977..db394ff 100644 --- a/src/ui/table/arg/max_width.rs +++ b/src/ui/table/arg/max_width.rs @@ -1,9 +1,13 @@ use clap::Parser; -/// The table max width argument parser +/// The table max width argument parser. #[derive(Debug, Parser)] -pub struct MaxTableWidthFlag { - /// The maximum width the table should not exceed - #[arg(long, short = 'w', value_name = "PIXELS")] +pub struct TableMaxWidthFlag { + /// The maximum width the table should not exceed. + /// + /// This argument will force the table not to exceed the given + /// width in pixels. Columns may shrink with ellipsis in order to + /// fit the width. + #[arg(long, short = 'w', name = "table_max_width", value_name = "PIXELS")] pub max_width: Option, }