~/bend-docscommunity

main.bend source

main.bend on the hub · documented module

# shake: a proven command-line argument parser for Bend 2, with subcommands and help.## Describe a program with `app`, `sub`, `flag`, `opt`, `many`, `pos` and# `rest`; `parse` binds argv against it, and# the result is read one command at a time, as clap's: `get`, `get_all` and# `on` read a command's own bindings, and `sub_name`, `sub_of`, `at` and# `path_of` reach the subcommands selected under it. `help` writes the usage# page for a command path. For a failed parse, `help_path` tells a request for help# from an error, `err_text` writes the message and `err_path` names the command# it failed in. `check` reports a spec that contradicts itself. `argv` is the# process's arguments. This file is the interface: everything under src/ is# internal.import Baseimport ./src/cli.bend as Simport ./src/args.bend as Argsimport ./src/check.bend as K# a program specdef Cli() -> Data:  S.Cli# a nested commanddef Sub() -> Data:  # noqa: L001 a type alias of the interface  S.Sub# one argument: a flag, an option, a positional or a rest positionaldef Arg() -> Data:  # noqa: L001 a type alias of the interface  S.Arg# a successful parse, one command at a time: the bindings the command made,# and the subcommand selected under it with its own Matcheddef Matched() -> Data:  S.Matched# a failed parsedef ParseErr() -> Data:  S.ParseErr# one way a spec contradicts itselfdef SpecErr() -> Data:  # noqa: L001 a type alias of the interface  K.SpecErr# a program: name, about, optional version, top-level args and commandsdef app(  +name: String,  +about: String,  version: Maybe<&2, String>,  args: List<&2, S.Arg>,  subs: List<&2, S.Sub>) -> S.Cli:  S.app(name, about, version, args, subs)# a nested commanddef sub(  # noqa: L001 a builder: its result is the Arg or Sub it names, nothing to state  +name: String,  +about: String,  args: List<&2, S.Arg>,  subs: List<&2, S.Sub>) -> S.Sub:  S.sub(name, about, args, subs)# a boolean flagdef flag(  # noqa: L001 a builder: its result is the Arg or Sub it names, nothing to state  +name: String,  short: Maybe<&2, String>,  long: Maybe<&2, String>,  +help: String) -> S.Arg:  S.flag(name, short, long, help)# a valued optiondef opt(  # noqa: L001 a builder: its result is the Arg or Sub it names, nothing to state  +name: String,  short: Maybe<&2, String>,  long: Maybe<&2, String>,  +help: String,  required: Bool,  default: Maybe<&2, String>,  choices: List<&2, String>) -> S.Arg:  S.opt(name, short, long, help, required, default, choices)# an option that may be given more than once; `get_all` reads every value, in# order, and `get` the lastdef many(  # noqa: L001 a builder: its result is the Arg or Sub it names, nothing to state  +name: String,  short: Maybe<&2, String>,  long: Maybe<&2, String>,  +help: String,  required: Bool,  default: Maybe<&2, String>,  choices: List<&2, String>) -> S.Arg:  S.many(name, short, long, help, required, default, choices)# a positionaldef pos(  # noqa: L001 a builder: its result is the Arg or Sub it names, nothing to state  +name: String,  +help: String,  required: Bool,  default: Maybe<&2, String>,  choices: List<&2, String>) -> S.Arg:  S.pos(name, help, required, default, choices)# a rest positional: every leftover word, as one namedef rest(  # noqa: L001 a builder: its result is the Arg or Sub it names, nothing to state  +name: String,  +help: String,  required: Bool,  default: Maybe<&2, String>,  choices: List<&2, String>) -> S.Arg:  S.rest(name, help, required, default, choices)# every way a spec contradicts itself: a rest positional that is not the# last, a repeated argument name or spelling, a repeated subcommand name, a# subcommand named `help`, a default outside its choices, a required# positional after an optional one. Empty for a well-formed spec, which is# what SPEC.md's parse guarantees are about; `parse` does not call itdef check(spec: S.Cli) -> List<&2, K.SpecErr>:  K.check(spec)# a report of `check` as one linedef spec_err_text(err: K.SpecErr) -> String:  K.text(err)# argv against a program specdef parse(spec: S.Cli, words: List<&2, String>) -> Result<&2, &2, S.ParseErr, S.Matched>:  S.parse(spec, words)# the value this command bound to `name` last, or None when it bound nonedef get(found: S.Matched, +name: String) -> Maybe<&2, String>:  S.get(found, name)# every value this command bound to `name`, in argv orderdef get_all(found: S.Matched, +name: String) -> List<&2, String>:  S.get_all(found, name)# whether this command's flag named `name` was setdef on(found: S.Matched, +name: String) -> Bool:  S.on(found, name)# the names of the subcommands selected below this command, in order: the# whole selected path for the Matched `parse` answersdef path_of(found: S.Matched) -> List<&2, String>:  S.path_of(found)# the name of the subcommand selected under this command, or Nonedef sub_name(found: S.Matched) -> Maybe<&2, String>:  S.sub_name(found)# the Matched of subcommand `name` when it is the one selected, else Nonedef sub_of(found: S.Matched, +name: String) -> Maybe<&2, S.Matched>:  S.sub_of(found, name)# the Matched of the command `path` names below this one, following `sub_of`# name by name, or None where a name is not the one selecteddef at(found: S.Matched, path: List<&2, String>) -> Maybe<&2, S.Matched>:  S.at(found, path)# the usage page for `path` under `spec`; an unknown name in the path is# skippeddef help(+spec: S.Cli, path: List<&2, String>) -> String:  S.help(spec, path)# the message for a failed parse, ending with the root usage line; empty for# a request for help, which the caller answers with `help`def err_text(+spec: S.Cli, err: S.ParseErr) -> String:  S.err_text(spec, err)# the command path where the parse failed: the path selected when it did, or# for a request for help the path it asks aboutdef err_path(err: S.ParseErr) -> List<&2, String>:  match err:    case S.UnknownFlag{at, _flag}:      at    case S.Missing{at, _name}:      at    case S.NoValue{at, _name}:      at    case S.BadValue{at, _name, _value}:      at    case S.NeedHelp{path}:      path    case S.Unexpected{at, _arg}:      at    case S.Repeated{at, _name}:      at# the command path of a request for help (`help`, `help <path>`, `--help`),# or None# when the parse failed for another reasondef help_path(err: S.ParseErr) -> Maybe<&2, List<&2, String>>:  match err:    case S.NeedHelp{path}:      Some{path}    case S.UnknownFlag{_at, _flag}:      None{}    case S.Missing{_at, _name}:      None{}    case S.NoValue{_at, _name}:      None{}    case S.BadValue{_at, _name, _value}:      None{}    case S.Unexpected{_at, _arg}:      None{}    case S.Repeated{_at, _name}:      None{}# the process's arguments, each word reusable, without the program name# that `IO.args` starts with (bend 2.0.32 and later). A compiled program's# runtime has already acted on `--bend-help`, `--gpu-build`, `--threads N`# and `--gpu X` and taken the first `--`, passing every word after it here# unexamined; so the `--` that `parse` sees is the user's second onedef argv() -> IO(List<&2, String>):  # noqa: L001 IO: reads argv  Args.argv()