argx 0.1.0

Expressive command-line parsing and configuration for Rust.
Documentation

Argx is a derive-first command-line parser and configuration library for Rust. Rust types define its interface. Generated static metadata drives parsing, typed binding, help, diagnostics, completions, schemas, and ordered configuration resolution.

The model is deliberately small:

  • Parser defines a root command.
  • Args defines reusable argument groups.
  • Subcommand defines typed child commands.
  • Config resolves typed values across explicitly ordered layers.
  • one static command model drives parsing, help, completions, and schema discovery.

Full API documentation and behavioral details are available on docs.rs/argx.

Installation

cargo add argx

Features

  • derive is enabled by default. It exports the Parser, Args, Subcommand, ValueEnum, and Config derives plus the #[argx(...)] attribute macro.
  • toml enables TOML configuration layers and implies derive.

Enable TOML support with:

cargo add argx --features toml

Quick start

use argx::{Args, Parser, Subcommand};

#[derive(Parser)]
#[argx(name = "acme", version = env!("CARGO_PKG_VERSION"))]
struct Cli {
    /// Enable verbose diagnostics.
    #[argx(long, global)]
    verbose: bool,

    #[argx(subcommand)]
    command: Command,
}

#[derive(Subcommand)]
enum Command {
    /// Read one object.
    Get(GetArgs),

    /// Print service status.
    Status,
}

#[derive(Args)]
struct GetArgs {
    /// Object identifier.
    id: String,
}

fn main() {
    let cli = Cli::parse();
    if cli.verbose {
        eprintln!("verbose mode enabled");
    }
    match cli.command {
        Command::Get(args) => println!("{}", args.id),
        Command::Status => println!("ok"),
    }
}
$ acme get object-7
object-7

$ acme --help
Usage: acme [OPTIONS] <COMMAND>
...

Rust field shapes determine cardinality and conversion, while Rust documentation and #[argx(...)] metadata define the user-facing CLI. The same derived model is reused for help, completions, and schema discovery rather than maintaining parallel descriptions of the command surface.

Configuration

#[derive(argx::Config)] resolves one typed configuration from an explicitly ordered stack of layers. Defaults, dotenv files, process environment, and argv all supply sparse values for the same Rust fields.

use argx::{Argv, Defaults, Environment};

#[derive(argx::Config)]
#[argx(prefix = "ACME")]
struct Config {
    #[argx(long, default = 4)]
    workers: usize,

    #[argx(long)]
    endpoint: String,
}

let config = Config::loader()
    .layer(Defaults)
    .layer(Environment)
    .layer(Argv::current())
    .resolve()?;

Layers are applied in declaration order. Later layers replace only fields they actually supply, so precedence comes from composition rather than a policy built into Argx. Declared defaults are not implicit: they participate only when Defaults is added. A non-optional field becomes required only after every configured layer has been considered.

A configuration-level prefix maps fields to environment variables. For example, #[argx(prefix = "ACME")] maps workers to ACME_WORKERS. A flattened server.workers field maps to ACME_SERVER_WORKERS. #[argx(env = "EXACT_NAME")] selects an exact variable instead. Environment layers inspect only mapped variables. Unrelated process variables are ignored.

Files are explicit layers too:

let config = Config::loader()
    .layer(Dotenv::new(".env"))
    .resolve()?;

With the toml feature enabled, Toml::new("acme.toml") adds a TOML layer. TOML interpolation can observe environment values established by earlier Dotenv or Environment layers, so layer order also controls interpolation visibility. Argx performs no file discovery.

Configuration fields participate in argv only when they carry CLI metadata such as long or short. Collection fields marked #[argx(delimited)] use the same comma-separated syntax in argv and environment layers, so --origins a,b and ACME_ORIGINS=a,b resolve to the same Vec<T> shape. #[argx(flatten)] composes a nested Config across every layer. See the configuration example for a runnable version.

Output

Argx reserves -O / --output and -F / --fields as global output controls. Text is the default. -O json selects structured JSON output, while repeatable comma-separated -F values select dotted fields from a schema-enabled handler result:

acme get object-7 -O json
acme get object-7 -O json -F id,owner.name

Use an invocation entry point when the application needs to honor these controls. The parsed command and output context remain separate, so applications keep ownership of dispatch and human-readable rendering while Argx handles JSON serialization and schema-validated projection:

use argx::{OutputFormat, Parser as _};

let invocation = Cli::try_parse_invocation()?;
let (cli, output) = invocation.into_parts();
let value = run(cli)?;

match output.format() {
    OutputFormat::Text => println!("{value:?}"),
    OutputFormat::Json => println!("{}", output.render_json(&value)?),
    _ => unreachable!(),
}
# Ok::<(), Box<dyn std::error::Error>>(())

--fields requires JSON output and a typed result schema for the selected command. Field selection applies only to successful output. See Invocation and Output for the embedding API.

Schema discovery

A parser marked #[argx(schema)] exposes its command interface as Draft 2020-12 JSON Schema for tooling and agents:

acme schema
acme schema objects
acme schema objects get
acme objects get object-7 --schema

Discovery is shallow while the selected command has children, so the root and objects forms list only their immediate child commands. Reaching a leaf such as objects get automatically exposes its complete invocation, result, error, and referenced type schemas. Use --full on a structural command when recursive expansion is useful:

acme schema objects --full

Both discovery forms describe the same selected command. #[argx(handler = CommandType)] can associate an executable leaf with typed result and error schemas, while #[argx(schema)] derives the underlying Rust data-model schemas through Schemars without requiring downstream users to depend on Schemars directly.

use argx::{Args, argx};

#[derive(Args)]
struct GetCommand {
    id: String,
}

#[argx(schema)]
struct GetOutput {
    id: String,
}

#[argx(schema)]
enum GetError {
    NotFound,
}

#[argx(handler = GetCommand)]
fn get(command: GetCommand) -> Result<GetOutput, GetError> {
    Ok(GetOutput { id: command.id })
}

See the schema example and crate documentation for structural schema composition and the exact discovery contract.

Examples

The examples are executable documentation. Start with basic for the smallest integration point or complete for the complete Argx API. The remaining examples isolate one major subsystem. Detailed behavior is documented on docs.rs/argx.

Example Focus Try it
basic Smallest complete parser and built-in help cargo run --example basic -- --help
complete Integrated reference application showing the complete Argx API cargo run --example complete -- get object-7 -O json -F id
arguments Arguments, defaults, aliases, constraints, and value enums cargo run --example arguments -- input.txt --format json
commands Subcommands, flattening, structured help, aliases, and versions cargo run --example commands -- --verbose add hello --force
configuration Ordered defaults, environment, and argv configuration cargo run --example configuration -- --workers 8
schema Schema discovery and typed handler result/error contracts cargo run --example schema -- schema objects get
completions Dynamic shell-completion adapters cargo run --example completions -- zsh

Support

Argx supports Linux and macOS natively. Windows is supported through the Windows Subsystem for Linux (WSL). Native Windows targets are not supported.

MSRV

The current MSRV (minimum supported Rust version) is 1.95.

Argx will keep a rolling MSRV policy of at least two versions behind the latest stable release (so if the latest stable release is 1.97, we would support 1.95).

Note that the MSRV is not increased automatically.

Contributing

Contributions to Argx are welcome. See the Contributing Guide for information on reporting bugs, proposing features, submitting pull requests, and the licensing terms that apply to contributions.

Security Policy

If you believe you have found a security vulnerability, please do not report it through GitHub Issues. See our Security Policy for reporting instructions.

Credit

Argx is inspired in part by Usage, Clap and Incur.

Usage was a particularly important influence on Argx’s compile-time architecture: static command metadata, separation of argv parsing from typed construction, compile-time composition of commands and argument groups, and the use of one authoritative CLI description to drive parsing and other derived behavior.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

This software includes third-party components subject to separate license terms. See THIRD_PARTY_NOTICES.md.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Argx by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.