blob: 1fc073004deacbfdc2e697c9a6ffd15e9084cddc [file] [log] [blame]
#![cfg(test)]
// Copyright (c) 2023 Google LLC All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
use argh::{
ArgsInfo, CommandInfoWithArgs, ErrorCodeInfo, FlagInfo, FlagInfoKind, FromArgs, Optionality,
PositionalInfo, SubCommandInfo,
};
fn assert_args_info<T: ArgsInfo>(expected: &CommandInfoWithArgs) {
let actual_value = T::get_args_info();
assert_eq!(expected, &actual_value)
}
const HELP_FLAG: FlagInfo<'_> = FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--help",
short: None,
description: "display usage information",
hidden: false,
};
/// Tests that exercise the JSON output for help text.
#[test]
fn args_info_test_subcommand() {
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
#[argh(subcommand)]
nested: MySubCommandEnum,
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
#[argh(subcommand)]
enum MySubCommandEnum {
One(SubCommandOne),
Two(SubCommandTwo),
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
/// First subcommand.
#[argh(subcommand, name = "one")]
struct SubCommandOne {
#[argh(option)]
/// how many x
x: usize,
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
/// Second subcommand.
#[argh(subcommand, name = "two")]
struct SubCommandTwo {
#[argh(switch)]
/// whether to fooey
fooey: bool,
}
let command_one = CommandInfoWithArgs {
name: "one",
description: "First subcommand.",
flags: &[
HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "x" },
optionality: Optionality::Required,
long: "--x",
short: None,
description: "how many x",
hidden: false,
},
],
..Default::default()
};
assert_args_info::<TopLevel>(&CommandInfoWithArgs {
name: "TopLevel",
description: "Top-level command.",
examples: &[],
flags: &[HELP_FLAG],
notes: &[],
positionals: &[],
error_codes: &[],
commands: vec![
SubCommandInfo { name: "one", command: command_one.clone() },
SubCommandInfo {
name: "two",
command: CommandInfoWithArgs {
name: "two",
description: "Second subcommand.",
flags: &[
HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--fooey",
short: None,
description: "whether to fooey",
hidden: false,
},
],
..Default::default()
},
},
],
});
assert_args_info::<SubCommandOne>(&command_one);
}
#[test]
fn args_info_test_multiline_doc_comment() {
#[derive(FromArgs, ArgsInfo)]
/// Short description
struct Cmd {
#[argh(switch)]
/// a switch with a description
/// that is spread across
/// a number of
/// lines of comments.
_s: bool,
}
assert_args_info::<Cmd>(
&CommandInfoWithArgs {
name: "Cmd",
description: "Short description",
flags: &[HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--s",
short: None,
description: "a switch with a description that is spread across a number of lines of comments.",
hidden:false
}
],
..Default::default()
});
}
#[test]
fn args_info_test_basic_args() {
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
/// Basic command args demonstrating multiple types and cardinality. "With quotes"
struct Basic {
/// should the power be on. "Quoted value" should work too.
#[argh(switch)]
power: bool,
/// option that is required because of no default and not Option<>.
#[argh(option, long = "required")]
required_flag: String,
/// optional speed if not specified it is None.
#[argh(option, short = 's')]
speed: Option<u8>,
/// repeatable option.
#[argh(option, arg_name = "url")]
link: Vec<String>,
}
assert_args_info::<Basic>(&CommandInfoWithArgs {
name: "Basic",
description:
"Basic command args demonstrating multiple types and cardinality. \"With quotes\"",
flags: &[
FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--help",
short: None,
description: "display usage information",
hidden: false,
},
FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--power",
short: None,
description: "should the power be on. \"Quoted value\" should work too.",
hidden: false,
},
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "required" },
optionality: Optionality::Required,
long: "--required",
short: None,
description: "option that is required because of no default and not Option<>.",
hidden: false,
},
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "speed" },
optionality: Optionality::Optional,
long: "--speed",
short: Some('s'),
description: "optional speed if not specified it is None.",
hidden: false,
},
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "url" },
optionality: Optionality::Repeating,
long: "--link",
short: None,
description: "repeatable option.",
hidden: false,
},
],
..Default::default()
});
}
#[test]
fn args_info_test_positional_args() {
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
/// Command with positional args demonstrating. "With quotes"
struct Positional {
/// the "root" position.
#[argh(positional, arg_name = "root")]
root_value: String,
/// trunk value
#[argh(positional)]
trunk: String,
/// leaves. There can be many leaves.
#[argh(positional)]
leaves: Vec<String>,
}
assert_args_info::<Positional>(&CommandInfoWithArgs {
name: "Positional",
description: "Command with positional args demonstrating. \"With quotes\"",
flags: &[HELP_FLAG],
positionals: &[
PositionalInfo {
name: "root",
description: "the \"root\" position.",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "trunk",
description: "trunk value",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "leaves",
description: "leaves. There can be many leaves.",
optionality: Optionality::Repeating,
hidden: false,
},
],
..Default::default()
});
}
#[test]
fn args_info_test_optional_positional_args() {
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
/// Command with positional args demonstrating last value is optional
struct Positional {
/// the "root" position.
#[argh(positional, arg_name = "root")]
root_value: String,
/// trunk value
#[argh(positional)]
trunk: String,
/// leaves. There can be an optional leaves.
#[argh(positional)]
leaves: Option<String>,
}
assert_args_info::<Positional>(&CommandInfoWithArgs {
name: "Positional",
description: "Command with positional args demonstrating last value is optional",
flags: &[FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--help",
short: None,
description: "display usage information",
hidden: false,
}],
positionals: &[
PositionalInfo {
name: "root",
description: "the \"root\" position.",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "trunk",
description: "trunk value",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "leaves",
description: "leaves. There can be an optional leaves.",
optionality: Optionality::Optional,
hidden: false,
},
],
..Default::default()
});
}
#[test]
fn args_info_test_default_positional_args() {
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
/// Command with positional args demonstrating last value is defaulted.
struct Positional {
/// the "root" position.
#[argh(positional, arg_name = "root")]
root_value: String,
/// trunk value
#[argh(positional)]
trunk: String,
/// leaves. There can be one leaf, defaults to hello.
#[argh(positional, default = "String::from(\"hello\")")]
leaves: String,
}
assert_args_info::<Positional>(&CommandInfoWithArgs {
name: "Positional",
description: "Command with positional args demonstrating last value is defaulted.",
flags: &[HELP_FLAG],
positionals: &[
PositionalInfo {
name: "root",
description: "the \"root\" position.",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "trunk",
description: "trunk value",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "leaves",
description: "leaves. There can be one leaf, defaults to hello.",
optionality: Optionality::Optional,
hidden: false,
},
],
..Default::default()
});
}
#[test]
fn args_info_test_notes_examples_errors() {
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
/// Command with Examples and usage Notes, including error codes.
#[argh(
note = r##"
These usage notes appear for {command_name} and how to best use it.
The formatting should be preserved.
one
two
three then a blank
and one last line with "quoted text"."##,
example = r##"
Use the command with 1 file:
`{command_name} /path/to/file`
Use it with a "wildcard":
`{command_name} /path/to/*`
a blank line
and one last line with "quoted text"."##,
error_code(0, "Success"),
error_code(1, "General Error"),
error_code(2, "Some error with \"quotes\"")
)]
struct NotesExamplesErrors {
/// the "root" position.
#[argh(positional, arg_name = "files")]
fields: Vec<std::path::PathBuf>,
}
assert_args_info::<NotesExamplesErrors>(
&CommandInfoWithArgs {
name: "NotesExamplesErrors",
description: "Command with Examples and usage Notes, including error codes.",
examples: &["\n Use the command with 1 file:\n `{command_name} /path/to/file`\n Use it with a \"wildcard\":\n `{command_name} /path/to/*`\n a blank line\n \n and one last line with \"quoted text\"."],
flags: &[HELP_FLAG
],
positionals: &[
PositionalInfo{
name: "files",
description: "the \"root\" position.",
optionality: Optionality::Repeating,
hidden:false
}
],
notes: &["\n These usage notes appear for {command_name} and how to best use it.\n The formatting should be preserved.\n one\n two\n three then a blank\n \n and one last line with \"quoted text\"."],
error_codes: & [ErrorCodeInfo { code: 0, description: "Success" }, ErrorCodeInfo { code: 1, description: "General Error" }, ErrorCodeInfo { code: 2, description: "Some error with \"quotes\"" }],
..Default::default()
});
}
#[test]
fn args_info_test_subcommands() {
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
///Top level command with "subcommands".
struct TopLevel {
/// show verbose output
#[argh(switch)]
verbose: bool,
/// this doc comment does not appear anywhere.
#[argh(subcommand)]
cmd: SubcommandEnum,
}
#[derive(FromArgs, ArgsInfo)]
#[argh(subcommand)]
/// Doc comments for subcommand enums does not appear in the help text.
enum SubcommandEnum {
Command1(Command1Args),
Command2(Command2Args),
Command3(Command3Args),
}
/// Command1 args are used for Command1.
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
#[argh(subcommand, name = "one")]
struct Command1Args {
/// the "root" position.
#[argh(positional, arg_name = "root")]
root_value: String,
/// trunk value
#[argh(positional)]
trunk: String,
/// leaves. There can be zero leaves, defaults to hello.
#[argh(positional, default = "String::from(\"hello\")")]
leaves: String,
}
/// Command2 args are used for Command2.
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
#[argh(subcommand, name = "two")]
struct Command2Args {
/// should the power be on. "Quoted value" should work too.
#[argh(switch)]
power: bool,
/// option that is required because of no default and not Option<>.
#[argh(option, long = "required")]
required_flag: String,
/// optional speed if not specified it is None.
#[argh(option, short = 's')]
speed: Option<u8>,
/// repeatable option.
#[argh(option, arg_name = "url")]
link: Vec<String>,
}
/// Command3 args are used for Command3 which has no options or arguments.
#[derive(FromArgs, ArgsInfo)]
#[argh(subcommand, name = "three")]
struct Command3Args {}
assert_args_info::<TopLevel>(&CommandInfoWithArgs {
name: "TopLevel",
description: "Top level command with \"subcommands\".",
flags: &[
HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--verbose",
short: None,
description: "show verbose output",
hidden: false,
},
],
positionals: &[],
commands: vec![
SubCommandInfo {
name: "one",
command: CommandInfoWithArgs {
name: "one",
description: "Command1 args are used for Command1.",
flags: &[HELP_FLAG],
positionals: &[
PositionalInfo {
name: "root",
description: "the \"root\" position.",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "trunk",
description: "trunk value",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "leaves",
description: "leaves. There can be zero leaves, defaults to hello.",
optionality: Optionality::Optional,
hidden: false,
},
],
..Default::default()
},
},
SubCommandInfo {
name: "two",
command: CommandInfoWithArgs {
name: "two",
description: "Command2 args are used for Command2.",
flags: &[
HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--power",
short: None,
description:
"should the power be on. \"Quoted value\" should work too.",
hidden: false,
},
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "required" },
optionality: Optionality::Required,
long: "--required",
short: None,
description:
"option that is required because of no default and not Option<>.",
hidden: false,
},
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "speed" },
optionality: Optionality::Optional,
long: "--speed",
short: Some('s'),
description: "optional speed if not specified it is None.",
hidden: false,
},
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "url" },
optionality: Optionality::Repeating,
long: "--link",
short: None,
description: "repeatable option.",
hidden: false,
},
],
..Default::default()
},
},
SubCommandInfo {
name: "three",
command: CommandInfoWithArgs {
name: "three",
description:
"Command3 args are used for Command3 which has no options or arguments.",
flags: &[HELP_FLAG],
positionals: &[],
..Default::default()
},
},
],
..Default::default()
});
}
#[test]
fn args_info_test_subcommand_notes_examples() {
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
///Top level command with "subcommands".
#[argh(
note = "Top level note",
example = "Top level example",
error_code(0, "Top level success")
)]
struct TopLevel {
/// this doc comment does not appear anywhere.
#[argh(subcommand)]
cmd: SubcommandEnum,
}
#[derive(FromArgs, ArgsInfo)]
#[argh(subcommand)]
/// Doc comments for subcommand enums does not appear in the help text.
enum SubcommandEnum {
Command1(Command1Args),
}
/// Command1 args are used for subcommand one.
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
#[argh(
subcommand,
name = "one",
note = "{command_name} is used as a subcommand of \"Top level\"",
example = "\"Typical\" usage is `{command_name}`.",
error_code(0, "one level success")
)]
struct Command1Args {
/// the "root" position.
#[argh(positional, arg_name = "root")]
root_value: String,
/// trunk value
#[argh(positional)]
trunk: String,
/// leaves. There can be many leaves.
#[argh(positional)]
leaves: Vec<String>,
}
let command_one = CommandInfoWithArgs {
name: "one",
description: "Command1 args are used for subcommand one.",
error_codes: &[ErrorCodeInfo { code: 0, description: "one level success" }],
examples: &["\"Typical\" usage is `{command_name}`."],
flags: &[HELP_FLAG],
notes: &["{command_name} is used as a subcommand of \"Top level\""],
positionals: &[
PositionalInfo {
name: "root",
description: "the \"root\" position.",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "trunk",
description: "trunk value",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "leaves",
description: "leaves. There can be many leaves.",
optionality: Optionality::Repeating,
hidden: false,
},
],
..Default::default()
};
assert_args_info::<TopLevel>(&CommandInfoWithArgs {
name: "TopLevel",
description: "Top level command with \"subcommands\".",
error_codes: &[ErrorCodeInfo { code: 0, description: "Top level success" }],
examples: &["Top level example"],
flags: &[HELP_FLAG],
notes: &["Top level note"],
commands: vec![SubCommandInfo { name: "one", command: command_one.clone() }],
..Default::default()
});
assert_args_info::<Command1Args>(&command_one);
}
#[test]
fn args_info_test_example() {
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
#[argh(
description = "Destroy the contents of <file> with a specific \"method of destruction\".",
example = "Scribble 'abc' and then run |grind|.\n$ {command_name} -s 'abc' grind old.txt taxes.cp",
note = "Use `{command_name} help <command>` for details on [<args>] for a subcommand.",
error_code(2, "The blade is too dull."),
error_code(3, "Out of fuel.")
)]
struct HelpExample {
/// force, ignore minor errors. This description is so long that it wraps to the next line.
#[argh(switch, short = 'f')]
force: bool,
/// documentation
#[argh(switch)]
really_really_really_long_name_for_pat: bool,
/// write <scribble> repeatedly
#[argh(option, short = 's')]
scribble: String,
/// say more. Defaults to $BLAST_VERBOSE.
#[argh(switch, short = 'v')]
verbose: bool,
#[argh(subcommand)]
command: HelpExampleSubCommands,
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
#[argh(subcommand)]
enum HelpExampleSubCommands {
BlowUp(BlowUp),
Grind(GrindCommand),
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
#[argh(subcommand, name = "blow-up")]
/// explosively separate
struct BlowUp {
/// blow up bombs safely
#[argh(switch)]
safely: bool,
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
#[argh(subcommand, name = "grind", description = "make smaller by many small cuts")]
struct GrindCommand {
/// wear a visor while grinding
#[argh(switch)]
safely: bool,
}
assert_args_info::<HelpExample>(
&CommandInfoWithArgs {
name: "HelpExample",
description: "Destroy the contents of <file> with a specific \"method of destruction\".",
examples: &["Scribble 'abc' and then run |grind|.\n$ {command_name} -s 'abc' grind old.txt taxes.cp"],
flags: &[HELP_FLAG,
FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--force", short: Some('f'), description: "force, ignore minor errors. This description is so long that it wraps to the next line.",
hidden:false },
FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--really-really-really-long-name-for-pat", short: None, description: "documentation",
hidden:false },
FlagInfo { kind: FlagInfoKind::Option { arg_name: "scribble"},
optionality: Optionality::Required, long: "--scribble", short: Some('s'), description: "write <scribble> repeatedly",
hidden:false },
FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--verbose", short: Some('v'), description: "say more. Defaults to $BLAST_VERBOSE.",
hidden:false }
],
notes: &["Use `{command_name} help <command>` for details on [<args>] for a subcommand."],
commands: vec![
SubCommandInfo { name: "blow-up",
command: CommandInfoWithArgs { name: "blow-up",
description: "explosively separate",
flags:& [HELP_FLAG,
FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--safely", short: None, description: "blow up bombs safely",
hidden:false }
],
..Default::default()
} },
SubCommandInfo {
name: "grind",
command: CommandInfoWithArgs {
name: "grind",
description: "make smaller by many small cuts",
flags: &[HELP_FLAG,
FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--safely", short: None, description: "wear a visor while grinding" ,hidden:false}],
..Default::default()
}
}],
error_codes: &[ErrorCodeInfo { code: 2, description: "The blade is too dull." }, ErrorCodeInfo { code: 3, description: "Out of fuel." }],
..Default::default()
}
);
}
#[test]
fn positional_greedy() {
#[allow(dead_code)]
#[derive(FromArgs, ArgsInfo)]
/// Woot
struct LastRepeatingGreedy {
#[argh(positional)]
/// fooey
pub a: u32,
#[argh(switch)]
/// woo
pub b: bool,
#[argh(option)]
/// stuff
pub c: Option<String>,
#[argh(positional, greedy)]
/// fooey
pub d: Vec<String>,
}
assert_args_info::<LastRepeatingGreedy>(&CommandInfoWithArgs {
name: "LastRepeatingGreedy",
description: "Woot",
flags: &[
HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--b",
short: None,
description: "woo",
hidden: false,
},
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "c" },
optionality: Optionality::Optional,
long: "--c",
short: None,
description: "stuff",
hidden: false,
},
],
positionals: &[
PositionalInfo {
name: "a",
description: "fooey",
optionality: Optionality::Required,
hidden: false,
},
PositionalInfo {
name: "d",
description: "fooey",
optionality: Optionality::Greedy,
hidden: false,
},
],
..Default::default()
});
}
#[test]
fn hidden_help_attribute() {
#[derive(FromArgs, ArgsInfo)]
/// Short description
struct Cmd {
/// this one should be hidden
#[argh(positional, hidden_help)]
_one: String,
#[argh(positional)]
/// this one is real
_two: String,
/// this one should be hidden
#[argh(option, hidden_help)]
_three: String,
}
assert_args_info::<Cmd>(&CommandInfoWithArgs {
name: "Cmd",
description: "Short description",
flags: &[
HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "three" },
optionality: Optionality::Required,
long: "--three",
short: None,
description: "this one should be hidden",
hidden: true,
},
],
positionals: &[
PositionalInfo {
name: "one",
description: "this one should be hidden",
optionality: Optionality::Required,
hidden: true,
},
PositionalInfo {
name: "two",
description: "this one is real",
optionality: Optionality::Required,
hidden: false,
},
],
..Default::default()
});
}
#[test]
fn test_dynamic_subcommand() {
#[derive(PartialEq, Debug)]
struct DynamicSubCommandImpl {
got: String,
}
impl argh::DynamicSubCommand for DynamicSubCommandImpl {
fn commands() -> &'static [&'static argh::CommandInfo] {
&[
&argh::CommandInfo { name: "three", description: "Third command" },
&argh::CommandInfo { name: "four", description: "Fourth command" },
&argh::CommandInfo { name: "five", description: "Fifth command" },
]
}
fn try_redact_arg_values(
_command_name: &[&str],
_args: &[&str],
) -> Option<Result<Vec<String>, argh::EarlyExit>> {
Some(Err(argh::EarlyExit::from("Test should not redact".to_owned())))
}
fn try_from_args(
command_name: &[&str],
args: &[&str],
) -> Option<Result<DynamicSubCommandImpl, argh::EarlyExit>> {
let command_name = match command_name.last() {
Some(x) => *x,
None => return Some(Err(argh::EarlyExit::from("No command".to_owned()))),
};
let description = Self::commands().iter().find(|x| x.name == command_name)?.description;
if args.len() > 1 {
Some(Err(argh::EarlyExit::from("Too many arguments".to_owned())))
} else if let Some(arg) = args.first() {
Some(Ok(DynamicSubCommandImpl { got: format!("{} got {:?}", description, arg) }))
} else {
Some(Err(argh::EarlyExit::from("Not enough arguments".to_owned())))
}
}
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
#[argh(subcommand)]
nested: MySubCommandEnum,
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
#[argh(subcommand)]
enum MySubCommandEnum {
One(SubCommandOne),
Two(SubCommandTwo),
#[argh(dynamic)]
ThreeFourFive(DynamicSubCommandImpl),
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
/// First subcommand.
#[argh(subcommand, name = "one")]
struct SubCommandOne {
#[argh(option)]
/// how many x
x: usize,
}
#[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
/// Second subcommand.
#[argh(subcommand, name = "two")]
struct SubCommandTwo {
#[argh(switch)]
/// whether to fooey
fooey: bool,
}
assert_args_info::<TopLevel>(&CommandInfoWithArgs {
name: "TopLevel",
description: "Top-level command.",
flags: &[HELP_FLAG],
commands: vec![
SubCommandInfo {
name: "one",
command: CommandInfoWithArgs {
name: "one",
description: "First subcommand.",
flags: &[
HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Option { arg_name: "x" },
optionality: Optionality::Required,
long: "--x",
short: None,
description: "how many x",
hidden: false,
},
],
..Default::default()
},
},
SubCommandInfo {
name: "two",
command: CommandInfoWithArgs {
name: "two",
description: "Second subcommand.",
flags: &[
HELP_FLAG,
FlagInfo {
kind: FlagInfoKind::Switch,
optionality: Optionality::Optional,
long: "--fooey",
short: None,
description: "whether to fooey",
hidden: false,
},
],
..Default::default()
},
},
SubCommandInfo {
name: "three",
command: CommandInfoWithArgs {
name: "three",
description: "Third command",
..Default::default()
},
},
SubCommandInfo {
name: "four",
command: CommandInfoWithArgs {
name: "four",
description: "Fourth command",
..Default::default()
},
},
SubCommandInfo {
name: "five",
command: CommandInfoWithArgs {
name: "five",
description: "Fifth command",
..Default::default()
},
},
],
..Default::default()
})
}