Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context-Aware Multiple Usage from Argument Groups #4191

Open
2 tasks done
andersonjwan opened this issue Sep 7, 2022 · 3 comments
Open
2 tasks done

Context-Aware Multiple Usage from Argument Groups #4191

andersonjwan opened this issue Sep 7, 2022 · 3 comments
Labels
A-help Area: documentation, including docs.rs, readme, examples, etc... C-enhancement Category: Raise on the bar on expectations S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing

Comments

@andersonjwan
Copy link

andersonjwan commented Sep 7, 2022

Please complete the following tasks

Clap Version

3.2.20

Describe your use case

Currently, the only method, as far as I am aware, to generate a USAGE section with more than two usage statements from a set of grouped positional arguments is to explicitly pass a string to clap::builder::Command::override_usage.

However, opting-in for this solution disables the useful context-aware capabilities that the library has to offer. For example, if a new set of positional arguments are added, the resulting USAGE section does not reflect these changes without explicitly adjusting the call to override_usage.

To provide a small working example, a small demonstration of the grep tool usage without overriding the usage section would be as follows:

fn main() {
    Command::new("jrep")
        .arg(
            Arg::new("BASIC")
                .required(true)
                .value_name("PATTERNS")
        )
        .arg(
            Arg::new("EXTENDED")
                .required(true)
                .long("regexp")
                .short('e')
                .value_name("PATTERNS"),
        )
        .arg(
            Arg::new("PATTERN FILE")
                .required(true),
                .long("file")
                .short('f')
                .takes_value(true),
        )
        .group(
            ArgGroup::new("pattern-input")
                .arg("BASIC")
                .arg("EXTENDED")
                .arg("PATTERN FILE")
                .required(true),
        )
        .arg(
            Arg::new("FILE")
                .required(true)
                .takes_value(true)
                .multiple_values(true),
        )
        .arg(
            Arg::new("count")
                .long("count")
                .short('c')
                .action(ArgAction::SetTrue),
        )
        .get_matches();
}

This results in the following USAGE section:

USAGE:
    jrep [OPTIONS] <PATTERNS|--regexp <PATTERNS>|--file <PATTERN FILE>> <FILE>...

However, the desirable output, only acquirable by overriding the usage string and losing context-aware generation is:

USAGE:
    jrep [OPTIONS] <PATTERN> <FILE>
    jrep [OPTIONS] --regexp <PATTERNS> <FILE>
    jrep [OPTIONS] --file <PATTERN FILE> <FILE>

Describe the solution you'd like

As a result, grouped arguments (especially positional), should be given the option to generate multiple USAGE statements for every possible combination (i.e., perform the cartesian product over each ArgGroup).

The interface for this option could look as follows:

fn main() {
    Command::new("jrep")
        .display_all_possible_usages(true)
        ...
}

Which, would generate the following USAGE section (assuming the same arguments as defined in the previous section):

USAGE:
    jrep [OPTIONS] <PATTERN> <FILE>
    jrep [OPTIONS] --regexp <PATTERNS> <FILE>
    jrep [OPTIONS] --file <PATTERN FILE> <FILE>

Alternatives, if applicable

The only alternative to this is to use the clap::build::Command::override_usage which, as stated above, inhibits the context-aware generation of the USAGE section.

Additional Context

From my own search, there are only two instances where multiple usage statements are discussed:

  1. Not obvious how to provide multiple usage statements with App::override_usage() #3413

The issue above only discusses making the USAGE string more intuitive to write and does not discuss the matter of deriving a USAGE section from the context-aware ArgGroups.

  1. Is it possible to have multiple usage lines? #2103

This discussion is over two years old. However, I would argue that the use-case and need for the feature are still relevant and possibly more feasible with the more recent versions.

@andersonjwan andersonjwan added the C-enhancement Category: Raise on the bar on expectations label Sep 7, 2022
@epage epage added A-help Area: documentation, including docs.rs, readme, examples, etc... S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing labels Sep 7, 2022
@epage
Copy link
Member

epage commented Sep 7, 2022

As a result, grouped arguments (especially positional), should be given the option to generate multiple USAGE statements for every possible combination (i.e., perform the cartesian product over each ArgGroup).

Rather than applying a cartesian product across every ArgGroup, I think it'd work better if instead we had group.multiple_usage(true) (ie you opt-in on a per-group basis. We'll need to make sure that we render the arg in its dedicated line rather than being elided in [OPTIONS] (if its optional).

We'll also want to look at several clap users today that use groups to verify any assumptions of how well this would work and if any tweaks might be needed.

A challenge for this will be balancing a usability improvement like this with our efforts to reduce compile time (#2037) and binary size (#1365) which are top complaints of clap today.

Currently, the only method, as far as I am aware, to generate a USAGE section with more than two usage statements from a set of grouped positional arguments is to explicitly pass a string to clap::builder::Command::override_usage.

clap will automatically generate two usage statements if args_conflicts_with_subcommands is used (example).

@andersonjwan
Copy link
Author

Rather than applying a cartesian product across every ArgGroup, I think it'd work better if instead we had group.multiple_usage(true) (ie you opt-in on a per-group basis. We'll need to make sure that we render the arg in its dedicated line rather than being elided in [OPTIONS] (if its optional).

Agreed. Having per-ArgGroup control over generating additional usage statements is a more concise and minimal assumption-based approach for this feature.

I would say, it may be possible to be even more granular and supply a per-argument within a group usage generation option. For example, the method ArgGroup::usage(mut self, arg_id) to specify which arguments generate separate usage statements. Of course, the breaking change would be to modify ArgGroup::arg(..., usage: bool) to specify during addition of a new argument.

We'll also want to look at several clap users today that use groups to verify any assumptions of how well this would work and if any tweaks might be needed.

I can perform a preliminary search of this and report back some results here.

A challenge for this will be balancing a usability improvement like this with our efforts to reduce compile time (#2037) and binary size (#1365) which are top complaints of clap today.

Noted. Extending the interface of the ArgGroup struct to support this option is trivial. However, to generate the actual USAGE section is a bit more work. Do you mind pointing me in the direction where the USAGE section gets generated? I would like to have a look and possibly, if feasible, to provide this feature. Else, if someone is better suited, such as yourself, by all means!

@epage
Copy link
Member

epage commented Sep 7, 2022

Do you mind pointing me in the direction where the USAGE section gets generated?

https://github.com/clap-rs/clap/blob/master/src/output/usage.rs

Tests would either go in

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-help Area: documentation, including docs.rs, readme, examples, etc... C-enhancement Category: Raise on the bar on expectations S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing
Projects
None yet
Development

No branches or pull requests

2 participants