From 817ed694eef94c9fe208efd2ccf59a13ea1e1b38 Mon Sep 17 00:00:00 2001 From: Colin Nelson Date: Fri, 6 Feb 2026 17:34:58 +0000 Subject: [PATCH] feat(help): add short to help output --- argh/tests/subcommand_short_help.rs | 73 +++++++++++++++++++++++++++++ argh_shared/src/lib.rs | 4 ++ 2 files changed, 77 insertions(+) create mode 100644 argh/tests/subcommand_short_help.rs diff --git a/argh/tests/subcommand_short_help.rs b/argh/tests/subcommand_short_help.rs new file mode 100644 index 0000000..39ab6d7 --- /dev/null +++ b/argh/tests/subcommand_short_help.rs @@ -0,0 +1,73 @@ +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")] +struct SubCommandTwo { + #[argh(switch)] + /// bar + bar: bool, +} + +#[test] +fn test_subcommand_short_help() { + let early_exit = TopLevel::from_args(&["cmd"], &["help"]).unwrap_err(); + let output = early_exit.output; + + // Check that 'one' has its short name 'o' + assert!(output.contains("one o"), "Help output should contain 'one o'"); + // Check that 'two' does NOT have a short name (it should just be 'two') + assert!(output.contains("two "), "Help output should contain 'two '"); + assert!(!output.contains("two t"), "Help output should NOT contain 'two t'"); + + let expected_part = " one o First subcommand. + two Second subcommand."; + assert!( + output.contains(expected_part), + "Help output did not match expected subcommand list formatting. Got: +{}", + output + ); +} + +#[test] +fn test_subcommand_short_help_own() { + // Invoke via full name + let early_exit_full = TopLevel::from_args(&["cmd"], &["one", "help"]).unwrap_err(); + assert!( + early_exit_full.output.contains("Usage: cmd one [--fooey]"), + "Usage should be 'cmd one ...'" + ); + + // Invoke via short name + let early_exit_short = TopLevel::from_args(&["cmd"], &["o", "help"]).unwrap_err(); + assert!( + early_exit_short.output.contains("Usage: cmd one [--fooey]"), + "Usage should still be 'cmd one ...' even if 'o' was used" + ); + + assert_eq!(early_exit_full.output, early_exit_short.output); +} diff --git a/argh_shared/src/lib.rs b/argh_shared/src/lib.rs index 4eb974f..f25f0a7 100644 --- a/argh_shared/src/lib.rs +++ b/argh_shared/src/lib.rs @@ -164,6 +164,10 @@ pub fn write_description(out: &mut String, cmd: &CommandInfo<'_>) { let mut current_line = INDENT.to_string(); current_line.push_str(cmd.name); + if *cmd.short != '\0' { + current_line.push_str(&format!(" {}", cmd.short)); + } + if cmd.description.is_empty() { new_line(&mut current_line, out); return;