Quickstart
Draft
This page is a draft. Some of what it documents is still in open pull requests, and details may change before release.
One small CLI, end to end: declare it, run it, complete it, and generate its docs — all from one declaration. Every output on this page is the real output of the code shown.
A new project
cargo new greet[dependencies]
usage = { package = "usage-rs", version = "6", features = ["completions"] }The declaration
use usage::{Args, Cli, Run, Subcommands};
/// Greets people, politely
#[derive(Cli)]
#[usage(bin = "greet", version = "0.1.0", completion)]
struct Greet {
/// Say it louder
#[usage(short = 'l', long, count, global)]
loud: u8,
#[usage(subcommand)]
command: Commands,
}
#[derive(Subcommands)]
#[usage(run)]
enum Commands {
Hello(Hello),
Completion(Completion),
}
/// Greet someone
#[derive(Args)]
struct Hello {
/// Who to greet
#[usage(env = "GREET_NAME", default = "world")]
name: String,
}
impl Run for Hello {
type Output = ();
fn run(self) {
println!("hello, {}", self.name);
}
}
/// Print a completion script
#[derive(Args)]
struct Completion {
/// Which shell to generate for
#[usage(long, choices("bash", "zsh", "fish"))]
shell: String,
}
impl Run for Completion {
type Output = ();
fn run(self) {
let shell = match self.shell.as_str() {
"bash" => usage::complete::Shell::Bash,
"zsh" => usage::complete::Shell::Zsh,
_ => usage::complete::Shell::Fish,
};
print!("{}", Greet::completion_script(shell));
}
}
fn main() {
Greet::parse().command.run()
}That is the whole program. The struct is the grammar, the doc comments are the help text, the Run impls are the commands, and main is one line: parse() handles help, version, errors, and completion requests, and #[usage(run)] generated the match that routes the selected command to its impl.
Run it
$ greet hello
hello, world
$ GREET_NAME=Jeff greet hello # env fallback, declared on the field
hello, Jeff
$ greet -l hello usage # a global flag, and a positional
hello, usageHelp was never declared, and never drifts from the declaration:
$ greet --help
greet 0.1.0
Greets people, politely
Usage: greet [-l --loud…] <SUBCOMMAND>
Commands:
completion <--shell <SHELL>>
Print a completion script
hello [NAME]
Greet someone
help
Print this message or the help of the given subcommand(s)
Flags:
-l, --loud… Say it louder
-h, --help Print help
-V, --version Print versionErrors are clap-shaped, exit status included:
$ greet helo
error: unrecognized subcommand 'helo'
tip: a similar subcommand exists: 'hello'
Usage: greet [-l --loud…] <SUBCOMMAND>
For more information, try '--help'.
$ echo $?
2Completions
The completion command above already works — #[usage(completion)] generated the script methods, and parse() answers the runtime protocol the script calls back with:
$ greet completion --shell fish | head -3
# @generated by usage-argv for `greet __complete_word__ --shell fish`
# Do not edit: regenerate it. Needs no other program, and no cached spec —
# the binary answers from the tables it was compiled with.Tab-completing greet now offers hello and completion with their descriptions; --shell offers bash, zsh, fish. install_completion can put the script where the shell looks for it — see Completions.
Docs and manpages
Every binary answers __usage_spec__ with its spec, so the rest of the toolkit needs no wiring:
greet __usage_spec__ > greet.usage.kdl
usage g markdown -f greet.usage.kdl --out-dir docs # markdown docs
usage g manpage -f greet.usage.kdl > greet.1 # man pageThe emitted spec is the same declaration, portably:
name greet
bin greet
version "0.1.0"
about "Greets people, politely"
subcommand_required #true
flag "-l --loud" help="Say it louder" global=#true count=#true var=#true
cmd hello help="Greet someone" {
arg "[NAME]" help="Who to greet" env=GREET_NAME default=world
}A test
[dev-dependencies]
usage = { package = "usage-rs", version = "6", features = ["test", "completions"] }#[test]
fn hello_takes_a_name() {
let words = usage::test::argv(["hello", "Jeff"]);
let cli = usage::test::parse(Greet::spec(), &words.words(), Greet::parse_from).unwrap();
assert!(matches!(cli.command, Commands::Hello(h) if h.name == "Jeff"));
}No process spawned, no output captured — the same tables parse() runs on. See Testing for help-page snapshots and completion assertions.
Where next
- Args and flags for the full attribute vocabulary
- Subcommands for nesting,
flatten, and value enums - Dispatch for contexts (
RunWith) and async commands - Migrating from clap if you have a CLI already