Rust quickstart
Build a greet CLI with a subcommand, environment fallback, shell completions, and an integration test. You will need Rust and Cargo; install the Usage CLI when you reach documentation generation.
A new project
cargo new greet
cd greet
mkdir -p src/cli testsThe example keeps the command-line declaration separate from the two command handlers:
greet/
├── Cargo.toml
├── src/
│ ├── main.rs
│ └── cli/
│ ├── mod.rs
│ ├── hello.rs
│ └── completion.rs
└── tests/
└── cli.rsCargo.toml
[dependencies]
usage = { package = "usage-rs", version = "6", features = ["completions"] }
[dev-dependencies]
usage = { package = "usage-rs", version = "6", features = ["test"] }src/cli/mod.rs
mod completion;
mod hello;
use completion::Completion;
use hello::Hello;
use usage::{Cli, Subcommands};
/// Greets people, politely
#[derive(Cli)]
#[usage(bin = "greet", version = "0.1.0", completion)]
pub(crate) struct Greet {
#[usage(subcommand)]
pub(crate) command: Commands,
}
#[derive(Subcommands)]
#[usage(run)] // Generate the match from each variant to its `Run` implementation.
pub(crate) enum Commands {
Hello(Hello),
Completion(Completion),
}Doc comments on these types become help text. completion adds the shell-script methods and the hidden request that parse() handles when a completion script calls the binary.
src/cli/hello.rs
use usage::{Args, Run};
/// Greet someone
#[derive(Args)]
pub(crate) 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);
}
}src/cli/completion.rs
use usage::{Args, Run};
use super::Greet;
/// Print a completion script
#[derive(Args)]
pub(crate) 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,
};
// Generate the script for the shell the user selected.
print!("{}", Greet::completion_script(shell));
}
}src/main.rs
mod cli;
use cli::Greet;
use usage::Run;
fn main() {
// `parse()` handles help, version, errors, and completion requests.
// `run()` is the match generated by `#[usage(run)]`.
Greet::parse().command.run()
}Run it
Build the executable and put this project's debug output on PATH for this shell. The examples below use a POSIX shell:
cargo build
export PATH="$PWD/target/debug:$PATH"You can also use cargo run -- hello instead of greet hello.
$ greet hello
hello, world
$ GREET_NAME=Jeff greet hello # env fallback, declared on the field
hello, JeffHelp comes from the types and their doc comments:
$ greet --help
greet 0.1.0
Greets people, politely
Usage: greet <SUBCOMMAND>
Commands:
completion Print a completion script
hello Greet someone
help Print this message or the help of the given subcommand(s)
Flags:
-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 <SUBCOMMAND>
For more information, try '--help'.
$ echo $?
2Completions
usage-rs provides the same dynamic completion system that powers mise. The generated script is specific to the user's shell. When they press Tab, it calls back into greet; parse() handles that hidden request and returns candidates and descriptions for the command line being typed.
$ greet completion --shell fish | head -1
# @generated by usage-argv for `greet __complete_word__ --shell fish`Tab-completing greet offers hello and completion with their descriptions; --shell offers bash, zsh, and fish. install_completion writes the generated script where that shell looks for it. See Completions for custom completers, aliases, file completion, and installation.
Docs and manpages
The generated spec can be passed to usage-cli for documentation, manpages, completion scripts, and other formats:
greet __usage_spec__ > greet.usage.kdl
usage generate markdown --file greet.usage.kdl --multi --out-dir docs
usage g manpage -f greet.usage.kdl > greet.1 # man pageThis is the spec for the example CLI:
name greet
bin greet
version "0.1.0"
about "Greets people, politely"
subcommand_required #true
flag "-h --help" help="Print help" action=help builtin=#true
flag "-V --version" help="Print version" action=version builtin=#true
cmd hello help="Greet someone" {
flag "-h --help" help="Print help" action=help builtin=#true
arg "[NAME]" help="Who to greet" env=GREET_NAME default=world
}
cmd completion help="Print a completion script" {
flag --shell help="Which shell to generate for" required=#true {
arg <SHELL> {
choices bash zsh fish
}
}
flag "-h --help" help="Print help" action=help builtin=#true
}tests/cli.rs
An integration test runs the compiled binary and captures the output a user sees:
#[test]
fn hello_greets_by_name() {
let output = usage::test::command!("greet", "hello", "Jeff").assert_success();
assert_eq!(output.stdout_text(), "hello, Jeff\n");
}command! also retains stderr and the exit status. See Testing for process-free parser assertions, help-page snapshots, and completion candidates.
Run the test from the project root:
cargo testWhere 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