Skip to main content

Crate fire

Crate fire 

Source
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 typeCommand-line behavior
TRequired --name <VALUE> option parsed with FromStr
Option<T>Optional --name <VALUE> option
boolValue-less --name flag, defaulting to false
&strBorrowed 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 help

Module 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 pub functions become subcommands.
  • Methods and generic functions are not supported.
  • A command returns () or Result<_, E> where E: 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.