rust_args_parser
Tiny, fast, callback-based CLI argument parser for Rust.
- 📦 Crate:
rust-args-parser - 📚 Docs: https://docs.rs/rust-args-parser
- 🔧 MSRV: 1.60
- ⚖️ License: MIT OR Apache-2.0
- 📝 Changelog
This crate is a pragmatic alternative to heavyweight frameworks when you want:
- Callbacks: options/positionals map directly to functions that mutate your context.
- Subcommands (nested
CmdSpec) with aliases. - Short clusters (
-vvj8) and long forms (--jobs=8). - Numeric look-ahead so tokens like
-1,-.5,+3.14,1e3are treated as values, not options. - Groups: mutually exclusive (
Xor) / at least one required (ReqOne). - ENV/Default overlays with clear precedence (CLI > ENV > Default).
- Readable matches with scope and provenance.
Quick start
use rust_args_parser as ap;
use OsStr;
CLI behavior
- Short clusters:
-vvj8⇒-v -v -j 8(flag callback fires once per-v). - Inline/next-arg values:
-j8/-j 8,--jobs=8/--jobs 8. - Negative numbers:
-d-3,--delta -3are values (not options). - End-of-options:
--makes the rest positional, even if they start with-.
Subcommands
Subcommands are nested CmdSpecs and scoped.
use rust_args_parser as ap; use OsStr;
let spec = new
.subcmd;
let mut ctx = default;
let m = parse?;
assert_eq!;
let v = m.view;
assert_eq!;
Root options are not accepted after you descend into a subcommand unless re-declared at that level.
Options, positionals, groups, validators
Options
- Flag:
OptSpec::flag("name", on_flag) - Value:
OptSpec::value("name", on_value) - Builders:
.short('j'),.long("jobs"),.metavar("N"),.help("…"),.env("VAR"),.default(OsString),.group("name"),.repeat(Repeat::Many),.validator(fn)
Positionals
PosSpec::new("NAME", on_value)then choose one:.required().many()(0..∞).range(min, max)
- Also
.help("…"),.validator(fn).
Groups
GroupMode::Xor— options in the same group are mutually exclusive.GroupMode::ReqOne— require at least one option from the group.
let spec = new
.opt
.opt
.group;
Validators
Validators run on CLI, ENV, and Default values. If a validator fails, the callback for that option/positional is not invoked.
Overlays & provenance
- Precedence: CLI > ENV > Default.
- Bind ENV via
.env("NAME"), defaults via.default(…). - Check where a value came from with
matches.is_set_from(name, Source::{Cli,Env,Default}). Matchesis scoped: usem.view()for the leaf command orm.at(&[])for root.
Built-ins & features
Feature flags (enabled by default unless you disable default-features):
help— built-in-h/--helpand--versionreturningError::ExitMsg { code: 0, message }.color— colorized help output (honorsNO_COLOR), withColorMode::{Auto,Always,Never}.suggest— suggestions for unknown options/commands.
Matches & views
Matches collects everything the parser saw. MatchView gives you a scoped, read-only accessor.
let m: Matches = parse?;
let leaf = m.view; // leaf scope
let root = m.at; // root scope
leaf.is_set;
root.is_set_from;
leaf.value; // first value
leaf.values; // all values for an option
leaf.pos_one; // single positional by name
leaf.pos_all; // all positionals with that name
Flags are stored as presence (
Value::Flag). The parser also counts flag occurrences internally so-vvvcalls the flag callback three times.
Errors
Top-level error type: ap::Error.
Error::User(String)/Error::UserAny(Box<dyn Error + Send + Sync>)Error::Parse(String)Error::ExitMsg { code, message }- Structured diagnostics:
UnknownOption { token, suggestions }UnknownCommand { token, suggestions }MissingValue { opt }UnexpectedPositional { token }
Typical handling:
match parse
Utilities (ap::util)
looks_like_number_token(&str) -> bool—-1,+3.14,-.5,1e3,-1.2e-3.strip_ansi_len(&str) -> usize— visible length, ignoring minimal ANSI sequences used in help.
Examples
See examples/:
basic.rs— flags, values, callbacks, errorssubcommands.rs— nested commands, leaf scopingenv_defaults.rs— ENV/default precedencegit.rs— realistic multi-command layout
Run:
Testing
A comprehensive test suite covers options/positionals, subcommands, groups, overlays, validators, suggestions, help, utils, and an end-to-end golden test.
# or core only
License
Dual-licensed under MIT or Apache-2.0 at your option.