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

Subcommand precedence over arguments with SubcommandsNegateReqs #793

Closed
klemens opened this issue Dec 29, 2016 · 5 comments
Closed

Subcommand precedence over arguments with SubcommandsNegateReqs #793

klemens opened this issue Dec 29, 2016 · 5 comments
Assignees
Milestone

Comments

@klemens
Copy link

klemens commented Dec 29, 2016

When using (at least) two positional arguments and a subcommand, the subcommand takes precedence when given as the second argument.

The goal is a cli of the following form, where the subcommand is only matched if it is the first argument. This makes it possible to e.g. use sub (or help) as the value for the second argument.

test <arg1> <arg2>
test sub ...

Example

println!("{:#?}", App::new("test")
    .setting(AppSettings::SubcommandsNegateReqs)
    .arg(Arg::with_name("arg1").required(true))
    .arg(Arg::with_name("arg2").required(true))
    .subcommand(SubCommand::with_name("sub"))
    .get_matches());

test 1 sub results in:

ArgMatches {
    args: {
        "arg1": MatchedArg { ... "1" ... }
    },
    subcommand: Some(
        SubCommand { ... }
    ),
    ...
}

while it would be much more useful (IMHO) if it resulted in:

ArgMatches {
    args: {
        "arg2": MatchedArg { ... "1" ... },
        "arg1": MatchedArg { ... "sub" ... }
    },
    subcommand: None,
    ...
}

Additionally, when the suggestions feature is enabled, any prefix or extended version of sub with at least length 2 triggers a "Did you mean 'sub'?" help screen. Without suggestions, only exact matches cause the described problem.

A workaround is to use test -- 1 sub or test 1 -- sub, which results in the second output.

Changing the meaning of SubcommandsNegateReqs is of course not backwards compatible, but a new setting that prefers arguments over subcommands could be introduced.

  • Rust version: 1.14.0
  • Clap version: 2.19.2
@kbknapp
Copy link
Member

kbknapp commented Dec 29, 2016

Thanks for the details! 😄 I'm a little confused on one point though, are you asking for when a subcommand conflicts with a required argument that the argument take precedence? Subcommands taking precedence also happens without requirements.

println!("{:#?}", App::new("test")
    .arg(Arg::with_name("arg1"))
    .arg(Arg::with_name("arg2"))
    .subcommand(SubCommand::with_name("sub"))
    .get_matches());

Has the same issue as what's listed above.

I'd be a little leary of having args take precdence though, as there's no way to "escape" a subcommand, but there is a way to "escape" an argument (test 1 -- sub as you mentioned).

@klemens
Copy link
Author

klemens commented Dec 29, 2016

You are right, SubcommandsNegateReqs and required arguments are not strictly necessary for reproducing the "problem", but omitting them allows something like test 1 2 sub, which is a totally different usage.

there's no way to "escape" a subcommand

This shouldn't be the default of course, but I think there is no other way¹ to implement my desired usage pattern:

app <destination> <command>
app config ...

I first noticed the problem when I wanted to send the help command using my application, which instead displayed the cli help, because of the automatically added help subcommand.

(1) There actually is another way: You can "abuse" AllowExternalSubcommands, so the first argument becomes the "external subcommand" if it is no internal one. However, then you can't use any advanced functionality of Arg and have to parse the additional arguments manually (or using a second clap App instance).

@kbknapp
Copy link
Member

kbknapp commented Dec 30, 2016

Ah ok, I understand now! I do think this is doable, but I'm unsure of what to call this new setting. Bascially you want, "If an arg is used, forget about subcommands entirely." i.e. down't allow the <cmd> [args] <cmd> [args] <cmd> format.

Perhaps something like, DisallowArgsBetweenSubcommands or ArgsNegateSubcommands (preferred)

@klemens
Copy link
Author

klemens commented Dec 30, 2016

If an arg is used, forget about subcommands entirely

Exactly! I think ArgsNegateSubcommands sounds better and fits nicely with SubcommandsNegateReqs.

Edit: I think this new setting should only apply to positional arguments, so that something like app --flag config ... stays possible. Therefore we maybe should call it PositionalArgsNegateSubcommands?

@kbknapp kbknapp added this to the 2.20.0 milestone Dec 31, 2016
@kbknapp kbknapp self-assigned this Dec 31, 2016
@homu homu closed this as completed in 5e2af8c Dec 31, 2016
@klemens
Copy link
Author

klemens commented Dec 31, 2016

Wow, thank you for the fast implementation, it works fine!

Have you seen my edit about only applying this setting to positional arguments? I don't currently need it, but I think it could be useful in something like temporarily specifying a different config file or a flag like -v for verbosity that applies to the main command and all subcommands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants