Skip to content

Commit

Permalink
Add support for custom help triggers
Browse files Browse the repository at this point in the history
  • Loading branch information
aminya authored and sadmac7000 committed Nov 13, 2023
1 parent 7c4d1e6 commit f1037ba
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Options:
-j, --jump whether or not to jump
--height how high to go
--pilot-nickname an optional nickname for the pilot
--help display usage information
--help, help display usage information
```

The resulting program can then be used in any of these ways:
Expand Down
14 changes: 9 additions & 5 deletions argh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
//! -j, --jump whether or not to jump
//! --height how high to go
//! --pilot-nickname an optional nickname for the pilot
//! --help display usage information
//! --help, help display usage information
//! ```
//!
//! The resulting program can then be used in any of these ways:
Expand All @@ -70,6 +70,7 @@
//!
//! #[derive(FromArgs)]
//! /// Reach new heights.
//! #[argh(help_triggers("-h", "--help", "help"))]
//! struct GoUp {
//! /// an optional nickname for the pilot
//! #[argh(option)]
Expand Down Expand Up @@ -421,7 +422,7 @@ pub trait FromArgs: Sized {
/// Command to manage a classroom.
///
/// Options:
/// --help display usage information
/// --help, help display usage information
///
/// Commands:
/// list list all the classes.
Expand All @@ -445,7 +446,7 @@ pub trait FromArgs: Sized {
///
/// Options:
/// --teacher-name list classes for only this teacher.
/// --help display usage information
/// --help, help display usage information
/// "#.to_string(),
/// status: Ok(()),
/// },
Expand Down Expand Up @@ -587,7 +588,7 @@ pub trait FromArgs: Sized {
/// Command to manage a classroom.
///
/// Options:
/// --help display usage information
/// --help, help display usage information
///
/// Commands:
/// list list all the classes.
Expand Down Expand Up @@ -912,7 +913,7 @@ pub fn parse_struct_args(

'parse_args: while let Some(&next_arg) = remaining_args.first() {
remaining_args = &remaining_args[1..];
if (next_arg == "--help" || next_arg == "help") && !options_ended {
if (parse_options.help_triggers.contains(&next_arg)) && !options_ended {
help = true;
continue;
}
Expand Down Expand Up @@ -959,6 +960,9 @@ pub struct ParseStructOptions<'a> {

/// The storage for argument output data.
pub slots: &'a mut [ParseStructOption<'a>],

/// help triggers is a list of strings that trigger printing of help
pub help_triggers: &'a [&'a str],
}

impl<'a> ParseStructOptions<'a> {
Expand Down
49 changes: 36 additions & 13 deletions argh/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,29 @@ fn custom_from_str_example() {
assert_eq!(f.five, 5);
}

#[test]
fn help_trigger_example() {
/// Height options
#[derive(FromArgs)]
#[argh(help_triggers("-h", "--help", "help"))]
struct Height {
/// how high to go
#[argh(option)]
_height: usize,
}

assert_help_string::<Height>(
r#"Usage: test_arg_0 --height <height>
Height options
Options:
--height how high to go
-h, --help, help display usage information
"#,
);
}

#[test]
fn nested_from_str_example() {
#[derive(FromArgs)]
Expand Down Expand Up @@ -298,7 +321,7 @@ Short description
Options:
--s a switch with a description that is spread across a number
of lines of comments.
--help display usage information
--help, help display usage information
"###,
);
}
Expand All @@ -322,7 +345,7 @@ A \description: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~\
Options:
--s a \description: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~\
--help display usage information
--help, help display usage information
"###,
);
}
Expand Down Expand Up @@ -459,7 +482,7 @@ Woot
Options:
-n, --n fooey
--help display usage information
--help, help display usage information
"###,
);
}
Expand All @@ -481,7 +504,7 @@ Woot
Options:
--option-name fooey
--help display usage information
--help, help display usage information
"###,
);
}
Expand Down Expand Up @@ -519,7 +542,7 @@ Positional Arguments:
b fooey
Options:
--help display usage information
--help, help display usage information
"###,
);
}
Expand Down Expand Up @@ -603,7 +626,7 @@ Positional Arguments:
Options:
--b woo
--c stuff
--help display usage information
--help, help display usage information
"###,
);
}
Expand Down Expand Up @@ -1012,7 +1035,7 @@ mod fuchsia_commandline_tools_rubric {
A type for testing `--help`/`help`
Options:
--help display usage information
--help, help display usage information
Commands:
first First subcommmand for testing `help`.
Expand All @@ -1023,7 +1046,7 @@ Commands:
First subcommmand for testing `help`.
Options:
--help display usage information
--help, help display usage information
Commands:
second Second subcommand for testing `help`.
Expand All @@ -1034,7 +1057,7 @@ Commands:
Second subcommand for testing `help`.
Options:
--help display usage information
--help, help display usage information
"###;

#[test]
Expand Down Expand Up @@ -1215,7 +1238,7 @@ Options:
documentation
-s, --scribble write <scribble> repeatedly
-v, --verbose say more. Defaults to $BLAST_VERBOSE.
--help display usage information
--help, help display usage information
Commands:
blow-up explosively separate
Expand Down Expand Up @@ -1255,7 +1278,7 @@ Positional Arguments:
name
Options:
--help display usage information
--help, help display usage information
"###,
);
}
Expand Down Expand Up @@ -1285,7 +1308,7 @@ Positional Arguments:
two this one is real
Options:
--help display usage information
--help, help display usage information
"###,
);
}
Expand Down Expand Up @@ -1648,7 +1671,7 @@ Woot
Options:
-n, --n fooey
--help display usage information
--help, help display usage information
"###
.to_owned(),
status: Ok(()),
Expand Down
9 changes: 7 additions & 2 deletions argh_derive/src/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub(crate) fn help(
ty_attrs: &TypeAttrs,
fields: &[StructField<'_>],
subcommand: Option<&StructField<'_>>,
help_triggers: &[String],
) -> TokenStream {
let mut format_lit = "Usage: {command_name}".to_string();

Expand Down Expand Up @@ -83,8 +84,12 @@ pub(crate) fn help(
for option in options {
option_description(errors, &mut format_lit, option);
}
// Also include "help"
option_description_format(&mut format_lit, None, "--help", "display usage information");
option_description_format(
&mut format_lit,
None,
&help_triggers.join(", "),
"display usage information",
);

let subcommand_calculation;
let subcommand_format_arg;
Expand Down
33 changes: 31 additions & 2 deletions argh_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,12 @@ fn impl_from_args_struct_from_args<'a>(
quote_spanned! { impl_span => None }
};

let help_triggers = get_help_triggers(type_attrs);

let help = if cfg!(feature = "help") {
// Identifier referring to a value containing the name of the current command as an `&[&str]`.
let cmd_name_str_array_ident = syn::Ident::new("__cmd_name", impl_span);
help::help(errors, cmd_name_str_array_ident, type_attrs, fields, subcommand)
help::help(errors, cmd_name_str_array_ident, type_attrs, fields, subcommand, &help_triggers)
} else {
quote! { String::new() }
};
Expand All @@ -392,6 +394,7 @@ fn impl_from_args_struct_from_args<'a>(
argh::ParseStructOptions {
arg_to_slot: &[ #( #flag_str_to_output_table_map ,)* ],
slots: &mut [ #( #flag_output_table, )* ],
help_triggers: &[ #( #help_triggers ),* ],
},
argh::ParseStructPositionals {
positionals: &mut [
Expand Down Expand Up @@ -424,6 +427,29 @@ fn impl_from_args_struct_from_args<'a>(
method_impl
}

/// get help triggers vector from type_attrs.help_triggers as a [`Vec<String>`]
///
/// Defaults to vec!["--help", "help"] if type_attrs.help_triggers is None
fn get_help_triggers(type_attrs: &TypeAttrs) -> Vec<String> {
let help_triggers = type_attrs.help_triggers.as_ref().map_or_else(
|| vec!["--help".to_owned(), "help".to_owned()],
|s| {
s.iter()
.filter_map(|s| {
let trigger = s.value();
let trigger_trimmed = trigger.trim().to_owned();
if trigger_trimmed.is_empty() {
None
} else {
Some(trigger_trimmed)
}
})
.collect::<Vec<_>>()
},
);
help_triggers
}

fn impl_from_args_struct_redact_arg_values<'a>(
errors: &Errors,
type_attrs: &TypeAttrs,
Expand Down Expand Up @@ -494,10 +520,12 @@ fn impl_from_args_struct_redact_arg_values<'a>(
quote! { "no subcommand name" }
};

let help_triggers = get_help_triggers(type_attrs);

let help = if cfg!(feature = "help") {
// Identifier referring to a value containing the name of the current command as an `&[&str]`.
let cmd_name_str_array_ident = syn::Ident::new("__cmd_name", impl_span);
help::help(errors, cmd_name_str_array_ident, type_attrs, fields, subcommand)
help::help(errors, cmd_name_str_array_ident, type_attrs, fields, subcommand, &help_triggers)
} else {
quote! { String::new() }
};
Expand All @@ -512,6 +540,7 @@ fn impl_from_args_struct_redact_arg_values<'a>(
argh::ParseStructOptions {
arg_to_slot: &[ #( #flag_str_to_output_table_map ,)* ],
slots: &mut [ #( #flag_output_table, )* ],
help_triggers: &[ #( #help_triggers ),* ],
},
argh::ParseStructPositionals {
positionals: &mut [
Expand Down
34 changes: 33 additions & 1 deletion argh_derive/src/parse_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

use syn::{parse::Parser, punctuated::Punctuated};

use {
crate::errors::Errors,
proc_macro2::Span,
Expand Down Expand Up @@ -271,6 +273,8 @@ pub struct TypeAttrs {
pub examples: Vec<syn::LitStr>,
pub notes: Vec<syn::LitStr>,
pub error_codes: Vec<(syn::LitInt, syn::LitStr)>,
/// Arguments that trigger printing of the help message
pub help_triggers: Option<Vec<syn::LitStr>>,
}

impl TypeAttrs {
Expand Down Expand Up @@ -317,6 +321,10 @@ impl TypeAttrs {
{
this.parse_attr_subcommand(errors, ident);
}
} else if name.is_ident("help_triggers") {
if let Some(m) = errors.expect_meta_list(&meta) {
Self::parse_help_triggers(m, errors, &mut this);
}
} else {
errors.err(
&meta,
Expand Down Expand Up @@ -405,6 +413,24 @@ impl TypeAttrs {
self.is_subcommand = Some(ident.clone());
}
}

// get the list of arguments that trigger printing of the help message as a vector of strings (help_arguments("-h", "--help", "help"))
fn parse_help_triggers(m: &syn::MetaList, errors: &Errors, this: &mut TypeAttrs) {
let parser = Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated;
match parser.parse(m.tokens.clone().into()) {
Ok(args) => {
let mut triggers = Vec::new();
for arg in args {
if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }) = arg {
triggers.push(lit_str);
}
}

this.help_triggers = Some(triggers);
}
Err(err) => errors.push(err),
}
}
}

/// Represents an enum variant's attributes.
Expand Down Expand Up @@ -604,7 +630,8 @@ fn parse_attr_description(errors: &Errors, m: &syn::MetaNameValue, slot: &mut Op
/// Checks that a `#![derive(FromArgs)]` enum has an `#[argh(subcommand)]`
/// attribute and that it does not have any other type-level `#[argh(...)]` attributes.
pub fn check_enum_type_attrs(errors: &Errors, type_attrs: &TypeAttrs, type_span: &Span) {
let TypeAttrs { is_subcommand, name, description, examples, notes, error_codes } = type_attrs;
let TypeAttrs { is_subcommand, name, description, examples, notes, error_codes, help_triggers } =
type_attrs;

// Ensure that `#[argh(subcommand)]` is present.
if is_subcommand.is_none() {
Expand Down Expand Up @@ -635,6 +662,11 @@ pub fn check_enum_type_attrs(errors: &Errors, type_attrs: &TypeAttrs, type_span:
if let Some(err_code) = error_codes.first() {
err_unused_enum_attr(errors, &err_code.0);
}
if let Some(triggers) = help_triggers {
if let Some(trigger) = triggers.first() {
err_unused_enum_attr(errors, trigger);
}
}
}

fn err_unused_enum_attr(errors: &Errors, location: &impl syn::spanned::Spanned) {
Expand Down

0 comments on commit f1037ba

Please sign in to comment.