Skip to main content

Crate flodl_cli_macros

Crate flodl_cli_macros 

Source
Expand description

#[derive(FdlArgs)] – proc-macro derive for flodl-cli’s argv parser.

This crate is re-exported by flodl-cli as flodl_cli::FdlArgs, so downstream binaries depend on flodl-cli, not on this crate directly.

The derive turns a plain struct with named fields into an argv parser plus JSON schema emitter plus ANSI-coloured help renderer. One struct is the single source of truth: doc-comments become help text, attribute metadata becomes schema, field types become typed values.

§Field attributes

Each field carries exactly one of #[option(...)] (named flag, kebab-cased from the field ident) or #[arg(...)] (positional). The field type determines cardinality:

  • bool – absent = false, present = true. #[option] only.
  • T – scalar, required. #[option] must supply default = "...".
  • Option<T> – scalar, optional. Absent = None.
  • Vec<T>#[option]: repeatable. #[arg]: variadic, last.

Supported keys for #[option]: short, default, choices, env, completer. For #[arg]: default, choices, variadic, completer. Reserved flags (--help, --version, --quiet, --env, and their shorts) cannot be shadowed; collisions error at derive time.

§Example

The example below depends on the flodl-cli crate; it is marked ignore because this crate is a proc-macro and doesn’t depend on flodl-cli itself. Copy the snippet into a flodl-cli-depending binary to try it.

use flodl_cli::{FdlArgs, parse_or_schema};

/// Train a model.
#[derive(FdlArgs, Debug)]
struct TrainArgs {
    /// Model architecture to use.
    #[option(short = 'm', choices = &["mlp", "resnet"], default = "mlp")]
    model: String,

    /// Number of epochs.
    #[option(short = 'e', default = "10")]
    epochs: u32,

    /// API key, read from env if flag is absent.
    #[option(env = "WANDB_API_KEY")]
    wandb_key: Option<String>,

    /// Extra dataset paths.
    #[arg(variadic)]
    datasets: Vec<String>,
}

fn main() {
    let args: TrainArgs = parse_or_schema();
    // `--help` and `--fdl-schema` are intercepted by parse_or_schema.
    let _ = args;
}

§Enum form (subcommands)

Deriving on an enum of newtype variants turns each variant into a subcommand. The derive is a thin dispatcher: it peels the leading subcommand token and delegates parsing, schema, and help to the wrapped type, which carries its own #[derive(FdlArgs)]. No field parsing happens at the enum level.

use flodl_cli::{FdlArgs, parse_or_schema};

/// flodl letter CLI.
#[derive(FdlArgs)]
enum Cli {
    /// Train a letter model on a dataset
    Train(TrainArgs),
    /// Evaluate a trained letter model on a test split
    Eval(EvalArgs),
    /// Generate samples (subcommand renamed from the variant ident)
    #[command(name = "gen")]
    Generate(GenArgs),
}

fn main() {
    match parse_or_schema::<Cli>() {
        Cli::Train(a) => { /* ... */ let _ = a; }
        Cli::Eval(a) => { let _ = a; }
        Cli::Generate(a) => { let _ = a; }
    }
}
  • Subcommand name = the variant ident kebab-cased (TrainSubscantrain-subscan), overridable with #[command(name = "...")].
  • Variant doc-comments become the per-subcommand descriptions shown in the parent --help command list.
  • --help is contextual: <bin> train --help shows train’s flags, <bin> --help shows the command list. --fdl-schema emits the full tree.
  • Only single-tuple (newtype) variants are supported — each subcommand is a struct. Unit, named-field, and multi-field variants are rejected at derive time. A variant may itself wrap another FdlArgs enum for nested subcommands.

See the flodl-cli crate for the user-facing API (parse_or_schema, FdlArgsTrait, Schema) and the full CLI reference.

Derive Macros§

FdlArgs
Derive FdlArgs to generate an argv parser, --fdl-schema JSON emitter, and ANSI-coloured --help renderer.