Modules

Args

moduleextensionSource

Available on macOS, Linux, Windows, Wasm

#import "Extensions/Args";

Command-line parsing from a struct: each field is an option, notes like @short=c refine it. parse handles --help, --version and bad input for you; try_parse returns the error instead, which suits tests. help_text and completions render the same description.

Example↗
Args :: #import "Extensions/Args";

Cli :: struct {
    paths: [..] string; @positional
    check: bool;       @short=c
    jobs:  int = 4;    @short=j @env=TOOL_JOBS @range(1,64)
}
HELP :: Args.Help(Cli).{ about = "Does things.", paths = "Files to read", check = "Only check", jobs = "Parallel jobs" };

main :: () {
    cli, set := Args.parse(Cli, HELP);  // --help, --version, errors (exit 2) are handled
    if set.jobs print("jobs: %\n", cli.jobs);  // set.<field> is true when the user supplied it
}

The parser is generated from the struct at compile time, so parsing is plain string comparisons and stores into the fields.

Procedures#

General#

($T: Type, $help: Help(T), argv: [] string, environment: [] string = .[]) -> (T, Error, Is_Set(T))

Parses argv (element 0 is the program name) into a T. Never prints and never exits. error.kind is .NONE on success; .HELP and .VERSION carry the text to print in error.text. On any error the returned struct holds what was parsed so far. environment holds "NAME=value" entries that win over the process environment when an @env option is looked up (for tests; leave it empty to read the real one).

Args :: #import "Extensions/Args";

Cli :: struct {
    paths: [..] string; @positional
    check: bool; @short=c
    jobs: int = 4; @short=j @env=TOOL_JOBS @range(1,64)
}
HELP :: Args.Help(Cli).{ about = "Does things.", paths = "Files to read", check = "Only check", jobs = "Parallel jobs" };

main :: () {
    cli, error, set := Args.try_parse(Cli, HELP, .["tool", "-c", "--jobs=8", "a.jai", "b.jai"]);
    assert(error.kind == .NONE);
    assert(cli.check && cli.jobs == 8 && cli.paths.count == 2 && cli.paths[1] == "b.jai");
    assert(set.jobs && set.check && set.paths);

    // The environment argument stands in for the process's own, and the option given on the line wins.
    cli, error, set = Args.try_parse(Cli, HELP, .["tool"], .["TOOL_JOBS=16"]);
    assert(error.kind == .NONE && cli.jobs == 16 && set.jobs);

    // A failure is returned, never printed: the kind says what went wrong, the message says it in words.
    cli, error, set = Args.try_parse(Cli, HELP, .["tool", "--jobs", "100"]);
    assert(error.kind != .NONE && error.message.count > 0);
    cli, error, set = Args.try_parse(Cli, HELP, .["tool", "--help"]);
    assert(error.kind == .HELP && error.text.count > 0);
}
↗
($T: Type, $help: Help(T)) -> (T, Is_Set(T))

Parses the process's arguments. --help and --version print and exit 0; a bad command line prints the error with a usage hint to stderr and exits 2.

Args :: #import "Extensions/Args";

Cli :: struct { name: string = "world"; @short=n }
HELP :: Args.Help(Cli).{ about = "Greets.", name = "Who to greet" };

cli, set := Args.parse(Cli, HELP);
print("hello, %\n", cli.name);
↗

This one is only checked to compile, not run.

($T: Type, $help: Help(T), shell: Shell, program := "") -> string

The completion script for shell, to be saved where that shell loads completions from. The program name is argv[0]'s basename unless program says otherwise.

Args :: #import "Extensions/Args";

Cli :: struct { verbose: bool; @short=v }
HELP :: Args.Help(Cli).{ verbose = "Talk more" };

script := Args.completions(Cli, HELP, .BASH, "tool");
assert(script.count > 0);
↗
($T: Type, $help: Help(T), program := "", color := false) -> string

The help text of the whole program (what --help prints), for tools that show it themselves.

Args :: #import "Extensions/Args";

Cli :: struct { verbose: bool; @short=v }
HELP :: Args.Help(Cli).{ about = "Does things.", verbose = "Talk more" };

text := Args.help_text(Cli, HELP, "tool");
assert(text.count > 0);
↗

Render#

(error: Error)

Prints a parse error the way the compilers print theirs:

Types#

:: enum

Why parsing stopped. HELP and VERSION are not failures: error.text is what to print.

NONE
HELP
VERSION
UNKNOWN_OPTION
UNKNOWN_COMMAND
MISSING_VALUE
INVALID_VALUE
OUT_OF_RANGE
UNEXPECTED_VALUE
UNEXPECTED_ARGUMENT
MISSING_REQUIRED
MISSING_COMMAND
CONFLICT
REQUIRES
:: struct

What went wrong (or what to print for --help), with enough detail to build your own message. message is ready to show; suggestion is the closest known name when the user mistyped one.

message: string
unknown option --chek``
option: string
the option or argument the message is about, as written (--chek)
value: string
the offending value, when there is one
suggestion: string
the nearest known name (--check), when one is close
command: string
the command that failed, tool build
usage: string
tool build [OPTIONS] <PATH>
text: string
for .HELP and .VERSION: what to print
:: enum

Which output gets colour in help and errors. AUTO follows NO_COLOR, FORCE_COLOR, CLICOLOR_FORCE, TERM and whether the stream is a terminal.

AUTO
ALWAYS
NEVER
:: struct

The help strings for T: about and version, then one string per option, named after the field. A subcommand's field holds that subcommand's own Help. A field not named in the literal has no help; that is a compile-time warning unless the field is @hidden.

about: string
version: string
:: struct

One bool per field of T: true when the user supplied the option (on the command line or through its @env). A subcommand field holds the Is_Set of that subcommand's struct.

:: enum

The standard stream a piece of output goes to; colour is decided per stream.

OUTPUT
ERROR