mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 17:15:12 +00:00
refactor envelope with clap derive api
This commit is contained in:
parent
4a77253c1d
commit
2c33dd2f9f
|
@ -6,6 +6,7 @@ use crate::{
|
||||||
account::command::AccountSubcommand,
|
account::command::AccountSubcommand,
|
||||||
completion::command::CompletionGenerateCommand,
|
completion::command::CompletionGenerateCommand,
|
||||||
config::{self, TomlConfig},
|
config::{self, TomlConfig},
|
||||||
|
envelope::command::EnvelopeSubcommand,
|
||||||
folder::command::FolderSubcommand,
|
folder::command::FolderSubcommand,
|
||||||
manual::command::ManualGenerateCommand,
|
manual::command::ManualGenerateCommand,
|
||||||
output::{ColorFmt, OutputFmt},
|
output::{ColorFmt, OutputFmt},
|
||||||
|
@ -96,6 +97,10 @@ pub enum HimalayaCommand {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Folder(FolderSubcommand),
|
Folder(FolderSubcommand),
|
||||||
|
|
||||||
|
/// Subcommand to manage envelopes
|
||||||
|
#[command(subcommand)]
|
||||||
|
Envelope(EnvelopeSubcommand),
|
||||||
|
|
||||||
/// Generate manual pages to a directory
|
/// Generate manual pages to a directory
|
||||||
#[command(arg_required_else_help = true)]
|
#[command(arg_required_else_help = true)]
|
||||||
Manual(ManualGenerateCommand),
|
Manual(ManualGenerateCommand),
|
||||||
|
@ -110,6 +115,7 @@ impl HimalayaCommand {
|
||||||
match self {
|
match self {
|
||||||
Self::Account(cmd) => cmd.execute(printer, config).await,
|
Self::Account(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Folder(cmd) => cmd.execute(printer, config).await,
|
Self::Folder(cmd) => cmd.execute(printer, config).await,
|
||||||
|
Self::Envelope(cmd) => cmd.execute(printer, config).await,
|
||||||
Self::Manual(cmd) => cmd.execute(printer).await,
|
Self::Manual(cmd) => cmd.execute(printer).await,
|
||||||
Self::Completion(cmd) => cmd.execute(printer).await,
|
Self::Completion(cmd) => cmd.execute(printer).await,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
//! Email CLI module.
|
|
||||||
//!
|
|
||||||
//! This module contains the command matcher, the subcommands and the
|
|
||||||
//! arguments related to the email domain.
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use clap::{Arg, ArgMatches, Command};
|
|
||||||
|
|
||||||
use crate::ui::table;
|
|
||||||
|
|
||||||
const ARG_PAGE: &str = "page";
|
|
||||||
const ARG_PAGE_SIZE: &str = "page-size";
|
|
||||||
const CMD_LIST: &str = "list";
|
|
||||||
const CMD_ENVELOPE: &str = "envelope";
|
|
||||||
|
|
||||||
pub type Page = usize;
|
|
||||||
pub type PageSize = usize;
|
|
||||||
|
|
||||||
/// Represents the email commands.
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum Cmd {
|
|
||||||
List(table::args::MaxTableWidth, Option<PageSize>, Page),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Email command matcher.
|
|
||||||
pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
|
||||||
let cmd = if let Some(m) = m.subcommand_matches(CMD_ENVELOPE) {
|
|
||||||
if let Some(m) = m.subcommand_matches(CMD_LIST) {
|
|
||||||
let max_table_width = table::args::parse_max_width(m);
|
|
||||||
let page_size = parse_page_size_arg(m);
|
|
||||||
let page = parse_page_arg(m);
|
|
||||||
Some(Cmd::List(max_table_width, page_size, page))
|
|
||||||
} else {
|
|
||||||
Some(Cmd::List(None, None, 0))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the envelope subcommand.
|
|
||||||
pub fn subcmd() -> Command {
|
|
||||||
Command::new(CMD_ENVELOPE)
|
|
||||||
.about("Subcommand to manage envelopes")
|
|
||||||
.long_about("Subcommand to manage envelopes like list")
|
|
||||||
.subcommands([Command::new(CMD_LIST)
|
|
||||||
.alias("lst")
|
|
||||||
.about("List envelopes")
|
|
||||||
.arg(page_size_arg())
|
|
||||||
.arg(page_arg())
|
|
||||||
.arg(table::args::max_width())])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the page size argument.
|
|
||||||
fn page_size_arg() -> Arg {
|
|
||||||
Arg::new(ARG_PAGE_SIZE)
|
|
||||||
.help("Page size")
|
|
||||||
.long("page-size")
|
|
||||||
.short('s')
|
|
||||||
.value_name("INT")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the page size argument parser.
|
|
||||||
fn parse_page_size_arg(matches: &ArgMatches) -> Option<usize> {
|
|
||||||
matches
|
|
||||||
.get_one::<String>(ARG_PAGE_SIZE)
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the page argument.
|
|
||||||
fn page_arg() -> Arg {
|
|
||||||
Arg::new(ARG_PAGE)
|
|
||||||
.help("Page number")
|
|
||||||
.short('p')
|
|
||||||
.long("page")
|
|
||||||
.value_name("INT")
|
|
||||||
.default_value("1")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the page argument parser.
|
|
||||||
fn parse_page_arg(matches: &ArgMatches) -> usize {
|
|
||||||
matches
|
|
||||||
.get_one::<String>(ARG_PAGE)
|
|
||||||
.unwrap()
|
|
||||||
.parse()
|
|
||||||
.ok()
|
|
||||||
.map(|page| 1.max(page) - 1)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
68
src/email/envelope/command/list.rs
Normal file
68
src/email/envelope/command/list.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
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::FolderNameOptionalArg,
|
||||||
|
printer::{PrintTableOpts, Printer},
|
||||||
|
ui::arg::max_width::MaxTableWidthFlag,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// List all envelopes from a folder
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct EnvelopeListCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub folder: FolderNameOptionalArg,
|
||||||
|
|
||||||
|
/// The page number
|
||||||
|
#[arg(long, short, value_name = "NUMBER", default_value = "1")]
|
||||||
|
pub page: usize,
|
||||||
|
|
||||||
|
/// The page size
|
||||||
|
#[arg(long, short = 's', value_name = "NUMBER")]
|
||||||
|
pub page_size: Option<usize>,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub table: MaxTableWidthFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub account: AccountNameFlag,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cache: DisableCacheFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnvelopeListCommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
info!("executing envelope list 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?;
|
||||||
|
|
||||||
|
let page_size = self
|
||||||
|
.page_size
|
||||||
|
.unwrap_or(account_config.email_listing_page_size());
|
||||||
|
let page = 1.max(self.page) - 1;
|
||||||
|
|
||||||
|
let envelopes = backend.list_envelopes(folder, page_size, page).await?;
|
||||||
|
|
||||||
|
printer.print_table(
|
||||||
|
Box::new(envelopes),
|
||||||
|
PrintTableOpts {
|
||||||
|
format: &account_config.email_reading_format,
|
||||||
|
max_width: self.table.max_width,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
24
src/email/envelope/command/mod.rs
Normal file
24
src/email/envelope/command/mod.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
pub mod list;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Subcommand;
|
||||||
|
|
||||||
|
use crate::{config::TomlConfig, printer::Printer};
|
||||||
|
|
||||||
|
use self::list::EnvelopeListCommand;
|
||||||
|
|
||||||
|
/// Subcommand to manage envelopes
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub enum EnvelopeSubcommand {
|
||||||
|
/// List all envelopes from a folder
|
||||||
|
#[command(alias = "lst")]
|
||||||
|
List(EnvelopeListCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnvelopeSubcommand {
|
||||||
|
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::List(cmd) => cmd.execute(printer, config).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,32 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use email::account::config::AccountConfig;
|
|
||||||
use log::{debug, trace};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
backend::Backend,
|
|
||||||
printer::{PrintTableOpts, Printer},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub async fn list<P: Printer>(
|
|
||||||
config: &AccountConfig,
|
|
||||||
printer: &mut P,
|
|
||||||
backend: &Backend,
|
|
||||||
folder: &str,
|
|
||||||
max_width: Option<usize>,
|
|
||||||
page_size: Option<usize>,
|
|
||||||
page: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
let page_size = page_size.unwrap_or(config.email_listing_page_size());
|
|
||||||
debug!("page size: {}", page_size);
|
|
||||||
|
|
||||||
let envelopes = backend.list_envelopes(&folder, page_size, page).await?;
|
|
||||||
trace!("envelopes: {:?}", envelopes);
|
|
||||||
|
|
||||||
printer.print_table(
|
|
||||||
Box::new(envelopes),
|
|
||||||
PrintTableOpts {
|
|
||||||
format: &config.email_reading_format,
|
|
||||||
max_width,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
pub mod args;
|
pub mod command;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod flag;
|
pub mod flag;
|
||||||
pub mod handlers;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use email::account::config::AccountConfig;
|
use email::account::config::AccountConfig;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||||
|
|
||||||
use crate::{folder, template};
|
use crate::template;
|
||||||
|
|
||||||
const ARG_CRITERIA: &str = "criterion";
|
const ARG_CRITERIA: &str = "criterion";
|
||||||
const ARG_HEADERS: &str = "headers";
|
const ARG_HEADERS: &str = "headers";
|
||||||
|
@ -71,7 +71,7 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
||||||
Some(Cmd::Attachments(ids))
|
Some(Cmd::Attachments(ids))
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_COPY) {
|
} else if let Some(m) = m.subcommand_matches(CMD_COPY) {
|
||||||
let ids = parse_ids_arg(m);
|
let ids = parse_ids_arg(m);
|
||||||
let folder = folder::args::parse_target_arg(m);
|
let folder = "INBOX";
|
||||||
Some(Cmd::Copy(ids, folder))
|
Some(Cmd::Copy(ids, folder))
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_DELETE) {
|
} else if let Some(m) = m.subcommand_matches(CMD_DELETE) {
|
||||||
let ids = parse_ids_arg(m);
|
let ids = parse_ids_arg(m);
|
||||||
|
@ -83,7 +83,7 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
||||||
Some(Cmd::Forward(id, headers, body))
|
Some(Cmd::Forward(id, headers, body))
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_MOVE) {
|
} else if let Some(m) = m.subcommand_matches(CMD_MOVE) {
|
||||||
let ids = parse_ids_arg(m);
|
let ids = parse_ids_arg(m);
|
||||||
let folder = folder::args::parse_target_arg(m);
|
let folder = "INBOX";
|
||||||
Some(Cmd::Move(ids, folder))
|
Some(Cmd::Move(ids, folder))
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_READ) {
|
} else if let Some(m) = m.subcommand_matches(CMD_READ) {
|
||||||
let ids = parse_ids_arg(m);
|
let ids = parse_ids_arg(m);
|
||||||
|
@ -158,12 +158,12 @@ pub fn subcmd() -> Command {
|
||||||
Command::new(CMD_COPY)
|
Command::new(CMD_COPY)
|
||||||
.alias("cp")
|
.alias("cp")
|
||||||
.about("Copy emails to the given folder")
|
.about("Copy emails to the given folder")
|
||||||
.arg(folder::args::target_arg())
|
// .arg(folder::args::target_arg())
|
||||||
.arg(ids_arg()),
|
.arg(ids_arg()),
|
||||||
Command::new(CMD_MOVE)
|
Command::new(CMD_MOVE)
|
||||||
.alias("mv")
|
.alias("mv")
|
||||||
.about("Move emails to the given folder")
|
.about("Move emails to the given folder")
|
||||||
.arg(folder::args::target_arg())
|
// .arg(folder::args::target_arg())
|
||||||
.arg(ids_arg()),
|
.arg(ids_arg()),
|
||||||
Command::new(CMD_DELETE)
|
Command::new(CMD_DELETE)
|
||||||
.aliases(["remove", "rm"])
|
.aliases(["remove", "rm"])
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use email::account::config::DEFAULT_INBOX_FOLDER;
|
||||||
|
|
||||||
/// The folder name argument parser
|
/// The folder name argument parser
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
@ -7,3 +8,11 @@ pub struct FolderNameArg {
|
||||||
#[arg(name = "folder-name", value_name = "FOLDER")]
|
#[arg(name = "folder-name", value_name = "FOLDER")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)]
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
|
@ -1,241 +0,0 @@
|
||||||
//! Folder CLI module.
|
|
||||||
//!
|
|
||||||
//! This module provides subcommands, arguments and a command matcher
|
|
||||||
//! related to the folder domain.
|
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use clap::{self, Arg, ArgAction, ArgMatches, Command};
|
|
||||||
use log::{debug, info};
|
|
||||||
|
|
||||||
use crate::ui::table;
|
|
||||||
|
|
||||||
const ARG_ALL: &str = "all";
|
|
||||||
const ARG_EXCLUDE: &str = "exclude";
|
|
||||||
const ARG_INCLUDE: &str = "include";
|
|
||||||
const ARG_GLOBAL_SOURCE: &str = "global-source";
|
|
||||||
const ARG_SOURCE: &str = "source";
|
|
||||||
const ARG_TARGET: &str = "target";
|
|
||||||
const CMD_CREATE: &str = "create";
|
|
||||||
const CMD_DELETE: &str = "delete";
|
|
||||||
const CMD_EXPUNGE: &str = "expunge";
|
|
||||||
const CMD_FOLDER: &str = "folder";
|
|
||||||
const CMD_LIST: &str = "list";
|
|
||||||
|
|
||||||
/// Represents the folder commands.
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum Cmd {
|
|
||||||
Create,
|
|
||||||
List(table::args::MaxTableWidth),
|
|
||||||
Expunge,
|
|
||||||
Delete,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the folder command matcher.
|
|
||||||
pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
|
||||||
let cmd = if let Some(m) = m.subcommand_matches(CMD_FOLDER) {
|
|
||||||
if let Some(_) = m.subcommand_matches(CMD_EXPUNGE) {
|
|
||||||
info!("expunge folder subcommand matched");
|
|
||||||
Some(Cmd::Expunge)
|
|
||||||
} else if let Some(_) = m.subcommand_matches(CMD_CREATE) {
|
|
||||||
debug!("create folder command matched");
|
|
||||||
Some(Cmd::Create)
|
|
||||||
} else if let Some(m) = m.subcommand_matches(CMD_LIST) {
|
|
||||||
debug!("list folders command matched");
|
|
||||||
let max_table_width = table::args::parse_max_width(m);
|
|
||||||
Some(Cmd::List(max_table_width))
|
|
||||||
} else if let Some(_) = m.subcommand_matches(CMD_DELETE) {
|
|
||||||
debug!("delete folder command matched");
|
|
||||||
Some(Cmd::Delete)
|
|
||||||
} else {
|
|
||||||
info!("no folder subcommand matched, falling back to subcommand list");
|
|
||||||
Some(Cmd::List(None))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the folder subcommand.
|
|
||||||
pub fn subcmd() -> Command {
|
|
||||||
Command::new(CMD_FOLDER)
|
|
||||||
.about("Subcommand to manage folders")
|
|
||||||
.long_about("Subcommand to manage folders like list, expunge or delete")
|
|
||||||
.subcommands([
|
|
||||||
Command::new(CMD_EXPUNGE).about("Delete emails marked for deletion"),
|
|
||||||
Command::new(CMD_CREATE)
|
|
||||||
.aliases(["add", "new"])
|
|
||||||
.about("Create a new folder"),
|
|
||||||
Command::new(CMD_LIST)
|
|
||||||
.about("List folders")
|
|
||||||
.arg(table::args::max_width()),
|
|
||||||
Command::new(CMD_DELETE)
|
|
||||||
.aliases(["remove", "rm"])
|
|
||||||
.about("Delete a folder with all its emails"),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the source folder argument.
|
|
||||||
pub fn global_args() -> impl IntoIterator<Item = Arg> {
|
|
||||||
[Arg::new(ARG_GLOBAL_SOURCE)
|
|
||||||
.help("Override the default INBOX folder")
|
|
||||||
.long_help(
|
|
||||||
"Override the default INBOX folder.
|
|
||||||
|
|
||||||
The given folder will be used by default for all other commands (when
|
|
||||||
applicable).",
|
|
||||||
)
|
|
||||||
.long("folder")
|
|
||||||
.short('f')
|
|
||||||
.global(true)
|
|
||||||
.value_name("name")]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_global_source_arg(matches: &ArgMatches) -> Option<&str> {
|
|
||||||
matches
|
|
||||||
.get_one::<String>(ARG_GLOBAL_SOURCE)
|
|
||||||
.map(String::as_str)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn source_arg(help: &'static str) -> Arg {
|
|
||||||
Arg::new(ARG_SOURCE).help(help).value_name("name")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_source_arg(matches: &ArgMatches) -> Option<&str> {
|
|
||||||
matches.get_one::<String>(ARG_SOURCE).map(String::as_str)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the all folders argument.
|
|
||||||
pub fn all_arg(help: &'static str) -> Arg {
|
|
||||||
Arg::new(ARG_ALL)
|
|
||||||
.help(help)
|
|
||||||
.long("all-folders")
|
|
||||||
.alias("all")
|
|
||||||
.short('A')
|
|
||||||
.action(ArgAction::SetTrue)
|
|
||||||
.conflicts_with(ARG_SOURCE)
|
|
||||||
.conflicts_with(ARG_INCLUDE)
|
|
||||||
.conflicts_with(ARG_EXCLUDE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the folders to include argument.
|
|
||||||
pub fn include_arg(help: &'static str) -> Arg {
|
|
||||||
Arg::new(ARG_INCLUDE)
|
|
||||||
.help(help)
|
|
||||||
.long("include-folder")
|
|
||||||
.alias("only")
|
|
||||||
.short('F')
|
|
||||||
.value_name("FOLDER")
|
|
||||||
.num_args(1..)
|
|
||||||
.action(ArgAction::Append)
|
|
||||||
.conflicts_with(ARG_SOURCE)
|
|
||||||
.conflicts_with(ARG_ALL)
|
|
||||||
.conflicts_with(ARG_EXCLUDE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the folders to exclude argument.
|
|
||||||
pub fn exclude_arg(help: &'static str) -> Arg {
|
|
||||||
Arg::new(ARG_EXCLUDE)
|
|
||||||
.help(help)
|
|
||||||
.long("exclude-folder")
|
|
||||||
.alias("except")
|
|
||||||
.short('x')
|
|
||||||
.value_name("FOLDER")
|
|
||||||
.num_args(1..)
|
|
||||||
.action(ArgAction::Append)
|
|
||||||
.conflicts_with(ARG_SOURCE)
|
|
||||||
.conflicts_with(ARG_ALL)
|
|
||||||
.conflicts_with(ARG_INCLUDE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the folders to exclude argument parser.
|
|
||||||
pub fn parse_exclude_arg(m: &ArgMatches) -> HashSet<String> {
|
|
||||||
m.get_many::<String>(ARG_EXCLUDE)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.map(ToOwned::to_owned)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the target folder argument.
|
|
||||||
pub fn target_arg() -> Arg {
|
|
||||||
Arg::new(ARG_TARGET)
|
|
||||||
.help("Specifies the target folder")
|
|
||||||
.value_name("TARGET")
|
|
||||||
.required(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the target folder argument parser.
|
|
||||||
pub fn parse_target_arg(matches: &ArgMatches) -> &str {
|
|
||||||
matches.get_one::<String>(ARG_TARGET).unwrap().as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use clap::{error::ErrorKind, Command};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_should_match_cmds() {
|
|
||||||
let arg = Command::new("himalaya")
|
|
||||||
.subcommand(subcmd())
|
|
||||||
.get_matches_from(&["himalaya", "folders"]);
|
|
||||||
assert_eq!(Some(Cmd::List(None)), matches(&arg).unwrap());
|
|
||||||
|
|
||||||
let arg = Command::new("himalaya")
|
|
||||||
.subcommand(subcmd())
|
|
||||||
.get_matches_from(&["himalaya", "folders", "list", "--max-width", "20"]);
|
|
||||||
assert_eq!(Some(Cmd::List(Some(20))), matches(&arg).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_should_match_source_arg() {
|
|
||||||
macro_rules! get_matches_from {
|
|
||||||
($($arg:expr),*) => {
|
|
||||||
Command::new("himalaya")
|
|
||||||
.arg(source_arg())
|
|
||||||
.get_matches_from(&["himalaya", $($arg,)*])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let app = get_matches_from![];
|
|
||||||
assert_eq!(None, app.get_one::<String>(ARG_SOURCE).map(String::as_str));
|
|
||||||
|
|
||||||
let app = get_matches_from!["-f", "SOURCE"];
|
|
||||||
assert_eq!(
|
|
||||||
Some("SOURCE"),
|
|
||||||
app.get_one::<String>(ARG_SOURCE).map(String::as_str)
|
|
||||||
);
|
|
||||||
|
|
||||||
let app = get_matches_from!["--folder", "SOURCE"];
|
|
||||||
assert_eq!(
|
|
||||||
Some("SOURCE"),
|
|
||||||
app.get_one::<String>(ARG_SOURCE).map(String::as_str)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_should_match_target_arg() {
|
|
||||||
macro_rules! get_matches_from {
|
|
||||||
($($arg:expr),*) => {
|
|
||||||
Command::new("himalaya")
|
|
||||||
.arg(target_arg())
|
|
||||||
.try_get_matches_from_mut(&["himalaya", $($arg,)*])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let app = get_matches_from![];
|
|
||||||
assert_eq!(ErrorKind::MissingRequiredArgument, app.unwrap_err().kind());
|
|
||||||
|
|
||||||
let app = get_matches_from!["TARGET"];
|
|
||||||
assert_eq!(
|
|
||||||
Some("TARGET"),
|
|
||||||
app.unwrap()
|
|
||||||
.get_one::<String>(ARG_TARGET)
|
|
||||||
.map(String::as_str)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -49,3 +49,194 @@ impl FolderSubcommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use email::{
|
||||||
|
account::config::AccountConfig,
|
||||||
|
backend::Backend,
|
||||||
|
envelope::{Envelope, Envelopes},
|
||||||
|
flag::Flags,
|
||||||
|
folder::{Folder, Folders},
|
||||||
|
message::Messages,
|
||||||
|
};
|
||||||
|
use std::{any::Any, fmt::Debug, io};
|
||||||
|
use termcolor::ColorSpec;
|
||||||
|
|
||||||
|
use crate::printer::{Print, PrintTable, WriteColor};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_should_list_mboxes() {
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
struct StringWriter {
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for StringWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.content
|
||||||
|
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.content = String::default();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl termcolor::WriteColor for StringWriter {
|
||||||
|
fn supports_color(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
|
||||||
|
io::Result::Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> io::Result<()> {
|
||||||
|
io::Result::Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WriteColor for StringWriter {}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct PrinterServiceTest {
|
||||||
|
pub writer: StringWriter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Printer for PrinterServiceTest {
|
||||||
|
fn print_table<T: Debug + PrintTable + erased_serde::Serialize + ?Sized>(
|
||||||
|
&mut self,
|
||||||
|
data: Box<T>,
|
||||||
|
opts: PrintTableOpts,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
data.print_table(&mut self.writer, opts)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn print_log<T: Debug + Print>(&mut self, _data: T) -> anyhow::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn print<T: Debug + Print + serde::Serialize>(
|
||||||
|
&mut self,
|
||||||
|
_data: T,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn is_json(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestBackend;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Backend for TestBackend {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
async fn add_folder(&mut self, _: &str) -> email::Result<()> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
async fn list_folders(&mut self) -> email::Result<Folders> {
|
||||||
|
Ok(Folders::from_iter([
|
||||||
|
Folder {
|
||||||
|
name: "INBOX".into(),
|
||||||
|
desc: "desc".into(),
|
||||||
|
},
|
||||||
|
Folder {
|
||||||
|
name: "Sent".into(),
|
||||||
|
desc: "desc".into(),
|
||||||
|
},
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
async fn expunge_folder(&mut self, _: &str) -> email::Result<()> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
async fn purge_folder(&mut self, _: &str) -> email::Result<()> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
async fn delete_folder(&mut self, _: &str) -> email::Result<()> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
async fn get_envelope(&mut self, _: &str, _: &str) -> email::Result<Envelope> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
async fn list_envelopes(
|
||||||
|
&mut self,
|
||||||
|
_: &str,
|
||||||
|
_: usize,
|
||||||
|
_: usize,
|
||||||
|
) -> email::Result<Envelopes> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn search_envelopes(
|
||||||
|
&mut self,
|
||||||
|
_: &str,
|
||||||
|
_: &str,
|
||||||
|
_: &str,
|
||||||
|
_: usize,
|
||||||
|
_: usize,
|
||||||
|
) -> email::Result<Envelopes> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn add_email(&mut self, _: &str, _: &[u8], _: &Flags) -> email::Result<String> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn get_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<Messages> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn preview_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<Messages> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn copy_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn move_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn add_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> email::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn set_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> email::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
async fn remove_flags(
|
||||||
|
&mut self,
|
||||||
|
_: &str,
|
||||||
|
_: Vec<&str>,
|
||||||
|
_: &Flags,
|
||||||
|
) -> email::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let account_config = AccountConfig::default();
|
||||||
|
let mut printer = PrinterServiceTest::default();
|
||||||
|
let mut backend = TestBackend {};
|
||||||
|
|
||||||
|
assert!(list(&account_config, &mut printer, &mut backend, None)
|
||||||
|
.await
|
||||||
|
.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
concat![
|
||||||
|
"\n",
|
||||||
|
"NAME │DESC \n",
|
||||||
|
"INBOX │desc \n",
|
||||||
|
"Sent │desc \n",
|
||||||
|
"\n"
|
||||||
|
],
|
||||||
|
printer.writer.content
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,247 +0,0 @@
|
||||||
//! Folder handling module.
|
|
||||||
//!
|
|
||||||
//! This module gathers all folder actions triggered by the CLI.
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use dialoguer::Confirm;
|
|
||||||
use email::account::config::AccountConfig;
|
|
||||||
use std::process;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
backend::Backend,
|
|
||||||
printer::{PrintTableOpts, Printer},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::Folders;
|
|
||||||
|
|
||||||
pub async fn expunge<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
|
||||||
backend.expunge_folder(folder).await?;
|
|
||||||
printer.print(format!("Folder {folder} successfully expunged!"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn list<P: Printer>(
|
|
||||||
config: &AccountConfig,
|
|
||||||
printer: &mut P,
|
|
||||||
backend: &Backend,
|
|
||||||
max_width: Option<usize>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let folders: Folders = backend.list_folders().await?.into();
|
|
||||||
printer.print_table(
|
|
||||||
// TODO: remove Box
|
|
||||||
Box::new(folders),
|
|
||||||
PrintTableOpts {
|
|
||||||
format: &config.email_reading_format,
|
|
||||||
max_width,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
|
||||||
backend.add_folder(folder).await?;
|
|
||||||
printer.print("Folder successfully created!")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
|
||||||
if let Some(false) | None = Confirm::new()
|
|
||||||
.with_prompt(format!("Confirm deletion of folder {folder}?"))
|
|
||||||
.default(false)
|
|
||||||
.report(false)
|
|
||||||
.interact_opt()?
|
|
||||||
{
|
|
||||||
process::exit(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
backend.delete_folder(folder).await?;
|
|
||||||
printer.print("Folder successfully deleted!")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use email::{
|
|
||||||
account::config::AccountConfig,
|
|
||||||
backend::Backend,
|
|
||||||
envelope::{Envelope, Envelopes},
|
|
||||||
flag::Flags,
|
|
||||||
folder::{Folder, Folders},
|
|
||||||
message::Messages,
|
|
||||||
};
|
|
||||||
use std::{any::Any, fmt::Debug, io};
|
|
||||||
use termcolor::ColorSpec;
|
|
||||||
|
|
||||||
use crate::printer::{Print, PrintTable, WriteColor};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn it_should_list_mboxes() {
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
struct StringWriter {
|
|
||||||
content: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Write for StringWriter {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.content
|
|
||||||
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
self.content = String::default();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl termcolor::WriteColor for StringWriter {
|
|
||||||
fn supports_color(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
|
|
||||||
io::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) -> io::Result<()> {
|
|
||||||
io::Result::Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WriteColor for StringWriter {}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct PrinterServiceTest {
|
|
||||||
pub writer: StringWriter,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Printer for PrinterServiceTest {
|
|
||||||
fn print_table<T: Debug + PrintTable + erased_serde::Serialize + ?Sized>(
|
|
||||||
&mut self,
|
|
||||||
data: Box<T>,
|
|
||||||
opts: PrintTableOpts,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
data.print_table(&mut self.writer, opts)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn print_log<T: Debug + Print>(&mut self, _data: T) -> anyhow::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn print<T: Debug + Print + serde::Serialize>(
|
|
||||||
&mut self,
|
|
||||||
_data: T,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn is_json(&self) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestBackend;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Backend for TestBackend {
|
|
||||||
fn name(&self) -> String {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
async fn add_folder(&mut self, _: &str) -> email::Result<()> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
async fn list_folders(&mut self) -> email::Result<Folders> {
|
|
||||||
Ok(Folders::from_iter([
|
|
||||||
Folder {
|
|
||||||
name: "INBOX".into(),
|
|
||||||
desc: "desc".into(),
|
|
||||||
},
|
|
||||||
Folder {
|
|
||||||
name: "Sent".into(),
|
|
||||||
desc: "desc".into(),
|
|
||||||
},
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
async fn expunge_folder(&mut self, _: &str) -> email::Result<()> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
async fn purge_folder(&mut self, _: &str) -> email::Result<()> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
async fn delete_folder(&mut self, _: &str) -> email::Result<()> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
async fn get_envelope(&mut self, _: &str, _: &str) -> email::Result<Envelope> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
async fn list_envelopes(
|
|
||||||
&mut self,
|
|
||||||
_: &str,
|
|
||||||
_: usize,
|
|
||||||
_: usize,
|
|
||||||
) -> email::Result<Envelopes> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn search_envelopes(
|
|
||||||
&mut self,
|
|
||||||
_: &str,
|
|
||||||
_: &str,
|
|
||||||
_: &str,
|
|
||||||
_: usize,
|
|
||||||
_: usize,
|
|
||||||
) -> email::Result<Envelopes> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn add_email(&mut self, _: &str, _: &[u8], _: &Flags) -> email::Result<String> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn get_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<Messages> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn preview_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<Messages> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn copy_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> email::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn move_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> email::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn add_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> email::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn set_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> email::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
async fn remove_flags(
|
|
||||||
&mut self,
|
|
||||||
_: &str,
|
|
||||||
_: Vec<&str>,
|
|
||||||
_: &Flags,
|
|
||||||
) -> email::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let account_config = AccountConfig::default();
|
|
||||||
let mut printer = PrinterServiceTest::default();
|
|
||||||
let mut backend = TestBackend {};
|
|
||||||
|
|
||||||
assert!(list(&account_config, &mut printer, &mut backend, None)
|
|
||||||
.await
|
|
||||||
.is_ok());
|
|
||||||
assert_eq!(
|
|
||||||
concat![
|
|
||||||
"\n",
|
|
||||||
"NAME │DESC \n",
|
|
||||||
"INBOX │desc \n",
|
|
||||||
"Sent │desc \n",
|
|
||||||
"\n"
|
|
||||||
],
|
|
||||||
printer.writer.content
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,6 @@
|
||||||
pub mod arg;
|
pub mod arg;
|
||||||
pub mod args;
|
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod handlers;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
use clap::{Arg, ArgMatches};
|
|
||||||
|
|
||||||
const ARG_MAX_TABLE_WIDTH: &str = "max-table-width";
|
|
||||||
|
|
||||||
pub(crate) type MaxTableWidth = Option<usize>;
|
|
||||||
|
|
||||||
/// Represents the max table width argument.
|
|
||||||
pub fn max_width() -> Arg {
|
|
||||||
Arg::new(ARG_MAX_TABLE_WIDTH)
|
|
||||||
.help("Defines a maximum width for the table")
|
|
||||||
.long("max-width")
|
|
||||||
.short('w')
|
|
||||||
.value_name("INT")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the max table width argument parser.
|
|
||||||
pub fn parse_max_width(matches: &ArgMatches) -> Option<usize> {
|
|
||||||
matches
|
|
||||||
.get_one::<String>(ARG_MAX_TABLE_WIDTH)
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
pub mod arg;
|
pub mod arg;
|
||||||
pub mod args;
|
|
||||||
pub mod table;
|
pub mod table;
|
||||||
|
|
||||||
pub use table::*;
|
pub use table::*;
|
||||||
|
|
Loading…
Reference in a new issue