usage-argv 6.4.1

Zero-allocation argv parser for usage specs
Documentation

A zero-allocation argv parser for usage specs.

This crate implements the binding rules of the argv grammar: which token becomes which flag or argument, when a word selects a subcommand, and what is an error. It does so without building a command tree, without allocating, and in one pass.

It is the runtime half of a compiled parser. The tables it reads are meant to be emitted by a derive macro as static data, so that starting a parse costs nothing at all: there is no construction step to pay for, only the walk over argv.

Shape of the API

Parsing yields [Event]s rather than a map. A map would have to allocate, and would then have to be read back out again — whereas generated code can assign an event straight into a struct field. This is the same reason serde deserializes into your type instead of into a Value.

use usage_argv::{Arg, Command, Event, Flag, Parser};

static FORCE: Flag = Flag { key: 0, longs: &["force"], shorts: b"f", ..Flag::BOOL };
static FILE: Arg = Arg { key: 1, ..Arg::REQUIRED };
static ROOT: Command = Command {
    name: "ex",
    flags: &[&FORCE],
    args: &[&FILE],
    ..Command::EMPTY
};

let argv = ["--force", "a.txt"].map(std::ffi::OsStr::new);
let mut parser = Parser::new(&ROOT, &argv);

let mut force = false;
let mut file = None;
while let Some(event) = parser.next_event() {
    match event.expect("valid command line") {
        Event::Flag { flag, .. } if flag.key == 0 => force = true,
        Event::Arg { value, .. } => file = Some(value),
        _ => {}
    }
}
assert!(force);
assert_eq!(file, Some(&b"a.txt"[..]));

Values are bytes

An [Event] carries &[u8], borrowed from argv. Converting to &str is the caller's step ([as_str]), and it is the right place for the only failure a value can have: a command line that is not valid UTF-8 still parses — flags match, subcommands route — and only the values that are actually looked at can fail to convert.

Slicing an OsStr into &str pieces safely is not possible without allocating or unsafe. Bytes are what is left, and they turn out to be the honest interface anyway.

The reverse conversion is [os_string_from_bytes], which lets a PathBuf field hold a filename that is not UTF-8 rather than a mangled copy of one. On Unix that is lossless and safe; on Windows, where WTF-8 makes it partial, a value that will not convert is reported. Either way this crate contains no unsafe, which a conversion that guessed would have cost.

What this crate does not do

Only binding. Required-ness, choices, env fallback, defaults, var_min and var_max are all decided after the last token is read, and they need to know a value's type, so they belong to the layer that owns the target struct. Keeping them out is what makes this loop small.

Features

  • spec — a parallel tree of cold metadata (help text, choices, defaults, effects) and a writer that emits it as a usage spec. Off by default: a successful parse never reads any of it, so a CLI that only wants a parser should not compile it.
  • complete — answering a partial command line ([complete]), the shell scripts that ask ([script]), and putting one of those scripts where its shell will look for it ([install]). Installing ships with the scripts rather than behind a gate of its own: a script a CLI still has to tell its users to redirect by hand is the unfinished half of shipping one.