Pular para o conteúdo

Builder @cli

Nesta página

O builder @cli transforma uma struct anotada numa CLI completa sem escrever um parser à mão. Cada campo vira uma opção ou argumento posicional; defaults, tipos e textos de ajuda saem do próprio código — sem divergir da fonte.

Flags e defaults

Anote a struct com @cli(name: "...") e cada campo com @arg(short, long, default, help). Args.parse_args(argv) recebe um array de strings e devolve uma instância tipada:

Três chamadas a parse_args demonstram: sem flags (defaults), forma longa e forma curta com =value. Nenhum argumento de processo real é necessário — o array é injetado diretamente, tornando o exemplo executável no 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 posicionais

@arg(positional) marca um campo como posicional. required torna-o obrigatório; multiple recolhe todos os posicionais restantes numa lista:

Simula cat main.txt x y z: o primeiro posicional vai para input, os demais preenchem extras. Também executável no sandbox — sem dependência do processo.

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 e --version automáticos

Com @cli(name, version) e help: "..." em cada campo, o runtime gera --help e --version sem código adicional. O texto nunca fica desatualizado porque vem diretamente da declaração:

O parse normal continua funcionando; para ver a saída de --help, execute zolo run 11-cli-help.zolo -- --help localmente. O sandbox não suporta --help via 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

Requer a CLI/host do Zolo — abra no playground ou rode localmente.

Subcomandos

Anote um enum com @subcommand num campo da struct principal. Cada variante torna-se um subcomando independente (com suas próprias flags, se necessário), e match faz o despacho — o mesmo padrão de git commit / cargo build:

tool -v build --arch x86_64, tool start e tool test — três invocações demonstradas passando arrays diretamente, sem dependência do processo 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

Desafio

Adicione um quarto subcomando Deploy { @arg(long) env: str } ao enum e implemente o braço correspondente no match. Chame Args.parse_args(["deploy", "--env", "prod"]) e verifique a saída.

Buscar no Zolo

9 resultados

enespt-br