std.cli
Declarative argument parsing. Describe the interface, get a parser and a help screen.
import std.cli
import std.os
spec = {
program: "report",
options: [
{ name: "format", short: "f", default: "text", help: "output format" },
{ name: "verbose", flag: true, help: "explain what is happening" },
],
args: [
{ name: "input", help: "the file to read" },
],
}
opts = cli.parse(spec, os.args())
print(opts.format) # "text" unless --format was given
print(opts.verbose) # false unless --verbose
print(opts.input)
print(opts.rest) # anything after the declared positionals
The surface
| call | |
|---|---|
cli.parse(spec, argv) | parse, or error with a usage message |
cli.help(spec) | the generated help text |
The spec
program- the name shown in usage.options- each{ name, short?, default?, flag?, help? }. Aflagtakes no
value and defaults to false.
args- positional parameters, in order.rest- whatever is left over, always present in the result.
Why declarative
The parser and the help text come from one description, so they cannot disagree. Hand-rolled argument parsing drifts from its --help output almost immediately, and the drift is invisible until someone follows the help and gets an error.
With std.defaults
A natural pairing: put the defaults in ecko.json and let flags override them.
import std.defaults
opts = cli.parse(spec, os.args())
port = if opts.port == null { defaults.port } else { int(opts.port) }
The manifest holds what the project wants; the command line holds what this run wants. See std.defaults.
The cli package
There is also a Layer-3 cli package written in Ecko, with typed flags and richer usage generation. This module is the built-in, no-dependency option; the package goes further.