Expand description
Command line parsing built from winnow parsers.
winnow-args parses a program’s arguments into typed values, typically a struct with one field per flag or positional. It distinguishes:
- flags, long (
--verbose) or short (-v); short flags can be bundled,-vqfor-v -q; - values of flags, given as the next word (
--path /tmp,-p /tmp) or attached (--path=/tmp,-p/tmp); - positionals, words identified by their position:
aandbincp a b; - subcommands, words that select a command with arguments of its own:
addingit add -p; --, after which every word is a positional.
A failed parse returns an Error with its kind, the word at fault and its
offset.
There are two ways to say which of these a program takes:
#[derive(Args)]on a struct. A field is a flag or a positional, its type says how many values it takes and what they convert to, and its doc comment is its help. This is what most programs want, and the faster of the two.- By hand, with
combinator. A flag is a winnow parser, andalt,repeatand the rest of winnow combine flags into a command line.
Either way the words are read where the shell left them: nothing is copied or
re-split, and the bytes need not be UTF-8 until a value’s type asks for text.
Both also cover command lines older than --long: +x options as in a bash
builtin, and flags whose order matters as in ld.
use std::path::PathBuf;
use winnow_args::Args;
/// Look somewhere.
#[derive(Args, Debug)]
struct Cli {
/// Say more.
#[arg(short, long)]
verbose: bool,
/// Where to look.
#[arg(short, long)]
path: Option<PathBuf>,
#[arg(positional)]
files: Vec<String>,
}
// In `main`: `let cli = Cli::parse();`
let cli = Cli::parse_from(["-v", "--path=/tmp", "a", "b"])?;
assert!(cli.verbose);
assert_eq!(cli.path, Some(PathBuf::from("/tmp")));
assert_eq!(cli.files, ["a", "b"]);§Where to look
Args,Subcommand,ValueEnumandOccurrence: the derives. Each page lists every attribute it takes.combinator: the same command lines written by hand, andtoken, the lexer under both.FromArg: what a value can be, and the types invaluefor aFromStrtype, a C-syntax number andkey=value.Errorandreport: what a failed parse holds, and how it is printed, with a tip when something close to what was typed is accepted.helpandcolor: the help text and its theme. A description’s`quoted`,*emphasized*and**strong**spans are painted, each by a paint of its own incolor::Palette.complete: completion scripts for bash, zsh, fish, elvish and PowerShell, for flags, subcommands and values.response:@filewords.run: calling the command a line selected, with#[arg(run)]on the enum of subcommands.with_env: a test’s own environment variables.
Man and markdown pages are made by winnow-args-man and
winnow-args-markdown, from what the derives write when the environment
variable WINNOW_ARGS_SPEC names a directory; the derive crate’s
documentation says how.
§Entry points
Args::parseinmain. Help, version and a failure are printed, and the process exits.Args::try_parsereturns theErrorfor the caller to handle.Args::parse_fromtakes the words after the program name: what a test calls.Args::parse_from_argvtakes the whole line, program name first.Args::parse_wordstakes words that are alreadyBStrs.Args::completion_requestgoes before any of them in a program that hascompletescripts.
§Performance
The parser is generated at compile time, so a parse costs the same however many commands the program declares; clap and bpaf build theirs at each start. A parse is a few thousand instructions and a few hundred nanoseconds. The measurements, against clap, bpaf and usage, and the method are in the repository’s benchmarks.
§Cargo features
derive(default): the derives.help-text(default): the prose of derived help. Without it, help still lists commands, flags, values and defaults, and the binary is smaller.terminal-size: wrap help to the terminal’s width whenCOLUMNSis unset.
§From clap and usage
The spellings that differ. Everything else is on the derive’s own page.
| clap | usage | winnow-args | |
|---|---|---|---|
| the command | #[derive(Parser)] | #[derive(Cli)] | #[derive(Args)] |
| its name, its version | #[command(name, version)] | #[usage(bin, version)] | #[arg(name, version)] |
| an attribute | #[arg], #[command] | #[usage] | #[arg] |
| subcommands | #[derive(Subcommand)] | #[derive(Subcommands)] | #[derive(Subcommand)] |
| words after the program name | none: the name comes first | parse_from | parse_from |
| the whole line | try_parse_from | parse_from_argv | parse_from_argv |
| a count | action = ArgAction::Count | count | count |
| a positional | no long or short | no short or long | #[arg(positional)] |
| a field with no attribute | a positional | a positional | --field-name |
| a default, an environment variable | default_value, env | default, env | default, env |
| several words, a delimiter | num_args, value_delimiter | num_args, delimiter | values, delimiter |
| a value that may be left off | default_missing_value | default_missing | default_missing |
| choices from an enum | #[arg(value_enum)] | #[usage(value_enum)] | the type derives ValueEnum |
| a conflict | conflicts_with = "file" | conflicts("--file") | conflicts("--file") |
| the flag that clears a switch | ArgAction::SetFalse | negate = "--no-force" | negate = "no-force" |
| shared flags | #[command(flatten)] | #[usage(flatten)] | #[arg(flatten)] |
| a subcommand field | #[command(subcommand)] | #[usage(subcommand)] | #[arg(subcommand)] |
| an unknown flag | an error | a value, unless unknown_flags = "error" | an error, unless unknown_flags = "value" |
Re-exports§
pub use env::with_env;pub use error::Error;pub use error::ErrorKind;pub use stream::Argv;pub use stream::words;pub use token::Arg;pub use value::ChoiceError;pub use value::FromArg;
Modules§
- color
- Colors for help and errors, for the terminal at hand.
- combinator
- Parsers written by hand: name a flag, then say what it is.
- complete
- Shell completion, answered by the program itself.
- env
- Environment variables as a source of values, after the command line and before defaults.
- error
- What went wrong, and where.
- help
- Help text:
staticdata the derive emits, rendered only when asked for. - response
- Response files:
@filewords that stand for the file’s contents. - run
- Running the command a line selected.
- stream
- The input:
argvas a slice of words. - token
- Lexing: one command-line item at a time.
- value
- Turning a value’s bytes into a Rust type.
Structs§
- BStr
- A word of the command line, and a flag’s value: bytes, not necessarily
UTF-8. What
FromArg::from_argis given. ImprovedDebugexperience for&[u8]UTF-8-ish streams
Traits§
- Args
- A type parsed from a whole command line.
- Globals
- The global flags of a subcommand’s ancestors, which it accepts as its own.
- Occurrence
- Flags and words kept in command-line order: an enum deriving
Occurrence, one variant per flag or positional, collected by an#[arg(sequence)]Vecfield of anArgsstruct. - Subcommand
- An enum of subcommands, selected by a word.
Functions§
- globals
Globalsfrom a closure. It does not answerGlobals::short: the derive callsinherit, which forwards the parent’s letters, so a lenient subcommand still sees them.- report
- Print what a failed parse means, and give the exit status: help and version
on stdout with 0, a bare
arg_required_else_helpinvocation’s help on stderr with 2, anything else as an error on stderr with 2. Colored withcolor::Theme::DEFAULTwhere the stream written to shows color; seereport_withfor another theme. - report_
with report, painted withtheme: its palette for the depthcolor::Depth::detectfinds on the stream written to.
Derive Macros§
- Args
- Derive
Argsfor a struct with named fields: the generated loop parses the whole command line. - Occurrence
- Derive
Occurrencefor an enum: one variant per flag or positional, kept in command-line order by an#[arg(sequence)]field of anArgsstruct. - Subcommand
- Derive
Subcommand(andArgs, so the enum can be the whole command line) for an enum of subcommands. - Value
Enum - Derive
FromArgfor an enum of unit variants, matched on the value’s bytes; help lists them as possible values.