使用clap编写自己的命令行
rust_snippets
clap::_features
https://docs.rs/clap/latest/clap/_features/index.html
- Default Features:
- std: Not Currently Used. Placeholder for supporting no_std environments in a backwards compatible manner.
- color: Turns on colored error messages.
- help: Auto-generate help output
- usage: Auto-generate usage
- error-context: Include contextual information for errors (which arg failed, etc)
-
suggestions: Turns on the Did you mean ‘–myoption’? feature for when users make typos.
- Optional features
- deprecated: Guided experience to prepare for next breaking release (at different stages of development, this may become default)
- derive: Enables the custom derive (i.e. #[derive(Parser)]). Without this you must use one of the other methods of creating a clap CLI listed above.
- cargo: Turns on macros that read values from CARGO_* environment variables.
- env: Turns on the usage of environment variables during parsing.
- unicode: Turns on support for unicode characters (including emoji) in arguments and help messages.
- wrap_help: Turns on the help text wrapping feature, based on the terminal size.
- string: Allow runtime generated strings (e.g. with Str).
clap::_derive::_tutorial ( Derive Tutorial )
https://docs.rs/clap/latest/clap/_derive/_tutorial/index.html
添加derive feature
cargo add clap --features derive
./target/debug/clap_basic.exe --help
配置Parser
#[derive(Parser)]
#[command(name = "MyApp")]
#[command(author = "Kevin K. <kbknapp@gmail.com>")]
#[command(version = "1.0")]
#[command(about = "Does awesome things", long_about = None)]
struct Cli {
#[arg(long)]
two: String,
#[arg(long)]
one: String,
}
Usage: 02_apps_derive[EXE] --two <TWO> --one <ONE>
Options:
--two <TWO>
--one <ONE>
-h, --help Print help information
-V, --version Print version information
#[derive(Parser)]
#[command(author, version, about, long_about = None)] // Read from `Cargo.toml`
struct Cli {
#[arg(long)]
two: String,
#[arg(long)]
one: String,
}
Usage: 02_crate_derive[EXE] --two <TWO> --one <ONE>
Options:
--two <TWO>
--one <ONE>
-h, --help Print help information
-V, --version Print version information
添加参数
上一节给出的方法,需要用户键入’–one –two’来指定某个参数的值
传统的命令行也有通过键入参数的顺序来获取参数的方式,即是省略了’–one –two’
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
// 注意: 这里没有 '#[arg(long)]'
name: Option<String>,
}
Usage: 03_03_positional_derive[EXE] [NAME]
Arguments:
[NAME]
参数的默认动作ArgAction是Set,要接受多值操作,使用Append
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
name: Vec<String>,
}
Usage: 03_03_positional_mult_derive[EXE] [NAME]...
Arguments:
[NAME]...
Options:
-h, --help Print help information
-V, --version Print version information
$ 03_03_positional_mult_derive
name: []
$ 03_03_positional_mult_derive bob
name: ["bob"]
Options
可以指定简写模式
#[arg(short = 'n')] and #[arg(long = "name")]
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[arg(short, long)]
name: Option<String>,
}
$ 03_02_option_derive
name: None
$ 03_02_option_derive --name bob
name: Some("bob")
$ 03_02_option_derive --name=bob
name: Some("bob")
$ 03_02_option_derive -n bob
name: Some("bob")
$ 03_02_option_derive -n=bob
name: Some("bob")
$ 03_02_option_derive -nbob
name: Some("bob")
Flags
标志也可以是可以开/关的开关。这是通过 #[arg(action = ArgAction::SetTrue)]
属性启用的
ArgAction默认是SetTrue。要接受多个标志,请使用 Count
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[arg(short, long, action = clap::ArgAction::Count)]
verbose: u8,
}
Usage: 03_01_flag_count_derive[EXE] [OPTIONS]
Options:
-v, --verbose...
-h, --help Print help information
-V, --version Print version information
$ 03_01_flag_count_derive
verbose: 0
$ 03_01_flag_count_derive --verbose
verbose: 1
$ 03_01_flag_count_derive --verbose --verbose
verbose: 2
子命令
子命令通过#[derive(Subcommand)] 派生并通过#[command(subcommand)] 属性添加。
- 方式一:
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Adds files to myapp
Add { name: Option<String> },
}
- 方式二:
use clap::{Args, Parser, Subcommand};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Adds files to myapp
Add(Add),
}
#[derive(Args)]
struct Add {
name: Option<String>,
}
fn main() {
let cli = Cli::parse();
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level cmd
match &cli.command {
Commands::Add(name) => {
println!("'myapp add' was used, name is: {:?}", name.name)
}
}
}
参数的默认值
use clap::Parser;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[arg(default_value_t = 2020)]
port: u16,
}
fn main() {
let cli = Cli::parse();
println!("port: {:?}", cli.port);
}
Usage: 03_05_default_values_derive[EXE] [PORT]
Arguments:
[PORT] [default: 2020]
Options:
-h, --help Print help information
-V, --version Print version information
$ 03_05_default_values_derive
port: 2020
参数的Validation
https://docs.rs/clap/latest/clap/macro.value_parser.html
枚举值
如果您有要测试的特定值的参数,则可以派生 ValueEnum。 允许您指定该参数的有效值
use clap::{Parser, ValueEnum};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// What mode to run the program in
#[arg(value_enum)]
mode: Mode,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Mode {
/// Run swiftly
Fast,
/// Crawl slowly but steadily
///
/// This paragraph is ignored because there is no long help text for possible values.
Slow,
}
$ 04_01_enum_derive fast
Hare
$ 04_01_enum_derive slow
Tortoise
$ 04_01_enum_derive medium
? failed
error: 'medium' isn't a valid value for '<MODE>'
[possible values: fast, slow]
验证值的有效性
验证并解析为任何数据类型
use clap::Parser;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Network port to use
#[arg(value_parser = clap::value_parser!(u16).range(1..))]
port: u16,
}
$ 04_02_parse_derive 22
PORT = 22
$ 04_02_parse_derive foobar
? failed
error: Invalid value 'foobar' for '<PORT>': invalid digit found in string
For more information try '--help'
$ 04_02_parse_derive 0
? failed
error: Invalid value '0' for '<PORT>': 0 is not in 1..=65535
自定义Parse验证有效性函数
use std::ops::RangeInclusive;
use clap::Parser;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Network port to use
#[arg(value_parser = port_in_range)] // port_in_range 就是自定义验证函数
port: u16,
}
fn main() {
let cli = Cli::parse();
println!("PORT = {}", cli.port);
}
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
// 自定义验证函数
fn port_in_range(s: &str) -> Result<u16, String> {
let port: usize = s
.parse()
.map_err(|_| format!("`{}` isn't a port number", s))?;
if PORT_RANGE.contains(&port) {
Ok(port as u16)
} else {
Err(format!(
"Port not in range {}-{}",
PORT_RANGE.start(),
PORT_RANGE.end()
))
}
}
验证参数间的关系
可以声明 Arg 甚至 ArgGroup 之间的依赖关系或冲突 假设有多个参数,并且您希望其中一个是必需的,但是将所有参数都设为必需,是不可行的,因为它们可能相互冲突
ArgGroups : https://docs.rs/clap/latest/clap/builder/struct.ArgGroup.html 最常见的用途是要求一个且只有一个参数出现在给定的参数集合之中。
use clap::{ArgGroup, Parser};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(group(
ArgGroup::new("vers") // derive种设置group
.required(true)
.args(["set_ver", "major", "minor", "patch"]), // Option: 四个参数只能出现一个
))]
struct Cli {
/// set version manually
#[arg(long, value_name = "VER")]
set_ver: Option<String>,
/// auto inc major
#[arg(long)]
major: bool,
/// auto inc minor
#[arg(long)]
minor: bool,
/// auto inc patch
#[arg(long)]
patch: bool,
/// some regular input
#[arg(group = "input")] //'input' group 参数
input_file: Option<String>,
/// some special input argument
#[arg(long, group = "input")] //'input' group 参数
spec_in: Option<String>,
#[arg(short, requires = "input")] // 'requires' 指定当该组存在时必须存在的参数或组
config: Option<String>,
}
$ ./target/debug/clap_basic.exe --set-ver 2.1.1
Version: 2.1.1
$ ./target/debug/clap_basic.exe --set-ver 2.1.1 --major 5
error: The argument '--set-ver <VER>' cannot be used with '--major'
$ ./target/debug/clap_basic.exe --major
Version: 2.2.3
$ ./target/debug/clap_basic.exe --major input
Version: 2.2.3
$ ./target/debug/clap_basic.exe input
error: The following required arguments were not provided:
<--set-ver <VER>|--major|--minor|--patch>
$ ./target/debug/clap_basic.exe --major input -c config.toml --spec-in input.txt
error: The argument '[INPUT_FILE]' cannot be used with '--spec-in <SPEC_IN>'
$ ./target/debug/clap_basic.exe --major input -c config.toml
Version: 2.2.3
Doing work using input input and config config.toml
$ ./target/debug/clap_basic.exe --major input --spec-in input.txt
error: The argument '[INPUT_FILE]' cannot be used with '--spec-in <SPEC_IN>'
$ ./target/debug/clap_basic.exe --major -c config.toml --spec-in input.txt
Version: 2.2.3
Doing work using input input.txt and config config.toml
$ ./target/debug/clap_basic.exe --major --spec-in input.txt
Version: 2.2.3
$ ./target/debug/clap_basic.exe --major -c config.toml
error: The following required arguments were not provided:
<INPUT_FILE|--spec-in <SPEC_IN>>
$ ./target/debug/clap_basic.exe --major input -c config.toml
Version: 2.2.3
Doing work using input input and config config.toml
用户自定义验证
https://docs.rs/clap/latest/clap/_derive/_tutorial/index.html#custom-validation
测试
clap 将大多数开发错误报告为 debug_assert!s。您应该有一个调用 Command::debug_assert 的测试,而不是检查每个子命令
use clap::Parser;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Network port to use
port: u16,
}
fn main() {
let cli = Cli::parse();
println!("PORT = {}", cli.port);
}
#[test]
fn verify_cli() {
use clap::CommandFactory;
Cli::command().debug_assert()
}
clap::_tutorial ( Builder Tutorial )
https://docs.rs/clap/latest/clap/_tutorial/index.html
可以使用使用字符串创建具有多个参数的应用程序。
添加cargo feature
cargo add clap --features derive
cargo build
快速开始
use std::path::PathBuf;
use clap::{arg, command, value_parser, ArgAction, Command};
pub fn builder_clap_test() {
let matches = command!() // requires `cargo` feature
// 位置参数
.arg(arg!([name] "Optional name to operate on"))
// --option参数
.arg(
arg!(
-c --config <FILE> "Sets a custom config file"
)
// We don't have syntax yet for optional options, so manually calling `required`
// https://docs.rs/clap/latest/clap/builder/struct.Arg.html#method.required
// 指定参数是否必须存, 默认是必须的
.required(false)
.value_parser(value_parser!(PathBuf)),
)
.arg(arg!(
-d --debug ... "Turn debugging information on"
))
.subcommand(
Command::new("test")
.about("does testing things")
.arg(arg!(-l --list "lists test values").action(ArgAction::SetTrue)),
// https://docs.rs/clap/latest/clap/builder/enum.ArgAction.html
// 解析时遇到参数时的行为
// Set,
// Append,
// SetTrue,
// SetFalse,
// Count,
// Help,
// Version,
)
.get_matches();
// You can check the value provided by positional arguments, or option arguments
if let Some(name) = matches.get_one::<String>("name") { // get_one : Gets the value of a specific option or positional argument.
println!("Value for name: {}", name);
}
if let Some(config_path) = matches.get_one::<PathBuf>("config") {
println!("Value for config: {}", config_path.display());
}
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match matches
.get_one::<u8>("debug")
.expect("Count's are defaulted")
{
0 => println!("Debug mode is off"),
1 => println!("Debug mode is kind of on"),
2 => println!("Debug mode is on"),
_ => println!("Don't be crazy"),
}
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level cmd
if let Some(matches) = matches.subcommand_matches("test") {
// "$ myapp test" was run
if *matches.get_one::<bool>("list").expect("defaulted by clap") {
// "$ myapp test -l" was run
println!("Printing testing lists...");
} else {
println!("Not printing testing lists...");
}
}
// Continued program logic goes here...
}
配置Parser
使用 Command 开始构建解析器
use clap::{arg, Command};
fn main() {
let matches = Command::new("MyApp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.arg(arg!(--two <VALUE>).required(true))
.arg(arg!(--one <VALUE>).required(true))
.get_matches();
// or 从 Cargo.toml 获取这些信息
// let matches = command!()
// .arg(arg!(--two <VALUE>).required(true))
// .arg(arg!(--one <VALUE>).required(true))
// .get_matches();
println!(
"two: {:?}",
matches.get_one::<String>("two").expect("required")
);
println!(
"one: {:?}",
matches.get_one::<String>("one").expect("required")
);
}
添加参数
顺序位置类参数
use clap::{command, Arg};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(Arg::new("name"))
.get_matches();
println!("name: {:?}", matches.get_one::<String>("name"));
}
注意: 默认的 ArgAction 是 [Set][crate::ArgAction::Set] 如果需要接受多个参数 使用 ArgAction::Append
se clap::{command, Arg, ArgAction};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(Arg::new("name").action(ArgAction::Append))
.get_matches();
println!("name: {:?}", matches.get_one::<String>("name"));
}
Options类参数
use clap::{command, Arg};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(Arg::new("name").short('n').long("name"))
.get_matches();
println!("name: {:?}", matches.get_one::<String>("name"));
}
// 也可以使用 宏 arg!
// arg!(-c --config <FILE> "Sets a custom config file")
Flags
标志也可以是可以打开/关闭的开关
use clap::{command, Arg, ArgAction};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue), // Flags
)
.get_matches();
println!("verbose: {:?}", matches.get_flag("verbose"));
}
$ 03_01_flag_bool
verbose: false
$ 03_01_flag_bool --verbose
verbose: true
$ 03_01_flag_bool --verbose --verbose
? failed
error: The argument '--verbose' was provided more than once, but cannot be used multiple times
要接受多个标志,请使用 Count
let matches = command!() // requires `cargo` feature
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::Count),
)
.get_matches();
$ 03_01_flag_count
verbose: 0
$ 03_01_flag_count --verbose
verbose: 1
$ 03_01_flag_count --verbose --verbose
verbose: 2
子命令
通过 Command::subcommand 添加的命令
use clap::{arg, command, Command};
fn main() {
let matches = command!() // requires `cargo` feature
.propagate_version(true) // 指定对所有子命令使用当前命令的版本。
.subcommand_required(true) // 如果运行时不存在子命令,则出错并正常退出。
.arg_required_else_help(true) // 如果不存在参数,则退出
.subcommand(
Command::new("add")
.about("Adds files to myapp")
.arg(arg!([NAME])),
)
.get_matches();
match matches.subcommand() {
Some(("add", sub_matches)) => println!(
"'myapp add' was used, name is: {:?}",
sub_matches.get_one::<String>("NAME")
),
_ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"),
}
}
$ 03_04_subcommands add bob
'myapp add' was used, name is: Some("bob")
默认值
use clap::{arg, command, value_parser};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
arg!([PORT])
.value_parser(value_parser!(u16))
.default_value("2020"),
)
.get_matches();
println!(
"port: {:?}",
matches
.get_one::<u16>("PORT")
.expect("default ensures there is always a value")
);
}
$ 03_05_default_values
port: 2020
$ 03_05_default_values 22
port: 22
Validation
如果想要限定参数的内容,可以使用枚举参数值
use clap::{arg, command};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
arg!(<MODE>)
.help("What mode to run the program in")
.value_parser(["fast", "slow"]), // 枚举参数值
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
match matches
.get_one::<String>("MODE")
.expect("'MODE' is required and parsing will fail if its missing")
.as_str()
{
"fast" => {
println!("Hare");
}
"slow" => {
println!("Tortoise");
}
_ => unreachable!(),
}
}
$ 04_01_possible fast
Hare
$ 04_01_possible slow
Tortoise
$ 04_01_possible medium
? failed
error: 'medium' isn't a valid value for '<MODE>'
[possible values: fast, slow]
如果启动deriver feature可以使用ValueEnum实现同样的功能
use clap::{arg, builder::PossibleValue, command, value_parser, ValueEnum};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Mode {
Fast,
Slow,
}
// Can also be derived with feature flag `derive`
impl ValueEnum for Mode {
fn value_variants<'a>() -> &'a [Self] {
&[Mode::Fast, Mode::Slow]
}
fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
Some(match self {
Mode::Fast => PossibleValue::new("fast").help("Run swiftly"),
Mode::Slow => PossibleValue::new("slow").help("Crawl slowly but steadily"),
})
}
}
impl std::fmt::Display for Mode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}
impl std::str::FromStr for Mode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
for variant in Self::value_variants() {
if variant.to_possible_value().unwrap().matches(s, false) {
return Ok(*variant);
}
}
Err(format!("Invalid variant: {}", s))
}
}
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
arg!(<MODE>)
.help("What mode to run the program in")
.value_parser(value_parser!(Mode)),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
match matches
.get_one::<Mode>("MODE")
.expect("'MODE' is required and parsing will fail if its missing")
{
Mode::Fast => {
println!("Hare");
}
Mode::Slow => {
println!("Tortoise");
}
}
}
验证参数值范围
use clap::{arg, command, value_parser};
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
arg!(<PORT>)
.help("Network port to use")
.value_parser(value_parser!(u16).range(1..)),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: u16 = *matches
.get_one::<u16>("PORT")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}
自定义验证函数
fn main() {
let matches = command!() // requires `cargo` feature
.arg(
arg!(<PORT>)
.help("Network port to use")
.value_parser(port_in_range),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: u16 = *matches
.get_one::<u16>("PORT")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
fn port_in_range(s: &str) -> Result<u16, String> {
let port: usize = s
.parse()
.map_err(|_| format!("`{}` isn't a port number", s))?;
if PORT_RANGE.contains(&port) {
Ok(port as u16)
} else {
Err(format!(
"Port not in range {}-{}",
PORT_RANGE.start(),
PORT_RANGE.end()
))
}
}
参数的相互关系
ArgGroups 使声明关系变得更容易,而不必单独列出每个关系,或者当您希望规则应用“任何但不是全部”参数时
use std::path::PathBuf;
use clap::{arg, command, value_parser, ArgAction, ArgGroup};
fn main() {
// Create application like normal
let matches = command!() // requires `cargo` feature
// Add the version arguments
.arg(arg!(--"set-ver" <VER> "set version manually"))
.arg(arg!(--major "auto inc major").action(ArgAction::SetTrue))
.arg(arg!(--minor "auto inc minor").action(ArgAction::SetTrue))
.arg(arg!(--patch "auto inc patch").action(ArgAction::SetTrue))
// Create a group, make it required, and add the above arguments
.group(
ArgGroup::new("vers")
.required(true)
.args(["set-ver", "major", "minor", "patch"]),
)
// Arguments can also be added to a group individually, these two arguments
// are part of the "input" group which is not required
.arg(
arg!([INPUT_FILE] "some regular input")
.value_parser(value_parser!(PathBuf))
.group("input"),
)
.arg(
arg!(--"spec-in" <SPEC_IN> "some special input argument")
.value_parser(value_parser!(PathBuf))
.group("input"),
)
// Now let's assume we have a -c [config] argument which requires one of
// (but **not** both) the "input" arguments
.arg(
arg!(config: -c <CONFIG>)
.value_parser(value_parser!(PathBuf))
.requires("input"),
)
.get_matches();
// Let's assume the old version 1.2.3
let mut major = 1;
let mut minor = 2;
let mut patch = 3;
// See if --set-ver was used to set the version manually
let version = if let Some(ver) = matches.get_one::<String>("set-ver") {
ver.to_owned()
} else {
// Increment the one requested (in a real program, we'd reset the lower numbers)
let (maj, min, pat) = (
matches.get_flag("major"),
matches.get_flag("minor"),
matches.get_flag("patch"),
);
match (maj, min, pat) {
(true, _, _) => major += 1,
(_, true, _) => minor += 1,
(_, _, true) => patch += 1,
_ => unreachable!(),
};
format!("{}.{}.{}", major, minor, patch)
};
println!("Version: {}", version);
// Check for usage of -c
if matches.contains_id("config") {
let input = matches
.get_one::<PathBuf>("INPUT_FILE")
.unwrap_or_else(|| matches.get_one::<PathBuf>("spec-in").unwrap())
.display();
println!(
"Doing work using input {} and config {}",
input,
matches.get_one::<PathBuf>("config").unwrap().display()
);
}
}
自定义验证
https://docs.rs/clap/latest/clap/_tutorial/index.html#custom-validation
测试
clap 将大多数开发错误报告为 debug_assert!s。 应该有一个调用 Command::debug_assert 的测试,而不是检查每个子命令
use clap::{arg, command, value_parser};
fn main() {
let matches = cmd().get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: usize = *matches
.get_one::<usize>("PORT")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}
fn cmd() -> clap::Command {
command!() // requires `cargo` feature
.arg(
arg!(<PORT>)
.help("Network port to use")
.value_parser(value_parser!(usize)),
)
}
#[test]
fn verify_cmd() {
cmd().debug_assert();
}