Expand description
Turn a Rust function or module into a command-line application with one attribute.
Rust Fire derives the command-line interface from ordinary Rust function signatures. It does not use a global command registry and does not require a separate runner macro.
§Quick start
Add #[fire::main] to a function:
/// Welcome a person.
#[fire::main]
fn welcome(
/// Person to welcome.
name: String,
/// Add an exclamation mark.
excited: bool,
) {
let suffix = if excited { "!" } else { "." };
println!("Welcome, {name}{suffix}");
}The generated executable accepts both --name Ferris and
--name=Ferris. Boolean parameters are flags, so excited is enabled with
--excited.
§Subcommands
Applying main to an inline module turns each function in that module
into a subcommand:
/// Account management commands.
#[fire::main]
mod cli {
/// Create an account.
pub fn create(
/// Account name.
name: String,
/// Assign administrator privileges.
admin: bool,
) {
println!("creating {name}, admin={admin}");
}
/// Remove an account.
pub fn remove(name: String) {
println!("removing {name}");
}
}Function and parameter names are converted from snake_case to
kebab-case. The example exposes create and remove as subcommands.
Only pub functions become subcommands, so a module can keep private
helper functions next to its commands.
§Parameter mapping
| Rust type | Command-line behavior |
|---|---|
T | Required --name <VALUE> option parsed with FromStr |
Option<T> | Optional --name <VALUE> option |
bool | Value-less --name flag, defaulting to false |
&str | Borrowed string option |
Every non-string value is parsed through FromStr.
A parse failure, missing value, repeated option, unknown option, unknown
command, or argument that is not valid UTF-8 is reported on stderr together
with the relevant usage line. CLI errors exit with status code 2.
A value that starts with - is never taken from the next argument, so it
has to be written as --name=-1.
§Generated help
Rust Fire automatically supports -h and --help. Function, module, and
parameter documentation comments become command descriptions:
Welcome a person.
Usage: app --name <NAME> [--excited]
Options:
--name <NAME> Person to welcome.
--excited Add an exclamation mark.
-h, --help Print helpModule applications additionally support app --help to list commands and
app <COMMAND> --help to describe one command.
§Fallible commands
A command returns either () or Result<_, E> where E implements
Display. An error is formatted through Display,
printed to stderr, and causes status code 2:
#[fire::main]
fn deploy(target: String) -> Result<(), &'static str> {
if target == "production" {
return Err("production deployments are disabled");
}
Ok(())
}The return value is dispatched through a trait rather than by matching on the return type, so type aliases work as well:
type Fallible<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[fire::main]
fn deploy(target: String) -> Fallible<()> {
Ok(())
}Any other return type is rejected at compile time.
§Async commands
Passing tokio to the attribute lets commands be async. Each async
command runs on a multi-threaded Tokio runtime, so the application must
depend on tokio with the rt-multi-thread
feature:
/// Fetch a URL.
#[fire::main(tokio)]
async fn fetch(url: String) {
// .await freely here
}The same argument works on modules; async and synchronous commands can be mixed in one module.
§Current limitations
- Command modules must be inline modules, and only their
pubfunctions become subcommands. - Methods and generic functions are not supported.
- A command returns
()orResult<_, E>whereE: Display; any other return type is a compile error. - Async functions require
#[fire::main(tokio)]. - Parameters are named options; positional arguments and short option names are not currently supported.
- Parameter attributes other than documentation comments are rejected.
Attribute Macros§
- main
- Turns a function or inline module into a complete command-line application.