usage-rs 6.10.0

A compiled CLI parser for Rust, built on usage specs
Documentation

The facade for building compiled Rust CLIs with usage.

Depend on usage-rs under the short crate name usage. That is the one package an application needs: derive macros, the argv runtime, help, and clap-shaped errors ship in the defaults. Completions stay behind a feature; low-level adopters that want only the binding runtime keep depending on usage-argv directly.

[dependencies]
usage = { package = "usage-rs", version = "6" }

Smaller binaries

CLIs without flatten_help or HelpAll actions can omit help-advanced:

usage = { package = "usage-rs", version = "6", default-features = false, features = ["help", "diagnostics", "completions"] }

Advanced help remains enabled by default. Declaring those actions without its feature fails at compile time; hand-written metadata requesting advanced help panics when rendered. Cargo features are additive: another dependency enabling it restores advanced help.

To keep the spec endpoint without linking the KDL writer, use #[usage(spec_endpoint_file = "cli.usage.kdl")]. The file is included at compile time, relative to the declaring crate. Generate it with Cli::to_kdl() in a development tool, and test that Cli::spec_request(&[usage::SPEC_REQUEST.as_ref()]) equals Cli::to_kdl(). Regenerate after changing CLI metadata or its version. to_kdl() still uses the live metadata, including settings and spec_extra; only the endpoint uses the file.

What happens after a parse can come from the same declaration: a command implements [Run], the subcommand enum says #[usage(run)], and the match that routes argv to the code carrying it out is generated rather than written. [RunWith] under #[usage(run_with)] hands each command shared state, and [RunAsync] / [RunAsyncWith] under #[usage(run_async)] / #[usage(run_async_with)] are the async pair. Nothing about any of it reaches the spec.

Enable portable expression validation only when a CLI declares validate rules:

usage = { package = "usage-rs", version = "6", features = ["validation"] }
use usage_rs as usage;
# #[cfg(feature = "spec")]
use usage::Cli;

# #[cfg(not(feature = "spec"))]
# fn main() {}
# #[cfg(feature = "spec")]
# fn main() {
#[derive(Cli)]
#[usage(bin = "ex")]
struct Ex {
    #[usage(long, value_hint = usage::ValueHint::FilePath)]
    file: Option<std::path::PathBuf>,
}

let argv = [std::ffi::OsStr::new("--file"), std::ffi::OsStr::new("input.txt")];
let ex = Ex::parse_from(&argv).expect("valid command line");
assert_eq!(ex.file.as_deref(), Some(std::path::Path::new("input.txt")));
# }

A derived [Args] type can also be parsed on its own. This lets an adapter reuse one command's exact argument contract without constructing or parsing the application's root CLI:

use usage_rs as usage;
# #[cfg(feature = "spec")]
use usage::Args;
#
# #[cfg(not(feature = "spec"))]
# fn main() {}
# #[cfg(feature = "spec")]
# fn main() {

#[derive(Debug, Args)]
struct Install {
    #[usage(short, long)]
    force: bool,
    tools: Vec<String>,
}

let argv = ["--force", "node@24"].map(std::ffi::OsStr::new);
let install = usage::parse_args_from::<Install>(&argv).unwrap();
assert!(install.force);
assert_eq!(install.tools, ["node@24"]);
# }