usage-argv 5.1.0

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, and this crate forbids unsafe. Bytes are what is left, and they turn out to be the honest interface anyway.

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.