RazrFalcon/pico-args

Forbid flags between options.

RazrFalcon opened this issue ยท 13 comments

This is a valid code now:

    let mut args = Arguments::from_vec(to_vec(&["-w", "-h", "20"]));
    args.contains("-h");
    let value: Result<u32, Error> = args.value_from_str("-w");
    assert_eq!(value.unwrap(), 20);

but it should be an error, I guess.

bluss commented

Interesting library. On this issue, shouldn't "-h" be the value to "-w" ? A random example would be "git grep -e -e" where the second -e ends up being the value to the first (and also the answer to, what do you do if you want to search by a string that starts with a dash?).

grep -e -e

Yes, it's an interesting edge case. pico-args simply parses the next argument as a value.

The problem with this issue is that I'm not sure that it can be fixed. Or should it be fixed at all. And I haven't tested other libraries yet. So this is just a reminder for myself.

pico-args, aka dumb-args, specifically designed for simple cases.

Not a bug.

So pico-args only gets told about each flag one at a time, and mutates the argument array with each argument that is parsed, incrementally discarding information like this. This would be able to be solved by providing information about every argument up-front, but that would be a huge API change. Personally this is a deal-breaker for me and I won't be able to use pico-args with issues like this, but it was definitely an interesting look, almost exactly what I was looking for. :)

This could also be solved by storing arguments as Vec<Option>. But this would require significant internal changes and will make codebase a bit more complex.

Personally, I don't see this is issue as critical. Can you at least provide an example when it's actually a problem?

Can you at least provide an example when it's actually a problem?

Just correctness. I almost didn't use getargs because of the UTF-8 requirement (it uses &str), but it does everything else correctly for my use case, to the point where I don't think I'll find anything better. The reason I chose it over pico-args is because I can use my own match to ensure every argument is parsed correctly from the start. (also it doesn't allocate when used with argv)

With pico-args flag values can be parsed as flags themselves. For example, for the input --takes-value -v some-value, if -v is parsed first, then it becomes --takes-value some-value, even though --takes-value should have taken the value -v.

I don't typically interpret "specifically designed for simple cases" as ignoring edge cases like these, but in this case it would require large API changes (a lot of work).

but in this case it would require large API changes

Why so? We just store args as

[Some("--takes-value"), Some("-v"), Some("some-value")]

so after checking for -v you will get

[Some("--takes-value"), None, Some("some-value")]

and --takes-value some-value would no longer work.

This is not that hard to implement, I just can't imagine how this edge-case can be abused.

Why so? We just store args as

[Some("--takes-value"), Some("-v"), Some("some-value")]

so after checking for -v you will get

[Some("--takes-value"), None, Some("some-value")]

and --takes-value some-value would no longer work.

This differs from the expected outcome of --takes-value being able to find its value being -v. This just causes --takes-value to fail because -v is taken early. And there's no way to detect that early because -v does not know that --takes-value takes an argument yet.

In order to solve that you would have to declare each flag before/as it is encountered which would indeed be a big API change.

Then it's still not a bug.

Then it's still not a bug.

It is a bug, it's just a bug in the API design rather than the implementation.

(anyway, I will stop pestering you about it now. good luck :)

Mentioned this limitation in the readme.

There is a way to fix this in the current API design: forbid implicit values. That is, accept only --option=VALUE for long options, and -oVALUE for short options, and not --option VALUE or -o VALUE. That way, each argument is freestanding - positional arguments are never values for options, and options are always options and not values.

You could add a couple new methods that only accept explicit values like that. It wouldn't be a breaking change and it would be opt-in.

It's somewhat pedantic/strict, but it's more correct than the values of options depending on the order in which you parsed them.

Too complicated. I'm fine with the current limitations.