TeXitoi / structopt

Parse command line arguments by defining a struct.
Other
2.7k stars 149 forks source link

Can't call subcommand after Option<Vec<_>> #509

Closed Ten0 closed 2 years ago

Ten0 commented 2 years ago

TLDR

error: The subcommand 'bar' wasn't recognized
    Did you mean 'bar'?

Code

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
struct Opt {
    #[structopt(long)]
    foo: Option<Vec<i32>>,
    #[structopt(subcommand)]
    subcommand: Subcommand,
}
#[derive(StructOpt, Debug)]
enum Subcommand {
    Bar,
}

fn main() {
    dbg!(Opt::from_args());
}

Command

cargo run -- --foo 1 2 -- bar

Expected output

[src/main.rs:16] Opt::from_args() = Opt {
    foo: Some([1, 2]),
    subcommand: Bar,
}

Actual output

error: The subcommand 'bar' wasn't recognized
    Did you mean 'bar'?

If you believe you received this message in error, try re-running with 'tmp -- bar'

USAGE:
    tmp --foo <foo>...

For more information try --help

Note that calling just cargo run -- bar works, but calling cargo run -- bar --foo 1 doesn't, and neither does cargo run -- --foo 1 bar, so it seems impossible to specify both foo and bar. Calling cargo run -- -- bar gives the same error.

TeXitoi commented 2 years ago

https://docs.rs/clap/2.33.3/clap/struct.Arg.html#method.multiple

TeXitoi commented 2 years ago

In particular the WARNING section. You can't pass subcommands after --, only parameters.

TeXitoi commented 2 years ago

If you want to be able to cargo run -- bar --foo 1, see https://docs.rs/clap/2.33.3/clap/struct.Arg.html#method.global

TeXitoi commented 2 years ago

Also, I think you want foo: Vec<i32>, Option<Vec<_> is for special cases when you want to know the difference between cmd --foo and cmd with cmd --foo 1 valid.

Ten0 commented 2 years ago

Sorry for the late answer, I've had to prioritize other things.

Also, I think you want foo: Vec<i32>, Option<Vec<_> is for special cases when you want to know the difference between cmd --foo and cmd with cmd --foo 1 valid.

That happens to be precisely what I mean to do.

https://docs.rs/clap/2.33.3/clap/struct.Arg.html#method.multiple In particular the WARNING section. You can't pass subcommands after --, only parameters.

The documentation says the following: "By default clap will parse the argument in question as a value only if a value is possible at that moment. Otherwise it will be parsed as a subcommand. In effect, this means using multiple(true) with no additional parameters and a possible value that coincides with a subcommand name, the subcommand cannot be called unless another argument is passed first."

This seems to mean that if we're out of parsing this particular multiple value, the subcommand should work.

If you want to be able to cargo run -- bar --foo 1, see https://docs.rs/clap/2.33.3/clap/struct.Arg.html#method.global

It seems a solution closer to what I was looking for (cargo run -- --foo 1 2 -- bar) would be to use https://docs.rs/clap/2.33.3/clap/struct.Arg.html#method.value_terminator.

However this works for any terminator other than --.

Overall this is maybe not a structopt issue, but it definitely seems to be at least a clap issue:

error: The subcommand 'bar' wasn't recognized
    Did you mean 'bar'?

is always an invalid error message regardless of the context in which it happens: it knows it's trying to parse a subcommand, it reads it properly, but doesn't match it to the exact same existing subcommand.

I've filed https://github.com/clap-rs/clap/issues/3024.