I spent a bit more time working on winnow-args. I improved it after trying to integrate it with brush, maki, em.
What changed
Mainly I expanded a bit relations between fields and groups to address what maki needs, improved the subcommand and multicommand support in preparation to em release and fixed more corner cases in brush improving winnow-args in the process.
Now we are closer to have feature parity with usage and clap but the road is still long.
The early adopters that used the derive API do not have to change anything in their code.
| Feature | 0.2.0 | 0.3.0 | 0.4.0 | 0.5.0 | usage 7 | clap 4 |
|---|---|---|---|---|---|---|
| Errors and rules | ||||||
| "Did you mean" tips | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Every missing required argument reported at once | – | – | ✓ | ✓ | ✓ | ✓ |
required_if, required_unless_all
|
– | 0.3.1 | ✓ | ✓ | ✓ | ✓ |
required_if_all |
– | – | ✓ | ✓ | ✓ | ✓ |
requires_if, exclusive
|
– | 0.3.1 | ✓ | ✓ | ✓ | ✓ |
requires naming a group |
– | – | ✓ | ✓ | ? | ✓ |
| Own message for a conflict or requirement | – | – | ✓ | ✓ | – | – |
validate, a check on the parsed struct |
– | 0.3.1 | ✓ | ✓ | ✓ | ~ |
| Values and defaults | ||||||
from_str, a value parsed by FromStr
|
– | ✓ | ✓ | ✓ | ✓ | ✓ |
default_from, a default made from other fields |
– | 0.3.1 | ✓ | ✓ | ✓ | ~ |
A bare env, named after the field |
– | – | ✓ | ✓ | ? | ✓ |
A repeated default on a Vec
|
– | – | ✓ | ✓ | ? | ✓ |
bool_value (--wait=false) |
– | – | ✓ | ✓ | ✓ | ~ |
value_names (--point <X> <Y>) |
– | – | ✓ | ✓ | ✓ | ✓ |
env_fallback, more variable names |
– | – | – | ✓ | ✓ | – |
| Subcommands | ||||||
Generated dispatch (run, run_async) |
0.2.1 | ✓ | ✓ | ✓ | ✓ | – |
| A flag that selects the default subcommand | 0.2.1 | ✓ | ✓ | ✓ | ✓ | – |
| Global flags in a flattened struct | 0.2.1 | ✓ | ✓ | ✓ | ✓ | ✓ |
| A subcommand variant with named fields | – | ✓ | ✓ | ✓ | ✓ | ✓ |
default_subcommand_on_empty |
– | 0.3.1 | ✓ | ✓ | ✓ | – |
| External subcommands | – | – | ✓ | ✓ | ✓ | ✓ |
| Multicall | – | – | ✓ | ✓ | ✓ | ✓ |
| Help | ||||||
*emphasis* and **strong** painted |
✓ | ✓ | ✓ | ✓ | ? | – |
| Global flags listed in a subcommand's help | – | ✓ | ✓ | ✓ | ✓ | ~ |
| Default subcommand marked, its page after the root's | – | ✓ | ✓ | ✓ | ✓ | – |
| Examples | – | ✓ | ✓ | ✓ | ✓ | ~ |
| A paragraph under a heading | – | ✓ | ✓ | ✓ | ✓ | ~ |
A flag's warning
|
– | ✓ | ✓ | ✓ | ✓ | – |
flatten_help |
– | ✓ | ✓ | ✓ | ✓ | ✓ |
hide_default_value, hide_env, display_order
|
– | 0.3.1 | ✓ | ✓ | ✓ | ✓ |
verbatim_doc_comment |
– | – | ✓ | ✓ | ✓ | ✓ |
Width in terminal columns (unicode) |
– | – | ✓ | ✓ | ✓ | ✓ |
visible_alias |
– | – | ✓ | ✓ | ✓ | ✓ |
long_version, before_help, usage
|
– | – | ✓ | ✓ | ✓ | ✓ |
| What each choice means | – | – | – | ✓ | ✓ | ✓ |
hide_choices |
– | – | – | ✓ | ✓ | ✓ |
help = CONSTANT |
– | – | – | ✓ | – | ✓ |
brief_help: --help prints the short page |
– | – | – | ✓ | – | – |
| Deprecation | ||||||
| Deprecated flags, positionals and subcommands | – | – | – | ✓ | ✓ | – |
deprecated_warn_at, deprecated_remove_at
|
– | – | – | ✓ | ✓ | – |
deprecated_env |
– | – | – | ✓ | ✓ | – |
| Completion and pages | ||||||
| Completion of a value by hint or function | ✓ | ✓ | ✓ | ✓ | ✓ | ~ |
| Man and markdown pages | ✓ | ✓ | ✓ | ✓ | ✓ | ~ |
| Effects, outputs, exit codes in the pages | – | ✓ | ✓ | ✓ | ✓ | – |
| A page for each multicall command | – | – | ✓ | ✓ | ? | – |
Here some more details
Errors and rules
An unknown flag, subcommand or value now comes with a tip for what was probably meant (0.2). The search runs only once there is an error, so a correct command line pays nothing for it.
When several required arguments are missing, they are all reported at once, each as --help lists it (0.4).
The rules gained most of what clap has: required_if,
required_unless_all, requires_if and exclusive (0.3.1), and
required_if_all, plus requires naming a group (0.4). conflicts, requires and groups take a message with the program's own wording.
For anything the attributes can't express, validate = function runs the program's own check on the parsed struct.
Values and defaults
We have new attributes to parse the cli values to types:
-
from_strparses a field with its type'sFromStr, for a type from another crate (0.3). -
default_frommakes a default from other fields, after their own defaults (0.3.1). -
bool_valuetakes--wait=false, andvalue_namesgives a flag several placeholders,--size <W> <H>(0.4).
A bare env reads the variable named after the field, and a Vec can repeat default (0.4). env_fallback reads more variables after env (0.5).
Subcommands
Most of it came with the effort to support completely all I need for em.
With default_subcommand_flags, a flag the root doesn't take selects the default subcommand, so
$ em -pu @world
Maps automatically to:
$ em emerge -pu @world # (0.2.1)
default_subcommand_on_empty runs it for a line with no command at all (0.3.1).
A subcommand can be a variant with named fields (0.3), and a flattened struct can hold global flags (0.2.1).
#[arg(run)] on an enum of subcommands generates the dispatch to the command it holds (0.2.1).
multicall makes one binary act as each of its subcommands when it is run under that subcommand's name, the way busybox's applets work (0.4).
Help and errors use the name it was run as. #[arg(external)] on a variant takes any word that names no other subcommand, plus the rest of the line, the way cargo foo runs cargo-foo.
Help
winnow-args starting point was to get the best possible performance, but having nice help does not impair this need so I tried to make it as expressive as possible.
Help has examples, a paragraph under a heading, and a flag's warning (0.3).
A subcommand's help lists the global flags it inherits, and the default subcommand is marked, with its page after the root's. *emphasis* and **strong** spans are painted (0.2), and an example's line is painted word by word (0.3.2).
0.4 added verbatim_doc_comment, widths in terminal columns
under the unicode feature, visible_alias, long_version, before_help and usage.
Help can say what each choice means (0.5). A ValueEnum variant's doc comment, or choices("fast" = "…") on a field, is listed one value a line by --help, while -h keeps the short [possible values: …].
A hand-written FromArg can answer from data only the running program has; brush uses this for the complete -A and -o choices, whose meanings live in brush-core.
0.5 also added hide_choices, help = CONSTANT, and brief_help, which makes --help print the short page.
Deprecation
A flag, a positional, a subcommand or an environment variable can be marked deprecated (0.5). Using one prints a warning.
deprecated_warn_at keeps the warning quiet until a later version, and deprecated_remove_at says when it goes away.
deprecated_env covers a variable that has been renamed.
Args::parse prints the warnings; Args::warnings returns them to a program that wants to report them itself.
Completion and pages
A value is completed by a hint, hint = "dir", or by the program itself, complete = function (0.2).
The completion script asks the program, so it always matches what the program accepts.
With WINNOW_ARGS_SPEC set, the derives write a TOML description of each type at compile time (0.2). winnow-args-man and winnow-args-markdown turn those descriptions into pages.
The program is only checked, never run, so the pages can be made for a target that can't run on the build host, this addresses one of the papercut that comes from generating man and completion from the cli.
Since then the pages have picked up examples, headings, effects, outputs and exit codes (0.3), a page for each multicall command, ls.1 rather than box-ls.1 (0.4), and deprecations (0.5).
Coming next
I'll keep improving the crate aiming to reach feature-parity (see the checklist tracking the features), you are welcome to use it, find bugs and send patches as usual.
Top comments (0)