refactor flag with clap derive api

This commit is contained in:
Clément DOUIN 2023-12-07 10:10:18 +01:00
parent 2c33dd2f9f
commit 5e1a03e3c1
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
9 changed files with 254 additions and 76 deletions

View file

@ -7,6 +7,7 @@ use crate::{
completion::command::CompletionGenerateCommand,
config::{self, TomlConfig},
envelope::command::EnvelopeSubcommand,
flag::command::FlagSubcommand,
folder::command::FolderSubcommand,
manual::command::ManualGenerateCommand,
output::{ColorFmt, OutputFmt},
@ -90,17 +91,21 @@ pub struct Cli {
#[derive(Subcommand, Debug)]
pub enum HimalayaCommand {
/// Subcommand to manage accounts
#[command(subcommand)]
#[command(subcommand, alias = "accounts")]
Account(AccountSubcommand),
/// Subcommand to manage folders
#[command(subcommand)]
#[command(subcommand, alias = "folders")]
Folder(FolderSubcommand),
/// Subcommand to manage envelopes
#[command(subcommand)]
#[command(subcommand, alias = "envelopes")]
Envelope(EnvelopeSubcommand),
/// Subcommand to manage flags
#[command(subcommand, alias = "flags")]
Flag(FlagSubcommand),
/// Generate manual pages to a directory
#[command(arg_required_else_help = true)]
Manual(ManualGenerateCommand),
@ -116,6 +121,7 @@ impl HimalayaCommand {
Self::Account(cmd) => cmd.execute(printer, config).await,
Self::Folder(cmd) => cmd.execute(printer, config).await,
Self::Envelope(cmd) => cmd.execute(printer, config).await,
Self::Flag(cmd) => cmd.execute(printer, config).await,
Self::Manual(cmd) => cmd.execute(printer).await,
Self::Completion(cmd) => cmd.execute(printer).await,
}

View file

@ -0,0 +1,51 @@
use clap::Parser;
use email::flag::{Flag, Flags};
use log::debug;
/// The ids and/or flags arguments parser
#[derive(Debug, Parser)]
pub struct IdsAndFlagsArgs {
/// 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.
#[arg(value_name = "ID-OR-FLAG", required = true)]
pub ids_and_flags: Vec<IdOrFlag>,
}
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum IdOrFlag {
Id(String),
Flag(Flag),
}
impl From<&str> for IdOrFlag {
fn from(value: &str) -> Self {
value
.parse::<usize>()
.map(|_| Self::Id(value.to_owned()))
.unwrap_or_else(|err| {
let flag = Flag::from(value);
debug!("cannot parse {value} as usize, parsing it as flag {flag}");
debug!("{err:?}");
Self::Flag(flag)
})
}
}
pub fn to_tuple<'a>(ids_and_flags: &'a [IdOrFlag]) -> (Vec<&'a str>, Flags) {
ids_and_flags.iter().fold(
(Vec::default(), Flags::default()),
|(mut ids, mut flags), arg| {
match arg {
IdOrFlag::Id(id) => {
ids.push(id.as_str());
}
IdOrFlag::Flag(flag) => {
flags.insert(flag.to_owned());
}
};
(ids, flags)
},
)
}

View file

@ -0,0 +1 @@
pub mod ids_and_flags;

View file

@ -0,0 +1,48 @@
use anyhow::Result;
use clap::Parser;
use log::info;
use crate::{
account::arg::name::AccountNameFlag,
backend::Backend,
cache::arg::disable::DisableCacheFlag,
config::TomlConfig,
flag::arg::ids_and_flags::{to_tuple, IdsAndFlagsArgs},
folder::arg::name::FolderNameArg,
printer::Printer,
};
/// Add flag(s) to an envelope
#[derive(Debug, Parser)]
pub struct FlagAddCommand {
#[command(flatten)]
pub folder: FolderNameArg,
#[command(flatten)]
pub args: IdsAndFlagsArgs,
#[command(flatten)]
pub account: AccountNameFlag,
#[command(flatten)]
pub cache: DisableCacheFlag,
}
impl FlagAddCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
info!("executing flag add command");
let folder = &self.folder.name;
let account = self.account.name.as_ref().map(String::as_str);
let cache = self.cache.disable;
let (toml_account_config, account_config) =
config.clone().into_account_configs(account, cache)?;
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
let (ids, flags) = to_tuple(&self.args.ids_and_flags);
backend.add_flags(folder, &ids, &flags).await?;
printer.print(format!("Flag(s) {flags} successfully added!"))
}
}

View file

@ -0,0 +1,39 @@
mod add;
mod remove;
mod set;
use anyhow::Result;
use clap::Subcommand;
use crate::{config::TomlConfig, printer::Printer};
use self::{add::FlagAddCommand, remove::FlagRemoveCommand, set::FlagSetCommand};
/// Subcommand to manage flags
#[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),
}
impl FlagSubcommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
match self {
Self::Add(cmd) => cmd.execute(printer, config).await,
Self::Set(cmd) => cmd.execute(printer, config).await,
Self::Remove(cmd) => cmd.execute(printer, config).await,
}
}
}

View file

@ -0,0 +1,48 @@
use anyhow::Result;
use clap::Parser;
use log::info;
use crate::{
account::arg::name::AccountNameFlag,
backend::Backend,
cache::arg::disable::DisableCacheFlag,
config::TomlConfig,
flag::arg::ids_and_flags::{to_tuple, IdsAndFlagsArgs},
folder::arg::name::FolderNameArg,
printer::Printer,
};
/// Remove flag(s) from an envelope
#[derive(Debug, Parser)]
pub struct FlagRemoveCommand {
#[command(flatten)]
pub folder: FolderNameArg,
#[command(flatten)]
pub args: IdsAndFlagsArgs,
#[command(flatten)]
pub account: AccountNameFlag,
#[command(flatten)]
pub cache: DisableCacheFlag,
}
impl FlagRemoveCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
info!("executing flag remove command");
let folder = &self.folder.name;
let account = self.account.name.as_ref().map(String::as_str);
let cache = self.cache.disable;
let (toml_account_config, account_config) =
config.clone().into_account_configs(account, cache)?;
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
let (ids, flags) = to_tuple(&self.args.ids_and_flags);
backend.remove_flags(folder, &ids, &flags).await?;
printer.print(format!("Flag(s) {flags} successfully removed!"))
}
}

View file

@ -0,0 +1,48 @@
use anyhow::Result;
use clap::Parser;
use log::info;
use crate::{
account::arg::name::AccountNameFlag,
backend::Backend,
cache::arg::disable::DisableCacheFlag,
config::TomlConfig,
flag::arg::ids_and_flags::{to_tuple, IdsAndFlagsArgs},
folder::arg::name::FolderNameArg,
printer::Printer,
};
/// Replace flag(s) of an envelope
#[derive(Debug, Parser)]
pub struct FlagSetCommand {
#[command(flatten)]
pub folder: FolderNameArg,
#[command(flatten)]
pub args: IdsAndFlagsArgs,
#[command(flatten)]
pub account: AccountNameFlag,
#[command(flatten)]
pub cache: DisableCacheFlag,
}
impl FlagSetCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
info!("executing flag set command");
let folder = &self.folder.name;
let account = self.account.name.as_ref().map(String::as_str);
let cache = self.cache.disable;
let (toml_account_config, account_config) =
config.clone().into_account_configs(account, cache)?;
let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
let (ids, flags) = to_tuple(&self.args.ids_and_flags);
backend.set_flags(folder, &ids, &flags).await?;
printer.print(format!("Flag(s) {flags} successfully set!"))
}
}

View file

@ -1,4 +1,6 @@
pub mod arg;
pub mod args;
pub mod command;
pub mod config;
pub mod handlers;
@ -6,7 +8,7 @@ use serde::Serialize;
use std::{collections::HashSet, ops};
/// Represents the flag variants.
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize)]
pub enum Flag {
Seen,
Answered,

View file

@ -2,9 +2,16 @@ 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 log::{debug, warn};
#[tokio::main]
async fn main() -> Result<()> {
#[cfg(not(target_os = "windows"))]
if let Err((_, err)) = coredump::register_panic_handler() {
warn!("cannot register custom panic handler: {err}");
debug!("{err:?}");
}
LoggerBuilder::new()
.parse_env(Env::new().filter_or(DEFAULT_FILTER_ENV, "warn"))
.format_timestamp(None)
@ -20,30 +27,12 @@ async fn main() -> Result<()> {
// fn create_app() -> clap::Command {
// clap::Command::new(env!("CARGO_PKG_NAME"))
// .version(env!("CARGO_PKG_VERSION"))
// .about(env!("CARGO_PKG_DESCRIPTION"))
// .author(env!("CARGO_PKG_AUTHORS"))
// .propagate_version(true)
// .infer_subcommands(true)
// .args(cache::args::global_args())
// .args(output::args::global_args())
// .subcommand(envelope::args::subcmd())
// .subcommand(flag::args::subcmd())
// .subcommand(message::args::subcmd())
// .subcommand(template::args::subcmd())
// }
// #[tokio::main]
// async fn main() -> Result<()> {
// #[cfg(not(target_os = "windows"))]
// if let Err((_, err)) = coredump::register_panic_handler() {
// warn!("cannot register custom panic handler: {err}");
// debug!("cannot register custom panic handler: {err:?}");
// }
// let default_env_filter = env_logger::DEFAULT_FILTER_ENV;
// env_logger::init_from_env(env_logger::Env::default().filter_or(default_env_filter, "off"));
// // check mailto command before app initialization
// let raw_args: Vec<String> = env::args().collect();
// if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
@ -59,58 +48,6 @@ async fn main() -> Result<()> {
// return message::handlers::mailto(&account_config, &backend, &mut printer, &url).await;
// }
// let app = _create_app();
// let m = app.get_matches();
// let some_config_path = config::args::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 toml_config = TomlConfig::from_some_path_or_default(some_config_path).await?;
// let mut printer = StdoutPrinter::try_from(&m)?;
// let (toml_account_config, account_config) = toml_config
// .clone()
// .into_account_configs(some_account_name, disable_cache)?;
// match envelope::args::matches(&m)? {
// Some(envelope::args::Cmd::List(max_width, page_size, page)) => {
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
// return envelope::handlers::list(
// &account_config,
// &mut printer,
// &backend,
// &folder,
// max_width,
// page_size,
// page,
// )
// .await;
// }
// _ => (),
// }
// match flag::args::matches(&m)? {
// Some(flag::args::Cmd::Set(ids, ref flags)) => {
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
// return flag::handlers::set(&mut printer, &backend, &folder, ids, flags).await;
// }
// Some(flag::args::Cmd::Add(ids, ref flags)) => {
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
// return flag::handlers::add(&mut printer, &backend, &folder, ids, flags).await;
// }
// Some(flag::args::Cmd::Remove(ids, ref flags)) => {
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
// let backend = Backend::new(toml_account_config, account_config.clone(), false).await?;
// return flag::handlers::remove(&mut printer, &backend, &folder, ids, flags).await;
// }
// _ => (),
// }
// match message::args::matches(&m)? {
// Some(message::args::Cmd::Attachments(ids)) => {
// let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER);
@ -259,6 +196,4 @@ async fn main() -> Result<()> {
// }
// _ => (),
// }
// Ok(())
// }