mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 17:15:12 +00:00
refactor folder with clap derive api
This commit is contained in:
parent
abe4c7f4ea
commit
4a77253c1d
1
src/account/arg/mod.rs
Normal file
1
src/account/arg/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod name;
|
26
src/account/arg/name.rs
Normal file
26
src/account/arg/name.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
/// The account name argument parser
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct AccountNameArg {
|
||||||
|
/// 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")]
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
)]
|
||||||
|
pub name: Option<String>,
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ use email::smtp::config::SmtpAuthConfig;
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
account::arg::name::AccountNameArg,
|
||||||
config::{
|
config::{
|
||||||
wizard::{prompt_passwd, prompt_secret},
|
wizard::{prompt_passwd, prompt_secret},
|
||||||
TomlConfig,
|
TomlConfig,
|
||||||
|
@ -16,26 +17,22 @@ use crate::{
|
||||||
|
|
||||||
/// Configure the given account
|
/// Configure the given account
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Command {
|
pub struct AccountConfigureCommand {
|
||||||
/// The name of the account that needs to be configured
|
#[command(flatten)]
|
||||||
///
|
pub account: AccountNameArg,
|
||||||
/// The account names are taken from the table at the root level
|
|
||||||
/// of your TOML configuration file.
|
|
||||||
#[arg(value_name = "NAME")]
|
|
||||||
pub account_name: String,
|
|
||||||
|
|
||||||
/// Force the account to reconfigure, even if it is already
|
/// Force the account to reconfigure, even if it has already been
|
||||||
/// configured
|
/// configured
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
pub force: bool,
|
pub force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl AccountConfigureCommand {
|
||||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
info!("executing account configure command");
|
info!("executing account configure command");
|
||||||
|
|
||||||
let (_, account_config) =
|
let (_, account_config) =
|
||||||
config.into_toml_account_config(Some(self.account_name.as_str()))?;
|
config.into_toml_account_config(Some(self.account.name.as_str()))?;
|
||||||
|
|
||||||
if self.force {
|
if self.force {
|
||||||
#[cfg(feature = "imap")]
|
#[cfg(feature = "imap")]
|
||||||
|
@ -106,7 +103,7 @@ impl Command {
|
||||||
|
|
||||||
printer.print(format!(
|
printer.print(format!(
|
||||||
"Account {} successfully {}configured!",
|
"Account {} successfully {}configured!",
|
||||||
self.account_name,
|
self.account.name,
|
||||||
if self.force { "re" } else { "" }
|
if self.force { "re" } else { "" }
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
|
|
|
@ -6,17 +6,17 @@ use crate::{
|
||||||
account::Accounts,
|
account::Accounts,
|
||||||
config::TomlConfig,
|
config::TomlConfig,
|
||||||
printer::{PrintTableOpts, Printer},
|
printer::{PrintTableOpts, Printer},
|
||||||
|
ui::arg::max_width::MaxTableWidthFlag,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// List all accounts
|
/// List all accounts
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Command {
|
pub struct AccountListCommand {
|
||||||
/// Define a maximum width for the table
|
#[command(flatten)]
|
||||||
#[arg(long, short = 'w', name = "PIXELS")]
|
pub table: MaxTableWidthFlag,
|
||||||
pub max_width: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl AccountListCommand {
|
||||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
info!("executing account list command");
|
info!("executing account list command");
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ impl Command {
|
||||||
.email_reading_format
|
.email_reading_format
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&Default::default()),
|
.unwrap_or(&Default::default()),
|
||||||
max_width: self.max_width,
|
max_width: self.table.max_width,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
@ -7,23 +7,27 @@ use clap::Subcommand;
|
||||||
|
|
||||||
use crate::{config::TomlConfig, printer::Printer};
|
use crate::{config::TomlConfig, printer::Printer};
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
configure::AccountConfigureCommand, list::AccountListCommand, sync::AccountSyncCommand,
|
||||||
|
};
|
||||||
|
|
||||||
/// Subcommand to manage accounts
|
/// Subcommand to manage accounts
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub enum Command {
|
pub enum AccountSubcommand {
|
||||||
/// Configure the given account
|
/// Configure an account
|
||||||
#[command(alias = "cfg")]
|
#[command(alias = "cfg")]
|
||||||
Configure(configure::Command),
|
Configure(AccountConfigureCommand),
|
||||||
|
|
||||||
/// List all exsting accounts
|
/// List all accounts
|
||||||
#[command(alias = "lst")]
|
#[command(alias = "lst")]
|
||||||
List(list::Command),
|
List(AccountListCommand),
|
||||||
|
|
||||||
/// Synchronize the given account locally
|
/// Synchronize an account locally
|
||||||
#[command()]
|
#[command()]
|
||||||
Sync(sync::Command),
|
Sync(AccountSyncCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl AccountSubcommand {
|
||||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Configure(cmd) => cmd.execute(printer, config).await,
|
Self::Configure(cmd) => cmd.execute(printer, config).await,
|
||||||
|
|
|
@ -12,7 +12,10 @@ use std::{
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{backend::BackendBuilder, config::TomlConfig, printer::Printer};
|
use crate::{
|
||||||
|
account::arg::name::AccountNameArg, backend::BackendBuilder, config::TomlConfig,
|
||||||
|
printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
const MAIN_PROGRESS_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
|
const MAIN_PROGRESS_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
|
||||||
ProgressStyle::with_template(" {spinner:.dim} {msg:.dim}\n {wide_bar:.cyan/blue} \n").unwrap()
|
ProgressStyle::with_template(" {spinner:.dim} {msg:.dim}\n {wide_bar:.cyan/blue} \n").unwrap()
|
||||||
|
@ -30,13 +33,9 @@ const SUB_PROGRESS_DONE_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Command {
|
pub struct AccountSyncCommand {
|
||||||
/// The name of the account that needs to be synchronized
|
#[command(flatten)]
|
||||||
///
|
pub account: AccountNameArg,
|
||||||
/// The account names are taken from the table at the root level
|
|
||||||
/// of your TOML configuration file.
|
|
||||||
#[arg(value_name = "ACCOUNT")]
|
|
||||||
pub account_name: String,
|
|
||||||
|
|
||||||
/// Run the synchronization without applying changes
|
/// Run the synchronization without applying changes
|
||||||
///
|
///
|
||||||
|
@ -60,7 +59,7 @@ pub struct Command {
|
||||||
pub all_folders: bool,
|
pub all_folders: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl AccountSyncCommand {
|
||||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
info!("executing account sync command");
|
info!("executing account sync command");
|
||||||
|
|
||||||
|
@ -79,7 +78,7 @@ impl Command {
|
||||||
|
|
||||||
let (toml_account_config, account_config) = config
|
let (toml_account_config, account_config) = config
|
||||||
.clone()
|
.clone()
|
||||||
.into_account_configs(Some(self.account_name.as_str()), true)?;
|
.into_account_configs(Some(self.account.name.as_str()), true)?;
|
||||||
|
|
||||||
let backend_builder =
|
let backend_builder =
|
||||||
BackendBuilder::new(toml_account_config, account_config.clone(), false).await?;
|
BackendBuilder::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
@ -110,11 +109,15 @@ impl Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
printer.print(format!(
|
printer.print(format!(
|
||||||
"Estimated patch length for account to be synchronized: {hunks_count}",
|
"Estimated patch length for account {} to be synchronized: {hunks_count}",
|
||||||
|
self.account.name
|
||||||
))?;
|
))?;
|
||||||
} else if printer.is_json() {
|
} else if printer.is_json() {
|
||||||
sync_builder.sync().await?;
|
sync_builder.sync().await?;
|
||||||
printer.print("Account successfully synchronized!")?;
|
printer.print(format!(
|
||||||
|
"Account {} successfully synchronized!",
|
||||||
|
self.account.name
|
||||||
|
))?;
|
||||||
} else {
|
} else {
|
||||||
let multi = MultiProgress::new();
|
let multi = MultiProgress::new();
|
||||||
let sub_progresses = Mutex::new(HashMap::new());
|
let sub_progresses = Mutex::new(HashMap::new());
|
||||||
|
@ -225,7 +228,10 @@ impl Command {
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
printer.print("Account successfully synchronized!")?;
|
printer.print(format!(
|
||||||
|
"Account {} successfully synchronized!",
|
||||||
|
self.account.name
|
||||||
|
))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod arg;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub(crate) mod wizard;
|
pub(crate) mod wizard;
|
||||||
|
|
19
src/cache/arg/disable.rs
vendored
Normal file
19
src/cache/arg/disable.rs
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
/// The disable cache flag parser
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct DisableCacheFlag {
|
||||||
|
/// 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
|
||||||
|
)]
|
||||||
|
pub disable: bool,
|
||||||
|
}
|
1
src/cache/arg/mod.rs
vendored
Normal file
1
src/cache/arg/mod.rs
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod disable;
|
1
src/cache/mod.rs
vendored
1
src/cache/mod.rs
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod arg;
|
||||||
pub mod args;
|
pub mod args;
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
|
38
src/cli.rs
38
src/cli.rs
|
@ -1,13 +1,13 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
account::command as account,
|
account::command::AccountSubcommand,
|
||||||
completion::command as completion,
|
completion::command::CompletionGenerateCommand,
|
||||||
config::{self, TomlConfig},
|
config::{self, TomlConfig},
|
||||||
man::command as man,
|
folder::command::FolderSubcommand,
|
||||||
|
manual::command::ManualGenerateCommand,
|
||||||
output::{ColorFmt, OutputFmt},
|
output::{ColorFmt, OutputFmt},
|
||||||
printer::Printer,
|
printer::Printer,
|
||||||
};
|
};
|
||||||
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
)]
|
)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Command,
|
pub command: HimalayaCommand,
|
||||||
|
|
||||||
/// Override the default configuration file path
|
/// Override the default configuration file path
|
||||||
///
|
///
|
||||||
|
@ -86,27 +86,31 @@ pub struct Cli {
|
||||||
pub color: ColorFmt,
|
pub color: ColorFmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Top-level CLI commands.
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
pub enum Command {
|
pub enum HimalayaCommand {
|
||||||
/// Subcommand to manage accounts
|
/// Subcommand to manage accounts
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Account(account::Command),
|
Account(AccountSubcommand),
|
||||||
|
|
||||||
/// Generate all man pages to the given directory
|
/// Subcommand to manage folders
|
||||||
#[command(arg_required_else_help = true, alias = "mans")]
|
#[command(subcommand)]
|
||||||
Man(man::Command),
|
Folder(FolderSubcommand),
|
||||||
|
|
||||||
/// Print completion script for the given shell to stdout
|
/// Generate manual pages to a directory
|
||||||
#[command(arg_required_else_help = true, aliases = ["completions", "compl", "comp"])]
|
#[command(arg_required_else_help = true)]
|
||||||
Completion(completion::Command),
|
Manual(ManualGenerateCommand),
|
||||||
|
|
||||||
|
/// Print completion script for a shell to stdout
|
||||||
|
#[command(arg_required_else_help = true)]
|
||||||
|
Completion(CompletionGenerateCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl HimalayaCommand {
|
||||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Account(cmd) => cmd.execute(printer, config).await,
|
Self::Account(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Man(cmd) => cmd.execute(printer).await,
|
Self::Folder(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Manual(cmd) => cmd.execute(printer).await,
|
||||||
Self::Completion(cmd) => cmd.execute(printer).await,
|
Self::Completion(cmd) => cmd.execute(printer).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,15 @@ use std::io;
|
||||||
|
|
||||||
use crate::{cli::Cli, printer::Printer};
|
use crate::{cli::Cli, printer::Printer};
|
||||||
|
|
||||||
/// Print completion script for the given shell to stdout
|
/// Print completion script for a shell to stdout
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Command {
|
pub struct CompletionGenerateCommand {
|
||||||
/// Shell that completion script should be generated for
|
/// Shell for which completion script should be generated for
|
||||||
#[arg(value_parser = value_parser!(Shell))]
|
#[arg(value_parser = value_parser!(Shell))]
|
||||||
pub shell: Shell,
|
pub shell: Shell,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl CompletionGenerateCommand {
|
||||||
pub async fn execute(self, printer: &mut impl Printer) -> Result<()> {
|
pub async fn execute(self, printer: &mut impl Printer) -> Result<()> {
|
||||||
info!("executing completion generate command");
|
info!("executing completion generate command");
|
||||||
|
|
||||||
|
|
1
src/folder/arg/mod.rs
Normal file
1
src/folder/arg/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod name;
|
9
src/folder/arg/name.rs
Normal file
9
src/folder/arg/name.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
/// The folder name argument parser
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct FolderNameArg {
|
||||||
|
/// The name of the folder
|
||||||
|
#[arg(name = "folder-name", value_name = "FOLDER")]
|
||||||
|
pub name: String,
|
||||||
|
}
|
40
src/folder/command/create.rs
Normal file
40
src/folder/command/create.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create a new folder
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct FolderCreateCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderCreateCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing folder create command");
|
||||||
|
|
||||||
|
let folder = &self.folder.name;
|
||||||
|
|
||||||
|
let some_account_name = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let (toml_account_config, account_config) = config
|
||||||
|
.clone()
|
||||||
|
.into_account_configs(some_account_name, self.cache.disable)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
|
backend.add_folder(&folder).await?;
|
||||||
|
printer.print(format!("Folder {folder} successfully created!"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
55
src/folder/command/delete.rs
Normal file
55
src/folder/command/delete.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use dialoguer::Confirm;
|
||||||
|
use log::info;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Delete a folder
|
||||||
|
///
|
||||||
|
/// All emails from a 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,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderDeleteCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing folder delete command");
|
||||||
|
|
||||||
|
let folder = &self.folder.name;
|
||||||
|
|
||||||
|
let confirm_msg = format!("Do you really want to delete the folder {folder}? All emails will be definitely deleted.");
|
||||||
|
let confirm = Confirm::new()
|
||||||
|
.with_prompt(confirm_msg)
|
||||||
|
.default(false)
|
||||||
|
.report(false)
|
||||||
|
.interact_opt()?;
|
||||||
|
if let Some(false) | None = confirm {
|
||||||
|
process::exit(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
let some_account_name = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let (toml_account_config, account_config) = config
|
||||||
|
.clone()
|
||||||
|
.into_account_configs(some_account_name, self.cache.disable)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
|
backend.delete_folder(&folder).await?;
|
||||||
|
printer.print(format!("Folder {folder} successfully deleted!"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
44
src/folder/command/expunge.rs
Normal file
44
src/folder/command/expunge.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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"
|
||||||
|
/// flag.
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct FolderExpungeCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderExpungeCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing folder expunge command");
|
||||||
|
|
||||||
|
let folder = &self.folder.name;
|
||||||
|
|
||||||
|
let some_account_name = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let (toml_account_config, account_config) = config
|
||||||
|
.clone()
|
||||||
|
.into_account_configs(some_account_name, self.cache.disable)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
|
backend.expunge_folder(&folder).await?;
|
||||||
|
printer.print(format!("Folder {folder} successfully expunged!"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
50
src/folder/command/list.rs
Normal file
50
src/folder/command/list.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag,
|
||||||
|
backend::Backend,
|
||||||
|
cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig,
|
||||||
|
folder::Folders,
|
||||||
|
printer::{PrintTableOpts, Printer},
|
||||||
|
ui::arg::max_width::MaxTableWidthFlag,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// List all folders
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct FolderListCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub table: MaxTableWidthFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderListCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing folder list command");
|
||||||
|
|
||||||
|
let some_account_name = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let (toml_account_config, account_config) = config
|
||||||
|
.clone()
|
||||||
|
.into_account_configs(some_account_name, self.cache.disable)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
|
let folders: Folders = backend.list_folders().await?.into();
|
||||||
|
|
||||||
|
printer.print_table(
|
||||||
|
Box::new(folders),
|
||||||
|
PrintTableOpts {
|
||||||
|
format: &account_config.email_reading_format,
|
||||||
|
max_width: self.table.max_width,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
51
src/folder/command/mod.rs
Normal file
51
src/folder/command/mod.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
mod create;
|
||||||
|
mod delete;
|
||||||
|
mod expunge;
|
||||||
|
mod list;
|
||||||
|
mod purge;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Subcommand;
|
||||||
|
|
||||||
|
use crate::{config::TomlConfig, printer::Printer};
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
create::FolderCreateCommand, delete::FolderDeleteCommand, expunge::FolderExpungeCommand,
|
||||||
|
list::FolderListCommand, purge::FolderPurgeCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Subcommand to manage accounts
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub enum FolderSubcommand {
|
||||||
|
/// Create a new folder
|
||||||
|
#[command(alias = "add")]
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderSubcommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Create(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::List(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Expunge(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Purge(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Delete(cmd) => cmd.execute(printer, config).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
src/folder/command/purge.rs
Normal file
55
src/folder/command/purge.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use dialoguer::Confirm;
|
||||||
|
use log::info;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
account::arg::name::AccountNameFlag, backend::Backend, cache::arg::disable::DisableCacheFlag,
|
||||||
|
config::TomlConfig, folder::arg::name::FolderNameArg, printer::Printer,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Purge a folder
|
||||||
|
///
|
||||||
|
/// All emails from a given folder are definitely deleted. The purged
|
||||||
|
/// folder will remain empty after executing of the command.
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct FolderPurgeCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameArg,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderPurgeCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing folder purge command");
|
||||||
|
|
||||||
|
let folder = &self.folder.name;
|
||||||
|
|
||||||
|
let confirm_msg = format!("Do you really want to purge the folder {folder}? All emails will be definitely deleted.");
|
||||||
|
let confirm = Confirm::new()
|
||||||
|
.with_prompt(confirm_msg)
|
||||||
|
.default(false)
|
||||||
|
.report(false)
|
||||||
|
.interact_opt()?;
|
||||||
|
if let Some(false) | None = confirm {
|
||||||
|
process::exit(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
let some_account_name = self.account.name.as_ref().map(String::as_str);
|
||||||
|
let (toml_account_config, account_config) = config
|
||||||
|
.clone()
|
||||||
|
.into_account_configs(some_account_name, self.cache.disable)?;
|
||||||
|
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
||||||
|
|
||||||
|
backend.purge_folder(&folder).await?;
|
||||||
|
printer.print(format!("Folder {folder} successfully purged!"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
|
pub mod arg;
|
||||||
pub mod args;
|
pub mod args;
|
||||||
|
pub mod command;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod handlers;
|
pub mod handlers;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub mod folder;
|
||||||
pub mod imap;
|
pub mod imap;
|
||||||
#[cfg(feature = "maildir")]
|
#[cfg(feature = "maildir")]
|
||||||
pub mod maildir;
|
pub mod maildir;
|
||||||
pub mod man;
|
pub mod manual;
|
||||||
#[cfg(feature = "notmuch")]
|
#[cfg(feature = "notmuch")]
|
||||||
pub mod notmuch;
|
pub mod notmuch;
|
||||||
pub mod output;
|
pub mod output;
|
||||||
|
|
50
src/main.rs
50
src/main.rs
|
@ -25,10 +25,8 @@ async fn main() -> Result<()> {
|
||||||
// .author(env!("CARGO_PKG_AUTHORS"))
|
// .author(env!("CARGO_PKG_AUTHORS"))
|
||||||
// .propagate_version(true)
|
// .propagate_version(true)
|
||||||
// .infer_subcommands(true)
|
// .infer_subcommands(true)
|
||||||
// .args(folder::args::global_args())
|
|
||||||
// .args(cache::args::global_args())
|
// .args(cache::args::global_args())
|
||||||
// .args(output::args::global_args())
|
// .args(output::args::global_args())
|
||||||
// .subcommand(folder::args::subcmd())
|
|
||||||
// .subcommand(envelope::args::subcmd())
|
// .subcommand(envelope::args::subcmd())
|
||||||
// .subcommand(flag::args::subcmd())
|
// .subcommand(flag::args::subcmd())
|
||||||
// .subcommand(message::args::subcmd())
|
// .subcommand(message::args::subcmd())
|
||||||
|
@ -67,63 +65,15 @@ async fn main() -> Result<()> {
|
||||||
// let some_config_path = config::args::parse_global_arg(&m);
|
// let some_config_path = config::args::parse_global_arg(&m);
|
||||||
// let some_account_name = account::command::parse_global_arg(&m);
|
// let some_account_name = account::command::parse_global_arg(&m);
|
||||||
// let disable_cache = cache::args::parse_disable_cache_arg(&m);
|
// let disable_cache = cache::args::parse_disable_cache_arg(&m);
|
||||||
// let folder = folder::args::parse_global_source_arg(&m);
|
|
||||||
|
|
||||||
// let toml_config = TomlConfig::from_some_path_or_default(some_config_path).await?;
|
// let toml_config = TomlConfig::from_some_path_or_default(some_config_path).await?;
|
||||||
|
|
||||||
// let mut printer = StdoutPrinter::try_from(&m)?;
|
// let mut printer = StdoutPrinter::try_from(&m)?;
|
||||||
|
|
||||||
// #[cfg(feature = "imap")]
|
|
||||||
// if let BackendConfig::Imap(imap_config) = &account_config.backend {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// match imap::args::matches(&m)? {
|
|
||||||
// Some(imap::args::Cmd::Notify(keepalive)) => {
|
|
||||||
// let backend =
|
|
||||||
// ImapBackend::new(account_config.clone(), imap_config.clone(), None).await?;
|
|
||||||
// imap::handlers::notify(&mut backend, &folder, keepalive).await?;
|
|
||||||
// return Ok(());
|
|
||||||
// }
|
|
||||||
// Some(imap::args::Cmd::Watch(keepalive)) => {
|
|
||||||
// let backend =
|
|
||||||
// ImapBackend::new(account_config.clone(), imap_config.clone(), None).await?;
|
|
||||||
// imap::handlers::watch(&mut backend, &folder, keepalive).await?;
|
|
||||||
// return Ok(());
|
|
||||||
// }
|
|
||||||
// _ => (),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let (toml_account_config, account_config) = toml_config
|
// let (toml_account_config, account_config) = toml_config
|
||||||
// .clone()
|
// .clone()
|
||||||
// .into_account_configs(some_account_name, disable_cache)?;
|
// .into_account_configs(some_account_name, disable_cache)?;
|
||||||
|
|
||||||
// // checks folder commands
|
|
||||||
// match folder::args::matches(&m)? {
|
|
||||||
// Some(folder::args::Cmd::Create) => {
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// let folder = folder
|
|
||||||
// .ok_or_else(|| anyhow!("the folder argument is missing"))
|
|
||||||
// .context("cannot create folder")?;
|
|
||||||
// return folder::handlers::create(&mut printer, &backend, &folder).await;
|
|
||||||
// }
|
|
||||||
// Some(folder::args::Cmd::List(max_width)) => {
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// return folder::handlers::list(&account_config, &mut printer, &backend, max_width)
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// Some(folder::args::Cmd::Expunge) => {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// return folder::handlers::expunge(&mut printer, &backend, &folder).await;
|
|
||||||
// }
|
|
||||||
// Some(folder::args::Cmd::Delete) => {
|
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
|
||||||
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
|
|
||||||
// return folder::handlers::delete(&mut printer, &backend, &folder).await;
|
|
||||||
// }
|
|
||||||
// _ => (),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// match envelope::args::matches(&m)? {
|
// match envelope::args::matches(&m)? {
|
||||||
// Some(envelope::args::Cmd::List(max_width, page_size, page)) => {
|
// Some(envelope::args::Cmd::List(max_width, page_size, page)) => {
|
||||||
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||||
|
|
|
@ -7,17 +7,17 @@ use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
use crate::{cli::Cli, printer::Printer};
|
use crate::{cli::Cli, printer::Printer};
|
||||||
|
|
||||||
/// Generate all man pages to the given directory
|
/// Generate manual pages to a directory
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Command {
|
pub struct ManualGenerateCommand {
|
||||||
/// Directory where man files should be generated in
|
/// Directory where man files should be generated in
|
||||||
#[arg(value_parser = dir_parser)]
|
#[arg(value_parser = dir_parser)]
|
||||||
pub dir: PathBuf,
|
pub dir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl ManualGenerateCommand {
|
||||||
pub async fn execute(self, printer: &mut impl Printer) -> Result<()> {
|
pub async fn execute(self, printer: &mut impl Printer) -> Result<()> {
|
||||||
info!("executing man generate command");
|
info!("executing manual generate command");
|
||||||
|
|
||||||
let cmd = Cli::command();
|
let cmd = Cli::command();
|
||||||
let cmd_name = cmd.get_name().to_string();
|
let cmd_name = cmd.get_name().to_string();
|
|
@ -9,10 +9,14 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Printer {
|
pub trait Printer {
|
||||||
|
// TODO: rename end
|
||||||
fn print<T: Debug + Print + serde::Serialize>(&mut self, data: T) -> Result<()>;
|
fn print<T: Debug + Print + serde::Serialize>(&mut self, data: T) -> Result<()>;
|
||||||
|
// TODO: rename log
|
||||||
fn print_log<T: Debug + Print>(&mut self, data: T) -> Result<()>;
|
fn print_log<T: Debug + Print>(&mut self, data: T) -> Result<()>;
|
||||||
|
// TODO: rename table
|
||||||
fn print_table<T: Debug + erased_serde::Serialize + PrintTable + ?Sized>(
|
fn print_table<T: Debug + erased_serde::Serialize + PrintTable + ?Sized>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
// TODO: remove Box
|
||||||
data: Box<T>,
|
data: Box<T>,
|
||||||
opts: PrintTableOpts,
|
opts: PrintTableOpts,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
|
9
src/ui/table/arg/max_width.rs
Normal file
9
src/ui/table/arg/max_width.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use clap::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 max_width: Option<usize>,
|
||||||
|
}
|
1
src/ui/table/arg/mod.rs
Normal file
1
src/ui/table/arg/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod max_width;
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod arg;
|
||||||
pub mod args;
|
pub mod args;
|
||||||
pub mod table;
|
pub mod table;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue