Rust Framework
Experimental — draft docs
The Rust framework is experimental. It is complete enough that usage-cli itself is built with it, but attribute names and APIs may still change between releases. These docs are a draft: some of what they document is still in open pull requests, and details may change before release.
The Rust framework builds your CLI from Rust types. You declare commands, flags, and args as structs and enums; a derive macro compiles that declaration into parse tables and a usage spec. The same declaration that parses argv is the spec that generates your docs, manpages, and shell completions.
use usage::Cli;
/// A tool that does things
#[derive(Cli)]
#[usage(bin = "ex", version = "1.0")]
struct Cli {
/// How many jobs to run at once
#[usage(short = 'j', long, env = "EX_JOBS", default = "4")]
jobs: Option<String>,
/// Print more
#[usage(short = 'v', long, count)]
verbose: u8,
/// Colorize output
#[usage(long, negate = "--no-color", default = "true")]
color: bool,
/// Files to process
files: Vec<String>,
}
fn main() {
let cli = Cli::parse();
// cli.jobs, cli.verbose, cli.color, cli.files are ready to use
}Doc comments are the help text: the first paragraph becomes the short help shown by -h, the whole comment becomes the long help shown by --help.
Installation
Add usage-rs to your Cargo.toml, aliased to usage:
[dependencies]
usage = { package = "usage-rs", version = "5" }The alias is supported directly — the derive resolves its runtime through the package name, so depending on usage-rs under any name works. usage-rs is a facade over two crates you can also use directly:
| Crate | Role |
|---|---|
usage-rs | The facade an application depends on; re-exports the whole runtime |
usage-derive | The derive macros: Cli, Args, Subcommands, ValueEnum |
usage-argv | The zero-allocation, zero-dependency runtime the derive emits code against |
Cargo features
| Feature | Default | What it enables |
|---|---|---|
spec | ✅ | Spec metadata and to_kdl(); gates the derives |
help | ✅ | -h / --help page rendering |
completions | Shell completion scripts and the runtime completion protocol | |
diagnostics | clap-shaped error messages from render_failure |
Two footguns worth knowing up front:
- Without
diagnostics, parse failures print as aDebug-formatted error rather than the friendly clap-shaped message. Enable it for anything user-facing. #[usage(completion)]without thecompletionsfeature is a deliberatecompile_error!that tells you which feature to add.
Parse entry points
#[derive(Cli)] generates these on your struct:
// parse std::env::args; print help/version/errors and exit as appropriate
pub fn parse() -> Self;
// parse the given argv; hand errors (including help/version requests) back to you
pub fn parse_from<'v>(argv: &'v [&'v OsStr]) -> Result<Self, usage::Error<'static, 'v>>;
// the static parse tables and spec metadata
pub fn command() -> &'static usage::Command<'static>;
pub fn spec() -> &'static usage::spec::Spec<'static>;
// the usage spec as KDL
pub fn to_kdl() -> String;parse() is the whole program shell: it prints the help page to stdout and exits 0 for -h/--help, prints {bin} {version} and exits 0 for --version, and prints a rendered failure to stderr and exits 2 — clap's exit status, so scripts that check for it keep working. parse_from gives you the same machinery without the process control; see Help, version, and errors for handling its Err variants.
One declaration, every artifact
Because the derive also emits a usage spec, everything on this site that consumes a spec works with your CLI. The pattern usage-cli itself ships is a hidden flag that prints the spec:
#[usage(long, hide)]
usage_spec: bool,if cli.usage_spec {
println!("{}", Cli::to_kdl().trim());
return;
}Then generate everything else from it:
mycli --usage-spec > mycli.usage.kdl
usage g markdown -f mycli.usage.kdl --out-dir docs
usage g manpage -f mycli.usage.kdl > mycli.1
usage g completion bash mycli --file mycli.usage.kdlSee Spec output for the round-trip guarantees and what the emitted KDL looks like.
Where to go next
- Args and flags — field types, attributes, env vars, defaults
- Subcommands — command enums, nesting,
flatten, value enums - Validation — choices, groups,
exclusive,delimiter, conflicts - Help, version, and errors — what the parser renders and how to hook it
- Completions — static scripts and runtime completion
- Spec output — the emitted KDL and usage-cli integration
Current limitations
The framework intentionally targets standard GNU-style CLIs, and a few clap features have no equivalent yet:
examplenodes exist in the spec format but cannot be declared from the derive — put an Examples section inafter_long_helpinstead (mise does this).value_optionalaffects help output only; the parser still requires a value for the flag.- There is no per-field
value_parser-style validation — values are built withFromStr, and a conversion failure becomes anInvalidValueerror. - Prefix matching (
infer_long_args) is suggested in error messages but never accepted. - Non-UTF-8 argv values are reported precisely in errors rather than lossily replaced, but cannot currently be accepted into fields (the crates forbid the
unsafeneeded to reconstruct anOsString).