Saltar al contenido

Builder @cli

En esta página

El builder @cli transforma una struct anotada en una CLI completa sin escribir un parser a mano. Cada campo se convierte en una opción o argumento posicional; los defaults, tipos y textos de ayuda salen del propio código — sin divergir de la fuente.

Flags y defaults

Anota la struct con @cli(name: "...") y cada campo con @arg(short, long, default, help). Args.parse_args(argv) recibe un array de strings y devuelve una instancia tipada:

Tres llamadas a parse_args demuestran: sin flags (defaults), forma larga y forma corta con =value. No se necesitan argumentos de proceso reales — el array se inyecta directamente, haciendo el ejemplo ejecutable en el sandbox.

09-cli-flags.zolo
Playground
// Feature: `@cli` builder — declarative argument parsing

// Syntax: annotate a struct with `@cli(name: "...")`. Each field

// becomes a CLI option via `@arg(short, long, default, help)`.

// `Args.parse_args(argv)` parses an array and returns a typed instance.

// When to use: real CLI tools — instead of hand-rolling

// `process.argv()` parsing, get types, defaults, --help and --version

// for free.


@cli(name: "demo")
struct Args {
  @arg(short, long, help: "Verbose")
  verbose: bool,
  @arg(long, short: "n", default: 10, help: "Count")
  count: int,
}

// No flags — defaults kick in.

let a = Args.parse_args([])
print(a.verbose)   // expected: false

print(a.count)     // expected: 10


// Long form: --flag value

let b = Args.parse_args(["--verbose", "--count", "20"])
print(b.verbose)   // expected: true

print(b.count)     // expected: 20


// Short form, with =value syntax.

let c = Args.parse_args(["-v", "-n=42"])
print(c.verbose)   // expected: true

print(c.count)     // expected: 42

Argumentos posicionales

@arg(positional) marca un campo como posicional. required lo hace obligatorio; multiple reúne todos los posicionales restantes en una lista:

Simula cat main.txt x y z: el primer posicional va a input, los demás llenan extras. También ejecutable en el sandbox — sin dependencia del proceso.

10-cli-positional.zolo
Playground
// Feature: `@arg(positional, ...)` — positional arguments

// Syntax: `positional` marks the field as a positional, `required`

// makes it mandatory, `multiple` collects all remaining args.

// When to use: file-input arguments (cat, mv, …), commands that

// take a target plus a variadic list of items.


@cli(name: "cat")
struct Args {
  @arg(positional, required, help: "Input file")
  input: str,
  @arg(positional, multiple, help: "Extra files")
  extras: [str],
}

// Single positional — extras stays empty.

let a = Args.parse_args(["main.txt"])
print(a.input)             // expected: main.txt

print(a.extras.len())  // expected: 0


// Multiple — first goes to `input`, the rest fill `extras`.

let b = Args.parse_args(["main.txt", "x", "y", "z"])
print(b.input)             // expected: main.txt

print(b.extras.len())  // expected: 3

print(b.extras[0])         // expected: x

print(b.extras[1])         // expected: y

print(b.extras[2])         // expected: z

--help y --version automáticos

Con @cli(name, version) y help: "..." en cada campo, el runtime genera --help y --version sin código adicional. El texto nunca queda desactualizado porque proviene directamente de la declaración:

El parsing normal sigue funcionando; para ver la salida de --help, ejecuta zolo run 11-cli-help.zolo -- --help localmente. El sandbox no admite --help vía process.argv().

11-cli-help.zolo
Playground
// Feature: `--help` and `--version` are auto-generated from the struct

// Syntax: `@cli(name, version)` plus per-field `help: "..."`. Passing

// `--help` prints usage + every flag with its help text and exits;

// `--version` prints the version line.

// When to use: every real CLI. The help text reflects the struct

// declaration, so it never drifts out of sync.


@cli(name: "demo", version: "1.0")
struct Args {
    @arg(short, long, help: "Verbose output")
    verbose: bool,

    @arg(long, default: 10, help: "Number of items")
    count: int,
}

// Normal parse — defaults flow through.

let a = Args.parse_args([])
print(a.verbose)   // expected: false

print(a.count)     // expected: 10


// To see the help text, run the file with `--help`:

//   zolo run 11-cli-help.zolo -- --help

// →  Usage: demo [OPTIONS]

//    Options:

//      -v, --verbose         Verbose output

//          --count <COUNT>   Number of items (default: 10)

//      -h, --help            Print help

//      -V, --version         Print version

//

// Or `--version`:

//   zolo run 11-cli-help.zolo -- --version

// → demo 1.0

Requiere la CLI o el host de Zolo; ábrelo en el playground o ejecútalo localmente.

Subcomandos

Anota un enum con @subcommand en un campo de la struct principal. Cada variante se convierte en un subcomando independiente (con sus propios flags si es necesario), y match gestiona el despacho — el mismo patrón de git commit / cargo build:

tool -v build --arch x86_64, tool start y tool test — tres invocaciones demostradas pasando arrays directamente, sin dependencia del proceso real.

12-cli-subcommands.zolo
Playground
// Feature: subcommands — `@subcommand` over an enum

// Syntax: declare an enum where each variant is a subcommand. A

// field with `@subcommand cmd: Command` becomes the dispatch slot.

// Each subcommand variant can declare its own flags via `@arg(...)`.

// When to use: tools shaped like `git commit`, `cargo build`, `kubectl get`.

// Variants without fields become no-flag subcommands; tuple/struct

// variants get their own flags.


enum Command {
    Build { @arg(long) arch: str },
    Start,
    Test,
}

@cli(name: "tool")
struct Args {
    @arg(short, long)
    verbose: bool,

    @subcommand
    cmd: Command,
}

// Dispatch via `match` — the idiomatic shape for an enum.

fn run(args: Args) {
    print(args.verbose)
    match args.cmd {
        .Build { arch } => print("build arch={arch}"),
        .Start => print("start"),
        .Test => print("test"),
    }
}

// `tool -v build --arch x86_64`

run(Args.parse_args(["-v", "build", "--arch", "x86_64"]))
// expected:

//   true

//   build arch=x86_64


// `tool start`

run(Args.parse_args(["start"]))
// expected:

//   false

//   start


// `tool test`

run(Args.parse_args(["test"]))
// expected:

//   false

//   test

Desafío

Agrega un cuarto subcomando Deploy { @arg(long) env: str } al enum e implementa el brazo correspondiente en match. Llama a Args.parse_args(["deploy", "--env", "prod"]) y verifica la salida.

Buscar en Zolo

9 resultados

enespt-br