Closed vocabulary for argument value types: every variant names a
type explicitly present in the app, so the reference table never
lists a type that does not exist. Shapes ($var targets, KEY=value
pairs) are not types: the central validator only checks value types
and arity, while each command’s lower enforces its own shapes with
command-specific errors.
ArgType::Any is the single exception: it renders as <any>,
visibly a placeholder rather than a type name.
ArgType::OneOf renders its inline options, likewise self-describing.
Canonical display for a byte count: largest exact unit (500B,
10KB, 2MB, 1GB, binary 10KIB and friends), falling back to
bytes. Round-trips through parse_byte_count.
Parse a byte-count token (500, 500B, 10KB, 2MB, 1GB, 1TB
decimal; 10KIB, 2MIB, 1GIB, 1TIB binary; a bare number means
bytes). Digit runs may group with _ separators (1_000_000),
Rust-literal style: leading, trailing, and doubled separators fail.
One optional space may separate the number from its unit (1 GB);
anything more stays rejected, and bare DSL tokens still split
upstream (this covers quoted strings, variables, and flag values).
Fractions stay rejected by design (1.5GB fails: float parsing
cannot hold exact byte boundaries past 2^53, and every sub-unit has
an exact integer spelling like 1500MB). Matching is
case-insensitive (1gb, 1GB, and 1Gb agree), mirroring
parse_duration: digits only, no fractions, no signs.
Parse a TIMEOUT duration token (500ms, 10s, 2m, 1h; a bare
number means seconds). One optional space may separate the number
from its unit (30 s), like byte counts: bare DSL tokens still
split upstream, so this covers quoted strings and variables.
Single-token KEY=value split for direct lower_command callers and
exotic keys the grammar cannot classify (single tokens only : no whitespace
reassembly, so the quoted-space corruption class cannot arise here).
Returns Ok(None) when there is no =.
Strip Rust-style _ digit separators when well-formed (digits and
at most one ., separators only between digits, never leading,
trailing, or doubled, with an optional leading +/- sign passing
through for signed domains); otherwise return the text untouched so
the caller’s own parse decides. The single rule behind every
string-to-number coercion (INT validation, assertion operands,
byte counts), so 1_000_000 and -1_000 read identically everywhere
while 1__0 and 1_ never silently become numbers. Unsigned
callers still reject - at parse time; + parses like every other
Rust-parsed number in the DSL.
Validate positional args against a command’s declared specs.
Missing required positionals, statically-known type violations, and
trailing positionals beyond a fixed-arity spec all fail here;
templates, variables, and fragments defer to the runtime resolvers.
A trailing Rest spec absorbs any number of positionals.