Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion argh/examples/simple_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ enum MySubCommandEnum {

#[derive(FromArgs, PartialEq, Debug)]
/// First subcommand.
#[argh(subcommand, name = "one")]
#[argh(subcommand, name = "one", short = 'o')]
struct SubCommandOne {
#[argh(option)]
/// how many x
Expand Down
5 changes: 4 additions & 1 deletion argh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@
//! // don't know about until runtime!
//! commands.push(&*Box::leak(Box::new(CommandInfo {
//! name: "dynamic_command",
//! short: &'d',
//! description: "A dynamic command",
//! })));
//!
Expand Down Expand Up @@ -1152,7 +1153,9 @@ impl ParseStructSubCommand<'_> {
remaining_args: &[&str],
) -> Result<bool, EarlyExit> {
for subcommand in self.subcommands.iter().chain(self.dynamic_subcommands.iter()) {
if subcommand.name == arg {
if subcommand.name == arg
|| arg.chars().count() == 1 && arg.chars().next().unwrap() == *subcommand.short
{
let mut command = cmd_name.to_owned();
command.push(subcommand.name);
let prepended_help;
Expand Down
30 changes: 27 additions & 3 deletions argh/tests/args_info_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ fn args_info_test_subcommand() {

let command_one = CommandInfoWithArgs {
name: "one",
short: &'\0',
description: "First subcommand.",
flags: &[
HELP_FLAG,
Expand All @@ -76,6 +77,7 @@ fn args_info_test_subcommand() {

assert_args_info::<TopLevel>(&CommandInfoWithArgs {
name: "TopLevel",
short: &'\0',
description: "Top-level command.",
examples: &[],
flags: &[HELP_FLAG],
Expand All @@ -88,6 +90,7 @@ fn args_info_test_subcommand() {
name: "two",
command: CommandInfoWithArgs {
name: "two",
short: &'\0',
description: "Second subcommand.",
flags: &[
HELP_FLAG,
Expand Down Expand Up @@ -124,6 +127,7 @@ fn args_info_test_multiline_doc_comment() {
assert_args_info::<Cmd>(
&CommandInfoWithArgs {
name: "Cmd",
short: &'\0',
description: "Short description",
flags: &[HELP_FLAG,
FlagInfo {
Expand Down Expand Up @@ -163,6 +167,7 @@ fn args_info_test_basic_args() {
}
assert_args_info::<Basic>(&CommandInfoWithArgs {
name: "Basic",
short: &'\0',
description:
"Basic command args demonstrating multiple types and cardinality. \"With quotes\"",
flags: &[
Expand Down Expand Up @@ -231,6 +236,7 @@ fn args_info_test_positional_args() {
}
assert_args_info::<Positional>(&CommandInfoWithArgs {
name: "Positional",
short: &'\0',
description: "Command with positional args demonstrating. \"With quotes\"",
flags: &[HELP_FLAG],
positionals: &[
Expand Down Expand Up @@ -278,6 +284,7 @@ fn args_info_test_optional_positional_args() {
}
assert_args_info::<Positional>(&CommandInfoWithArgs {
name: "Positional",
short: &'\0',
description: "Command with positional args demonstrating last value is optional",
flags: &[FlagInfo {
kind: FlagInfoKind::Switch,
Expand Down Expand Up @@ -332,6 +339,7 @@ fn args_info_test_default_positional_args() {
}
assert_args_info::<Positional>(&CommandInfoWithArgs {
name: "Positional",
short: &'\0',
description: "Command with positional args demonstrating last value is defaulted.",
flags: &[HELP_FLAG],
positionals: &[
Expand Down Expand Up @@ -393,6 +401,7 @@ fn args_info_test_notes_examples_errors() {
assert_args_info::<NotesExamplesErrors>(
&CommandInfoWithArgs {
name: "NotesExamplesErrors",
short: &'\0',
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
Expand Down Expand Up @@ -481,6 +490,7 @@ fn args_info_test_subcommands() {

assert_args_info::<TopLevel>(&CommandInfoWithArgs {
name: "TopLevel",
short: &'\0',
description: "Top level command with \"subcommands\".",
flags: &[
HELP_FLAG,
Expand All @@ -499,6 +509,7 @@ fn args_info_test_subcommands() {
name: "one",
command: CommandInfoWithArgs {
name: "one",
short: &'\0',
description: "Command1 args are used for Command1.",
flags: &[HELP_FLAG],
positionals: &[
Expand Down Expand Up @@ -528,6 +539,7 @@ fn args_info_test_subcommands() {
name: "two",
command: CommandInfoWithArgs {
name: "two",
short: &'\0',
description: "Command2 args are used for Command2.",
flags: &[
HELP_FLAG,
Expand Down Expand Up @@ -573,6 +585,7 @@ fn args_info_test_subcommands() {
name: "three",
command: CommandInfoWithArgs {
name: "three",
short: &'\0',
description:
"Command3 args are used for Command3 which has no options or arguments.",
flags: &[HELP_FLAG],
Expand Down Expand Up @@ -635,6 +648,7 @@ fn args_info_test_subcommand_notes_examples() {

let command_one = CommandInfoWithArgs {
name: "one",
short: &'\0',
description: "Command1 args are used for subcommand one.",
error_codes: &[ErrorCodeInfo { code: 0, description: "one level success" }],
examples: &["\"Typical\" usage is `{command_name}`."],
Expand Down Expand Up @@ -665,6 +679,7 @@ fn args_info_test_subcommand_notes_examples() {

assert_args_info::<TopLevel>(&CommandInfoWithArgs {
name: "TopLevel",
short: &'\0',
description: "Top level command with \"subcommands\".",
error_codes: &[ErrorCodeInfo { code: 0, description: "Top level success" }],
examples: &["Top level example"],
Expand Down Expand Up @@ -735,6 +750,7 @@ fn args_info_test_example() {
assert_args_info::<HelpExample>(
&CommandInfoWithArgs {
name: "HelpExample",
short: &'\0',
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,
Expand All @@ -752,6 +768,7 @@ fn args_info_test_example() {
commands: vec![
SubCommandInfo { name: "blow-up",
command: CommandInfoWithArgs { name: "blow-up",
short: &'\0',
description: "explosively separate",
flags:& [HELP_FLAG,
FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--safely", short: None, description: "blow up bombs safely",
Expand Down Expand Up @@ -796,6 +813,7 @@ fn positional_greedy() {
}
assert_args_info::<LastRepeatingGreedy>(&CommandInfoWithArgs {
name: "LastRepeatingGreedy",
short: &'\0',
description: "Woot",
flags: &[
HELP_FLAG,
Expand Down Expand Up @@ -892,9 +910,9 @@ fn test_dynamic_subcommand() {
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" },
&argh::CommandInfo { name: "three", short: &'\0', description: "Third command" },
&argh::CommandInfo { name: "four", short: &'\0', description: "Fourth command" },
&argh::CommandInfo { name: "five", short: &'\0', description: "Fifth command" },
]
}

Expand Down Expand Up @@ -960,13 +978,15 @@ fn test_dynamic_subcommand() {

assert_args_info::<TopLevel>(&CommandInfoWithArgs {
name: "TopLevel",
short: &'\0',
description: "Top-level command.",
flags: &[HELP_FLAG],
commands: vec![
SubCommandInfo {
name: "one",
command: CommandInfoWithArgs {
name: "one",
short: &'\0',
description: "First subcommand.",
flags: &[
HELP_FLAG,
Expand All @@ -986,6 +1006,7 @@ fn test_dynamic_subcommand() {
name: "two",
command: CommandInfoWithArgs {
name: "two",
short: &'\0',
description: "Second subcommand.",
flags: &[
HELP_FLAG,
Expand All @@ -1005,6 +1026,7 @@ fn test_dynamic_subcommand() {
name: "three",
command: CommandInfoWithArgs {
name: "three",
short: &'\0',
description: "Third command",
..Default::default()
},
Expand All @@ -1013,6 +1035,7 @@ fn test_dynamic_subcommand() {
name: "four",
command: CommandInfoWithArgs {
name: "four",
short: &'\0',
description: "Fourth command",
..Default::default()
},
Expand All @@ -1021,6 +1044,7 @@ fn test_dynamic_subcommand() {
name: "five",
command: CommandInfoWithArgs {
name: "five",
short: &'\0',
description: "Fifth command",
..Default::default()
},
Expand Down
13 changes: 9 additions & 4 deletions argh/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ fn dynamic_subcommand_example() {
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" },
&argh::CommandInfo { name: "three", short: &'\0', description: "Third command" },
&argh::CommandInfo { name: "four", short: &'\0', description: "Fourth command" },
&argh::CommandInfo { name: "five", short: &'\0', description: "Fifth command" },
]
}

Expand Down Expand Up @@ -1280,7 +1280,11 @@ Options:

impl argh::DynamicSubCommand for HelpExamplePlugin {
fn commands() -> &'static [&'static argh::CommandInfo] {
&[&argh::CommandInfo { name: "plugin", description: "Example dynamic command" }]
&[&argh::CommandInfo {
name: "plugin",
short: &'\0',
description: "Example dynamic command",
}]
}

fn try_redact_arg_values(
Expand Down Expand Up @@ -1848,6 +1852,7 @@ fn subcommand_does_not_panic() {
#[argh(subcommand)]
enum SubCommandEnum {
Cmd(SubCommand),
CmdTwo(SubCommandTwo),
}

#[derive(FromArgs, PartialEq, Debug)]
Expand Down
69 changes: 69 additions & 0 deletions argh/tests/short_alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use argh::FromArgs;

#[derive(FromArgs, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
#[argh(subcommand)]
nested: MySubCommandEnum,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum MySubCommandEnum {
One(SubCommandOne),
Two(SubCommandTwo),
}

#[derive(FromArgs, PartialEq, Debug)]
/// First subcommand.
#[argh(subcommand, name = "one", short = 'o')]
struct SubCommandOne {
#[argh(switch)]
/// fooey
fooey: bool,
}

#[derive(FromArgs, PartialEq, Debug)]
/// Second subcommand.
#[argh(subcommand, name = "two")]
// No short alias for this one
struct SubCommandTwo {
#[argh(switch)]
/// bar
bar: bool,
}

#[test]
fn test_short_alias_dispatch() {
let expected = TopLevel { nested: MySubCommandEnum::One(SubCommandOne { fooey: true }) };

// Test with full name "one"
let actual = TopLevel::from_args(&["cmd"], &["one", "--fooey"]).expect("failed parsing 'one'");
assert_eq!(actual, expected);

// Test with short alias "o"
let actual_short =
TopLevel::from_args(&["cmd"], &["o", "--fooey"]).expect("failed parsing 'o'");
assert_eq!(actual_short, expected);
}

#[test]
fn test_short_alias_redaction() {
// Verify that redaction also works with short aliases
let args = vec!["o", "--fooey"];
let redacted = TopLevel::redact_arg_values(&["cmd"], &args).expect("redaction failed");
// Since it's a switch, it might be kept or redacted depending on impl, but we check matching.
// Redaction usually returns the args with values redacted. For switch there are no values.
// We mainly want to ensure it doesn't error with "no subcommand matched".
assert!(!redacted.is_empty());
}

#[test]
fn test_no_short_alias_for_two() {
// "two" has no short alias, so "t" should fail (unless "t" is prefix matching? argh doesn't do prefix matching for subcommands by default I think, but let's see)
// Actually argh requires exact match for subcommands unless strict is disabled?
// Let's assume strict.

let res = TopLevel::from_args(&["cmd"], &["t", "--bar"]);
assert!(res.is_err());
}
4 changes: 2 additions & 2 deletions argh/tests/ui/duplicate-name/duplicate-long-name.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ error: The long name of "--foo" was already used here.
error: Later usage here.
--> tests/ui/duplicate-name/duplicate-long-name.rs:8:5
|
8 | / /// foo2
9 | | #[argh(option, long = "foo")]
8 | / /// foo2
9 | | #[argh(option, long = "foo")]
10 | | foo2: u32,
| |_____________^

Expand Down
4 changes: 2 additions & 2 deletions argh/tests/ui/duplicate-name/duplicate-short-name.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ error: The short name of "-f" was already used here.
error: Later usage here.
--> tests/ui/duplicate-name/duplicate-short-name.rs:8:5
|
8 | / /// foo2
9 | | #[argh(option, short = 'f')]
8 | / /// foo2
9 | | #[argh(option, short = 'f')]
10 | | foo2: u32,
| |_____________^

Expand Down
Loading