Disable help triggers on subcommands

This commit is contained in:
topjohnwu
2025-11-03 10:22:41 -08:00
committed by John Wu
parent 1599bfc2c5
commit 020e23ea13
3 changed files with 43 additions and 39 deletions

View File

@@ -697,7 +697,7 @@ pub fn parse_struct_args(
'parse_args: while let Some(&next_arg) = remaining_args.first() { 'parse_args: while let Some(&next_arg) = remaining_args.first() {
remaining_args = &remaining_args[1..]; remaining_args = &remaining_args[1..];
if (parse_options.help_triggers().contains(&next_arg)) && !options_ended { if (parse_options.help_triggers.contains(&next_arg)) && !options_ended {
help = true; help = true;
continue; continue;
} }
@@ -765,7 +765,7 @@ impl<'a> ParseStructOptions<'a> {
.arg_to_slot .arg_to_slot
.iter() .iter()
.find_map(|&(name, pos)| if name == arg { Some(pos) } else { None }) .find_map(|&(name, pos)| if name == arg { Some(pos) } else { None })
.ok_or_else(|| unrecognized_argument(arg, self.arg_to_slot, self.help_triggers()))?; .ok_or_else(|| unrecognized_argument(arg, self.arg_to_slot, self.help_triggers))?;
match self.slots[pos] { match self.slots[pos] {
ParseStructOption::Flag(ref mut b) => b.set_flag(arg), ParseStructOption::Flag(ref mut b) => b.set_flag(arg),
@@ -791,14 +791,6 @@ impl<'a> ParseStructOptions<'a> {
Ok(()) Ok(())
} }
fn help_triggers(&self) -> &'a [&'a str] {
if self.help_triggers.is_empty() {
&["-h", "--help"]
} else {
self.help_triggers
}
}
} }
fn unrecognized_argument( fn unrecognized_argument(

View File

@@ -423,22 +423,27 @@ fn impl_from_args_struct_from_args<'a>(
/// ///
/// Defaults to vec!["-h", "--help"] if type_attrs.help_triggers is None /// Defaults to vec!["-h", "--help"] if type_attrs.help_triggers is None
fn get_help_triggers(type_attrs: &TypeAttrs) -> Vec<String> { fn get_help_triggers(type_attrs: &TypeAttrs) -> Vec<String> {
type_attrs if type_attrs.is_subcommand.is_some() {
.help_triggers // Subcommands should never have any help triggers
.as_ref() Vec::new()
.map_or_else(Vec::new, |s| { } else {
s.iter() type_attrs.help_triggers.as_ref().map_or_else(
.filter_map(|s| { || vec!["-h".to_string(), "--help".to_string()],
let trigger = s.value(); |s| {
let trigger_trimmed = trigger.trim().to_owned(); s.iter()
if trigger_trimmed.is_empty() { .filter_map(|s| {
None let trigger = s.value();
} else { let trigger_trimmed = trigger.trim().to_owned();
Some(trigger_trimmed) if trigger_trimmed.is_empty() {
} None
}) } else {
.collect::<Vec<_>>() Some(trigger_trimmed)
}) }
})
.collect::<Vec<_>>()
},
)
}
} }
/// Ensures that only trailing positional args are non-required. /// Ensures that only trailing positional args are non-required.

View File

@@ -306,54 +306,61 @@ impl TypeAttrs {
continue; continue;
} }
let ml = if let Some(ml) = argh_attr_to_meta_list(errors, attr) { let ml: Vec<syn::Meta> = if let Some(ml) = argh_attr_to_meta_list(errors, attr) {
ml ml.into_iter().collect()
} else { } else {
continue; continue;
}; };
for meta in ml { for meta in ml.iter() {
let name = meta.path(); let name = meta.path();
if name.is_ident("description") { if name.is_ident("description") {
if let Some(m) = errors.expect_meta_name_value(&meta) { if let Some(m) = errors.expect_meta_name_value(meta) {
parse_attr_description(errors, m, &mut this.description); parse_attr_description(errors, m, &mut this.description);
} }
} else if name.is_ident("error_code") { } else if name.is_ident("error_code") {
if let Some(m) = errors.expect_meta_list(&meta) { if let Some(m) = errors.expect_meta_list(meta) {
this.parse_attr_error_code(errors, m); this.parse_attr_error_code(errors, m);
} }
} else if name.is_ident("example") { } else if name.is_ident("example") {
if let Some(m) = errors.expect_meta_name_value(&meta) { if let Some(m) = errors.expect_meta_name_value(meta) {
this.parse_attr_example(errors, m); this.parse_attr_example(errors, m);
} }
} else if name.is_ident("name") { } else if name.is_ident("name") {
if let Some(m) = errors.expect_meta_name_value(&meta) { if let Some(m) = errors.expect_meta_name_value(meta) {
this.parse_attr_name(errors, m); this.parse_attr_name(errors, m);
} }
} else if name.is_ident("note") { } else if name.is_ident("note") {
if let Some(m) = errors.expect_meta_name_value(&meta) { if let Some(m) = errors.expect_meta_name_value(meta) {
this.parse_attr_note(errors, m); this.parse_attr_note(errors, m);
} }
} else if name.is_ident("subcommand") { } else if name.is_ident("subcommand") {
if let Some(ident) = errors.expect_meta_word(&meta).and_then(|p| p.get_ident()) if let Some(ident) = errors.expect_meta_word(meta).and_then(|p| p.get_ident()) {
{
this.parse_attr_subcommand(errors, ident); this.parse_attr_subcommand(errors, ident);
} }
} else if name.is_ident("help_triggers") { } else if name.is_ident("help_triggers") {
if let Some(m) = errors.expect_meta_list(&meta) { if let Some(m) = errors.expect_meta_list(meta) {
Self::parse_help_triggers(m, errors, &mut this); Self::parse_help_triggers(m, errors, &mut this);
} }
} else { } else {
errors.err( errors.err(
&meta, meta,
concat!( concat!(
"Invalid type-level `argh` attribute\n", "Invalid type-level `argh` attribute\n",
"Expected one of: `description`, `error_code`, `example`, `name`, ", "Expected one of: `description`, `error_code`, `example`, `name`, ",
"`note`, `subcommand`", "`note`, `subcommand`, `help_triggers`",
), ),
); );
} }
} }
if this.is_subcommand.is_some() && this.help_triggers.is_some() {
let help_meta = ml
.iter()
.find(|meta| meta.path().is_ident("help_triggers"))
.unwrap();
errors.err(help_meta, "Cannot use `help_triggers` on a subcommand");
}
} }
this.check_error_codes(errors); this.check_error_codes(errors);