Open epage opened 2 years ago
Comment by epage Monday Nov 15, 2021 at 20:05 GMT
Would the summary be that we for optional values, we should be encouraging requires_delimiter
(granted, I've not checked what that does in the short flag case or if that deviates from these standards)?
Comment by mkayaalp Monday Nov 15, 2021 at 20:36 GMT
That changes the parsing for multiple_values
, right? Couldn't test that with clap_derive
but, the following gives the same error (I tried to adapt the cargo expand
output to use the builder pattern):
let opt = App::new("clap-test")
.arg(Arg::new("foo")
.short('f')
.long("foo")
.takes_value(true)
.value_name("FOO")
.min_values(0)
.max_values(1)
.multiple_values(false)
.use_delimiter(true)
.require_delimiter(true))
.arg(Arg::new("input")
.takes_value(true)
.value_name("INPUT")
.required(true)).get_matches();
The POSIX guideline says:
When multiple option-arguments are specified to follow a single option, they should be presented as a single argument, using
<comma>
characters within that argument or<blank>
characters within that argument to separate them.
So, -O a,b,c
or -O "a b c"
Solaris guideline says:
Groups of option-arguments following an option must either be separated by commas or separated by tab or space character and quoted (
-o xxx,z,yy
or-o"xxx z yy"
).
I don't know if the lack of space between option and quote in -o"xxx z yy"
is a typo, but if the option-arguments were optional, then that would be correct (instead of -o "xxx z yy"
).
Comment by mkayaalp Monday Nov 15, 2021 at 20:48 GMT
This could be a separate issue, but the synopsis for optional option-argument should be:
USAGE:
clap-test [--foo[=FOO]] <INPUT>
or
USAGE:
clap-test [-f[FOO]] <INPUT>
Comment by epage Monday Nov 15, 2021 at 20:50 GMT
Sorry, I mean to recommend requires_equals
. requires_delimiter
is for the other case you mentioned.
Comment by epage Monday Nov 15, 2021 at 20:50 GMT
(too many generic names,. keep referring to the wrong one. #3026 talks about cleaning this up)
Comment by mkayaalp Monday Nov 15, 2021 at 20:58 GMT
Sorry, I mean to recommend
requires_equals
Yes. That works for long, but it also requires it for short: -f
works (no arg) but -fval
does not work anymore. It requires -f=val
.
Comment by epage Monday Nov 15, 2021 at 21:07 GMT
Yeah, I was worried it didn't do the right thing in that case.
Comment by epage Monday Nov 15, 2021 at 21:11 GMT
I tried to summarize this at the bottom of your "Parsing" section. Mind double checking it?
Comment by epage Monday Nov 15, 2021 at 21:22 GMT
Random observations:
1 atm the color flag in my CLIs is --color=<WHEN>
when ideally it would be --color[=<WHEN>]
. One of the things that has held me back from this is I've been unsure how users would react to the inconsistency of some args requiring =
while others do not, depending on whether they have flag/option duality or just an option. For the user,. when thinking about it as an option, they are likely to think of it like any other option.
2 For the most part, users are constructing the optional-optional value, we are just providing the building blocks. In following that pattern, we'd need to be able to allow users to specify the policy in a way that isn't confusing. So how do we communicate this? One thought is to have an enum listing out delimited (=
), arg (`), or concatenate (
-svalue) and the user can provide a set of these to a long and short function. This requires the user to build it up how they want. We'd want to document what the recommendations would be. We could have
clap_derivedo "the right thing" for
Option<Option
My main concern about this is the overall complexity, not just in code but in API design and user code. Is there a simpler way?
Comment by mkayaalp Monday Nov 15, 2021 at 21:26 GMT
I tried to summarize this at the bottom of your "Parsing" section. Mind double checking it?
That's correct. Although I don't know if I would say clap
supports -s
and -s value
when it interprets the operand (positional argument) to be the option-argument and gives an error.
Comment by mkayaalp Monday Nov 15, 2021 at 21:43 GMT
atm the color flag in my CLIs is
--color=<WHEN>
when ideally it would be--color[=<WHEN>]
I would say --color[=WHEN]
.
For the user, when thinking about it as an option, they are likely to think of it like any other option.
But --name=value
IS the correct form. Re-quoting GNU:
To specify an argument for a long option, write --name=value.
Allowing a space is the parser being lenient.
Same thing with -s value
being the norm for short options and -svalue
being allowed for leniency. Except for optional option-arguments. Then, it's the other way around. Solaris did have a good reason to outlaw those, I think.
Comment by epage Monday Nov 15, 2021 at 21:51 GMT
Allowing a space is the parser being lenient.
While GNU is saying one thing, we still have to deal with user expectations. As a user, I never use =
and I expect many others do the same. It doesn't matter what standard I reference, the user will still be frustrated at the inconsistency.
Comment by mkayaalp Monday Nov 15, 2021 at 21:52 GMT
we'd need to be able to allow users to specify the policy in a way that isn't confusing.
We could say "short" option follows the POSIX guidelines, and "long" option follows the GNU guidelines. Then have a custom option escape hatch that changes the prefix, separator, delimiter, with allow/disallow/require flags for each.
Comment by mkayaalp Monday Nov 15, 2021 at 22:00 GMT
we still have to deal with user expectations.
I agree, I am not saying we make the parser strict (although, it would be a really good addition to AppSettings
).
As a user, I am expecting --foo input
to parse it as option foo
without argument and input
as the operand. And I would also expect --foo optarg input
to parse as option foo
with optarg
as the argument and input
as the operand (which could very well fail, as it is easily ambiguous: if input
was optional or variadic
).
But I would prefer to show [--foo[=OPTARG]]
in the usage as the canonical form.
Comment by mkayaalp Monday Nov 15, 2021 at 22:28 GMT
For the most part, users are constructing the optional-optional value, we are just providing the building blocks.
Well, we do have a short/long/operand distinction. If we had simpler building blocks, short/long options could be "non-positional optional operands with labels" and short and long could only differ in their label length and prefix (-
/--
) and alias each other. We would need to have a combining/stacking feature for short options to use.
Going the other way, maybe we need another distinction for optional option-arguments: short_optional_arg
and long_optional_arg
. We could make sure the type was suitable for clap_derive
: Option<Option<T>>
or Option<Vec<T>>
.
Issue by mkayaalp Monday Nov 15, 2021 at 19:57 GMT Originally opened as https://github.com/clap-rs/clap/issues/3030
Please complete the following tasks
Rust Version
rustc 1.56.0 (09c42c458 2021-10-18)
Clap Version
master
Minimal reproducible code
Steps to reproduce the bug with the above code
or
Actual Behaviour
Expected Behaviour
Additional Context
There needs to be some special handling of options with optional arguments.
Optional option-arguments are bad
First of all, users really need to be discouraged (if we ever add some lint-like feature, e.g.
#[clap(enable-warnings)
).POSIX says:
Solaris says:
Parsing
I know some people have a preference for
-f OptArg
over-fOptArg
, but if the option-argument is optional, then only the latter is correct. If there is a space, it means the optional argument is not there.From POSIX.1-2017 - Ch. 12: Utility Conventions:
Same thing with the long option, which is a GNU extension.
From GNU C Library Reference Manual - Ch. 25.1.1: Program Argument Syntax Conventions:
See this SO question about how
getopt
handles this:getopt
does not parse optional arguments to parameters.In clap terms, these are saying we should only allow
--long
--long=value
-s
-svalue
In contrast, clap supports
--long
--long=value
--long value
-s
-s value
-s=value
-svalue
requires_equals(true)
--long
--long=value
-s
-s=value
Debug Output