Skip to main content

usage_argv/
lib.rs

1//! A zero-allocation argv parser for [usage](https://usage.jdx.dev) specs.
2//!
3//! This crate implements the binding rules of [the argv grammar]: which token
4//! becomes which flag or argument, when a word selects a subcommand, and what
5//! is an error. It does so without building a command tree, without allocating,
6//! and in one pass.
7//!
8//! It is the runtime half of a compiled parser. The tables it reads are meant to
9//! be emitted by a derive macro as `static` data, so that starting a parse costs
10//! nothing at all: there is no construction step to pay for, only the walk over
11//! `argv`.
12//!
13//! # Shape of the API
14//!
15//! Parsing yields [`Event`]s rather than a map. A map would have to allocate,
16//! and would then have to be read back out again — whereas generated code can
17//! assign an event straight into a struct field. This is the same reason serde
18//! deserializes into your type instead of into a `Value`.
19//!
20//! ```
21//! use usage_argv::{Arg, Command, Event, Flag, Parser};
22//!
23//! static FORCE: Flag = Flag { key: 0, longs: &["force"], shorts: b"f", ..Flag::BOOL };
24//! static FILE: Arg = Arg { key: 1, ..Arg::REQUIRED };
25//! static ROOT: Command = Command {
26//!     name: "ex",
27//!     flags: &[&FORCE],
28//!     args: &[&FILE],
29//!     ..Command::EMPTY
30//! };
31//!
32//! let argv = ["--force", "a.txt"].map(std::ffi::OsStr::new);
33//! let mut parser = Parser::new(&ROOT, &argv);
34//!
35//! let mut force = false;
36//! let mut file = None;
37//! while let Some(event) = parser.next_event() {
38//!     match event.expect("valid command line") {
39//!         Event::Flag { flag, .. } if flag.key == 0 => force = true,
40//!         Event::Arg { value, .. } => file = Some(value),
41//!         _ => {}
42//!     }
43//! }
44//! assert!(force);
45//! assert_eq!(file, Some(&b"a.txt"[..]));
46//! ```
47//!
48//! # Values are bytes
49//!
50//! An [`Event`] carries `&[u8]`, borrowed from `argv`. Converting to `&str` is
51//! the caller's step ([`as_str`]), and it is the right place for the only
52//! failure a value can have: a command line that is not valid UTF-8 still
53//! *parses* — flags match, subcommands route — and only the values that are
54//! actually looked at can fail to convert.
55//!
56//! Slicing an `OsStr` into `&str` pieces safely is not possible without
57//! allocating or `unsafe`. Bytes are what is left, and they turn out to be the
58//! honest interface anyway.
59//!
60//! The reverse conversion is [`os_string_from_bytes`], which lets a `PathBuf`
61//! field hold a filename that is not UTF-8 rather than a mangled copy of one. On
62//! Unix that is lossless and safe; on Windows, where WTF-8 makes it partial, a
63//! value that will not convert is reported. Either way this crate contains no
64//! `unsafe`, which a conversion that guessed would have cost.
65//!
66//! # What this crate does not do
67//!
68//! Only binding. Required-ness, `choices`, `env` fallback, defaults, `var_min`
69//! and `var_max` are all decided *after* the last token is read, and they need to
70//! know a value's type, so they belong to the layer that owns the target struct.
71//! Keeping them out is what makes this loop small.
72//!
73//! # Features
74//!
75//! - `spec` — a parallel tree of cold metadata (help text, choices, defaults,
76//!   effects) and a writer that emits it as a usage spec. Off by default: a
77//!   successful parse never reads any of it, so a CLI that only wants a parser
78//!   should not compile it.
79//! - `complete` — answering a partial command line ([`complete`]), the shell
80//!   scripts that ask ([`script`]), and putting one of those scripts where its
81//!   shell will look for it ([`install`]). Installing ships with the scripts
82//!   rather than behind a gate of its own: a script a CLI still has to tell its
83//!   users to redirect by hand is the unfinished half of shipping one.
84//!
85//! [the argv grammar]: https://usage.jdx.dev/spec/argv
86
87// One module opts out: `tty`, which asks the kernel how wide the terminal is. `forbid` would
88// make that opt-out impossible, and the alternative to the opt-out is a dependency — this
89// crate has none, and a CLI that wraps its help at 80 columns on a wide terminal is worse
90// than two dozen lines of reviewed FFI. Everything else in the crate is still refused.
91#![deny(unsafe_code)]
92
93/// Terminate at the compiled CLI entry-point boundary.
94///
95/// Kept in the runtime rather than expanded into an adopter crate so a project that
96/// forbids direct `std::process::exit` calls does not attribute the derive's process
97/// boundary to application code. `Cli::parse_from*` continues to return errors.
98#[doc(hidden)]
99#[allow(clippy::disallowed_methods)]
100pub fn __usage_process_exit(status: i32) -> ! {
101    std::process::exit(status)
102}
103
104use std::ffi::{OsStr, OsString};
105
106/// A value's shell-native completion class for `#[usage(value_hint = ...)]`.
107///
108/// This lives in the runtime crate so a declaration never needs clap merely to describe what
109/// kind of path a shell should offer. It is metadata only and adds no work to a successful
110/// parse.
111#[derive(Clone, Copy, Debug, Eq, PartialEq)]
112pub enum ValueHint {
113    /// Let the shell use its normal fallback behavior.
114    Unknown,
115    /// No structured hint applies; suppress the shell's path fallback.
116    Other,
117    /// A path to a file.
118    FilePath,
119    /// A path to either a file or a directory.
120    AnyPath,
121    /// A path to a directory.
122    DirPath,
123    /// A path to an executable file.
124    ExecutablePath,
125    /// A command name, resolved through the shell's command table and `PATH`.
126    CommandName,
127    /// One string containing a command and any arguments.
128    CommandString,
129    /// A trailing argv vector: complete the first value as a command, then its arguments.
130    CommandWithArguments,
131    /// A local operating-system user name.
132    Username,
133    /// A host name known to the shell or operating system.
134    Hostname,
135    /// A web address. This suppresses path fallback but offers no finite candidate set.
136    Url,
137    /// An email address. This suppresses path fallback but offers no finite candidate set.
138    EmailAddress,
139}
140
141#[cfg(feature = "complete")]
142pub mod complete;
143#[cfg(feature = "diagnostics")]
144pub mod diagnostic;
145#[cfg(feature = "spec")]
146pub mod embedded;
147#[cfg(feature = "complete")]
148pub mod install;
149#[cfg(feature = "complete")]
150pub mod script;
151
152/// Checks that the `complete` feature is on, with an explanation when it is not.
153///
154/// `#[usage(completion)]` generates code that reaches into [`complete`], which is behind a
155/// feature the *depending* crate enables — a derive cannot turn on a feature of another crate.
156/// Without this, the failure was `unresolved module complete`, which says nothing about the
157/// attribute that caused it.
158#[cfg(feature = "complete")]
159#[macro_export]
160macro_rules! __usage_needs_complete_feature {
161    () => {};
162}
163
164/// See [`__usage_needs_complete_feature`].
165#[cfg(not(feature = "complete"))]
166#[macro_export]
167macro_rules! __usage_needs_complete_feature {
168    () => {
169        ::core::compile_error!(
170            "`#[usage(completion)]` needs usage-argv's `complete` feature. Add it where \
171             usage-argv is depended on: usage-argv = { version = \"…\", features = \
172             [\"spec\", \"complete\"] }"
173        );
174    };
175}
176#[cfg(feature = "spec")]
177pub mod help;
178#[cfg(feature = "spec")]
179mod order;
180// Behind no feature: two traits and no code, so there is nothing here for a binary that
181// does not dispatch to pay for, and a hand-written CLI on the bare runtime can use them.
182pub mod run;
183// Behind no feature either, and public: an application that lays out its own output beside a
184// help page — a table, a progress line — should not have to work the width out a second way
185// and disagree with the page it sits next to.
186#[cfg(feature = "spec")]
187pub mod spec;
188pub mod tty;
189#[cfg(feature = "spec")]
190pub use spec::{parse_args_from, parse_args_from_argv};
191#[cfg(feature = "spec")]
192pub mod warn;
193
194pub use run::{Run, RunAsync, RunAsyncWith, RunWith};
195
196/// How deep a command tree this parser will descend.
197///
198/// The ancestor chain is kept in a fixed-size array so that a parse allocates
199/// nothing; this is that array's size. mise, the largest usage CLI, is four
200/// levels deep.
201pub const MAX_DEPTH: usize = 16;
202
203/// A command: its flags, its positional arguments, and its subcommands.
204///
205/// Every field is a borrowed slice so that a derive can emit the whole tree as
206/// `static` data. Use `..Command::EMPTY` to fill in the parts you do not need.
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub struct Command<'a> {
209    /// The canonical name, used to select this command.
210    pub name: &'a str,
211    /// Alternative names that also select it.
212    pub aliases: &'a [&'a str],
213    pub flags: &'a [&'a Flag<'a>],
214    /// Positional arguments, in the order they are filled.
215    pub args: &'a [&'a Arg<'a>],
216    /// A repeatable group of scoped flags and positional arguments, if this command has one.
217    ///
218    /// Behind a reference because hardly any command has one: held inline, the clause's five
219    /// slices were two thirds of every command's table whether it declared one or not.
220    pub clause: ::core::option::Option<&'a Clause<'a>>,
221    pub subcommands: &'a [&'a Command<'a>],
222    /// Where a word goes when it names no subcommand of this one.
223    ///
224    /// The spec's `default_subcommand`. `mise build` means `mise run build`: the word names
225    /// no command, so the parser descends into `run` and lets *`run`* have it — even where
226    /// this command declares an argument of its own, which is what makes the property worth
227    /// having rather than a synonym for a positional.
228    ///
229    /// Applied at most once per parse, so a CLI cannot loop through it, and only where a
230    /// subcommand could still be selected.
231    ///
232    /// Resolve it with [`find_subcommand`], which turns a name that no subcommand answers to
233    /// into a compile error.
234    pub default_subcommand: ::core::option::Option<&'a Command<'a>>,
235    /// Look ahead past parent/default flags before implicitly selecting the default.
236    /// A sibling name before any default-only flag stays on the parent; after one,
237    /// later words are the default command's args.
238    pub default_subcommand_flags: bool,
239    /// Select the default subcommand for an otherwise empty successful invocation.
240    pub default_subcommand_on_empty: bool,
241    /// Whether an unmatched word is forwarded as an external command plus the rest of argv.
242    ///
243    /// clap's `allow_external_subcommands`. Known subcommands still win; a
244    /// [`default_subcommand`](Self::default_subcommand) still catches first. Once the
245    /// unmatched word is taken, remaining tokens — including `--help` — are not parsed
246    /// as this command's flags.
247    pub external_subcommand: bool,
248    /// Show this command's help when no argv token follows its name.
249    ///
250    /// This is clap's `arg_required_else_help`. It deliberately observes argv rather than
251    /// bound values: an environment variable or default may fill a field, but neither means
252    /// the user supplied an argument to this invocation.
253    pub arg_required_else_help: bool,
254    /// Selecting a subcommand suppresses this command's required arguments.
255    pub subcommand_negates_reqs: bool,
256    /// Once this command binds a flag or positional, selecting one of its
257    /// subcommands is an error.
258    pub args_conflicts_with_subcommands: bool,
259    /// Let a known subcommand interrupt a variadic argument that would otherwise consume it.
260    pub subcommand_precedence_over_arg: bool,
261    /// Let a later required positional take a word while an earlier optional positional
262    /// remains empty.
263    pub allow_missing_positional: bool,
264    /// Disable delimiter splitting for positional values after `--` or on an
265    /// automatic trailing argument. Inherited by subcommands.
266    pub dont_delimit_trailing_values: bool,
267    /// What an unrecognized flag-like token means here, or `None` to keep whatever the
268    /// enclosing command said. See [`UnknownFlags`].
269    ///
270    /// Inherited rather than resolved per command, which is what usage-lib does — its
271    /// `effective_unknown_flags` walks outward from the command that ran and falls back to
272    /// the spec's. Resolving it in the tables instead was possible only for a builder that
273    /// can see the whole tree: a derive expands one struct at a time and cannot see its
274    /// parent, so `#[usage(unknown_flags = "error")]` on the root reached the root alone and
275    /// a subcommand had no way to say it at all.
276    ///
277    /// The parser carries the effective value down as it descends, so a command that states
278    /// nothing costs nothing.
279    pub unknown_flags: ::core::option::Option<UnknownFlags>,
280    /// Whether this command answers to `--version` and `-V`.
281    ///
282    /// Set on the root, and only when the CLI declares a version: clap adds the flag exactly
283    /// then, and a `--version` that answers with nothing is worse than one that is not there.
284    /// A field rather than a rule about depth, so a CLI that wants it on a subcommand — clap's
285    /// `propagate_version` — has somewhere to say so.
286    pub version: bool,
287    /// Do not synthesize `--help` and `-h` for this command.
288    pub disable_help_flag: bool,
289    /// Do not synthesize the `help` subcommand route for this command.
290    pub disable_help_subcommand: bool,
291    /// Do not synthesize `--version` and `-V` for this command.
292    pub disable_version_flag: bool,
293    /// Caller-assigned identifier, echoed back in [`Event::Command`].
294    ///
295    /// Wide enough for a derive to make these unique without coordination: two
296    /// macro expansions cannot see each other, so the generated keys carry a hash
297    /// of the type they came from in the high half and a per-type index in the low
298    /// half. A parse dispatches on this, so a collision would bind the wrong field
299    /// — [`Spec::to_kdl`](crate::spec::Spec::to_kdl) checks the tree for duplicates
300    /// in debug builds.
301    pub key: u64,
302}
303
304impl Command<'_> {
305    /// A command with nothing declared, for use with struct update syntax.
306    pub const EMPTY: Command<'static> = Command {
307        name: "",
308        aliases: &[],
309        flags: &[],
310        args: &[],
311        clause: ::core::option::Option::None,
312        subcommands: &[],
313        default_subcommand: ::core::option::Option::None,
314        default_subcommand_flags: false,
315        default_subcommand_on_empty: false,
316        external_subcommand: false,
317        arg_required_else_help: false,
318        subcommand_negates_reqs: false,
319        args_conflicts_with_subcommands: false,
320        subcommand_precedence_over_arg: false,
321        allow_missing_positional: false,
322        dont_delimit_trailing_values: false,
323        unknown_flags: ::core::option::Option::None,
324        version: false,
325        disable_help_flag: false,
326        disable_help_subcommand: false,
327        disable_version_flag: false,
328        key: 0,
329    };
330}
331
332/// A repeatable group of scoped flags and positional arguments.
333#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334pub struct Clause<'a> {
335    pub key: u64,
336    pub name: &'a str,
337    /// An explicit token that starts the next instance. When absent, consuming the
338    /// clause's sole required positional completes the current instance.
339    pub separator: ::core::option::Option<&'a [u8]>,
340    pub flags: &'a [&'a Flag<'a>],
341    pub args: &'a [&'a Arg<'a>],
342}
343
344/// Basename of argv[0] for a multicall CLI: last path component, with a trailing
345/// `.exe` stripped so Windows and Unix agree.
346pub fn multicall_basename(argv0: &str) -> &str {
347    let name = argv0.rsplit(['/', '\\']).next().unwrap_or(argv0);
348    match name.get(name.len().saturating_sub(4)..) {
349        Some(ext) if ext.eq_ignore_ascii_case(".exe") => &name[..name.len() - 4],
350        _ => name,
351    }
352}
353
354/// The applet name to parse as the first word, when argv[0] is not the dispatcher.
355///
356/// `None` means a dispatcher invocation (`busybox ls`): skip argv[0] and parse the
357/// rest. `Some` is a symlink invocation (`ls -l`): inject the basename.
358pub fn multicall_applet<'a>(argv0: &'a str, name: &str, bin: Option<&str>) -> Option<&'a str> {
359    let base = multicall_basename(argv0);
360    if !name.is_empty() && base == multicall_basename(name) {
361        return None;
362    }
363    if let Some(bin) = bin {
364        if !bin.is_empty() && base == multicall_basename(bin) {
365            return None;
366        }
367    }
368    Some(base)
369}
370
371/// Resolved identity of a derive-generated binding type.
372#[derive(Clone, Copy)]
373pub struct BindingType(pub fn() -> &'static str);
374
375impl BindingType {
376    pub fn name(self) -> &'static str {
377        (self.0)()
378    }
379}
380
381impl ::core::fmt::Debug for BindingType {
382    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
383        f.debug_tuple("BindingType").field(&self.name()).finish()
384    }
385}
386
387impl PartialEq for BindingType {
388    fn eq(&self, other: &Self) -> bool {
389        self.name() == other.name()
390    }
391}
392
393impl Eq for BindingType {}
394
395/// A flag, addressed by any of its long or short forms.
396#[derive(Debug, Clone, Copy, PartialEq, Eq)]
397pub struct Flag<'a> {
398    /// Caller-assigned identifier, echoed back in [`Event::Flag`]. This is how
399    /// generated code knows which field to assign without any string comparison.
400    /// See [`Command::key`] on why it is this wide.
401    pub key: u64,
402    /// Compatibility key for mirroring a redeclared child global into an ancestor field.
403    ///
404    /// Zero means no typed binding contract is declared. Derive-generated tables hash the
405    /// binding shape and portable metadata so only equivalent bindings receive the same event.
406    pub binding_key: u64,
407    /// Resolved Rust value type for a derive-generated binding.
408    ///
409    /// This is separate from [`Self::binding_key`] because token spellings are not type
410    /// identities: an imported alias and a fully qualified path can name the same type.
411    pub binding_type: Option<BindingType>,
412    /// Unused by binding, kept so a table entry can carry its own name for
413    /// diagnostics.
414    pub name: &'a str,
415    /// Long forms, written without the leading `--`.
416    pub longs: &'a [&'a str],
417    /// Short forms, as single bytes.
418    ///
419    /// **Should be ASCII.** A cluster like `-xyz` is walked one byte at a time, so a
420    /// non-ASCII short can never be matched, and the remainder after a value-taking one —
421    /// which becomes its value — would begin in the middle of a character.
422    /// `#[derive(Cli)]` rejects a non-ASCII `short`; a table written by hand should keep to
423    /// it. Nothing is unsound if it does not: the value would simply be cut in a place that
424    /// makes no sense, and on Windows would then fail to convert.
425    pub shorts: &'a [u8],
426    /// A long form that sets the flag to false, written without the `--`.
427    pub negate: Option<&'a str>,
428    /// Whether the flag takes a value.
429    pub takes_value: bool,
430    /// Whether one occurrence of this flag keeps taking values, until a flag-like
431    /// token or the end of the command line.
432    ///
433    /// This is the spec's variadic flag *argument* (`--include <pattern>...`). It
434    /// is not the spec's flag-level `var=#true`, which means the flag may be
435    /// repeated and takes one value each time — repetition needs nothing from the
436    /// parser, since it already reports every occurrence separately. Conflating
437    /// the two makes a merely repeatable flag greedy enough to eat a positional.
438    pub variadic: bool,
439    /// How many values one variadic occurrence may take, after which the next word
440    /// belongs to whatever comes next.
441    ///
442    /// Only for [`variadic`](Self::variadic). A merely repeatable flag — the spec's
443    /// `var=#true` — is bounded on how many times it was *given*, which no single token
444    /// can decide, so that bound stays with the metadata and is checked after the parse.
445    pub var_max: ::core::option::Option<u32>,
446    /// The byte that makes one word several values, if the flag declares one.
447    ///
448    /// Here rather than with the metadata for the same reason [`var_max`](Self::var_max)
449    /// is: it decides *where* a word lands. A bound counts values, and a delimiter is what
450    /// makes a word stop being one of them — `--include a,b,c` is three, so a `var_max` of
451    /// two is already past its bound on the single word it was entitled to take. Binding
452    /// cannot count without it.
453    pub delimiter: ::core::option::Option<u8>,
454    /// Whether a detached value may itself look like a flag.
455    ///
456    /// The default is to refuse: `--jobs --force` is far more likely a forgotten
457    /// value than a jobs of `"--force"`. Declared, the next token is taken
458    /// whatever it looks like — including `--` — which is clap's
459    /// `allow_hyphen_values` and the spec's property of the same name. A variadic
460    /// occurrence still stops collecting at a later flag-like token, so a second
461    /// occurrence of the flag is not eaten as a value.
462    pub allow_hyphen_values: bool,
463    /// Whether a detached value may be a negative number while other flag-like
464    /// tokens still stop collection or report as flags.
465    pub allow_negative_numbers: bool,
466    /// A token that ends one variadic occurrence without becoming a value.
467    pub value_terminator: ::core::option::Option<&'a [u8]>,
468    /// Whether the value must be attached with `=`.
469    ///
470    /// `--flag=value` is accepted and `--flag value` is not, which is clap's
471    /// `require_equals` and the spec's property of the same name. A short's
472    /// attached form (`-i9229`, `-i=9229`) still binds: only the following word
473    /// is refused.
474    pub require_equals: bool,
475    /// Whether this value-taking flag may be present without a value.
476    ///
477    /// A missing value emits the flag event with `value: None`; bindings such as
478    /// `Option<Option<T>>` can therefore distinguish an absent flag from a bare
479    /// flag and from a flag with an explicit value.
480    pub value_optional: bool,
481    /// Whether a boolean long flag accepts an attached `true` or `false` value.
482    ///
483    /// This does not make the flag value-taking in the ordinary sense: a detached
484    /// word is never consumed, and help keeps rendering a switch. Only
485    /// `--flag=true` and `--flag=false` opt into an explicit boolean value.
486    pub bool_value: bool,
487    /// Value used when the flag is present but no value is given.
488    ///
489    /// clap's `default_missing_value` and the spec's `default_missing`. `--color`
490    /// binds this, `--color=never` binds `never`, and an absent flag is not bound.
491    /// Combined with [`Self::require_equals`], a following word is still refused.
492    pub default_missing: ::core::option::Option<&'a [u8]>,
493    /// Whether the flag is recognized by every command beneath the one that
494    /// declares it.
495    pub global: bool,
496    /// Whether this declared flag binds a field or requests a built-in response.
497    pub action: ArgAction,
498}
499
500impl Flag<'_> {
501    /// A value-less flag, for use with struct update syntax.
502    pub const BOOL: Flag<'static> = Flag {
503        key: 0,
504        binding_key: 0,
505        binding_type: None,
506        name: "",
507        longs: &[],
508        shorts: &[],
509        negate: None,
510        takes_value: false,
511        variadic: false,
512        var_max: ::core::option::Option::None,
513        delimiter: ::core::option::Option::None,
514        allow_hyphen_values: false,
515        allow_negative_numbers: false,
516        value_terminator: ::core::option::Option::None,
517        require_equals: false,
518        value_optional: false,
519        bool_value: false,
520        default_missing: ::core::option::Option::None,
521        global: false,
522        action: ArgAction::Set,
523    };
524
525    /// A flag that takes a value, for use with struct update syntax.
526    pub const VALUE: Flag<'static> = Flag {
527        takes_value: true,
528        ..Flag::BOOL
529    };
530}
531
532/// What supplying a declared flag does.
533#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
534pub enum ArgAction {
535    /// Bind the flag to its declared field.
536    #[default]
537    Set,
538    /// Show help, choosing the long form for a long spelling and the short form otherwise.
539    Help,
540    /// Always show short help.
541    HelpShort,
542    /// Always show long help.
543    HelpLong,
544    /// Show long help for this command and every visible descendant.
545    HelpAll,
546    /// Show version information.
547    Version,
548}
549
550/// A positional argument.
551#[derive(Debug, Clone, Copy, PartialEq, Eq)]
552pub struct Arg<'a> {
553    /// Caller-assigned identifier, echoed back in [`Event::Arg`]. See
554    /// [`Command::key`] on why it is this wide.
555    pub key: u64,
556    /// Prefix that classifies this positional independently of declaration order.
557    /// The prefix is removed from the value emitted in [`Event::Arg`].
558    pub sigil: ::core::option::Option<&'a [u8]>,
559    /// Whether post-binding requires this positional to have a value. Kept in the hot
560    /// table because `allow_missing_positional` must reserve words for later required args.
561    pub required: bool,
562    /// Whether this argument keeps taking values once it has one.
563    pub var: bool,
564    /// How many words a variadic may take before the next argument gets the rest.
565    ///
566    /// A bound belongs here, in the table binding reads, rather than with the metadata:
567    /// it decides *where* a word lands, not whether what landed is acceptable. clap's
568    /// `num_args` works the same way, and every spec in the wild is generated from a clap
569    /// command. `u32` rather than `usize` because a CLI that bounds a variadic above four
570    /// billion has other problems, and this table is read on the hot path.
571    pub var_max: ::core::option::Option<u32>,
572    /// The byte that makes one word several values, if the argument declares one.
573    ///
574    /// See [`Flag::delimiter`]: a bound counts values, and only this says how many values a
575    /// word carries.
576    pub delimiter: ::core::option::Option<u8>,
577    /// Whether a negative-number token is accepted as this positional even in
578    /// strict flag mode.
579    pub allow_negative_numbers: bool,
580    /// A token that ends this variadic positional without becoming a value.
581    pub value_terminator: ::core::option::Option<&'a [u8]>,
582    /// This argument's relationship to the `--` separator.
583    pub double_dash: DoubleDash,
584    /// Unused by binding, kept so a table entry can carry its own name for
585    /// diagnostics.
586    pub name: &'a str,
587}
588
589impl Arg<'_> {
590    /// A single-value argument, for use with struct update syntax.
591    pub const REQUIRED: Arg<'static> = Arg {
592        key: 0,
593        sigil: ::core::option::Option::None,
594        required: true,
595        var: false,
596        var_max: ::core::option::Option::None,
597        delimiter: ::core::option::Option::None,
598        allow_negative_numbers: false,
599        value_terminator: ::core::option::Option::None,
600        double_dash: DoubleDash::Optional,
601        name: "",
602    };
603
604    /// A variadic argument, for use with struct update syntax.
605    pub const VAR: Arg<'static> = Arg {
606        var: true,
607        ..Arg::REQUIRED
608    };
609}
610
611/// What to do with a flag-like token that names no flag in scope.
612///
613/// The default is [`UnknownFlags::Value`]: the token carries on to the positional
614/// arguments, because a spec is often parsing a command line whose flags belong to
615/// something else — a wrapped tool, a task script. A CLI that owns all of its
616/// flags declares [`UnknownFlags::Error`] and gets typo detection instead.
617///
618/// Stored per command and already resolved: inheritance is a question for whoever
619/// builds the tables, and answering it at compile time keeps it out of the parse.
620#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
621pub enum UnknownFlags {
622    /// Offer the token to the positionals. If none can take it, it is an
623    /// unexpected argument.
624    #[default]
625    Value,
626    /// Reject the token.
627    Error,
628}
629
630/// How an argument relates to the `--` separator.
631#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
632pub enum DoubleDash {
633    /// Values may appear on either side of a `--`.
634    #[default]
635    Optional,
636    /// Values are accepted only after a `--`.
637    Required,
638    /// A `--` is kept as a value rather than consumed as a separator.
639    Preserve,
640    /// Once the argument takes a value, behave as if a `--` had been given, so
641    /// the rest of the command line is values. A wrapper can then forward flags
642    /// without its caller typing the separator.
643    Automatic,
644}
645
646/// Something the parser bound.
647#[derive(Debug, Clone, Copy, PartialEq, Eq)]
648pub enum Event<'t, 'a, 'v> {
649    /// A subcommand was selected; parsing continues inside it.
650    Command(&'t Command<'t>),
651    /// A flag was given. `value` is `Some` for a flag that takes one, and
652    /// `negated` is true when the flag was set through its `negate` form.
653    Flag {
654        flag: &'t Flag<'t>,
655        value: Option<&'v [u8]>,
656        negated: bool,
657    },
658    /// A word was bound to a positional argument. A variadic argument produces
659    /// one event per value.
660    Arg {
661        arg: &'t Arg<'t>,
662        value: &'v [u8],
663        /// Whether this value should be split by the argument's declared delimiter.
664        delimit: bool,
665    },
666    /// Ended one clause instance and began the next.
667    ClauseSeparator { clause: Clause<'t> },
668    /// An unmatched word was forwarded as an external command: the name, then
669    /// every remaining token, including flags.
670    External { values: &'a [&'v OsStr] },
671}
672
673/// A binding failure.
674///
675/// Carries the offending token so a caller can render a good message, but no
676/// message of its own: rendering belongs to a cold path, and building a string
677/// here would allocate on the way to reporting that nothing was allocated.
678///
679/// `non_exhaustive`, because an error enum grows: a caller matching on it needs a
680/// fallback arm so that recognizing a new failure is never a breaking change.
681// No `Copy`: one variant owns its message. `Clone` stays, and the enum is still 40 bytes
682// because that variant is boxed, so nothing on the hot path grew.
683#[derive(Debug, Clone, PartialEq, Eq)]
684#[non_exhaustive]
685pub enum Error<'t, 'v> {
686    /// A flag-like token matched no flag in scope. `token` is the whole token as
687    /// typed, so a bundle containing an unrecognized letter reports `-fz` rather
688    /// than the letter alone — which is also the unit in which it is rejected.
689    UnknownFlag { token: &'v [u8] },
690    /// A flag that needs a value did not get one, either because the command
691    /// line ended or because the next token was flag-like.
692    MissingFlagValue { flag: &'t Flag<'t> },
693    /// A word arrived with no argument left to hold it.
694    UnexpectedArg { token: &'v [u8] },
695    /// A word was offered to a `double_dash = "required"` argument before any
696    /// `--` had been seen.
697    ArgRequiresDoubleDash { arg: &'t Arg<'t> },
698    /// A subcommand was selected after this command had already bound an argument.
699    SubcommandConflict { subcommand: &'t Command<'t> },
700    /// The command tree is deeper than [`MAX_DEPTH`].
701    TooDeep,
702
703    // The rest are raised *after* the parse, by whoever owns the target type: they
704    // need to know a value's declared type, which the parser deliberately does not.
705    // They share this enum so that a caller has one error to handle rather than two.
706    /// Something the command requires was never given.
707    MissingRequired {
708        /// The flag or argument's name, as the spec calls it.
709        name: &'t str,
710    },
711    /// A flag that is not repeatable was given more than once.
712    DuplicateFlag {
713        /// The flag's name, as the spec calls it.
714        name: &'t str,
715    },
716    /// A value was given that is not among the declared choices.
717    ///
718    /// Carries the choices rather than the offending value: rendering the value means
719    /// owning it, and an error that allocates on a path this crate promises not to
720    /// allocate on would be a poor trade for a better message. Diagnostics are a
721    /// separate layer.
722    InvalidChoice {
723        name: &'t str,
724        choices: &'t [&'t str],
725    },
726    /// Fewer values than `var_min`.
727    VarTooFew {
728        name: &'t str,
729        min: usize,
730        got: usize,
731    },
732    /// More values than `var_max`.
733    VarTooMany {
734        name: &'t str,
735        max: usize,
736        got: usize,
737    },
738    /// Two flags declared to conflict were both given.
739    ///
740    /// Carries both names because either one alone reads as a puzzle: which flag is
741    /// unwelcome depends entirely on what else is on the command line.
742    ConflictingFlags {
743        /// The flag whose declaration names the conflict.
744        name: &'t str,
745        /// The flag it cannot be given with, as the declaration spells it.
746        other: &'t str,
747    },
748    /// A value was given that the field's type could not be built from.
749    ///
750    /// Boxed, and the only error here that owns anything. Everything else borrows the
751    /// tables or argv, which is what keeps a *successful* parse allocation-free — and the
752    /// box keeps `Error` the size it was, so the `Result` this rides in on the hot path
753    /// does not grow. A value that will not convert has already failed, and a message
754    /// worth reading is worth one allocation.
755    InvalidValue(::std::boxed::Box<InvalidValue<'t>>),
756    /// A required group had none of its members given.
757    ///
758    /// Carries the members as members rather than as a rendered sentence: the caller
759    /// decides how to say it, and a completion asking what would satisfy this needs the
760    /// list rather than the prose.
761    MissingGroup {
762        /// The group's declared name, which appears in the message so a command with
763        /// several groups does not report the same sentence twice.
764        group: &'t str,
765        /// The flags that would satisfy it, as the declaration spells them.
766        members: &'t [&'t str],
767    },
768    /// A subcommand was required, and none was given.
769    MissingSubcommand,
770    /// `--help` or `-h` was given, and `cmd` is what it was asked about.
771    ///
772    /// Not a failure, and returned as one anyway: a parse that stops to print help has not
773    /// produced a value, and every caller already handles the "no value" shape. clap does the
774    /// same thing for the same reason.
775    ///
776    /// `long` distinguishes the two: `-h` prints the short form and `--help` the long one, as
777    /// clap has them. The caller renders — this crate does not print, because a library that
778    /// writes to stdout on its own is one an adopter cannot embed.
779    Help { cmd: &'t Command<'t>, long: bool },
780    /// `arg_required_else_help` found no command-line arguments for `cmd`.
781    ///
782    /// Unlike an explicit help request, this is a usage failure: clap prints the short help to
783    /// stderr and exits with status 2. Keeping the shape separate lets embedders preserve that
784    /// terminal contract without guessing why [`Error::Help`] was returned.
785    MissingArgsHelp { cmd: &'t Command<'t> },
786    /// Recursive long help was requested for `cmd` and every visible descendant.
787    HelpAll { cmd: &'t Command<'t> },
788    /// `--version` or `-V` was asked for. Not a failure either — the caller prints and leaves.
789    ///
790    /// The version string lives in the spec rather than the parse tables. `long` lets the
791    /// caller choose `long_version` for `--version` while `-V` retains the concise value.
792    Version { long: bool },
793}
794
795/// The high half of every key one declaration's items get.
796///
797/// A derive cannot see other expansions, so it cannot hand out keys from a shared
798/// counter: it hashes the declaration it was given instead. It cannot see a module path
799/// either, which is why the module is mixed in *here* — `module_path!()` is available to
800/// the generated code as a compile-time string, so two byte-identical declarations in
801/// different modules end up with different keys rather than colliding.
802///
803/// `declaration` is a hash the derive computed over the item's own tokens.
804pub const fn key_base(module: &str, declaration: u32) -> u64 {
805    // FNV-1a, continuing from the declaration's hash rather than starting over, so both
806    // halves contribute. Spelled out rather than taken from a `Hasher`, which is not
807    // guaranteed to be stable between compilations — and these are baked into a binary.
808    let mut hash: u32 = declaration;
809    let bytes = module.as_bytes();
810    let mut i = 0;
811    while i < bytes.len() {
812        hash ^= bytes[i] as u32;
813        hash = hash.wrapping_mul(0x0100_0193);
814        i += 1;
815    }
816    (hash as u64) << 32
817}
818
819/// Why a value would not convert into the type its field holds.
820///
821/// Separate from [`Error`] so that the enum stays small: this is reached through a `Box`.
822#[derive(Debug, Clone, PartialEq, Eq)]
823pub struct InvalidValue<'t> {
824    /// The flag or argument's name, as the spec calls it.
825    pub name: &'t str,
826    /// The text that would not convert.
827    pub value: ::std::string::String,
828    /// What the type's own conversion complained about.
829    pub reason: ::std::string::String,
830}
831
832/// A command-wide validation or finalization failure.
833///
834/// Return this from a `#[usage(validate_with = ...)]` hook or from the
835/// `TryFrom` implementation named by `#[usage(try_into = ...)]`. The derive
836/// turns it into the same [`Error::InvalidValue`] diagnostic used by field
837/// conversion, so callers keep one parse error type.
838#[derive(Debug, Clone, PartialEq, Eq)]
839pub struct ValidationError {
840    name: &'static str,
841    value: String,
842    reason: String,
843}
844
845impl ValidationError {
846    /// Start an error attributed to a flag, positional, or command name.
847    pub fn field(name: &'static str) -> Self {
848        Self {
849            name,
850            value: String::new(),
851            reason: String::new(),
852        }
853    }
854
855    /// Record the value that failed the command-wide invariant.
856    pub fn value(mut self, value: impl Into<String>) -> Self {
857        self.value = value.into();
858        self
859    }
860
861    /// Explain the invariant that the value did not satisfy.
862    pub fn reason(mut self, reason: impl Into<String>) -> Self {
863        self.reason = reason.into();
864        self
865    }
866
867    /// Convert this application-level failure into the parser's diagnostic.
868    pub fn into_parse_error<'v>(self) -> Error<'static, 'v> {
869        Error::InvalidValue(Box::new(InvalidValue {
870            name: self.name,
871            value: self.value,
872            reason: self.reason,
873        }))
874    }
875}
876
877/// Interpret a value as UTF-8.
878///
879/// The parser hands back bytes borrowed from `argv`; this is the conversion most
880/// callers want, and the point at which a non-UTF-8 command line is rejected —
881/// but only for the values actually inspected.
882pub fn as_str(value: &[u8]) -> Result<&str, std::str::Utf8Error> {
883    std::str::from_utf8(value)
884}
885
886/// How many entries a group of tables holds in total.
887///
888/// The length for [`concat_flags`] and [`concat_args`], which need it as a const generic — so
889/// it has to be computable separately from the concatenation itself.
890///
891/// ```
892/// use usage_argv::{table_len, Flag};
893///
894/// static A: Flag = Flag { name: "a", ..Flag::BOOL };
895/// static B: Flag = Flag { name: "b", ..Flag::BOOL };
896/// const GROUPS: &[&[&Flag]] = &[&[&A], &[], &[&B]];
897/// const N: usize = table_len(GROUPS);
898/// assert_eq!(N, 2);
899/// ```
900pub const fn table_len<T>(groups: &[&[T]]) -> usize {
901    let mut total = 0;
902    let mut i = 0;
903    while i < groups.len() {
904        total += groups[i].len();
905        i += 1;
906    }
907    total
908}
909
910/// Join groups of flag tables into one, at compile time.
911///
912/// This is how `#[usage(flatten)]` stays free. A flattened struct's flags have to appear in
913/// the parent's own table, and the parent's macro expansion cannot see them — it has only a
914/// type. But it can name that type's [`CommandArgs::COMMAND`](crate::spec::CommandArgs::COMMAND),
915/// and a `const fn` can read through it, so the two lists become one `static` array before the
916/// program runs. The parser then walks a single flat slice, exactly as it does for a command
917/// that declared everything itself: flatten costs nothing at run time.
918///
919/// Groups are laid out in the order given, which is what lets a flattened group sit *between*
920/// two of the parent's own declarations — necessary for positional arguments, where order is
921/// the meaning.
922///
923/// `N` must be [`table_len`] of the same groups. It cannot be inferred, and a wrong one fails
924/// to compile rather than leaving the difference filled with padding.
925///
926/// ```
927/// use usage_argv::{concat_flags, table_len, Flag};
928///
929/// static FORCE: Flag = Flag { name: "force", longs: &["force"], ..Flag::BOOL };
930/// static QUIET: Flag = Flag { name: "quiet", longs: &["quiet"], ..Flag::BOOL };
931/// static SHARED: &[&Flag] = &[&QUIET];
932///
933/// const GROUPS: &[&[&Flag]] = &[&[&FORCE], SHARED];
934/// static FLAGS: [&Flag; table_len(GROUPS)] = concat_flags(GROUPS);
935///
936/// assert_eq!(FLAGS.iter().map(|f| f.name).collect::<Vec<_>>(), ["force", "quiet"]);
937/// ```
938pub const fn concat_flags<const N: usize>(
939    groups: &[&[&'static Flag<'static>]],
940) -> [&'static Flag<'static>; N] {
941    // Every slot is written below, but an array has to start somewhere and `MaybeUninit`
942    // would mean `unsafe`. A `Flag` nobody can reach is cheaper than that.
943    static PLACEHOLDER: Flag<'static> = Flag::BOOL;
944    let mut out = [&PLACEHOLDER; N];
945    let mut at = 0;
946    let mut g = 0;
947    while g < groups.len() {
948        let group = groups[g];
949        let mut i = 0;
950        while i < group.len() {
951            out[at] = group[i];
952            at += 1;
953            i += 1;
954        }
955        g += 1;
956    }
957    assert!(
958        at == N,
959        "`N` must be `table_len` of the same groups, or the table would keep a placeholder \
960         that answers to nothing"
961    );
962    out
963}
964
965/// Join groups of argument tables into one, at compile time.
966///
967/// The positional counterpart of [`concat_flags`] — see there for why this exists. Order
968/// matters more here: an argument's position *is* its identity, so a flattened group has to
969/// land exactly where the field was written.
970///
971/// Two functions rather than one generic: each needs a value to fill an array with before
972/// overwriting it, and there is no way to ask a type parameter for one in a `const fn`.
973pub const fn concat_args<const N: usize>(
974    groups: &[&[&'static Arg<'static>]],
975) -> [&'static Arg<'static>; N] {
976    static PLACEHOLDER: Arg<'static> = Arg::REQUIRED;
977    let mut out = [&PLACEHOLDER; N];
978    let mut at = 0;
979    let mut g = 0;
980    while g < groups.len() {
981        let group = groups[g];
982        let mut i = 0;
983        while i < group.len() {
984            out[at] = group[i];
985            at += 1;
986            i += 1;
987        }
988        g += 1;
989    }
990    assert!(
991        at == N,
992        "`N` must be `table_len` of the same groups, or the table would keep a placeholder \
993         that answers to nothing"
994    );
995    out
996}
997
998/// The key `--help` answers to, and the one `-h` does.
999///
1000/// Reserved rather than generated: a derive builds keys from a hash of the type they came from
1001/// in the high half and an index in the low half, so the top of the range belongs to nobody.
1002/// Generated code compares against these to tell a help request from a flag of its own.
1003pub const HELP_LONG_KEY: u64 = u64::MAX;
1004/// See [`HELP_LONG_KEY`].
1005pub const HELP_SHORT_KEY: u64 = u64::MAX - 1;
1006
1007/// `--help`, which every command answers to.
1008///
1009/// In the parse table and *not* in the metadata, which is the whole trick: the parser has to
1010/// recognise the flag, and help output must not list it — a spec does not declare `--help`, so
1011/// showing one would make the rendered page disagree with the spec it came from.
1012pub static HELP_LONG: Flag<'static> = Flag {
1013    key: HELP_LONG_KEY,
1014    name: "help",
1015    longs: &["help"],
1016    action: ArgAction::HelpLong,
1017    ..Flag::BOOL
1018};
1019
1020/// See [`HELP_LONG_KEY`].
1021pub const VERSION_LONG_KEY: u64 = u64::MAX - 2;
1022/// See [`HELP_LONG_KEY`].
1023pub const VERSION_SHORT_KEY: u64 = u64::MAX - 3;
1024
1025/// `--version`, where the CLI declared one.
1026///
1027/// In the parse table and not in the metadata, exactly as `--help` is: a spec does not declare
1028/// `--version`, so listing one would make the rendered page disagree with the spec it came from.
1029pub static VERSION_LONG: Flag<'static> = Flag {
1030    key: VERSION_LONG_KEY,
1031    name: "version",
1032    longs: &["version"],
1033    action: ArgAction::Version,
1034    ..Flag::BOOL
1035};
1036
1037/// `-V`, which clap also supplies.
1038pub static VERSION_SHORT: Flag<'static> = Flag {
1039    key: VERSION_SHORT_KEY,
1040    name: "version",
1041    shorts: b"V",
1042    action: ArgAction::Version,
1043    ..Flag::BOOL
1044};
1045
1046/// `-h`, which prints the shorter form.
1047pub static HELP_SHORT: Flag<'static> = Flag {
1048    key: HELP_SHORT_KEY,
1049    name: "help",
1050    shorts: b"h",
1051    action: ArgAction::HelpShort,
1052    ..Flag::BOOL
1053};
1054
1055/// A named subcommand of a given command, by name or alias.
1056///
1057/// Free rather than a method because `help` resolves a path *without* descending: the words
1058/// after it are a question about a command rather than a walk into one.
1059///
1060/// Names across every subcommand before any alias, the precedence the grammar states — and
1061/// the reason this is the only implementation of it on argv's side. `ex run` and `ex help run`
1062/// selecting different commands would be exactly the divergence this rule was written to end.
1063pub(crate) fn find_named<'t>(cmd: &'t Command<'t>, name: &[u8]) -> Option<&'t Command<'t>> {
1064    let subcommands = || cmd.subcommands.iter().copied();
1065    subcommands()
1066        .find(|c| c.name.as_bytes() == name)
1067        .or_else(|| subcommands().find(|c| c.aliases.iter().any(|a| a.as_bytes() == name)))
1068}
1069
1070/// What a caller should print for a parse failure, and what to exit with.
1071///
1072/// The one entry point a generated `parse()` reaches for, and the reason it exists here rather
1073/// than in the derive: whether the good rendering is available is a *feature of this crate* in
1074/// the adopter's dependency graph, and a `#[cfg]` written into generated code is evaluated in
1075/// the adopter's crate, where the feature is not theirs to see. That is how a metadata field
1076/// once got silently dropped; the answer is that the cfg lives beside the thing it gates.
1077///
1078/// With `diagnostics` on, this is the clap-shaped message. Without it, the error's `Debug`
1079/// form — which is still better than nothing and is what a parser-only build asked for.
1080///
1081/// [`Error::Help`] and [`Error::Version`] are not failures and must be handled before this.
1082#[cfg(feature = "diagnostics")]
1083pub fn render_failure(spec: &spec::Spec<'_>, argv: &[&OsStr], error: &Error<'_, '_>) -> String {
1084    diagnostic::render(spec, argv, error, diagnostic::Style::auto())
1085}
1086
1087/// A parse failure, never coloured.
1088///
1089/// [`render_failure`] asks the environment whether to colour, which is right for a process and
1090/// wrong for anything that keeps the string: a test that asserts on a message, or a snapshot of
1091/// one, would pass or fail by whether stderr happened to be a terminal. The renderer is the
1092/// same; only the answer to that question is fixed.
1093#[cfg(feature = "diagnostics")]
1094pub fn render_failure_plain(
1095    spec: &spec::Spec<'_>,
1096    argv: &[&OsStr],
1097    error: &Error<'_, '_>,
1098) -> String {
1099    diagnostic::render(spec, argv, error, diagnostic::Style::PLAIN)
1100}
1101
1102/// Render a failure through a spec-declared executable view.
1103///
1104/// `argv` is the original full argv, including the view executable as argv0.
1105#[cfg(feature = "diagnostics")]
1106pub fn render_failure_view<'a>(
1107    spec: &'a spec::Spec<'a>,
1108    argv: &[&OsStr],
1109    error: &Error<'_, '_>,
1110    view: &'a spec::ViewMeta<'a>,
1111) -> String {
1112    diagnostic::render_view(spec, argv, error, diagnostic::Style::auto(), view)
1113}
1114
1115/// What a caller should print for a parse failure, without the renderer that makes it readable.
1116///
1117/// See the other half. A caller that wants the clap-shaped message turns on `diagnostics`;
1118/// this is what a parser-only build asked for, and it still says which error it was.
1119#[cfg(all(feature = "spec", not(feature = "diagnostics")))]
1120pub fn render_failure(spec: &spec::Spec<'_>, argv: &[&OsStr], error: &Error<'_, '_>) -> String {
1121    let _ = (spec, argv);
1122    ::std::format!("error: {error:?}\n")
1123}
1124
1125/// A parse failure without the renderer, which is plain either way.
1126#[cfg(all(feature = "spec", not(feature = "diagnostics")))]
1127pub fn render_failure_plain(
1128    spec: &spec::Spec<'_>,
1129    argv: &[&OsStr],
1130    error: &Error<'_, '_>,
1131) -> String {
1132    render_failure(spec, argv, error)
1133}
1134
1135/// Render a failure through a declared view without the optional diagnostics renderer.
1136#[cfg(all(feature = "spec", not(feature = "diagnostics")))]
1137pub fn render_failure_view(
1138    spec: &spec::Spec<'_>,
1139    argv: &[&OsStr],
1140    error: &Error<'_, '_>,
1141    view: &spec::ViewMeta<'_>,
1142) -> String {
1143    let _ = (spec, argv, view);
1144    ::std::format!("error: {error:?}\n")
1145}
1146
1147/// Answer an error from a generated `parse()` the way a command-line program does, and leave.
1148///
1149/// A help request goes to stdout and exits 0; a request for help because nothing was given goes
1150/// to stderr and exits 2; any other failure is rendered to stderr and exits 2, which is clap's
1151/// status, so a script that checks for it keeps working. [`Error::Version`] is the caller's to
1152/// answer before this, since only the generated code knows the version it prints.
1153///
1154/// Here rather than expanded into every CLI: the body is the same for all of them, and only the
1155/// spec — which carries any computed identity the generated code has already evaluated — and
1156/// the tables differ. `all_argv` includes argv0, which a view's route needs; `argv` does not.
1157#[cfg(feature = "spec")]
1158#[doc(hidden)]
1159pub fn __usage_exit_on_error(
1160    error: Error<'_, '_>,
1161    spec: &spec::Spec<'_>,
1162    root: &Command<'_>,
1163    all_argv: &[&OsStr],
1164    argv: &[&OsStr],
1165    view: Option<&spec::ViewMeta<'_>>,
1166) -> ! {
1167    let (want, to_stderr, cmd) = match error {
1168        Error::Help { cmd, long } => {
1169            let want = if long {
1170                help::Page::Long
1171            } else {
1172                help::Page::Short
1173            };
1174            (want, false, cmd)
1175        }
1176        Error::MissingArgsHelp { cmd } => (help::Page::Short, true, cmd),
1177        Error::HelpAll { cmd } => (help::Page::All, false, cmd),
1178        error => {
1179            let failure = match view {
1180                Some(view) => render_failure_view(spec, all_argv, &error, view),
1181                None => render_failure(spec, argv, &error),
1182            };
1183            ::std::eprint!("{failure}");
1184            __usage_process_exit(2);
1185        }
1186    };
1187    let style = if to_stderr {
1188        help::Style::auto_stderr()
1189    } else {
1190        help::Style::auto()
1191    };
1192    let page = match view {
1193        Some(view) => help::page_view(spec, root, all_argv, cmd, view, want, style),
1194        None => help::page(spec, root, argv, cmd, want, style),
1195    };
1196    // `None` is only reachable if the command came from another CLI's tables.
1197    if let Some(page) = page {
1198        if to_stderr {
1199            ::std::eprint!("{page}");
1200        } else {
1201            ::std::print!("{page}");
1202        }
1203    }
1204    __usage_process_exit(if to_stderr { 2 } else { 0 })
1205}
1206
1207/// Print `bin version` for a generated `parse()`, and leave.
1208///
1209/// The half of [`__usage_exit_on_error`] the generated code answers itself, because the
1210/// version is a declaration of that CLI; the printing and the exit are the same everywhere.
1211#[cfg(feature = "spec")]
1212#[doc(hidden)]
1213pub fn __usage_exit_version(
1214    bin: &str,
1215    view: Option<&spec::ViewMeta<'_>>,
1216    version: &dyn std::fmt::Display,
1217) -> ! {
1218    let bin = view.map_or(bin, |view| view.bin);
1219    ::std::println!("{bin} {version}");
1220    __usage_process_exit(0)
1221}
1222
1223/// What a caller should print for the deprecations a command line used.
1224///
1225/// The same arrangement as [`render_failure`], and for the same reason: whether the coloured
1226/// rendering is available is a feature of *this* crate in the adopter's dependency graph, so the
1227/// `#[cfg]` lives beside the thing it gates rather than in generated code.
1228///
1229/// Warnings are not failures. A caller prints these to stderr and carries on.
1230#[cfg(feature = "diagnostics")]
1231pub fn render_warnings(warnings: &[warn::Warning<'_>]) -> String {
1232    diagnostic::render_warnings(warnings, diagnostic::Style::auto())
1233}
1234
1235/// The same wording without the renderer that colours it. See the other half.
1236#[cfg(all(feature = "spec", not(feature = "diagnostics")))]
1237pub fn render_warnings(warnings: &[warn::Warning<'_>]) -> String {
1238    warn::render_warnings(warnings)
1239}
1240
1241/// The word a tool sends to ask a binary for its own spec.
1242///
1243/// Not a flag and not a command: a spec request is not something this CLI *does*, so it is
1244/// answered before the parse and stays out of the tables — the same reason
1245/// `__complete_word__` is a word rather than a subcommand. It also keeps the endpoint from
1246/// perturbing the document it prints, which a declared flag would not.
1247pub const SPEC_REQUEST: &str = "__usage_spec__";
1248
1249/// Whether this argv asks for the spec rather than for the CLI to run.
1250///
1251/// Only the first word counts: `mycli build __usage_spec__` passes the word through as an
1252/// ordinary value, because a request is the whole invocation or it is nothing.
1253///
1254/// A root that declares a command of that name keeps it, which is the precedence the `help`
1255/// subcommand already has. The check is here rather than in the derive because a `Cli` derive
1256/// expands one struct and cannot see the variant names of a separate `Subcommands` enum — the
1257/// static tables can.
1258pub fn is_spec_request(root: &Command<'_>, argv: &[&OsStr]) -> bool {
1259    let [first, ..] = argv else { return false };
1260    first.as_encoded_bytes() == SPEC_REQUEST.as_bytes()
1261        && find_named(root, SPEC_REQUEST.as_bytes()).is_none()
1262}
1263
1264/// Whether a flag is one of the two the parser supplies rather than the CLI declaring it.
1265pub fn is_help_flag(flag: &Flag<'_>) -> bool {
1266    matches!(
1267        flag.action,
1268        ArgAction::Help | ArgAction::HelpShort | ArgAction::HelpLong | ArgAction::HelpAll
1269    )
1270}
1271
1272/// Whether a flag is one of the two the parser supplies for `--version`.
1273pub fn is_version_flag(flag: &Flag<'_>) -> bool {
1274    flag.action == ArgAction::Version
1275}
1276
1277/// Whether one exact root argument selects a declared or synthesized version action.
1278///
1279/// Declared flags are checked first because they shadow the built-in `--version` and `-V`
1280/// spellings. Executable views use this before projection so custom version spellings keep
1281/// reporting the package that owns the view.
1282pub fn is_version_arg(cmd: &Command<'_>, word: &OsStr) -> bool {
1283    let token = word.as_encoded_bytes();
1284    if let Some(long) = token.strip_prefix(b"--") {
1285        if let Some(flag) = cmd.flags.iter().find(|flag| {
1286            flag.longs
1287                .iter()
1288                .any(|spelling| spelling.as_bytes() == long)
1289        }) {
1290            return is_version_flag(flag);
1291        }
1292        if cmd.flags.iter().any(|flag| {
1293            flag.negate
1294                .is_some_and(|spelling| spelling.as_bytes() == long)
1295        }) {
1296            return false;
1297        }
1298        return long == b"version" && cmd.version && !cmd.disable_version_flag;
1299    }
1300    if let [b'-', short] = token {
1301        if let Some(flag) = cmd.flags.iter().find(|flag| flag.shorts.contains(short)) {
1302            return is_version_flag(flag);
1303        }
1304        return *short == b'V' && cmd.version && !cmd.disable_version_flag;
1305    }
1306    false
1307}
1308
1309/// Resolve a subcommand by name or alias, at compile time.
1310///
1311/// For [`Command::default_subcommand`], which names a command that a derive cannot see: the
1312/// variants of a subcommand enum are a different macro expansion, so the name is all the
1313/// parent has. Searching the list in a `const fn` closes that gap — the answer is the same
1314/// `&'static` the table already holds, found before the program runs.
1315///
1316/// A name no subcommand answers to is a **compile error**, since this panics during const
1317/// evaluation. That is the whole point of doing it here rather than at startup.
1318///
1319/// ```
1320/// use usage_argv::{find_subcommand, Command};
1321///
1322/// static RUN: Command = Command { name: "run", ..Command::EMPTY };
1323/// static SUBS: &[&Command] = &[&RUN];
1324/// static ROOT: Command = Command {
1325///     name: "ex",
1326///     subcommands: SUBS,
1327///     default_subcommand: Some(find_subcommand(SUBS, "run")),
1328///     ..Command::EMPTY
1329/// };
1330/// assert_eq!(ROOT.default_subcommand.unwrap().name, "run");
1331/// ```
1332pub const fn find_subcommand<'a>(
1333    subcommands: &'a [&'a Command<'a>],
1334    name: &str,
1335) -> &'a Command<'a> {
1336    // Names first, then aliases: a command's own name outranks another command's alias, so
1337    // the answer does not depend on the order the table happens to list them in. Checking
1338    // each candidate's name *and* aliases in one pass instead let whichever command came
1339    // first win, and usage-lib resolved the same spec to the last one.
1340    let mut i = 0;
1341    while i < subcommands.len() {
1342        if str_eq(subcommands[i].name, name) {
1343            return subcommands[i];
1344        }
1345        i += 1;
1346    }
1347    // Aliases answer too, because usage-lib resolves the name against names, aliases and
1348    // hidden aliases alike — so a spec may point `default_subcommand` at any of them.
1349    let mut i = 0;
1350    while i < subcommands.len() {
1351        let candidate = subcommands[i];
1352        let mut a = 0;
1353        while a < candidate.aliases.len() {
1354            if str_eq(candidate.aliases[a], name) {
1355                return candidate;
1356            }
1357            a += 1;
1358        }
1359        i += 1;
1360    }
1361    panic!("`default_subcommand` names a command that this one does not have")
1362}
1363
1364/// Refuse two subcommands that answer to the same name, aliases included.
1365///
1366/// A derive expansion can validate aliases written on one enum, but aliases may also live on
1367/// the independently expanded `Args` structs its variants wrap. This final, joined-table check
1368/// is where both declarations are visible.
1369pub const fn assert_unique_subcommand_names(subcommands: &[&Command<'_>]) {
1370    const fn form<'a>(cmd: &'a Command<'a>, at: usize) -> Option<&'a str> {
1371        if at == 0 {
1372            Some(cmd.name)
1373        } else if at <= cmd.aliases.len() {
1374            Some(cmd.aliases[at - 1])
1375        } else {
1376            None
1377        }
1378    }
1379
1380    let mut command = 0;
1381    while command < subcommands.len() {
1382        let mut at = 0;
1383        while let Some(name) = form(subcommands[command], at) {
1384            let mut other_command = command;
1385            while other_command < subcommands.len() {
1386                let mut other_at = if other_command == command { at + 1 } else { 0 };
1387                while let Some(other) = form(subcommands[other_command], other_at) {
1388                    assert!(
1389                        !str_eq(name, other),
1390                        "two subcommands answer to the same name, counting aliases"
1391                    );
1392                    other_at += 1;
1393                }
1394                other_command += 1;
1395            }
1396            at += 1;
1397        }
1398        command += 1;
1399    }
1400}
1401
1402/// Assert that flags scoped to a clause do not reuse a command-level spelling.
1403///
1404/// Derive-generated command tables include scoped flags so the parser can find them. Entries
1405/// that share a key are therefore the same declaration and are ignored here; every other pair
1406/// must have disjoint long, short, alias, and negated spellings.
1407pub const fn assert_clause_flag_spellings(command: &Command<'_>) {
1408    let Some(clause) = command.clause else {
1409        return;
1410    };
1411    let mut scoped_at = 0;
1412    while scoped_at < clause.flags.len() {
1413        let scoped = clause.flags[scoped_at];
1414        let mut command_at = 0;
1415        while command_at < command.flags.len() {
1416            let flag = command.flags[command_at];
1417            if flag.key != scoped.key && const_flag_forms_overlap(flag, scoped) {
1418                panic!("a clause flag spelling conflicts with a command-level flag");
1419            }
1420            command_at += 1;
1421        }
1422        scoped_at += 1;
1423    }
1424}
1425
1426const fn const_flag_forms_overlap(a: &Flag<'_>, b: &Flag<'_>) -> bool {
1427    let mut i = 0;
1428    while i < a.longs.len() {
1429        let mut j = 0;
1430        while j < b.longs.len() {
1431            if const_bytes_eq(a.longs[i].as_bytes(), b.longs[j].as_bytes()) {
1432                return true;
1433            }
1434            j += 1;
1435        }
1436        if let Some(negate) = b.negate {
1437            if const_bytes_eq(a.longs[i].as_bytes(), negate.as_bytes()) {
1438                return true;
1439            }
1440        }
1441        i += 1;
1442    }
1443    i = 0;
1444    while i < a.shorts.len() {
1445        let mut j = 0;
1446        while j < b.shorts.len() {
1447            if a.shorts[i] == b.shorts[j] {
1448                return true;
1449            }
1450            j += 1;
1451        }
1452        i += 1;
1453    }
1454    if let Some(negate) = a.negate {
1455        let mut j = 0;
1456        while j < b.longs.len() {
1457            if const_bytes_eq(negate.as_bytes(), b.longs[j].as_bytes()) {
1458                return true;
1459            }
1460            j += 1;
1461        }
1462        if let Some(other) = b.negate {
1463            if const_bytes_eq(negate.as_bytes(), other.as_bytes()) {
1464                return true;
1465            }
1466        }
1467    }
1468    false
1469}
1470
1471const fn const_bytes_eq(a: &[u8], b: &[u8]) -> bool {
1472    if a.len() != b.len() {
1473        return false;
1474    }
1475    let mut i = 0;
1476    while i < a.len() {
1477        if a[i] != b[i] {
1478            return false;
1479        }
1480        i += 1;
1481    }
1482    true
1483}
1484
1485/// `==` on strings, in a `const fn`.
1486const fn str_eq(a: &str, b: &str) -> bool {
1487    let (a, b) = (a.as_bytes(), b.as_bytes());
1488    if a.len() != b.len() {
1489        return false;
1490    }
1491    let mut i = 0;
1492    while i < a.len() {
1493        if a[i] != b[i] {
1494            return false;
1495        }
1496        i += 1;
1497    }
1498    true
1499}
1500
1501/// Rebuild an [`OsString`] from bytes the parser handed back.
1502///
1503/// This is the reverse of [`OsStr::as_encoded_bytes`], and it is how a `PathBuf` field
1504/// receives a filename the operating system accepts but UTF-8 does not — `/tmp/\xff` stays
1505/// `/tmp/\xff` rather than becoming a *different* filename with `U+FFFD` in it.
1506///
1507/// Where the platform cannot hold those bytes, they are handed back in the `Err` — as
1508/// `String::from_utf8` does — so the caller can name the value in its error without this
1509/// having to copy it for a case that is nearly never taken.
1510///
1511/// # Why this is not `unsafe`, and why it is not lossless everywhere
1512///
1513/// On **Unix** an `OsString` is an arbitrary byte sequence, so the conversion is total and
1514/// uses the safe [`OsStringExt::from_vec`]. Every byte survives, which is the case that
1515/// matters: non-UTF-8 filenames are ordinary there.
1516///
1517/// [`OsStringExt::from_vec`]: std::os::unix::ffi::OsStringExt::from_vec
1518///
1519/// On **Windows** the encoding is WTF-8, where not every byte sequence is valid, and the only
1520/// constructor that accepts one is `OsString::from_encoded_bytes_unchecked` — whose
1521/// precondition this function cannot enforce. It takes a `Vec<u8>` from a safe caller, so
1522/// there is no way to know the bytes came from `as_encoded_bytes` rather than from anywhere
1523/// else, and a safe function with a precondition that can be violated is unsound however
1524/// carefully its callers behave today.
1525///
1526/// So on Windows the bytes go through UTF-8, and one that is not valid UTF-8 is refused
1527/// rather than assumed. What that gives up is a Windows argument containing an unpaired
1528/// surrogate, which is reported instead of accepted; what it buys is that this crate needs no
1529/// `unsafe` at all.
1530pub fn os_string_from_bytes(value: Vec<u8>) -> Result<OsString, Vec<u8>> {
1531    #[cfg(unix)]
1532    {
1533        Ok(std::os::unix::ffi::OsStringExt::from_vec(value))
1534    }
1535    #[cfg(not(unix))]
1536    {
1537        match String::from_utf8(value) {
1538            Ok(text) => Ok(OsString::from(text)),
1539            Err(bad) => Err(bad.into_bytes()),
1540        }
1541    }
1542}
1543
1544/// One [`Error::InvalidValue`], built out of line.
1545///
1546/// Cold and never inlined on purpose: this is the failure path of every value
1547/// conversion in every generated `build`, and inlining it there is what made
1548/// those functions large.
1549#[cold]
1550#[inline(never)]
1551pub(crate) fn invalid_value_error<'t, 'v>(
1552    name: &'t str,
1553    value: String,
1554    reason: String,
1555) -> Error<'t, 'v> {
1556    Error::InvalidValue(Box::new(InvalidValue {
1557        name,
1558        value,
1559        reason,
1560    }))
1561}
1562
1563/// One [`Error::InvalidValue`] for a word that was not UTF-8.
1564///
1565/// The error half of what a generated `build` does per text field: the check stays
1566/// inline at the field, and this — the lossy rendering and the allocations — lives
1567/// here once instead of once per field.
1568#[cold]
1569#[inline(never)]
1570pub fn invalid_utf8_value<'t, 'v>(name: &'t str, bad: std::string::FromUtf8Error) -> Error<'t, 'v> {
1571    invalid_value_error(
1572        name,
1573        String::from_utf8_lossy(bad.as_bytes()).into_owned(),
1574        bad.utf8_error().to_string(),
1575    )
1576}
1577
1578/// One [`Error::InvalidValue`] for a value whose type would not build from it.
1579///
1580/// Takes the reason as `&dyn Display` so one copy serves every `FromStr` error type.
1581#[cold]
1582#[inline(never)]
1583pub fn invalid_parsed_value<'t, 'v>(
1584    name: &'t str,
1585    value: String,
1586    reason: &dyn std::fmt::Display,
1587) -> Error<'t, 'v> {
1588    invalid_value_error(name, value, reason.to_string())
1589}
1590
1591/// One [`Error::InvalidValue`] for a word that is not one of a value enum's choices.
1592#[cold]
1593#[inline(never)]
1594pub fn invalid_choice_value<'t, 'v>(name: &'t str, value: String) -> Error<'t, 'v> {
1595    invalid_value_error(name, value, String::from("not one of the declared values"))
1596}
1597
1598/// One [`Error::InvalidValue`] for bytes the platform cannot hold in a path.
1599#[cold]
1600#[inline(never)]
1601pub fn invalid_os_value<'t, 'v>(name: &'t str, bytes: Vec<u8>) -> Error<'t, 'v> {
1602    invalid_value_error(
1603        name,
1604        String::from_utf8_lossy(&bytes).into_owned(),
1605        "this platform cannot hold these bytes in a path".to_string(),
1606    )
1607}
1608
1609/// Convert every repeated value of one text field, reporting `name` for the
1610/// first that is not UTF-8.
1611///
1612/// The shared body of what a generated `build` does per collecting text field:
1613/// one loop in the binary rather than one per field. Converts element by
1614/// element rather than with `collect` so the error can carry the value that
1615/// failed rather than only that one did.
1616///
1617/// The empty case is answered here rather than in the shared loop, and this is
1618/// what keeps sharing the loop free: a command at mise's scale declares dozens
1619/// of collecting fields and a command line names one or two of them, so most of
1620/// these calls have nothing to convert. Testing that at the field costs a branch;
1621/// reaching the loop to learn it costs the call.
1622#[inline]
1623pub fn utf8_values<'t, 'v>(
1624    values: Vec<Vec<u8>>,
1625    name: &'t str,
1626) -> Result<Vec<String>, Error<'t, 'v>> {
1627    if values.is_empty() {
1628        return Ok(Vec::new());
1629    }
1630    utf8_values_given(values, name)
1631}
1632
1633#[inline(never)]
1634fn utf8_values_given<'t, 'v>(
1635    values: Vec<Vec<u8>>,
1636    name: &'t str,
1637) -> Result<Vec<String>, Error<'t, 'v>> {
1638    let mut out = Vec::with_capacity(values.len());
1639    for value in values {
1640        match String::from_utf8(value) {
1641            Ok(text) => out.push(text),
1642            Err(bad) => return Err(invalid_utf8_value(name, bad)),
1643        }
1644    }
1645    Ok(out)
1646}
1647
1648/// Convert every repeated value of one field through
1649/// [`FromStr`](std::str::FromStr), reporting `name` for the first that fails.
1650///
1651/// Monomorphized once per target type rather than expanded once per field, and
1652/// the empty case is answered at the field for the reason [`utf8_values`] gives.
1653#[inline]
1654pub fn parsed_values<'t, 'v, T>(
1655    values: Vec<Vec<u8>>,
1656    name: &'t str,
1657) -> Result<Vec<T>, Error<'t, 'v>>
1658where
1659    T: std::str::FromStr,
1660    T::Err: std::fmt::Display,
1661{
1662    if values.is_empty() {
1663        return Ok(Vec::new());
1664    }
1665    parsed_values_given(values, name)
1666}
1667
1668#[inline(never)]
1669fn parsed_values_given<'t, 'v, T>(
1670    values: Vec<Vec<u8>>,
1671    name: &'t str,
1672) -> Result<Vec<T>, Error<'t, 'v>>
1673where
1674    T: std::str::FromStr,
1675    T::Err: std::fmt::Display,
1676{
1677    let mut out = Vec::with_capacity(values.len());
1678    for value in values {
1679        let text = match String::from_utf8(value) {
1680            Ok(text) => text,
1681            Err(bad) => return Err(invalid_utf8_value(name, bad)),
1682        };
1683        match text.parse() {
1684            Ok(parsed) => out.push(parsed),
1685            Err(reason) => return Err(invalid_parsed_value(name, text, &reason)),
1686        }
1687    }
1688    Ok(out)
1689}
1690
1691/// Convert every repeated value of one path-like field, reporting `name` for
1692/// the first the platform cannot hold.
1693///
1694/// `T` is what the field collects — [`PathBuf`](std::path::PathBuf) or
1695/// [`OsString`] — so one body serves both. The same platform note as
1696/// [`os_string_from_bytes`] applies: lossless on Unix, partial on Windows. The
1697/// empty case is answered at the field for the reason [`utf8_values`] gives.
1698#[inline]
1699pub fn os_values<'t, 'v, T: From<OsString>>(
1700    values: Vec<Vec<u8>>,
1701    name: &'t str,
1702) -> Result<Vec<T>, Error<'t, 'v>> {
1703    if values.is_empty() {
1704        return Ok(Vec::new());
1705    }
1706    os_values_given(values, name)
1707}
1708
1709#[inline(never)]
1710fn os_values_given<'t, 'v, T: From<OsString>>(
1711    values: Vec<Vec<u8>>,
1712    name: &'t str,
1713) -> Result<Vec<T>, Error<'t, 'v>> {
1714    let mut out = Vec::with_capacity(values.len());
1715    for value in values {
1716        match os_string_from_bytes(value) {
1717            Ok(os) => out.push(T::from(os)),
1718            Err(bytes) => return Err(invalid_os_value(name, bytes)),
1719        }
1720    }
1721    Ok(out)
1722}
1723
1724/// What a generated `apply` stores for a flag that takes one value: the word, or nothing
1725/// when the flag arrived without one.
1726///
1727/// Out of line because the copy is an allocation, a `memcpy`, and the drop of whatever the
1728/// field held before — a few dozen instructions that would otherwise be expanded at every
1729/// value-taking field of every command.
1730#[doc(hidden)]
1731#[inline(never)]
1732pub fn bind_text(slot: &mut Vec<u8>, value: Option<&[u8]>) {
1733    *slot = value.map(<[u8]>::to_vec).unwrap_or_default();
1734}
1735
1736/// [`bind_text`] for an `Option<T>` field, which a flag without a value still fills.
1737#[doc(hidden)]
1738#[inline(never)]
1739pub fn bind_some_text(slot: &mut Option<Vec<u8>>, value: Option<&[u8]>) {
1740    *slot = Some(value.map(<[u8]>::to_vec).unwrap_or_default());
1741}
1742
1743/// [`bind_text`] for an `Option<Option<T>>` field, where a flag given without a value is
1744/// the inner `None`.
1745#[doc(hidden)]
1746#[inline(never)]
1747pub fn bind_optional_text(slot: &mut Option<Vec<u8>>, value: Option<&[u8]>) {
1748    *slot = value.map(<[u8]>::to_vec);
1749}
1750
1751/// [`bind_text`] for a collecting field: one more occurrence, appended.
1752#[doc(hidden)]
1753#[inline(never)]
1754pub fn bind_push_text(slot: &mut Vec<Vec<u8>>, value: Option<&[u8]>) {
1755    slot.push(value.map(<[u8]>::to_vec).unwrap_or_default());
1756}
1757
1758/// [`bind_push_text`] for a field with a value delimiter: every part, appended in order.
1759#[doc(hidden)]
1760#[inline(never)]
1761pub fn bind_push_split(slot: &mut Vec<Vec<u8>>, value: Option<&[u8]>, delimiter: u8) {
1762    for part in value.unwrap_or_default().split(|b| *b == delimiter) {
1763        slot.push(part.to_vec());
1764    }
1765}
1766
1767/// A single binding pass over `argv`.
1768///
1769/// [`Command::default_subcommand_flags`] adds a read-only lookahead before binding
1770/// to choose an implicit command boundary.
1771///
1772/// Created with [`Parser::new`] and driven with [`Parser::next_event`].
1773pub struct Parser<'t, 'a, 'v> {
1774    argv: &'a [&'v OsStr],
1775    /// Index of the next token to read.
1776    pos: usize,
1777    /// The command currently in scope.
1778    cmd: &'t Command<'t>,
1779    /// The canonical root, used to hide root globals omitted by an executable view.
1780    #[cfg(feature = "spec")]
1781    root: &'t Command<'t>,
1782    /// The executable projection being parsed, if argv0 selected one.
1783    #[cfg(feature = "spec")]
1784    view: Option<&'t spec::ViewMeta<'t>>,
1785    /// What an unrecognized flag-like token means in the command currently in scope.
1786    ///
1787    /// Carried rather than looked up, because it is inherited: a command that states
1788    /// nothing keeps what the enclosing one said, and walking back up the ancestors on
1789    /// every unrecognized token would pay for the inheritance at the wrong moment.
1790    unknown_flags: UnknownFlags,
1791    /// Effective inherited trailing-delimiter policy.
1792    dont_delimit_trailing_values: bool,
1793    /// The chain above `cmd`, used to find inherited global flags. Fixed size so
1794    /// that nothing is allocated.
1795    ancestors: [Option<&'t Command<'t>>; MAX_DEPTH],
1796    depth: usize,
1797    /// Bytes left in a short-flag bundle, if one is partly read.
1798    bundle: &'v [u8],
1799    /// The whole token the current bundle came from, so an error raised part way
1800    /// through it can still name what the user typed.
1801    bundle_token: &'v [u8],
1802    /// A variadic flag that is still collecting values.
1803    collecting: Option<&'t Flag<'t>>,
1804    /// Where the command in scope began, as an index into `argv`.
1805    cmd_start: usize,
1806    /// Where each ancestor's own words began, in step with `ancestors`.
1807    starts: [usize; MAX_DEPTH],
1808    /// How many values it has taken, so a bound can stop it.
1809    collected: u32,
1810    /// Which of `cmd.args` is next to fill.
1811    arg_pos: usize,
1812    /// How many words the variadic at `arg_pos` has taken, for the same reason.
1813    arg_taken: u32,
1814    /// Whether any word has been bound to a positional of `cmd`. Once one has,
1815    /// no further word can select a subcommand.
1816    arg_filled: bool,
1817    /// Whether this command has bound any flag or positional. Unlike
1818    /// `arg_filled`, flags count because clap's command policy treats both as
1819    /// arguments that exclude a later subcommand.
1820    command_arg_found: bool,
1821    positional_arg_found: bool,
1822    /// Whether flag interpretation has stopped. A `--` does this, and so does an
1823    /// `automatic` argument taking a value.
1824    flags_stopped: bool,
1825    /// Whether a `--` was actually consumed as a separator.
1826    ///
1827    /// Tracked apart from `flags_stopped` because the two can differ: an
1828    /// `automatic` argument stops flag interpretation without any separator being
1829    /// typed, and a `preserve` argument keeps one as a value rather than
1830    /// consuming it. Callers asking this question want to know what the user
1831    /// wrote, not what state the parser reached.
1832    separator_seen: bool,
1833    /// Whether a root clause separator was consumed, so empty-default routing cannot fire.
1834    clause_separator_seen: bool,
1835    /// Whether the default subcommand has already been taken.
1836    ///
1837    /// Once, per parse: a default subcommand that itself declares one would otherwise
1838    /// descend on every word until the tree ran out.
1839    default_taken: bool,
1840    /// First default-only flag, when lookahead found no explicit sibling.
1841    default_flag_at: Option<usize>,
1842    /// Cursor just after the implicit boundary bundle, whose shorts keep parent ownership.
1843    default_bundle_end: usize,
1844    /// Set once a fatal error has been reported, so iteration stops.
1845    done: bool,
1846    /// Whether declared built-in actions stop parsing with their action error.
1847    ///
1848    /// Invocation parsing does; completion walking only needs the grammar position after the
1849    /// flag, and must not execute an action while inspecting a partial command line.
1850    action_errors: bool,
1851    /// The `argv` range the `help` *word* resolved as a command path, if one was typed.
1852    ///
1853    /// Empty for `--help`, which asks about wherever the parse had got to. For the word, the
1854    /// question is about a command deeper than the parse reached, and only this walk knows
1855    /// which tokens named it: a caller re-scanning `argv` would count a flag's detached value
1856    /// that happens to spell a sibling's name. Two indices rather than the commands
1857    /// themselves, so the parser keeps allocating nothing.
1858    help_span: (usize, usize),
1859    /// An implicit clause boundary follows the positional event that completed it.
1860    pending_clause_boundary: ::core::option::Option<&'t Clause<'t>>,
1861}
1862
1863impl<'t: 'v, 'a, 'v> Parser<'t, 'a, 'v> {
1864    /// Begin parsing `argv` against `root`.
1865    ///
1866    /// `argv` excludes the program name.
1867    pub fn new(root: &'t Command<'t>, argv: &'a [&'v OsStr]) -> Self {
1868        Self::with_action_errors(root, argv, true)
1869    }
1870
1871    /// Begin a non-executing parse for completion walking.
1872    #[cfg(feature = "complete")]
1873    pub(crate) fn for_completion(root: &'t Command<'t>, argv: &'a [&'v OsStr]) -> Self {
1874        Self::with_action_errors(root, argv, false)
1875    }
1876
1877    fn with_action_errors(
1878        root: &'t Command<'t>,
1879        argv: &'a [&'v OsStr],
1880        action_errors: bool,
1881    ) -> Self {
1882        let mut parser = Parser {
1883            argv,
1884            pos: 0,
1885            cmd: root,
1886            #[cfg(feature = "spec")]
1887            root,
1888            #[cfg(feature = "spec")]
1889            view: None,
1890            unknown_flags: match root.unknown_flags {
1891                ::core::option::Option::Some(mode) => mode,
1892                // Nothing above the root to inherit from, so the default stands.
1893                ::core::option::Option::None => UnknownFlags::Value,
1894            },
1895            dont_delimit_trailing_values: root.dont_delimit_trailing_values,
1896            ancestors: [None; MAX_DEPTH],
1897            depth: 0,
1898            bundle: &[],
1899            bundle_token: &[],
1900            collecting: None,
1901            cmd_start: 0,
1902            starts: [0; MAX_DEPTH],
1903            collected: 0,
1904            arg_pos: 0,
1905            arg_taken: 0,
1906            arg_filled: false,
1907            command_arg_found: false,
1908            positional_arg_found: false,
1909            flags_stopped: false,
1910            separator_seen: false,
1911            clause_separator_seen: false,
1912            default_taken: false,
1913            default_flag_at: None,
1914            default_bundle_end: 0,
1915            done: false,
1916            action_errors,
1917            help_span: (0, 0),
1918            pending_clause_boundary: ::core::option::Option::None,
1919        };
1920        if root.default_subcommand_flags {
1921            parser.default_flag_at = parser.default_flag_route();
1922        }
1923        parser
1924    }
1925
1926    /// Find the implicit boundary without binding anything. Parent spellings win while
1927    /// scanning the prefix; after the boundary the ordinary child/global scope applies.
1928    /// Unknown flags stop lookahead because their value arity cannot be guessed.
1929    fn default_flag_route(&self) -> Option<usize> {
1930        let default = self.cmd.default_subcommand?;
1931        let mut at = None;
1932        let mut i = 0;
1933        while let Some(token) = self.argv.get(i).map(bytes) {
1934            let scope = if at.is_some() { default } else { self.cmd };
1935            if token == b"--"
1936                || token == b"-"
1937                || scope.clause.is_some_and(|c| c.separator == Some(token))
1938            {
1939                return at;
1940            }
1941            if !is_flag_like(token) {
1942                // A sibling name before any default-only flag stays on the parent.
1943                // After one, later words are the default command's args (`-u pkg`
1944                // when `pkg` is also a command).
1945                if at.is_none()
1946                    && (self.find_subcommand(token).is_some()
1947                        || (token == b"help" && !self.cmd.disable_help_subcommand))
1948                {
1949                    return None;
1950                }
1951                return at;
1952            }
1953            let mut value_flag = None;
1954            let mut attached = None;
1955            if let Some(body) = token.strip_prefix(b"--") {
1956                let end = body.iter().position(|b| *b == b'=').unwrap_or(body.len());
1957                let name = &body[..end];
1958                let parent = self.find_long(name).or_else(|| self.find_negation(name));
1959                if parent.is_none()
1960                    && ((name == b"help" && !self.cmd.disable_help_flag)
1961                        || (name == b"version"
1962                            && self.cmd.version
1963                            && !self.cmd.disable_version_flag))
1964                {
1965                    i += 1;
1966                    continue;
1967                }
1968                let flag = parent.or_else(|| {
1969                    default.flags.iter().copied().find(|f| {
1970                        f.longs.iter().any(|l| l.as_bytes() == name)
1971                            || f.negate.is_some_and(|n| n.as_bytes() == name)
1972                    })
1973                });
1974                let Some(flag) = flag else {
1975                    return at;
1976                };
1977                if parent.is_none() {
1978                    at.get_or_insert(i);
1979                }
1980                if flag.takes_value && flag.negate.is_none_or(|n| n.as_bytes() != name) {
1981                    value_flag = Some(flag);
1982                    attached = (end < body.len()).then(|| &body[end + 1..]);
1983                }
1984            } else {
1985                for (offset, byte) in token[1..].iter().enumerate() {
1986                    let parent = self.find_short(*byte);
1987                    let Some(flag) = parent.or_else(|| {
1988                        default
1989                            .flags
1990                            .iter()
1991                            .copied()
1992                            .find(|f| f.shorts.contains(byte))
1993                    }) else {
1994                        return at;
1995                    };
1996                    if parent.is_none() {
1997                        at.get_or_insert(i);
1998                    }
1999                    if flag.takes_value {
2000                        value_flag = Some(flag);
2001                        let rest = &token[offset + 2..];
2002                        attached =
2003                            (!rest.is_empty()).then_some(rest.strip_prefix(b"=").unwrap_or(rest));
2004                        break;
2005                    }
2006                }
2007            }
2008            i += 1;
2009            if let Some(flag) = value_flag {
2010                let scope = if at.is_some() { default } else { self.cmd };
2011                let is_separator =
2012                    |next: &[u8]| scope.clause.is_some_and(|c| c.separator == Some(next));
2013                let first = if let Some(value) = attached {
2014                    Some(value)
2015                } else if !flag.require_equals {
2016                    self.argv
2017                        .get(i)
2018                        .map(bytes)
2019                        .filter(|next| {
2020                            !is_separator(next)
2021                                && (flag.allow_hyphen_values
2022                                    || !is_flag_like(next)
2023                                    || (flag.allow_negative_numbers && is_negative_number(next)))
2024                        })
2025                        .inspect(|_| i += 1)
2026                } else {
2027                    None
2028                };
2029                if first.is_none() && !flag.value_optional && flag.default_missing.is_none() {
2030                    return at;
2031                }
2032                if flag.variadic {
2033                    let mut count = first.map_or(0, |v| values_in(v, flag.delimiter));
2034                    while flag.var_max.is_none_or(|max| count < max) {
2035                        let Some(next) = self.argv.get(i).map(bytes) else {
2036                            break;
2037                        };
2038                        if is_separator(next) {
2039                            break;
2040                        }
2041                        if next == b"--" || flag.value_terminator.is_some_and(|end| end == next) {
2042                            if next != b"--" {
2043                                i += 1;
2044                            }
2045                            break;
2046                        }
2047                        if is_flag_like(next)
2048                            && !(flag.allow_negative_numbers && is_negative_number(next))
2049                        {
2050                            break;
2051                        }
2052                        if self.cmd.subcommand_precedence_over_arg
2053                            && self.find_subcommand(next).is_some()
2054                        {
2055                            break;
2056                        }
2057                        count += values_in(next, flag.delimiter);
2058                        i += 1;
2059                    }
2060                }
2061            }
2062        }
2063        at
2064    }
2065
2066    /// Restrict inherited root globals to those carried by an executable view.
2067    #[cfg(feature = "spec")]
2068    pub fn with_view(mut self, view: &'t spec::ViewMeta<'t>) -> Self {
2069        self.view = Some(view);
2070        self
2071    }
2072
2073    /// The command in scope: the root, or the deepest subcommand selected so far.
2074    pub fn command(&self) -> &'t Command<'t> {
2075        self.cmd
2076    }
2077
2078    /// Whether a `--` was consumed as a separator.
2079    ///
2080    /// False when flag interpretation stopped for another reason, such as an
2081    /// `automatic` argument taking a value, and false for a `--` that a
2082    /// `preserve` argument kept as a value.
2083    pub fn double_dash_seen(&self) -> bool {
2084        self.separator_seen
2085    }
2086
2087    /// Every command entered so far, and where each one's own words begin.
2088    ///
2089    /// The ancestors are already kept for flag scoping; this is the same chain with the offsets,
2090    /// which is what lets a completion hand a callback the words of *its* command rather than of
2091    /// the deepest one — a global flag is declared on an ancestor.
2092    pub fn command_path(&self) -> Vec<(&'t Command<'t>, usize)> {
2093        let mut out = Vec::with_capacity(self.depth + 1);
2094        for (i, ancestor) in self.ancestors[..self.depth].iter().enumerate() {
2095            if let Some(cmd) = ancestor {
2096                // An ancestor's own words start where the one before it descended, and the
2097                // root's start at the beginning.
2098                out.push((*cmd, self.starts[i]));
2099            }
2100        }
2101        out.push((self.cmd, self.cmd_start));
2102        out
2103    }
2104
2105    /// The `argv` range the `help` word resolved as a command path.
2106    ///
2107    /// Empty unless the word was typed. Every token in it named a subcommand of the one before
2108    /// it — the parser resolved them itself, so nothing here is a flag or a flag's value.
2109    pub fn help_span(&self) -> (usize, usize) {
2110        self.help_span
2111    }
2112
2113    /// Where the command in scope began: the index in `argv` just after its name, or at the
2114    /// unmatched word routed into a default subcommand.
2115    ///
2116    /// `argv[command_start()..]` is what that command was given, which is what a completion
2117    /// callback needs to be handed its own command's half-parsed struct rather than the root's.
2118    pub fn command_start(&self) -> usize {
2119        self.cmd_start
2120    }
2121
2122    /// Whether flag interpretation has stopped, for any reason.
2123    ///
2124    /// Wider than [`double_dash_seen`](Self::double_dash_seen), and the question completion
2125    /// asks: past a separator *or* past the first value of an `automatic` argument, a
2126    /// dash-prefixed word is a value, so there is no flag there to offer.
2127    pub fn flags_stopped(&self) -> bool {
2128        self.flags_stopped
2129    }
2130
2131    /// A variadic flag that is still claiming words.
2132    ///
2133    /// Asked *between* events, because the answer is gone by the end: the call that finds argv
2134    /// exhausted is the one that clears it. A completion needs it — the next word after
2135    /// `--tools a ⌶` is another tool, not the positional that follows.
2136    pub fn collecting(&self) -> Option<&'t Flag<'t>> {
2137        self.collecting
2138    }
2139
2140    /// The positional the next word would fill, if there is one left.
2141    ///
2142    /// A variadic stays here until it reaches its bound, which is what makes it the answer to
2143    /// "what could go where the cursor is" as many times as it can be filled.
2144    pub fn pending_arg(&self) -> Option<&'t Arg<'t>> {
2145        self.next_arg()
2146    }
2147
2148    /// Flags a word here could name: this command's own, then any ancestor's globals.
2149    ///
2150    /// The same set the parser itself would look in, so what is offered and what is accepted
2151    /// cannot disagree — including the shadowing rule, where a subcommand redeclaring an
2152    /// inherited name hides it.
2153    pub fn flags_in_scope(&self) -> impl Iterator<Item = &'t Flag<'t>> + '_ {
2154        self.in_scope()
2155    }
2156
2157    /// Read the next event.
2158    ///
2159    /// Returns `None` when `argv` is exhausted. An `Err` is terminal: the parse
2160    /// stops there, since continuing past a token that could not be understood
2161    /// would only produce bindings derived from a guess. Events already yielded
2162    /// before an error are therefore not a partial result to be used — a caller
2163    /// that assigned them into fields should discard the whole attempt.
2164    ///
2165    /// One case is stronger than that, because the grammar demands it: a short
2166    /// bundle containing an unrecognized letter yields the error *instead of*, not
2167    /// after, the letters that did match.
2168    #[allow(clippy::should_implement_trait)] // not an Iterator: items borrow from self's tables
2169    pub fn next_event(&mut self) -> Option<Result<Event<'t, 'a, 'v>, Error<'t, 'v>>> {
2170        if self.done {
2171            return None;
2172        }
2173        let event = self.step();
2174        if matches!(event, Some(Ok(Event::Flag { .. } | Event::Arg { .. }))) {
2175            self.command_arg_found = true;
2176        }
2177        if matches!(event, Some(Ok(Event::Arg { .. }))) {
2178            self.positional_arg_found = true;
2179        }
2180        if event.is_none()
2181            && self.depth == 0
2182            && self.cmd.default_subcommand_on_empty
2183            && !self.default_taken
2184            && !self.positional_arg_found
2185            && !self.separator_seen
2186            && !self.clause_separator_seen
2187        {
2188            if let Some(default) = self.cmd.default_subcommand {
2189                if self.cmd.args_conflicts_with_subcommands && self.command_arg_found {
2190                    self.done = true;
2191                    return Some(Err(Error::SubcommandConflict {
2192                        subcommand: default,
2193                    }));
2194                }
2195                self.default_taken = true;
2196                match self.descend(default) {
2197                    Ok(()) => return Some(Ok(Event::Command(default))),
2198                    Err(error) => {
2199                        self.done = true;
2200                        return Some(Err(error));
2201                    }
2202                }
2203            }
2204        }
2205        if let Some(Err(_)) = event {
2206            self.done = true;
2207        }
2208        event
2209    }
2210
2211    /// Count parent flags in the shared boundary token before entering the child.
2212    fn default_bundle_has_parent_flag(&self, default: &Command<'_>) -> bool {
2213        let Some(token) = self.argv.get(self.pos).map(bytes) else {
2214            return false;
2215        };
2216        if !token.starts_with(b"-") || token.starts_with(b"--") {
2217            return false;
2218        }
2219        for byte in &token[1..] {
2220            if self.find_short(*byte).is_some() {
2221                return true;
2222            }
2223            match default.flags.iter().find(|f| f.shorts.contains(byte)) {
2224                Some(flag) if !flag.takes_value => {}
2225                _ => break,
2226            }
2227        }
2228        false
2229    }
2230
2231    fn step(&mut self) -> Option<Result<Event<'t, 'a, 'v>, Error<'t, 'v>>> {
2232        if self.default_flag_at == Some(self.pos) && self.bundle.is_empty() {
2233            self.default_flag_at = None;
2234            if let Some(default) = self.cmd.default_subcommand {
2235                if self.cmd.args_conflicts_with_subcommands
2236                    && (self.command_arg_found || self.default_bundle_has_parent_flag(default))
2237                {
2238                    return Some(Err(Error::SubcommandConflict {
2239                        subcommand: default,
2240                    }));
2241                }
2242                if self.argv.get(self.pos).is_some_and(|word| {
2243                    let token = bytes(word);
2244                    token.starts_with(b"-") && !token.starts_with(b"--")
2245                }) {
2246                    self.default_bundle_end = self.pos + 1;
2247                }
2248                self.default_taken = true;
2249                return Some(self.descend(default).map(|()| Event::Command(default)));
2250            }
2251        }
2252        if let Some(clause) = self.pending_clause_boundary.take() {
2253            self.arg_pos = 0;
2254            self.arg_taken = 0;
2255            self.arg_filled = false;
2256            self.collecting = None;
2257            self.flags_stopped = false;
2258            return Some(Ok(Event::ClauseSeparator { clause: *clause }));
2259        }
2260        // A partly-read short bundle takes priority: its remaining bytes are
2261        // still part of the token being processed.
2262        if !self.bundle.is_empty() {
2263            return Some(self.short_flag());
2264        }
2265
2266        if self.cmd.subcommand_precedence_over_arg && !self.flags_stopped {
2267            if let Some(token) = self.argv.get(self.pos).map(bytes) {
2268                if let Some(sub) = self.find_subcommand(token) {
2269                    if self.cmd.args_conflicts_with_subcommands && self.command_arg_found {
2270                        return Some(Err(Error::SubcommandConflict { subcommand: sub }));
2271                    }
2272                    self.pos += 1;
2273                    return Some(self.descend(sub).map(|()| Event::Command(sub)));
2274                }
2275            }
2276        }
2277
2278        // A variadic flag keeps claiming tokens until one of them could be
2279        // something else.
2280        if let Some(flag) = self.collecting {
2281            match self.argv.get(self.pos) {
2282                Some(next)
2283                    if flag
2284                        .value_terminator
2285                        .is_some_and(|terminator| bytes(next) == terminator) =>
2286                {
2287                    self.pos += 1;
2288                    self.collecting = None;
2289                    return self.step();
2290                }
2291                Some(next)
2292                    if (!is_flag_like(bytes(next))
2293                        || (flag.allow_negative_numbers && is_negative_number(bytes(next))))
2294                        && self.clause_separator(bytes(next)).is_none()
2295                        && bytes(next) != b"--" =>
2296                {
2297                    self.pos += 1;
2298                    self.collected += values_in(bytes(next), flag.delimiter);
2299                    // Same rule as a positional: a bounded occurrence takes that many and
2300                    // leaves the rest to whatever follows.
2301                    if flag.var_max.is_some_and(|max| self.collected >= max) {
2302                        self.collecting = None;
2303                    }
2304                    // Stopping is only the same as staying within the bound while one word
2305                    // is one value. A delimited word can carry the occurrence past it in a
2306                    // single step, and that word cannot be split between two owners, so the
2307                    // overshoot is an error rather than a place to stop.
2308                    if let Some(max) = flag.var_max.filter(|max| self.collected > *max) {
2309                        return Some(Err(Error::VarTooMany {
2310                            name: flag.name,
2311                            max: max as usize,
2312                            got: self.collected as usize,
2313                        }));
2314                    }
2315                    return Some(Ok(Event::Flag {
2316                        flag,
2317                        value: Some(bytes(next)),
2318                        negated: false,
2319                    }));
2320                }
2321                // A token that could be something else ends the run — but the *end of argv*
2322                // decides nothing. Clearing there threw away the answer to "would the next
2323                // word be claimed?", which is the question a completion asks and no parse
2324                // ever does: once argv is exhausted there are no more events either way.
2325                Some(_) => self.collecting = None,
2326                None => {}
2327            }
2328        }
2329
2330        let token = bytes(self.argv.get(self.pos)?);
2331        self.pos += 1;
2332
2333        // A clause separator remains syntax after an automatic positional stopped flags.
2334        // Only an explicit `--` protects a literal separator.
2335        if let Some(clause) = self.clause_separator(token) {
2336            self.arg_pos = 0;
2337            self.arg_taken = 0;
2338            self.arg_filled = false;
2339            self.collecting = None;
2340            self.flags_stopped = false;
2341            if self.depth == 0 {
2342                self.clause_separator_seen = true;
2343            }
2344            return Some(Ok(Event::ClauseSeparator { clause: *clause }));
2345        }
2346
2347        // An automatic trailing argument stops flag interpretation without consuming an
2348        // explicit separator. A later `--` must still unlock a required trailing argument
2349        // (clap's `last`), while a separator already consumed makes every later `--` data.
2350        if self.flags_stopped && (token != b"--" || self.separator_seen) {
2351            return Some(self.word(token));
2352        }
2353
2354        if token == b"--" {
2355            // `preserve` wants the separator itself as a value, so ask the
2356            // argument that would receive it before treating it as syntax.
2357            if self
2358                .next_arg()
2359                .is_some_and(|a| a.double_dash == DoubleDash::Preserve)
2360            {
2361                return Some(self.word(token));
2362            }
2363            self.flags_stopped = true;
2364            self.separator_seen = true;
2365            // An explicit separator unlocks any argument that required one, even
2366            // if earlier arguments are still unfilled.
2367            if let Some(idx) = self.current_args()[self.arg_pos..]
2368                .iter()
2369                .position(|a| a.double_dash == DoubleDash::Required)
2370            {
2371                // The count belongs to the argument at `arg_pos`, so jumping past it has
2372                // to leave the count behind: a bounded variadic before the separator would
2373                // otherwise lend its total to the argument after it, which then stops
2374                // early or at once.
2375                self.arg_pos += idx;
2376                self.arg_taken = 0;
2377            }
2378            return self.step();
2379        }
2380
2381        if self.arg_taken > 0
2382            && self.next_arg().is_some_and(|arg| {
2383                arg.value_terminator
2384                    .is_some_and(|terminator| token == terminator)
2385            })
2386        {
2387            self.advance_arg();
2388            return self.step();
2389        }
2390
2391        // An exact declared short outranks the numeric shape. This keeps ordinary negative
2392        // numbers available as values while allowing clap-compatible spellings such as fd's
2393        // `-0` / `--print0` switch.
2394        let declared_numeric_short = matches!(token, [b'-', short]
2395            if short.is_ascii_digit() && self.find_short(*short).is_some());
2396
2397        if !declared_numeric_short
2398            && is_negative_number(token)
2399            && self
2400                .next_arg()
2401                .is_some_and(|arg| arg.allow_negative_numbers)
2402        {
2403            return Some(self.word(token));
2404        }
2405
2406        if !declared_numeric_short
2407            && is_negative_number(token)
2408            && self.cmd.external_subcommand
2409            && !self.arg_filled
2410        {
2411            return Some(self.word(token));
2412        }
2413
2414        if is_flag_like(token) {
2415            if token.starts_with(b"--") {
2416                return Some(self.long_flag(token));
2417            }
2418            // Check the whole bundle before emitting anything from it. Events go
2419            // out one at a time, so discovering an unknown letter half way
2420            // through would mean the earlier letters had already been applied —
2421            // and the grammar rejects the entire token, not the tail of it.
2422            match self.check_bundle(token) {
2423                Ok(()) => {}
2424                // Unrecognized, so it is a word unless this command wants it refused.
2425                Err(e) if self.unknown_flags == UnknownFlags::Error => {
2426                    return Some(Err(e));
2427                }
2428                Err(_) => return Some(self.word(token)),
2429            }
2430            self.bundle = &token[1..];
2431            self.bundle_token = token;
2432            return Some(self.short_flag());
2433        }
2434
2435        Some(self.word(token))
2436    }
2437
2438    fn long_flag(&mut self, token: &'v [u8]) -> Result<Event<'t, 'a, 'v>, Error<'t, 'v>> {
2439        let body = &token[2..];
2440        let (name, attached) = match body.iter().position(|&b| b == b'=') {
2441            Some(i) => (&body[..i], Some(&body[i + 1..])),
2442            None => (body, None),
2443        };
2444
2445        if let Some(flag) = self.find_long(name) {
2446            let value = if flag.takes_value {
2447                match attached {
2448                    Some(v) => Some(v),
2449                    None => self.take_detached_value(flag)?,
2450                }
2451            } else if flag.bool_value {
2452                validate_bool_value(flag, attached)?
2453            } else {
2454                None
2455            };
2456            if flag.variadic {
2457                if let Some(value) = value {
2458                    self.start_collecting(flag, value)?;
2459                }
2460            }
2461            if let Some(error) = self.flag_action(flag, true) {
2462                return Err(error);
2463            }
2464            return Ok(Event::Flag {
2465                flag,
2466                value,
2467                negated: false,
2468            });
2469        }
2470
2471        if let Some(flag) = self.find_negation(name) {
2472            return Ok(Event::Flag {
2473                flag,
2474                value: if flag.bool_value {
2475                    validate_bool_value(flag, attached)?
2476                } else {
2477                    None
2478                },
2479                negated: true,
2480            });
2481        }
2482
2483        // Where the CLI declared a version, `--version` answers with it — asked after the
2484        // command's own flags, so a CLI declaring its own keeps it.
2485        let version_command = self.version_command();
2486        if name == b"version" && version_command.version && !version_command.disable_version_flag {
2487            return Ok(Event::Flag {
2488                flag: &VERSION_LONG,
2489                value: None,
2490                negated: false,
2491            });
2492        }
2493
2494        // Every CLI answers to `--help`, and none of them declares it. Asked *after* the
2495        // command's own flags, so a CLI that declares its own `--help` keeps it.
2496        if name == b"help" && !self.cmd.disable_help_flag {
2497            return Ok(Event::Flag {
2498                flag: &HELP_LONG,
2499                value: None,
2500                negated: false,
2501            });
2502        }
2503
2504        if self.unknown_flags == UnknownFlags::Error {
2505            return Err(Error::UnknownFlag { token });
2506        }
2507        // Not a flag here, so it is a word like any other.
2508        self.word(token)
2509    }
2510
2511    /// Walk a short-flag token without binding anything, to find out whether all
2512    /// of it is recognized.
2513    ///
2514    /// Scanning stops at the first letter whose flag takes a value, because
2515    /// everything after it is that value rather than more letters.
2516    fn check_bundle(&self, token: &'v [u8]) -> Result<(), Error<'t, 'v>> {
2517        let mut rest = &token[1..];
2518        while let Some((&byte, tail)) = rest.split_first() {
2519            match self.find_short(byte) {
2520                None => return Err(Error::UnknownFlag { token }),
2521                Some(flag) if flag.takes_value => return Ok(()),
2522                Some(_) => rest = tail,
2523            }
2524        }
2525        Ok(())
2526    }
2527
2528    fn short_flag(&mut self) -> Result<Event<'t, 'a, 'v>, Error<'t, 'v>> {
2529        let byte = self.bundle[0];
2530        let rest = &self.bundle[1..];
2531
2532        let Some(flag) = self.find_short(byte) else {
2533            // check_bundle already rejected any token containing an unrecognized
2534            // letter, so this is unreachable — but a parser should report rather
2535            // than panic if that ever stops being true.
2536            self.bundle = &[];
2537            return Err(Error::UnknownFlag {
2538                token: self.bundle_token,
2539            });
2540        };
2541
2542        if !flag.takes_value {
2543            self.bundle = rest;
2544            if let Some(error) = self.flag_action(flag, false) {
2545                self.bundle = &[];
2546                return Err(error);
2547            }
2548            return Ok(Event::Flag {
2549                flag,
2550                value: None,
2551                negated: false,
2552            });
2553        }
2554
2555        // A value-taking short ends the token: everything after it is the value,
2556        // less one separating `=`.
2557        self.bundle = &[];
2558        let value = if rest.is_empty() {
2559            self.take_detached_value(flag)?
2560        } else if rest[0] == b'=' {
2561            Some(&rest[1..])
2562        } else {
2563            Some(rest)
2564        };
2565        if flag.variadic {
2566            if let Some(value) = value {
2567                self.start_collecting(flag, value)?;
2568            }
2569        }
2570        if let Some(error) = self.flag_action(flag, false) {
2571            return Err(error);
2572        }
2573        Ok(Event::Flag {
2574            flag,
2575            value,
2576            negated: false,
2577        })
2578    }
2579
2580    fn flag_action(&self, flag: &'t Flag<'t>, long_spelling: bool) -> Option<Error<'t, 'v>> {
2581        if matches!(
2582            flag.key,
2583            HELP_LONG_KEY | HELP_SHORT_KEY | VERSION_LONG_KEY | VERSION_SHORT_KEY
2584        ) || !self.action_errors
2585        {
2586            return None;
2587        }
2588        match flag.action {
2589            ArgAction::Set => None,
2590            ArgAction::Help => Some(Error::Help {
2591                cmd: self.cmd,
2592                long: long_spelling,
2593            }),
2594            ArgAction::HelpShort => Some(Error::Help {
2595                cmd: self.cmd,
2596                long: false,
2597            }),
2598            ArgAction::HelpLong => Some(Error::Help {
2599                cmd: self.cmd,
2600                long: true,
2601            }),
2602            ArgAction::HelpAll => Some(Error::HelpAll { cmd: self.cmd }),
2603            ArgAction::Version => Some(Error::Version {
2604                long: long_spelling,
2605            }),
2606        }
2607    }
2608
2609    /// Take the following token as a flag's value.
2610    ///
2611    /// Refuses a flag-like token unless [`Flag::allow_hyphen_values`] is set:
2612    /// `--jobs --force` is far more likely a forgotten value than a deliberate
2613    /// one, and the attached form is available for the deliberate case. Declared,
2614    /// the next token is taken whatever it looks like, including `--`.
2615    fn take_detached_value(
2616        &mut self,
2617        flag: &'t Flag<'t>,
2618    ) -> Result<Option<&'v [u8]>, Error<'t, 'v>> {
2619        if flag.require_equals {
2620            return self.missing_or_default(flag);
2621        }
2622        match self.argv.get(self.pos) {
2623            Some(next)
2624                if self.clause_separator(bytes(next)).is_none()
2625                    && (flag.allow_hyphen_values
2626                        || !is_flag_like(bytes(next))
2627                        || (flag.allow_negative_numbers && is_negative_number(bytes(next)))) =>
2628            {
2629                self.pos += 1;
2630                Ok(Some(bytes(next)))
2631            }
2632            _ => self.missing_or_default(flag),
2633        }
2634    }
2635
2636    fn missing_or_default(&self, flag: &'t Flag<'t>) -> Result<Option<&'v [u8]>, Error<'t, 'v>> {
2637        match flag.default_missing {
2638            Some(value) => Ok(Some(value)),
2639            None if flag.value_optional => Ok(None),
2640            None => Err(Error::MissingFlagValue { flag }),
2641        }
2642    }
2643
2644    fn clause_separator(&self, token: &[u8]) -> Option<&'t Clause<'t>> {
2645        (!self.separator_seen)
2646            .then_some(self.cmd.clause)
2647            .flatten()
2648            .filter(|clause| clause.separator.is_some_and(|separator| token == separator))
2649    }
2650
2651    fn word(&mut self, token: &'v [u8]) -> Result<Event<'t, 'a, 'v>, Error<'t, 'v>> {
2652        // Subcommands are only matched where descent is still possible: once a
2653        // positional of this command has taken a word, a later word that happens
2654        // to equal a subcommand name is just a value.
2655        if !self.arg_filled && !self.flags_stopped {
2656            if let Some(sub) = self.find_subcommand(token) {
2657                if self.cmd.args_conflicts_with_subcommands && self.command_arg_found {
2658                    return Err(Error::SubcommandConflict { subcommand: sub });
2659                }
2660                self.descend(sub)?;
2661                return Ok(Event::Command(sub));
2662            }
2663
2664            // `ex help config ls` — the line every page with a Commands section has printed
2665            // all along ("help  Print this message or the help of the given subcommand(s)"),
2666            // and which until now did nothing. The page is what decides the condition here:
2667            // it prints that line where there are subcommands, so that is where the word is
2668            // answered, and to a leaf `help` is a word like any other.
2669            //
2670            // Asked *after* the subcommand lookup, so a CLI that declares a `help` of its own
2671            // keeps it — the same rule the two help flags follow.
2672            //
2673            // The words after it name a command, resolved here rather than descended into:
2674            // descending would bind them, and they are a question rather than an invocation.
2675            if token == b"help"
2676                && !self.cmd.disable_help_subcommand
2677                && !self.cmd.subcommands.is_empty()
2678            {
2679                let mut cmd = self.cmd;
2680                let from = self.pos;
2681                while let Some(next) = self.argv.get(self.pos) {
2682                    let Some(sub) = find_named(cmd, bytes(next)) else {
2683                        break;
2684                    };
2685                    cmd = sub;
2686                    self.pos += 1;
2687                }
2688                // Kept for `help::route_to`: which mount was asked about is not recoverable
2689                // from `cmd`, since two mounts of one `Subcommands` type are one address.
2690                self.help_span = (from, self.pos);
2691                // The long form, as `ex config --help` gives: someone who typed a whole word to
2692                // ask for help wants the fuller answer.
2693                return Err(Error::Help { cmd, long: true });
2694            }
2695
2696            // A word that names no subcommand goes to the default one, if there is one.
2697            //
2698            // Only a word, though. A dash-prefixed token that named no flag arrives here as a
2699            // value — that is what `unknown_flags = value` means — and it was never a
2700            // candidate to *select* anything, so it binds where it was typed. usage-lib stops
2701            // looking for subcommands at an unrecognised flag for the same reason. `--` is
2702            // excluded on the same grounds: it reaches this function only when a `preserve`
2703            // argument wants it as a value.
2704            //
2705            // The token is *not* consumed: the cursor steps back so the next event reads it
2706            // again, now against the command just descended into. That is what lets it be a
2707            // subcommand of the default (`mise build` where `build` is a task the mount
2708            // added) as easily as an argument of it, without this function having to decide
2709            // which — and without yielding two events for one word.
2710            if let Some(default) = self.cmd.default_subcommand {
2711                // `-` joins `--` in being excluded, and for the reason already written above:
2712                // a value was never a candidate to *select* anything. `is_flag_like` calls a
2713                // lone `-` a value — conventionally stdin — so it passed this guard and
2714                // descended, where mise's `run` has no positional and the parse failed.
2715                // usage-lib and clap both bind it to the root's own `[TASK]` instead.
2716                let default_accepts_negative = is_negative_number(token)
2717                    && default
2718                        .args
2719                        .first()
2720                        .is_some_and(|arg| arg.allow_negative_numbers);
2721                if !self.default_taken
2722                    && (!is_flag_like(token) || default_accepts_negative)
2723                    && token != b"--"
2724                    && token != b"-"
2725                {
2726                    self.default_taken = true;
2727                    self.descend(default)?;
2728                    self.pos -= 1;
2729                    // Unlike an explicitly named command, the default command receives the
2730                    // word that caused descent. Keep its argv boundary at that word so
2731                    // command-level policies and completion callbacks see the same input the
2732                    // command parser is about to re-read.
2733                    self.cmd_start = self.pos;
2734                    return Ok(Event::Command(default));
2735                }
2736            }
2737
2738            // Known subcommands and the default route keep precedence. A sigil positional
2739            // then claims its classified word before an external-subcommand catch-all can.
2740            if let Some((arg, sigil)) = self.match_sigil_arg(token) {
2741                if token.len() == sigil.len() {
2742                    return Err(invalid_value_error(
2743                        arg.name,
2744                        as_str(token).unwrap_or_default().to_string(),
2745                        format!(
2746                            "expected a value after sigil {:?}",
2747                            as_str(sigil).unwrap_or_default()
2748                        ),
2749                    ));
2750                }
2751                return Ok(Event::Arg {
2752                    arg,
2753                    value: &token[sigil.len()..],
2754                    delimit: true,
2755                });
2756            }
2757
2758            // An unmatched word that names no subcommand is forwarded as an external
2759            // command: this word, then every token after it, including flags. Known
2760            // subcommands already won above, and a default_subcommand already caught.
2761            if self.cmd.external_subcommand
2762                && (!is_flag_like(token) || is_negative_number(token))
2763                && token != b"--"
2764                && token != b"-"
2765            {
2766                let from = self.pos - 1;
2767                self.pos = self.argv.len();
2768                return Ok(Event::External {
2769                    values: &self.argv[from..],
2770                });
2771            }
2772        }
2773
2774        if self.arg_filled && !self.flags_stopped {
2775            if let Some((arg, sigil)) = self.match_sigil_arg(token) {
2776                if token.len() == sigil.len() {
2777                    return Err(invalid_value_error(
2778                        arg.name,
2779                        as_str(token).unwrap_or_default().to_string(),
2780                        format!(
2781                            "expected a value after sigil {:?}",
2782                            as_str(sigil).unwrap_or_default()
2783                        ),
2784                    ));
2785                }
2786                return Ok(Event::Arg {
2787                    arg,
2788                    value: &token[sigil.len()..],
2789                    delimit: true,
2790                });
2791            }
2792        }
2793
2794        self.skip_sigil_args();
2795        self.reserve_for_required_positionals();
2796        let Some(arg) = self.next_arg() else {
2797            return Err(Error::UnexpectedArg { token });
2798        };
2799
2800        if arg.double_dash == DoubleDash::Required && !self.separator_seen {
2801            return Err(Error::ArgRequiresDoubleDash { arg });
2802        }
2803
2804        self.arg_filled = true;
2805        // An `automatic` argument stops flag interpretation from here on, as
2806        // though the caller had typed the separator themselves.
2807        let trailing_value = self.separator_seen || arg.double_dash == DoubleDash::Automatic;
2808        let delimit = !(self.dont_delimit_trailing_values && trailing_value);
2809        if arg.double_dash == DoubleDash::Automatic {
2810            self.flags_stopped = true;
2811        }
2812        // A variadic keeps taking values, so the cursor stays put — until it reaches its
2813        // bound, at which point the words after it belong to whatever comes next. That is
2814        // what makes `[a]… [b]` expressible at all.
2815        if arg.var {
2816            self.arg_taken += values_in(token, delimit.then_some(arg.delimiter).flatten());
2817            // Before advancing, which resets the count: as with a variadic flag, reaching
2818            // the bound and passing it are the same event once a word can carry several
2819            // values, and only the second is a mistake.
2820            if let Some(max) = arg.var_max.filter(|max| self.arg_taken > *max) {
2821                return Err(Error::VarTooMany {
2822                    name: arg.name,
2823                    max: max as usize,
2824                    got: self.arg_taken as usize,
2825                });
2826            }
2827            if arg.var_max.is_some_and(|max| self.arg_taken >= max) {
2828                self.advance_arg();
2829            }
2830        } else {
2831            self.advance_arg();
2832        }
2833        if self
2834            .cmd
2835            .clause
2836            .is_some_and(|clause| clause.separator.is_none() && self.next_arg().is_none())
2837        {
2838            self.pending_clause_boundary = self.cmd.clause;
2839        }
2840        Ok(Event::Arg {
2841            arg,
2842            value: token,
2843            delimit,
2844        })
2845    }
2846
2847    fn descend(&mut self, sub: &'t Command<'t>) -> Result<(), Error<'t, 'v>> {
2848        if self.depth >= MAX_DEPTH {
2849            return Err(Error::TooDeep);
2850        }
2851        self.ancestors[self.depth] = Some(self.cmd);
2852        self.starts[self.depth] = self.cmd_start;
2853        self.depth += 1;
2854        self.cmd = sub;
2855        // Only a command that says something changes it, which is what inheriting means.
2856        if let ::core::option::Option::Some(mode) = sub.unknown_flags {
2857            self.unknown_flags = mode;
2858        }
2859        self.dont_delimit_trailing_values |= sub.dont_delimit_trailing_values;
2860        // Where this command's own words start, which is what lets a completion hand a callback
2861        // the half-parsed struct of the command it was declared on rather than of the root.
2862        self.cmd_start = self.pos;
2863        self.arg_pos = 0;
2864        self.arg_taken = 0;
2865        self.arg_filled = false;
2866        self.command_arg_found = false;
2867        Ok(())
2868    }
2869
2870    /// Move to the next positional, forgetting what the last one took.
2871    fn advance_arg(&mut self) {
2872        self.skip_sigil_args();
2873        self.arg_pos += 1;
2874        self.arg_taken = 0;
2875        self.skip_sigil_args();
2876    }
2877
2878    /// A variadic flag occurrence begins, counting from zero.
2879    ///
2880    /// The value it was given on the same token counts, which is why this starts at what
2881    /// that value holds: `--include a b` with `var_max=2` takes `a` and `b`, not three
2882    /// words — and `--include a,b` has already taken both on the one token.
2883    fn start_collecting(&mut self, flag: &'t Flag<'t>, first: &[u8]) -> Result<(), Error<'t, 'v>> {
2884        self.collected = values_in(first, flag.delimiter);
2885        if let Some(max) = flag.var_max.filter(|max| self.collected > *max) {
2886            return Err(Error::VarTooMany {
2887                name: flag.name,
2888                max: max as usize,
2889                got: self.collected as usize,
2890            });
2891        }
2892        self.collecting = if flag.var_max.is_some_and(|max| self.collected >= max) {
2893            None
2894        } else {
2895            Some(flag)
2896        };
2897        Ok(())
2898    }
2899
2900    fn next_arg(&self) -> Option<&'t Arg<'t>> {
2901        self.current_args()[self.arg_pos..]
2902            .iter()
2903            .find(|arg| arg.sigil.is_none())
2904            .copied()
2905    }
2906
2907    fn skip_sigil_args(&mut self) {
2908        while self
2909            .current_args()
2910            .get(self.arg_pos)
2911            .is_some_and(|arg| arg.sigil.is_some())
2912        {
2913            self.arg_pos += 1;
2914        }
2915    }
2916
2917    fn match_sigil_arg(&self, token: &[u8]) -> Option<(&'t Arg<'t>, &'t [u8])> {
2918        if self.flags_stopped {
2919            return None;
2920        }
2921        let own = self.current_args().iter().copied();
2922        let inherited = self.ancestors[..self.depth]
2923            .iter()
2924            .rev()
2925            .filter_map(|cmd| *cmd)
2926            .flat_map(|cmd| cmd.args.iter().copied());
2927        own.chain(inherited)
2928            .filter_map(|arg| {
2929                let sigil = arg.sigil?;
2930                (token.len() >= sigil.len() && token.starts_with(sigil)).then_some((arg, sigil))
2931            })
2932            .max_by_key(|(_, sigil)| sigil.len())
2933    }
2934
2935    fn current_args(&self) -> &'t [&'t Arg<'t>] {
2936        self.cmd
2937            .clause
2938            .map(|clause| clause.args)
2939            .unwrap_or(self.cmd.args)
2940    }
2941
2942    /// Skip empty optional positionals when every remaining value is needed by a later
2943    /// required positional. This is clap's opt-in `allow_missing_positional` policy.
2944    fn reserve_for_required_positionals(&mut self) {
2945        if !self.cmd.allow_missing_positional || self.arg_taken != 0 {
2946            return;
2947        }
2948        loop {
2949            let Some(current) = self.next_arg() else {
2950                return;
2951            };
2952            if current.required {
2953                return;
2954            }
2955            let required_after = self.current_args()[self.arg_pos + 1..]
2956                .iter()
2957                .filter(|arg| arg.required && arg.sigil.is_none())
2958                .count();
2959            if required_after == 0 {
2960                return;
2961            }
2962            let remaining_values = 1 + self.argv[self.pos..]
2963                .iter()
2964                .filter(|word| {
2965                    (self.flags_stopped || !is_flag_like(bytes(word)))
2966                        && self.match_sigil_arg(bytes(word)).is_none()
2967                })
2968                .count();
2969            if remaining_values > required_after {
2970                return;
2971            }
2972            self.advance_arg();
2973        }
2974    }
2975
2976    #[cfg(feature = "spec")]
2977    fn view_allows_own_flag(&self, flag: &Flag<'_>) -> bool {
2978        match self.view {
2979            None => true,
2980            // The promoted command keeps its own surface. While the injected path is
2981            // still at the host root, however, only explicitly carried globals belong
2982            // to the view; root-local flags are not part of the projected executable.
2983            Some(view) => {
2984                !core::ptr::eq(self.cmd, self.root) || is_version_flag(flag) || view.carries(flag)
2985            }
2986        }
2987    }
2988
2989    #[cfg(not(feature = "spec"))]
2990    fn view_allows_own_flag(&self, _flag: &Flag<'_>) -> bool {
2991        true
2992    }
2993
2994    #[cfg(feature = "spec")]
2995    fn view_allows_inherited_flag(&self, flag: &Flag<'_>) -> bool {
2996        match self.view {
2997            None => true,
2998            // A portable view carries selected host globals, not globals declared
2999            // by intermediate commands on a multi-segment promoted path.
3000            Some(view) => {
3001                self.root
3002                    .flags
3003                    .iter()
3004                    .any(|root| core::ptr::eq(*root, flag))
3005                    && (is_version_flag(flag) || view.carries(flag))
3006            }
3007        }
3008    }
3009
3010    #[cfg(not(feature = "spec"))]
3011    fn view_allows_inherited_flag(&self, _flag: &Flag<'_>) -> bool {
3012        true
3013    }
3014
3015    #[cfg(feature = "spec")]
3016    fn inherited_flag_is_in_scope(&self, flag: &Flag<'_>) -> bool {
3017        flag.global || (self.view.is_some() && is_version_flag(flag))
3018    }
3019
3020    #[cfg(not(feature = "spec"))]
3021    fn inherited_flag_is_in_scope(&self, flag: &Flag<'_>) -> bool {
3022        flag.global
3023    }
3024
3025    /// Flags in scope: this command's own, then any ancestor's globals.
3026    ///
3027    /// Own flags come first so that a subcommand redeclaring an inherited name
3028    /// shadows it, which is what mise relies on when it redeclares root globals
3029    /// on `run` with different shorts.
3030    fn in_scope(&self) -> impl Iterator<Item = &'t Flag<'t>> + '_ {
3031        let own = self
3032            .cmd
3033            .flags
3034            .iter()
3035            .copied()
3036            .filter(|flag| self.view_allows_own_flag(flag));
3037        let inherited = self.ancestors[..self.depth]
3038            .iter()
3039            .rev()
3040            .filter_map(|c| *c)
3041            .flat_map(|c| c.flags.iter().copied())
3042            .filter(|flag| self.inherited_flag_is_in_scope(flag))
3043            .filter(|flag| self.view_allows_inherited_flag(flag));
3044        own.chain(inherited)
3045    }
3046
3047    fn find_long(&self, name: &[u8]) -> Option<&'t Flag<'t>> {
3048        self.in_scope()
3049            .find(|f| f.longs.iter().any(|l| l.as_bytes() == name))
3050    }
3051
3052    fn find_negation(&self, name: &[u8]) -> Option<&'t Flag<'t>> {
3053        self.in_scope()
3054            .find(|f| f.negate.is_some_and(|n| n.as_bytes() == name))
3055    }
3056
3057    fn find_short(&self, byte: u8) -> Option<&'t Flag<'t>> {
3058        // Only the boundary token is shared. Later tokens use ordinary child/global scope.
3059        if self.default_taken && self.pos == self.default_bundle_end {
3060            if let Some(flag) = self.ancestors[0].and_then(|parent| {
3061                parent
3062                    .flags
3063                    .iter()
3064                    .copied()
3065                    .find(|f| f.shorts.contains(&byte))
3066            }) {
3067                return Some(flag);
3068            }
3069        }
3070        self.in_scope()
3071            .find(|f| f.shorts.contains(&byte))
3072            // As for `--help`: supplied by the parser, and only where the command has not
3073            // declared a `-h` of its own.
3074            .or(if byte == b'h' && !self.cmd.disable_help_flag {
3075                Some(&HELP_SHORT)
3076            } else if byte == b'V'
3077                && self.version_command().version
3078                && !self.version_command().disable_version_flag
3079            {
3080                Some(&VERSION_SHORT)
3081            } else {
3082                None
3083            })
3084    }
3085
3086    #[cfg(feature = "spec")]
3087    fn version_command(&self) -> &'t Command<'t> {
3088        if self.view.is_some() {
3089            self.root
3090        } else {
3091            self.cmd
3092        }
3093    }
3094
3095    #[cfg(not(feature = "spec"))]
3096    fn version_command(&self) -> &'t Command<'t> {
3097        self.cmd
3098    }
3099
3100    fn find_subcommand(&self, name: &[u8]) -> Option<&'t Command<'t>> {
3101        // Shared with `help` rather than spelled out again, so descending into a command and
3102        // asking about one cannot drift apart.
3103        find_named(self.cmd, name)
3104    }
3105}
3106
3107/// View a token as bytes.
3108///
3109/// `as_encoded_bytes` is a plain accessor with no conversion and no allocation.
3110/// The reverse direction is the one with a cost — see [`os_string_from_bytes`] —
3111/// which is why values come back as bytes.
3112fn bytes<'v>(s: &&'v OsStr) -> &'v [u8] {
3113    s.as_encoded_bytes()
3114}
3115
3116/// How many values one word carries.
3117///
3118/// One, until a delimiter is declared — and then one per separator, counting the same way
3119/// splitting on it does: `a,b` is two, `a,` is two with an empty second, and `` is one.
3120/// Counted rather than split because binding only needs the number, and the split itself
3121/// belongs to the layer that owns the values.
3122fn values_in(word: &[u8], delimiter: ::core::option::Option<u8>) -> u32 {
3123    match delimiter {
3124        Some(d) => 1 + word.iter().filter(|b| **b == d).count() as u32,
3125        None => 1,
3126    }
3127}
3128
3129/// Whether a token should be read as a flag.
3130///
3131/// `-` alone is a value, conventionally stdin. Other dash-prefixed tokens are
3132/// flag-like; a field may make the narrower negative-number exception.
3133fn is_flag_like(token: &[u8]) -> bool {
3134    matches!(token, [b'-', rest @ ..] if !rest.is_empty())
3135}
3136
3137fn is_negative_number(token: &[u8]) -> bool {
3138    token.strip_prefix(b"-").is_some_and(is_number)
3139}
3140
3141/// Whether the text after a `-` is a number, so `-1`, `-2.5`, and `-1e5` are values
3142/// while `-1x` is a flag-shaped token that names nothing.
3143///
3144/// Digits, at most one `.`, and an optional exponent. Deliberately narrower than
3145/// `f64::from_str`, which also accepts `inf` and `NaN` — `-inf` is far likelier to be
3146/// a misspelled flag than a number somebody meant to pass.
3147///
3148/// usage-lib applies the same rule, and the corpus pins the edges so the two cannot
3149/// drift apart: they disagreed about `-1e5` when this was a hand-rolled scanner on
3150/// one side and a float parse on the other.
3151///
3152/// Written out rather than deferred to `f64::from_str` because this runs on the hot
3153/// path, and a parse would mean a UTF-8 check on a slice already decided by its
3154/// bytes.
3155fn is_number(rest: &[u8]) -> bool {
3156    let (mantissa, exponent) = match rest.iter().position(|b| matches!(b, b'e' | b'E')) {
3157        Some(at) => (&rest[..at], Some(&rest[at + 1..])),
3158        None => (rest, None),
3159    };
3160
3161    let mut seen_digit = false;
3162    let mut seen_dot = false;
3163    for &b in mantissa {
3164        match b {
3165            b'0'..=b'9' => seen_digit = true,
3166            b'.' if !seen_dot => seen_dot = true,
3167            _ => return false,
3168        }
3169    }
3170    if !seen_digit {
3171        return false;
3172    }
3173
3174    match exponent {
3175        None => true,
3176        // An exponent needs digits of its own, and may carry a sign.
3177        Some(exp) => {
3178            let digits = exp
3179                .strip_prefix(b"+")
3180                .or_else(|| exp.strip_prefix(b"-"))
3181                .unwrap_or(exp);
3182            !digits.is_empty() && digits.iter().all(|b| b.is_ascii_digit())
3183        }
3184    }
3185}
3186
3187fn validate_bool_value<'t, 'v>(
3188    flag: &'t Flag<'t>,
3189    value: Option<&'v [u8]>,
3190) -> Result<Option<&'v [u8]>, Error<'t, 'v>> {
3191    match value {
3192        None | Some(b"true" | b"false") => Ok(value),
3193        Some(_) => Err(Error::InvalidChoice {
3194            name: flag.name,
3195            choices: &["true", "false"],
3196        }),
3197    }
3198}
3199
3200#[cfg(test)]
3201mod tests {
3202    use super::*;
3203
3204    #[test]
3205    fn clause_separator_resets_inner_args_and_survives_automatic_mode() {
3206        static TASK: Arg = Arg {
3207            key: 91,
3208            name: "task",
3209            ..Arg::REQUIRED
3210        };
3211        static REST: Arg = Arg {
3212            key: 92,
3213            name: "args",
3214            double_dash: DoubleDash::Automatic,
3215            ..Arg::VAR
3216        };
3217        static ROOT: Command = Command {
3218            name: "ex",
3219            clause: Some(&Clause {
3220                key: 90,
3221                name: "tasks",
3222                separator: Some(b":::"),
3223                flags: &[],
3224                args: &[&TASK, &REST],
3225            }),
3226            ..Command::EMPTY
3227        };
3228        let argv = [
3229            OsStr::new("lint"),
3230            OsStr::new("--fix"),
3231            OsStr::new(":::"),
3232            OsStr::new("test"),
3233        ];
3234        let mut parser = Parser::new(&ROOT, &argv);
3235        let mut seen = Vec::new();
3236        while let Some(event) = parser.next_event() {
3237            match event.expect("valid clause") {
3238                Event::Arg { arg, value, .. } => {
3239                    seen.push((arg.name, String::from_utf8_lossy(value).into_owned()))
3240                }
3241                Event::ClauseSeparator { clause } => seen.push((clause.name, ":::".into())),
3242                _ => {}
3243            }
3244        }
3245        assert_eq!(
3246            seen,
3247            [
3248                ("task", "lint".into()),
3249                ("args", "--fix".into()),
3250                ("tasks", ":::".into()),
3251                ("task", "test".into())
3252            ]
3253        );
3254    }
3255
3256    #[test]
3257    fn clause_separator_is_not_a_scoped_flag_value() {
3258        static POSTINSTALL: Flag = Flag {
3259            key: 93,
3260            name: "postinstall",
3261            longs: &["postinstall"],
3262            takes_value: true,
3263            variadic: true,
3264            ..Flag::BOOL
3265        };
3266        static TOOL: Arg = Arg {
3267            key: 94,
3268            name: "tool",
3269            ..Arg::REQUIRED
3270        };
3271        static ROOT: Command = Command {
3272            name: "ex",
3273            flags: &[&POSTINSTALL],
3274            clause: Some(&Clause {
3275                key: 95,
3276                name: "tools",
3277                separator: Some(b":::"),
3278                flags: &[&POSTINSTALL],
3279                args: &[&TOOL],
3280            }),
3281            ..Command::EMPTY
3282        };
3283        let argv = [OsStr::new("--postinstall"), OsStr::new(":::")];
3284        let mut parser = Parser::new(&ROOT, &argv);
3285        assert!(matches!(
3286            parser.next_event(),
3287            Some(Err(Error::MissingFlagValue { flag })) if flag.key == POSTINSTALL.key
3288        ));
3289
3290        let argv = [
3291            OsStr::new("--postinstall"),
3292            OsStr::new("setup"),
3293            OsStr::new(":::"),
3294            OsStr::new("tool"),
3295        ];
3296        let mut parser = Parser::new(&ROOT, &argv);
3297        assert!(matches!(
3298            parser.next_event(),
3299            Some(Ok(Event::Flag {
3300                value: Some(b"setup"),
3301                ..
3302            }))
3303        ));
3304        assert!(matches!(
3305            parser.next_event(),
3306            Some(Ok(Event::ClauseSeparator { clause })) if clause.key == 95
3307        ));
3308        assert!(matches!(
3309            parser.next_event(),
3310            Some(Ok(Event::Arg { value: b"tool", .. }))
3311        ));
3312    }
3313
3314    static FORCE: Flag = Flag {
3315        key: 1,
3316        longs: &["force"],
3317        shorts: b"f",
3318        ..Flag::BOOL
3319    };
3320    static EXPLICIT_BOOL: Flag = Flag {
3321        key: 20,
3322        name: "color",
3323        longs: &["color"],
3324        negate: Some("no-color"),
3325        bool_value: true,
3326        ..Flag::BOOL
3327    };
3328    static EXPLICIT_BOOL_ROOT: Command = Command {
3329        name: "ex",
3330        flags: &[&EXPLICIT_BOOL],
3331        ..Command::EMPTY
3332    };
3333    static JOBS: Flag = Flag {
3334        key: 2,
3335        longs: &["jobs"],
3336        shorts: b"j",
3337        allow_negative_numbers: true,
3338        ..Flag::VALUE
3339    };
3340    static COLOR: Flag = Flag {
3341        key: 3,
3342        longs: &["color"],
3343        negate: Some("no-color"),
3344        ..Flag::BOOL
3345    };
3346    static VERBOSE: Flag = Flag {
3347        key: 4,
3348        longs: &["verbose"],
3349        shorts: b"v",
3350        global: true,
3351        ..Flag::BOOL
3352    };
3353    static FILE: Arg = Arg {
3354        key: 10,
3355        name: "file",
3356        allow_negative_numbers: true,
3357        ..Arg::REQUIRED
3358    };
3359    static REST: Arg = Arg {
3360        key: 11,
3361        name: "rest",
3362        ..Arg::VAR
3363    };
3364    static INSTALL: Command = Command {
3365        name: "install",
3366        aliases: &["i"],
3367        flags: &[&FORCE],
3368        key: 100,
3369        ..Command::EMPTY
3370    };
3371    /// Same shape as ROOT, but a CLI that owns all of its flags. The subcommand says
3372    /// nothing and inherits it, which is the point: only the root declares the mode.
3373    static STRICT_INSTALL: Command = Command {
3374        name: "install",
3375        aliases: &["i"],
3376        flags: &[&FORCE],
3377        key: 100,
3378        ..Command::EMPTY
3379    };
3380    static STRICT: Command = Command {
3381        name: "ex",
3382        flags: &[&FORCE, &JOBS, &COLOR, &VERBOSE],
3383        args: &[&FILE, &REST],
3384        subcommands: &[&STRICT_INSTALL],
3385        unknown_flags: Some(UnknownFlags::Error),
3386        ..Command::EMPTY
3387    };
3388    static ROOT: Command = Command {
3389        name: "ex",
3390        flags: &[&FORCE, &JOBS, &COLOR, &VERBOSE],
3391        args: &[&FILE, &REST],
3392        subcommands: &[&INSTALL],
3393        ..Command::EMPTY
3394    };
3395    static ARGUMENT_CONFLICT: Command = Command {
3396        name: "ex",
3397        flags: &[&FORCE],
3398        subcommands: &[&INSTALL],
3399        args_conflicts_with_subcommands: true,
3400        ..Command::EMPTY
3401    };
3402
3403    // A CLI shaped exactly like mise's root: a default subcommand, a positional of its own,
3404    // and a subcommand under the default — which is the arrangement that tells routing from
3405    // a plain positional.
3406    static TASK: Arg = Arg {
3407        key: 20,
3408        name: "task",
3409        ..Arg::REQUIRED
3410    };
3411    static RUN_TASK: Arg = Arg {
3412        key: 21,
3413        name: "run_task",
3414        ..Arg::REQUIRED
3415    };
3416    static DEEP: Command = Command {
3417        name: "deep",
3418        args: &[&RUN_TASK],
3419        key: 203,
3420        ..Command::EMPTY
3421    };
3422    static LINT: Command = Command {
3423        name: "lint",
3424        subcommands: &[&DEEP],
3425        // A default of its own, so that a parse which forgot it had already taken one would
3426        // have somewhere to go. Nothing else in these fixtures can show the latch working.
3427        default_subcommand: Some(&DEEP),
3428        key: 202,
3429        ..Command::EMPTY
3430    };
3431    static RUN: Command = Command {
3432        name: "run",
3433        args: &[&RUN_TASK],
3434        subcommands: &[&LINT],
3435        key: 200,
3436        ..Command::EMPTY
3437    };
3438    static DEFAULTING: Command = Command {
3439        name: "mise",
3440        flags: &[&VERBOSE],
3441        args: &[&TASK],
3442        subcommands: &[&RUN, &INSTALL],
3443        default_subcommand: Some(find_subcommand(&[&RUN, &INSTALL], "run")),
3444        ..Command::EMPTY
3445    };
3446
3447    /// Collect every event, or the first error.
3448    fn parse<'t: 'v, 'v>(
3449        root: &'t Command<'t>,
3450        argv: &'v [&'v OsStr],
3451    ) -> Result<Vec<Event<'t, 'v, 'v>>, Error<'t, 'v>> {
3452        let mut parser = Parser::new(root, argv);
3453        let mut events = Vec::new();
3454        while let Some(event) = parser.next_event() {
3455            events.push(event?);
3456        }
3457        Ok(events)
3458    }
3459
3460    fn argv<const N: usize>(tokens: [&str; N]) -> [&OsStr; N] {
3461        tokens.map(OsStr::new)
3462    }
3463
3464    #[test]
3465    fn long_boolean() {
3466        let a = argv(["--force"]);
3467        assert_eq!(
3468            parse(&ROOT, &a).unwrap(),
3469            vec![Event::Flag {
3470                flag: &FORCE,
3471                value: None,
3472                negated: false
3473            }]
3474        );
3475    }
3476
3477    #[test]
3478    fn long_boolean_accepts_only_opted_in_attached_values() {
3479        for (token, negated, value) in [
3480            ("--color=false", false, b"false".as_slice()),
3481            ("--color=true", false, b"true".as_slice()),
3482            ("--no-color=false", true, b"false".as_slice()),
3483        ] {
3484            let a = argv([token]);
3485            assert_eq!(
3486                parse(&EXPLICIT_BOOL_ROOT, &a).unwrap(),
3487                vec![Event::Flag {
3488                    flag: &EXPLICIT_BOOL,
3489                    value: Some(value),
3490                    negated,
3491                }]
3492            );
3493        }
3494
3495        let a = argv(["--color=maybe"]);
3496        assert!(matches!(
3497            parse(&EXPLICIT_BOOL_ROOT, &a),
3498            Err(Error::InvalidChoice { name: "color", .. })
3499        ));
3500
3501        let a = argv(["--force=false"]);
3502        assert_eq!(
3503            parse(&ROOT, &a).unwrap(),
3504            vec![Event::Flag {
3505                flag: &FORCE,
3506                value: None,
3507                negated: false,
3508            }]
3509        );
3510    }
3511
3512    #[test]
3513    fn long_value_forms() {
3514        for tokens in [vec!["--jobs=8"], vec!["--jobs", "8"]] {
3515            let a: Vec<&OsStr> = tokens.iter().map(|t| OsStr::new(*t)).collect();
3516            assert_eq!(
3517                parse(&ROOT, &a).unwrap(),
3518                vec![Event::Flag {
3519                    flag: &JOBS,
3520                    value: Some(b"8"),
3521                    negated: false
3522                }],
3523                "{tokens:?}"
3524            );
3525        }
3526    }
3527
3528    #[test]
3529    fn long_value_keeps_later_equals() {
3530        let a = argv(["--jobs=a=b"]);
3531        let Event::Flag { value, .. } = parse(&ROOT, &a).unwrap()[0] else {
3532            panic!("expected a flag");
3533        };
3534        assert_eq!(value, Some(&b"a=b"[..]));
3535    }
3536
3537    #[test]
3538    fn long_value_attached_empty_is_empty_not_absent() {
3539        let a = argv(["--jobs="]);
3540        let Event::Flag { value, .. } = parse(&ROOT, &a).unwrap()[0] else {
3541            panic!("expected a flag");
3542        };
3543        assert_eq!(value, Some(&b""[..]));
3544    }
3545
3546    #[test]
3547    fn long_value_refuses_flaglike_next_word() {
3548        let a = argv(["--jobs", "--force"]);
3549        assert_eq!(
3550            parse(&ROOT, &a),
3551            Err(Error::MissingFlagValue { flag: &JOBS })
3552        );
3553    }
3554
3555    #[test]
3556    fn long_value_accepts_negative_number() {
3557        let a = argv(["--jobs", "-1"]);
3558        let Event::Flag { value, .. } = parse(&ROOT, &a).unwrap()[0] else {
3559            panic!("expected a flag");
3560        };
3561        assert_eq!(value, Some(&b"-1"[..]));
3562    }
3563
3564    #[test]
3565    fn missing_optional_positional_reserves_the_last_word() {
3566        static OPTIONAL: Arg = Arg {
3567            key: 90,
3568            name: "optional",
3569            required: false,
3570            ..Arg::REQUIRED
3571        };
3572        static REQUIRED: Arg = Arg {
3573            key: 91,
3574            name: "required",
3575            ..Arg::REQUIRED
3576        };
3577        static CMD: Command = Command {
3578            name: "ex",
3579            args: &[&OPTIONAL, &REQUIRED],
3580            allow_missing_positional: true,
3581            ..Command::EMPTY
3582        };
3583
3584        let one = argv(["value"]);
3585        assert_eq!(
3586            parse(&CMD, &one).unwrap(),
3587            vec![Event::Arg {
3588                arg: &REQUIRED,
3589                value: b"value",
3590                delimit: true
3591            }]
3592        );
3593        let two = argv(["optional", "required"]);
3594        assert_eq!(
3595            parse(&CMD, &two).unwrap(),
3596            vec![
3597                Event::Arg {
3598                    arg: &OPTIONAL,
3599                    value: b"optional",
3600                    delimit: true
3601                },
3602                Event::Arg {
3603                    arg: &REQUIRED,
3604                    value: b"required",
3605                    delimit: true
3606                },
3607            ]
3608        );
3609    }
3610
3611    #[test]
3612    fn negative_numbers_are_narrowly_opted_in() {
3613        static PLAIN: Flag = Flag {
3614            key: 90,
3615            name: "plain",
3616            longs: &["plain"],
3617            ..Flag::VALUE
3618        };
3619        static VALUE: Arg = Arg {
3620            key: 91,
3621            name: "value",
3622            ..Arg::REQUIRED
3623        };
3624        static CMD: Command = Command {
3625            name: "ex",
3626            flags: &[&PLAIN],
3627            args: &[&VALUE],
3628            unknown_flags: Some(UnknownFlags::Error),
3629            ..Command::EMPTY
3630        };
3631
3632        let flag = argv(["--plain", "-1"]);
3633        assert_eq!(
3634            parse(&CMD, &flag),
3635            Err(Error::MissingFlagValue { flag: &PLAIN })
3636        );
3637        let positional = argv(["-1"]);
3638        assert_eq!(
3639            parse(&CMD, &positional),
3640            Err(Error::UnknownFlag { token: b"-1" })
3641        );
3642    }
3643
3644    #[test]
3645    fn an_exact_declared_digit_short_outranks_a_negative_number() {
3646        static PRINT0: Flag = Flag {
3647            key: 92,
3648            name: "print0",
3649            shorts: b"0",
3650            ..Flag::BOOL
3651        };
3652        static VALUE: Arg = Arg {
3653            key: 93,
3654            name: "value",
3655            required: false,
3656            allow_negative_numbers: true,
3657            ..Arg::REQUIRED
3658        };
3659        static CMD: Command = Command {
3660            name: "fd",
3661            flags: &[&PRINT0],
3662            args: &[&VALUE],
3663            unknown_flags: Some(UnknownFlags::Error),
3664            ..Command::EMPTY
3665        };
3666
3667        assert_eq!(
3668            parse(&CMD, &argv(["-0"])),
3669            Ok(vec![Event::Flag {
3670                flag: &PRINT0,
3671                value: None,
3672                negated: false,
3673            }])
3674        );
3675        assert!(matches!(
3676            parse(&CMD, &argv(["-1"])),
3677            Ok(events) if matches!(events.as_slice(), [Event::Arg { value: b"-1", .. }])
3678        ));
3679    }
3680
3681    #[test]
3682    fn negation_of_value_flag_does_not_consume_a_value() {
3683        static MODE: Flag = Flag {
3684            key: 9,
3685            name: "mode",
3686            longs: &["mode"],
3687            negate: Some("no-mode"),
3688            ..Flag::VALUE
3689        };
3690        static NEGATED_VALUE: Command = Command {
3691            name: "ex",
3692            flags: &[&MODE],
3693            args: &[&FILE],
3694            ..Command::EMPTY
3695        };
3696
3697        let a = argv(["--no-mode", "input"]);
3698        assert_eq!(
3699            parse(&NEGATED_VALUE, &a).unwrap(),
3700            vec![
3701                Event::Flag {
3702                    flag: &MODE,
3703                    value: None,
3704                    negated: true
3705                },
3706                Event::Arg {
3707                    arg: &FILE,
3708                    value: b"input",
3709                    delimit: true,
3710                }
3711            ]
3712        );
3713    }
3714
3715    #[test]
3716    fn no_abbreviation() {
3717        // A prefix names no flag, so by default it is a value like any other word.
3718        let a = argv(["--forc"]);
3719        assert_eq!(
3720            parse(&ROOT, &a).unwrap(),
3721            vec![Event::Arg {
3722                arg: &FILE,
3723                value: b"--forc",
3724                delimit: true,
3725            }]
3726        );
3727
3728        // And a CLI that owns its flags hears about it, which is the whole reason
3729        // the strict mode exists.
3730        assert!(matches!(
3731            parse(&STRICT, &a),
3732            Err(Error::UnknownFlag { token: b"--forc" })
3733        ));
3734    }
3735
3736    #[test]
3737    fn an_unknown_flag_is_a_value_by_default() {
3738        // The default, and the case it is for: a command line being forwarded to
3739        // something whose flags this spec does not know.
3740        let a = argv(["--wat", "keep"]);
3741        assert_eq!(
3742            parse(&ROOT, &a).unwrap(),
3743            vec![
3744                Event::Arg {
3745                    arg: &FILE,
3746                    value: b"--wat",
3747                    delimit: true,
3748                },
3749                Event::Arg {
3750                    arg: &REST,
3751                    value: b"keep",
3752                    delimit: true,
3753                },
3754            ]
3755        );
3756
3757        // With nowhere to put it, it is an unexpected argument — the same error an
3758        // extra word gets, rather than a special one about flags.
3759        static ONE: Command = Command {
3760            name: "ex",
3761            args: &[&FILE],
3762            ..Command::EMPTY
3763        };
3764        let a = argv(["a", "--wat"]);
3765        assert_eq!(
3766            parse(&ONE, &a),
3767            Err(Error::UnexpectedArg { token: b"--wat" })
3768        );
3769    }
3770
3771    #[test]
3772    fn negation() {
3773        let a = argv(["--no-color"]);
3774        assert_eq!(
3775            parse(&ROOT, &a).unwrap(),
3776            vec![Event::Flag {
3777                flag: &COLOR,
3778                value: None,
3779                negated: true
3780            }]
3781        );
3782    }
3783
3784    #[test]
3785    fn short_bundle_and_attached_value() {
3786        let a = argv(["-fj8"]);
3787        assert_eq!(
3788            parse(&ROOT, &a).unwrap(),
3789            vec![
3790                Event::Flag {
3791                    flag: &FORCE,
3792                    value: None,
3793                    negated: false
3794                },
3795                Event::Flag {
3796                    flag: &JOBS,
3797                    value: Some(b"8"),
3798                    negated: false
3799                },
3800            ]
3801        );
3802    }
3803
3804    #[test]
3805    fn short_value_strips_one_equals() {
3806        for (tokens, want) in [(["-j=8"], &b"8"[..]), (["-j==8"], &b"=8"[..])] {
3807            let a = argv(tokens);
3808            let Event::Flag { value, .. } = parse(&ROOT, &a).unwrap()[0] else {
3809                panic!("expected a flag");
3810            };
3811            assert_eq!(value, Some(want), "{tokens:?}");
3812        }
3813    }
3814
3815    #[test]
3816    fn bare_dash_is_a_value() {
3817        let a = argv(["-"]);
3818        assert_eq!(
3819            parse(&ROOT, &a).unwrap(),
3820            vec![Event::Arg {
3821                arg: &FILE,
3822                value: b"-",
3823                delimit: true,
3824            }]
3825        );
3826    }
3827
3828    #[test]
3829    fn positionals_then_variadic() {
3830        let a = argv(["one", "two", "three"]);
3831        assert_eq!(
3832            parse(&ROOT, &a).unwrap(),
3833            vec![
3834                Event::Arg {
3835                    arg: &FILE,
3836                    value: b"one",
3837                    delimit: true,
3838                },
3839                Event::Arg {
3840                    arg: &REST,
3841                    value: b"two",
3842                    delimit: true,
3843                },
3844                Event::Arg {
3845                    arg: &REST,
3846                    value: b"three",
3847                    delimit: true,
3848                },
3849            ]
3850        );
3851    }
3852
3853    #[test]
3854    fn subcommand_and_alias_route_the_same() {
3855        for token in ["install", "i"] {
3856            let a = argv([token]);
3857            assert_eq!(
3858                parse(&ROOT, &a).unwrap(),
3859                vec![Event::Command(&INSTALL)],
3860                "{token}"
3861            );
3862        }
3863    }
3864
3865    #[test]
3866    fn a_parent_argument_can_exclude_a_later_subcommand() {
3867        let a = argv(["--force", "install"]);
3868        assert!(matches!(
3869            parse(&ARGUMENT_CONFLICT, &a),
3870            Err(Error::SubcommandConflict { subcommand }) if subcommand.name == "install"
3871        ));
3872    }
3873
3874    #[test]
3875    fn subcommand_only_routes_before_a_positional_is_filled() {
3876        let a = argv(["other", "install"]);
3877        assert_eq!(
3878            parse(&ROOT, &a).unwrap(),
3879            vec![
3880                Event::Arg {
3881                    arg: &FILE,
3882                    value: b"other",
3883                    delimit: true,
3884                },
3885                Event::Arg {
3886                    arg: &REST,
3887                    value: b"install",
3888                    delimit: true,
3889                },
3890            ]
3891        );
3892    }
3893
3894    #[test]
3895    fn a_word_naming_no_subcommand_goes_to_the_default_one() {
3896        // usage-lib's answer, which this reproduces: `mise build` comes back as commands
3897        // `["mise", "run"]` with the word bound to *run's* argument — not to `mise`'s own
3898        // `[TASK]`, which is what makes this more than a synonym for a positional.
3899        let a = argv(["build"]);
3900        assert_eq!(
3901            parse(&DEFAULTING, &a).unwrap(),
3902            vec![
3903                Event::Command(&RUN),
3904                Event::Arg {
3905                    arg: &RUN_TASK,
3906                    value: b"build",
3907                    delimit: true,
3908                },
3909            ]
3910        );
3911    }
3912
3913    // A shared table, as a flattened struct's would be. Declared outside the tests so both
3914    // can splice it, which is the arrangement it exists to model.
3915    static SHARED_QUIET: Flag = Flag {
3916        key: 300,
3917        name: "quiet",
3918        longs: &["quiet"],
3919        ..Flag::BOOL
3920    };
3921    static SHARED_FLAGS: &[&Flag] = &[&SHARED_QUIET];
3922    static SHARED_WHAT: Arg = Arg {
3923        key: 301,
3924        name: "what",
3925        ..Arg::REQUIRED
3926    };
3927    static SHARED_ARGS: &[&Arg] = &[&SHARED_WHAT];
3928
3929    #[test]
3930    fn concatenating_tables_keeps_the_order_they_were_given_in() {
3931        // The property positional arguments depend on: a flattened group lands where the
3932        // field was written, not at the end. `[&FILE], SHARED, [&REST]` has to stay in that
3933        // order or `ex a b c` binds the wrong words.
3934        const ARGS: &[&[&Arg]] = &[&[&FILE], SHARED_ARGS, &[&REST]];
3935        static TABLE: [&Arg; table_len(ARGS)] = concat_args(ARGS);
3936        assert_eq!(
3937            TABLE.iter().map(|a| a.name).collect::<Vec<_>>(),
3938            ["file", "what", "rest"]
3939        );
3940
3941        // Empty groups contribute nothing and disturb nothing, which is what lets the derive
3942        // emit a group per field without checking whether it is empty first.
3943        const WITH_GAPS: &[&[&Flag]] = &[&[], &[&FORCE], &[], SHARED_FLAGS, &[]];
3944        static FLAGS: [&Flag; table_len(WITH_GAPS)] = concat_flags(WITH_GAPS);
3945        // By long form: these fixtures do not all set `name`, and the placeholder's is also
3946        // empty — so comparing names could not tell a real entry from a leftover slot.
3947        assert_eq!(
3948            FLAGS.iter().map(|f| f.longs).collect::<Vec<_>>(),
3949            [&["force"], &["quiet"]]
3950        );
3951    }
3952
3953    #[test]
3954    fn a_concatenated_table_parses_like_a_declared_one() {
3955        // The point of doing this at compile time: what the parser walks is one flat slice,
3956        // indistinguishable from a command that declared everything itself.
3957        const FLAG_GROUPS: &[&[&Flag]] = &[&[&FORCE], SHARED_FLAGS];
3958        const ARG_GROUPS: &[&[&Arg]] = &[SHARED_ARGS, &[&REST]];
3959        static FLAGS: [&Flag; table_len(FLAG_GROUPS)] = concat_flags(FLAG_GROUPS);
3960        static ARGS: [&Arg; table_len(ARG_GROUPS)] = concat_args(ARG_GROUPS);
3961        static JOINED: Command = Command {
3962            name: "joined",
3963            flags: &FLAGS,
3964            args: &ARGS,
3965            ..Command::EMPTY
3966        };
3967
3968        let a = argv(["--quiet", "one", "two", "--force"]);
3969        assert_eq!(
3970            parse(&JOINED, &a).unwrap(),
3971            vec![
3972                Event::Flag {
3973                    flag: &SHARED_QUIET,
3974                    value: None,
3975                    negated: false
3976                },
3977                Event::Arg {
3978                    arg: &SHARED_WHAT,
3979                    value: b"one",
3980                    delimit: true,
3981                },
3982                Event::Arg {
3983                    arg: &REST,
3984                    value: b"two",
3985                    delimit: true,
3986                },
3987                Event::Flag {
3988                    flag: &FORCE,
3989                    value: None,
3990                    negated: false
3991                },
3992            ]
3993        );
3994    }
3995
3996    #[test]
3997    fn an_unknown_flag_is_not_routed() {
3998        // A dash-prefixed token that names no flag becomes a value here (the default for
3999        // `unknown_flags`), and it must not thereby become a *subcommand* word: usage-lib
4000        // stops looking for subcommands at an unrecognised flag, and binds it to the command
4001        // still in scope. Verified against usage-lib, where `ex --wat` comes back as commands
4002        // `["ex"]` with `ROOT_TASK = "--wat"`.
4003        for token in ["--wat", "-x"] {
4004            let a = argv([token]);
4005            assert_eq!(
4006                parse(&DEFAULTING, &a).unwrap(),
4007                vec![Event::Arg {
4008                    arg: &TASK,
4009                    value: token.as_bytes(),
4010                    delimit: true,
4011                }],
4012                "{token} should bind where it was typed, not in the default subcommand"
4013            );
4014        }
4015    }
4016
4017    #[test]
4018    fn a_named_subcommand_is_not_routed() {
4019        // The default is for words that name nothing. A word that names a sibling still
4020        // selects it, and the root's own argument is still reachable behind one.
4021        let a = argv(["install"]);
4022        assert_eq!(
4023            parse(&DEFAULTING, &a).unwrap(),
4024            vec![Event::Command(&INSTALL)]
4025        );
4026    }
4027
4028    #[test]
4029    fn an_unmatched_word_is_forwarded_when_external_subcommand_is_set() {
4030        static CATCH: Command = Command {
4031            name: "ex",
4032            flags: &[&VERBOSE],
4033            subcommands: &[&INSTALL],
4034            external_subcommand: true,
4035            unknown_flags: Some(UnknownFlags::Error),
4036            ..Command::EMPTY
4037        };
4038        let a = argv(["foo", "--help", "bar"]);
4039        assert_eq!(
4040            parse(&CATCH, &a).unwrap(),
4041            vec![Event::External { values: &a[..] }]
4042        );
4043
4044        let a = argv(["install"]);
4045        assert_eq!(parse(&CATCH, &a).unwrap(), vec![Event::Command(&INSTALL)]);
4046
4047        let a = argv(["--verbose", "foo", "--verbose"]);
4048        assert_eq!(
4049            parse(&CATCH, &a).unwrap(),
4050            vec![
4051                Event::Flag {
4052                    flag: &VERBOSE,
4053                    value: None,
4054                    negated: false
4055                },
4056                Event::External { values: &a[1..] }
4057            ]
4058        );
4059
4060        let a = argv(["--wat"]);
4061        assert_eq!(
4062            parse(&CATCH, &a),
4063            Err(Error::UnknownFlag { token: b"--wat" })
4064        );
4065
4066        // A negative number is a value, not a flag, so it can be the unmatched word.
4067        let a = argv(["-1", "rest"]);
4068        assert_eq!(
4069            parse(&CATCH, &a).unwrap(),
4070            vec![Event::External { values: &a[..] }]
4071        );
4072    }
4073
4074    #[test]
4075    fn a_default_subcommand_outranks_an_external_one() {
4076        static CATCH_DEFAULT: Command = Command {
4077            name: "ex",
4078            subcommands: &[&RUN],
4079            default_subcommand: Some(&RUN),
4080            external_subcommand: true,
4081            ..Command::EMPTY
4082        };
4083        let a = argv(["build"]);
4084        assert_eq!(
4085            parse(&CATCH_DEFAULT, &a).unwrap(),
4086            vec![
4087                Event::Command(&RUN),
4088                Event::Arg {
4089                    arg: &RUN_TASK,
4090                    value: b"build",
4091                    delimit: true,
4092                }
4093            ]
4094        );
4095    }
4096
4097    #[test]
4098    fn a_default_subcommand_starts_at_the_word_it_receives() {
4099        let a = argv(["build"]);
4100        let mut parser = Parser::new(&DEFAULTING, &a);
4101        assert_eq!(parser.next_event(), Some(Ok(Event::Command(&RUN))));
4102        assert_eq!(parser.command_start(), 0);
4103        assert_eq!(
4104            parser.next_event(),
4105            Some(Ok(Event::Arg {
4106                arg: &RUN_TASK,
4107                value: b"build",
4108                delimit: true,
4109            }))
4110        );
4111    }
4112
4113    #[test]
4114    fn a_root_clause_separator_does_not_select_empty_default() {
4115        static CHILD: Command = Command {
4116            name: "run",
4117            ..Command::EMPTY
4118        };
4119        static ROOT: Command = Command {
4120            name: "ex",
4121            clause: Some(&Clause {
4122                key: 400,
4123                name: "items",
4124                separator: Some(b":::"),
4125                flags: &[],
4126                args: &[],
4127            }),
4128            subcommands: &[&CHILD],
4129            default_subcommand: Some(&CHILD),
4130            default_subcommand_on_empty: true,
4131            ..Command::EMPTY
4132        };
4133        let a = argv([":::"]);
4134        assert_eq!(
4135            parse(&ROOT, &a).unwrap(),
4136            vec![Event::ClauseSeparator {
4137                clause: *ROOT.clause.unwrap(),
4138            }]
4139        );
4140    }
4141
4142    #[test]
4143    fn the_default_can_be_named_by_an_alias() {
4144        // usage-lib resolves the name against subcommand names, aliases and hidden aliases
4145        // alike, so a spec may point `default_subcommand` at any of them.
4146        static BY_ALIAS: Command = Command {
4147            name: "mise",
4148            args: &[&TASK],
4149            subcommands: &[&INSTALL],
4150            // `INSTALL` answers to "i" as well as to its name.
4151            default_subcommand: Some(find_subcommand(&[&INSTALL], "i")),
4152            ..Command::EMPTY
4153        };
4154        assert!(::core::ptr::eq(
4155            BY_ALIAS.default_subcommand.expect("declared"),
4156            &INSTALL
4157        ));
4158    }
4159
4160    #[test]
4161    fn a_name_outranks_another_commands_alias() {
4162        // A spec `assert_unique_subcommand_names` would reject, resolved anyway: a parser
4163        // handed a table nothing validated still has to answer, and the answer is the
4164        // command whose own name it is. Both orders, because taking the first candidate
4165        // that matched on either name or alias made this depend on which was listed first
4166        // — and usage-lib, building a map, took the last.
4167        static ALPHA: Command = Command {
4168            name: "alpha",
4169            aliases: &["run"],
4170            key: 300,
4171            ..Command::EMPTY
4172        };
4173        static PLAIN_RUN: Command = Command {
4174            name: "run",
4175            key: 301,
4176            ..Command::EMPTY
4177        };
4178        for subcommands in [&[&ALPHA, &PLAIN_RUN] as &[&Command], &[&PLAIN_RUN, &ALPHA]] {
4179            assert!(::core::ptr::eq(
4180                find_subcommand(subcommands, "run"),
4181                &PLAIN_RUN
4182            ));
4183            let root: Command = Command {
4184                name: "ex",
4185                subcommands,
4186                ..Command::EMPTY
4187            };
4188            let a = argv(["run"]);
4189            assert_eq!(parse(&root, &a).unwrap(), vec![Event::Command(&PLAIN_RUN)]);
4190            // The alias still reaches its own command by every name it does not share.
4191            let a = argv(["alpha"]);
4192            assert_eq!(parse(&root, &a).unwrap(), vec![Event::Command(&ALPHA)]);
4193            // `ex help run` asks about the command `ex run` selects. These are separate
4194            // lookups — help resolves a path without descending — and answering differently
4195            // for a colliding word is the divergence this rule exists to end.
4196            let a = argv(["help", "run"]);
4197            match parse(&root, &a) {
4198                Err(Error::Help { cmd, .. }) => {
4199                    assert!(
4200                        ::core::ptr::eq(cmd, &PLAIN_RUN),
4201                        "got help for {}",
4202                        cmd.name
4203                    )
4204                }
4205                other => panic!("expected a help request, got {other:?}"),
4206            }
4207        }
4208    }
4209
4210    #[test]
4211    #[should_panic(expected = "two subcommands answer to the same name")]
4212    fn an_alias_cannot_shadow_a_sibling_command() {
4213        static ADD: Command = Command {
4214            name: "add",
4215            aliases: &["install"],
4216            ..Command::EMPTY
4217        };
4218        assert_unique_subcommand_names(&[&INSTALL, &ADD]);
4219    }
4220
4221    #[test]
4222    fn the_word_is_re_examined_against_the_command_it_reached() {
4223        // The reason the cursor steps back rather than the token being consumed: `lint` names
4224        // nothing at the root, and once inside `run` it names a subcommand. mise's mounted
4225        // task names arrive exactly this way.
4226        let a = argv(["lint"]);
4227        assert_eq!(
4228            parse(&DEFAULTING, &a).unwrap(),
4229            vec![Event::Command(&RUN), Event::Command(&LINT)]
4230        );
4231    }
4232
4233    #[test]
4234    fn the_default_is_taken_at_most_once_per_parse() {
4235        // usage-lib latches this for the whole parse rather than per command, and the shape
4236        // that shows the difference needs two of them: `lint` routes through `run`, and `lint`
4237        // declares a default too. A second word there would descend again — walking a CLI
4238        // deeper than anything the user typed — so the answer is that it does not.
4239        let a = argv(["lint", "zzz"]);
4240        assert_eq!(
4241            parse(&DEFAULTING, &a),
4242            Err(Error::UnexpectedArg { token: b"zzz" }),
4243            "the second word must not reach `deep`"
4244        );
4245
4246        // Reached explicitly, the same command still takes it: the latch bounds routing, not
4247        // the tree.
4248        let a = argv(["lint", "deep", "zzz"]);
4249        assert_eq!(
4250            parse(&DEFAULTING, &a).unwrap(),
4251            vec![
4252                Event::Command(&RUN),
4253                Event::Command(&LINT),
4254                Event::Command(&DEEP),
4255                Event::Arg {
4256                    arg: &RUN_TASK,
4257                    value: b"zzz",
4258                    delimit: true,
4259                },
4260            ]
4261        );
4262    }
4263
4264    #[test]
4265    fn a_flag_before_the_word_still_belongs_to_the_root() {
4266        // Routing happens at the word, so anything typed before it was addressed to the
4267        // command the user was actually at.
4268        let a = argv(["--verbose", "build"]);
4269        assert_eq!(
4270            parse(&DEFAULTING, &a).unwrap(),
4271            vec![
4272                Event::Flag {
4273                    flag: &VERBOSE,
4274                    value: None,
4275                    negated: false
4276                },
4277                Event::Command(&RUN),
4278                Event::Arg {
4279                    arg: &RUN_TASK,
4280                    value: b"build",
4281                    delimit: true,
4282                },
4283            ]
4284        );
4285    }
4286
4287    #[test]
4288    fn nothing_routes_after_the_separator() {
4289        // Past `--` there are no subcommands left to select, so there is no default to reach
4290        // either: the words are values of whatever the command declares.
4291        let a = argv(["--", "build"]);
4292        assert_eq!(
4293            parse(&DEFAULTING, &a).unwrap(),
4294            vec![Event::Arg {
4295                arg: &TASK,
4296                value: b"build",
4297                delimit: true,
4298            }]
4299        );
4300    }
4301
4302    #[test]
4303    fn globals_are_inherited_but_plain_flags_are_not() {
4304        let a = argv(["install", "--verbose"]);
4305        assert_eq!(
4306            parse(&ROOT, &a).unwrap(),
4307            vec![
4308                Event::Command(&INSTALL),
4309                Event::Flag {
4310                    flag: &VERBOSE,
4311                    value: None,
4312                    negated: false
4313                }
4314            ]
4315        );
4316
4317        // `--jobs` belongs to the root and is not global, so it is not a flag here.
4318        // Strictly that is an unknown flag; leniently it is a word, and `install`
4319        // declares no argument to hold one — either way it is never read as the
4320        // root's flag, which is what this test is about.
4321        let a = argv(["install", "--jobs", "8"]);
4322        assert!(matches!(parse(&STRICT, &a), Err(Error::UnknownFlag { .. })));
4323        assert!(matches!(
4324            parse(&ROOT, &a),
4325            Err(Error::UnexpectedArg { token: b"--jobs" })
4326        ));
4327    }
4328
4329    #[test]
4330    fn double_dash_protects_flaglike_values() {
4331        let a = argv(["--", "--force", "-x"]);
4332        assert_eq!(
4333            parse(&ROOT, &a).unwrap(),
4334            vec![
4335                Event::Arg {
4336                    arg: &FILE,
4337                    value: b"--force",
4338                    delimit: true,
4339                },
4340                Event::Arg {
4341                    arg: &REST,
4342                    value: b"-x",
4343                    delimit: true,
4344                },
4345            ]
4346        );
4347    }
4348
4349    #[test]
4350    fn second_double_dash_is_a_value() {
4351        let a = argv(["--", "a", "--", "b"]);
4352        let values: Vec<&[u8]> = parse(&ROOT, &a)
4353            .unwrap()
4354            .iter()
4355            .filter_map(|e| match e {
4356                Event::Arg { value, .. } => Some(*value),
4357                _ => None,
4358            })
4359            .collect();
4360        assert_eq!(values, vec![&b"a"[..], &b"--"[..], &b"b"[..]]);
4361    }
4362
4363    #[test]
4364    fn allow_hyphen_values_takes_a_flaglike_detached_value() {
4365        static ARGS: Flag = Flag {
4366            key: 6,
4367            name: "args",
4368            longs: &["args"],
4369            shorts: b"a",
4370            takes_value: true,
4371            allow_hyphen_values: true,
4372            ..Flag::BOOL
4373        };
4374        static DIR: Flag = Flag {
4375            key: 7,
4376            name: "working-dir",
4377            longs: &["working-dir"],
4378            shorts: b"d",
4379            ..Flag::VALUE
4380        };
4381        static HYPHEN: Command = Command {
4382            name: "ex",
4383            flags: &[&ARGS, &DIR],
4384            args: &[&REST],
4385            ..Command::EMPTY
4386        };
4387
4388        let a = argv(["-a", "-destroy"]);
4389        assert_eq!(
4390            parse(&HYPHEN, &a).unwrap(),
4391            vec![Event::Flag {
4392                flag: &ARGS,
4393                value: Some(b"-destroy"),
4394                negated: false
4395            }]
4396        );
4397
4398        let a = argv(["--args", "--", "-x"]);
4399        assert_eq!(
4400            parse(&HYPHEN, &a).unwrap(),
4401            vec![
4402                Event::Flag {
4403                    flag: &ARGS,
4404                    value: Some(b"--"),
4405                    negated: false
4406                },
4407                Event::Arg {
4408                    arg: &REST,
4409                    value: b"-x",
4410                    delimit: true,
4411                },
4412            ]
4413        );
4414    }
4415
4416    #[test]
4417    fn require_equals_refuses_a_detached_value() {
4418        static INSPECT: Flag = Flag {
4419            key: 8,
4420            name: "inspect",
4421            longs: &["inspect"],
4422            shorts: b"i",
4423            takes_value: true,
4424            require_equals: true,
4425            ..Flag::BOOL
4426        };
4427        static EQ: Command = Command {
4428            name: "ex",
4429            flags: &[&INSPECT],
4430            ..Command::EMPTY
4431        };
4432
4433        let a = argv(["--inspect=9229"]);
4434        assert_eq!(
4435            parse(&EQ, &a).unwrap(),
4436            vec![Event::Flag {
4437                flag: &INSPECT,
4438                value: Some(b"9229"),
4439                negated: false
4440            }]
4441        );
4442
4443        let a = argv(["--inspect", "9229"]);
4444        assert!(matches!(
4445            parse(&EQ, &a),
4446            Err(Error::MissingFlagValue { .. })
4447        ));
4448
4449        let a = argv(["-i9229"]);
4450        assert_eq!(
4451            parse(&EQ, &a).unwrap(),
4452            vec![Event::Flag {
4453                flag: &INSPECT,
4454                value: Some(b"9229"),
4455                negated: false
4456            }]
4457        );
4458
4459        static ALL: Flag = Flag {
4460            key: 9,
4461            name: "all",
4462            longs: &["all"],
4463            shorts: b"a",
4464            ..Flag::BOOL
4465        };
4466        static BUNDLE: Command = Command {
4467            name: "ex",
4468            flags: &[&ALL, &INSPECT],
4469            ..Command::EMPTY
4470        };
4471        let a = argv(["-ai", "9229"]);
4472        assert!(
4473            matches!(parse(&BUNDLE, &a), Err(Error::MissingFlagValue { .. })),
4474            "a require_equals short reached through a bundle still refuses the following word"
4475        );
4476    }
4477
4478    #[test]
4479    fn default_missing_binds_when_the_value_is_left_off() {
4480        static COLOR: Flag = Flag {
4481            key: 9,
4482            name: "color",
4483            longs: &["color"],
4484            takes_value: true,
4485            default_missing: Some(b"always"),
4486            ..Flag::BOOL
4487        };
4488        static VERBOSE: Flag = Flag {
4489            key: 10,
4490            name: "verbose",
4491            longs: &["verbose"],
4492            ..Flag::BOOL
4493        };
4494        static MISSING: Command = Command {
4495            name: "ex",
4496            flags: &[&COLOR, &VERBOSE],
4497            ..Command::EMPTY
4498        };
4499
4500        let a = argv(["--color"]);
4501        assert_eq!(
4502            parse(&MISSING, &a).unwrap(),
4503            vec![Event::Flag {
4504                flag: &COLOR,
4505                value: Some(b"always"),
4506                negated: false
4507            }]
4508        );
4509
4510        let a = argv(["--color=never"]);
4511        assert_eq!(
4512            parse(&MISSING, &a).unwrap(),
4513            vec![Event::Flag {
4514                flag: &COLOR,
4515                value: Some(b"never"),
4516                negated: false
4517            }]
4518        );
4519
4520        let a = argv(["--color", "--verbose"]);
4521        assert_eq!(
4522            parse(&MISSING, &a).unwrap(),
4523            vec![
4524                Event::Flag {
4525                    flag: &COLOR,
4526                    value: Some(b"always"),
4527                    negated: false
4528                },
4529                Event::Flag {
4530                    flag: &VERBOSE,
4531                    value: None,
4532                    negated: false
4533                },
4534            ]
4535        );
4536
4537        let a = argv(["--color="]);
4538        assert_eq!(
4539            parse(&MISSING, &a).unwrap(),
4540            vec![Event::Flag {
4541                flag: &COLOR,
4542                value: Some(b""),
4543                negated: false
4544            }]
4545        );
4546    }
4547
4548    #[test]
4549    fn optional_flag_value_distinguishes_bare_and_explicit_forms() {
4550        static BUMP: Flag = Flag {
4551            key: 11,
4552            name: "bump",
4553            longs: &["bump"],
4554            takes_value: true,
4555            value_optional: true,
4556            ..Flag::BOOL
4557        };
4558        static OPTIONAL: Command = Command {
4559            name: "ex",
4560            flags: &[&BUMP],
4561            ..Command::EMPTY
4562        };
4563
4564        assert_eq!(parse(&OPTIONAL, &argv([])).unwrap(), vec![]);
4565        assert_eq!(
4566            parse(&OPTIONAL, &argv(["--bump"])).unwrap(),
4567            vec![Event::Flag {
4568                flag: &BUMP,
4569                value: None,
4570                negated: false,
4571            }]
4572        );
4573        assert_eq!(
4574            parse(&OPTIONAL, &argv(["--bump=5"])).unwrap(),
4575            vec![Event::Flag {
4576                flag: &BUMP,
4577                value: Some(b"5"),
4578                negated: false,
4579            }]
4580        );
4581
4582        static INCLUDE: Flag = Flag {
4583            key: 12,
4584            name: "include",
4585            longs: &["include"],
4586            takes_value: true,
4587            variadic: true,
4588            value_optional: true,
4589            ..Flag::BOOL
4590        };
4591        static VERBOSE: Flag = Flag {
4592            key: 13,
4593            name: "verbose",
4594            longs: &["verbose"],
4595            ..Flag::BOOL
4596        };
4597        static VARIADIC: Command = Command {
4598            name: "ex",
4599            flags: &[&INCLUDE, &VERBOSE],
4600            args: &[&REST],
4601            ..Command::EMPTY
4602        };
4603        assert_eq!(
4604            parse(&VARIADIC, &argv(["--include", "--verbose", "file"])).unwrap(),
4605            vec![
4606                Event::Flag {
4607                    flag: &INCLUDE,
4608                    value: None,
4609                    negated: false,
4610                },
4611                Event::Flag {
4612                    flag: &VERBOSE,
4613                    value: None,
4614                    negated: false,
4615                },
4616                Event::Arg {
4617                    arg: &REST,
4618                    value: b"file",
4619                    delimit: true,
4620                },
4621            ]
4622        );
4623    }
4624
4625    #[test]
4626    fn default_missing_with_require_equals_leaves_the_following_word() {
4627        static INSPECT: Flag = Flag {
4628            key: 11,
4629            name: "inspect",
4630            longs: &["inspect"],
4631            takes_value: true,
4632            require_equals: true,
4633            default_missing: Some(b"9229"),
4634            ..Flag::BOOL
4635        };
4636        static BOTH: Command = Command {
4637            name: "ex",
4638            flags: &[&INSPECT],
4639            args: &[&REST],
4640            ..Command::EMPTY
4641        };
4642
4643        let a = argv(["--inspect"]);
4644        assert_eq!(
4645            parse(&BOTH, &a).unwrap(),
4646            vec![Event::Flag {
4647                flag: &INSPECT,
4648                value: Some(b"9229"),
4649                negated: false
4650            }]
4651        );
4652
4653        let a = argv(["--inspect", "80"]);
4654        assert_eq!(
4655            parse(&BOTH, &a).unwrap(),
4656            vec![
4657                Event::Flag {
4658                    flag: &INSPECT,
4659                    value: Some(b"9229"),
4660                    negated: false
4661                },
4662                Event::Arg {
4663                    arg: &REST,
4664                    value: b"80",
4665                    delimit: true,
4666                },
4667            ]
4668        );
4669
4670        let a = argv(["--inspect="]);
4671        assert_eq!(
4672            parse(&BOTH, &a).unwrap(),
4673            vec![Event::Flag {
4674                flag: &INSPECT,
4675                value: Some(b""),
4676                negated: false
4677            }]
4678        );
4679    }
4680
4681    #[test]
4682    fn variadic_flag_collects_until_a_flaglike_token() {
4683        static INCLUDE: Flag = Flag {
4684            key: 5,
4685            name: "include",
4686            longs: &["include"],
4687            shorts: b"i",
4688            takes_value: true,
4689            variadic: true,
4690            ..Flag::BOOL
4691        };
4692        static GREEDY: Command = Command {
4693            name: "ex",
4694            flags: &[&INCLUDE, &FORCE],
4695            args: &[&FILE],
4696            ..Command::EMPTY
4697        };
4698
4699        let a = argv(["--include", "x", "y", "--force"]);
4700        assert_eq!(
4701            parse(&GREEDY, &a).unwrap(),
4702            vec![
4703                Event::Flag {
4704                    flag: &INCLUDE,
4705                    value: Some(b"x"),
4706                    negated: false
4707                },
4708                Event::Flag {
4709                    flag: &INCLUDE,
4710                    value: Some(b"y"),
4711                    negated: false
4712                },
4713                Event::Flag {
4714                    flag: &FORCE,
4715                    value: None,
4716                    negated: false
4717                },
4718            ]
4719        );
4720    }
4721
4722    #[test]
4723    fn value_terminators_end_variadic_owners_without_binding() {
4724        static INCLUDE: Flag = Flag {
4725            key: 92,
4726            name: "include",
4727            longs: &["include"],
4728            takes_value: true,
4729            variadic: true,
4730            value_terminator: Some(b";"),
4731            ..Flag::BOOL
4732        };
4733        static ITEMS: Arg = Arg {
4734            key: 93,
4735            name: "items",
4736            var: true,
4737            value_terminator: Some(b";"),
4738            ..Arg::REQUIRED
4739        };
4740        static AFTER: Arg = Arg {
4741            key: 94,
4742            name: "after",
4743            ..Arg::REQUIRED
4744        };
4745        static FLAG_CMD: Command = Command {
4746            name: "ex",
4747            flags: &[&INCLUDE],
4748            args: &[&AFTER],
4749            ..Command::EMPTY
4750        };
4751        static ARG_CMD: Command = Command {
4752            name: "ex",
4753            args: &[&ITEMS, &AFTER],
4754            ..Command::EMPTY
4755        };
4756
4757        let flag = argv(["--include", "a", ";", "tail"]);
4758        assert_eq!(
4759            parse(&FLAG_CMD, &flag).unwrap(),
4760            vec![
4761                Event::Flag {
4762                    flag: &INCLUDE,
4763                    value: Some(b"a"),
4764                    negated: false,
4765                },
4766                Event::Arg {
4767                    arg: &AFTER,
4768                    value: b"tail",
4769                    delimit: true,
4770                },
4771            ]
4772        );
4773
4774        let positional = argv(["a", ";", "tail"]);
4775        assert_eq!(
4776            parse(&ARG_CMD, &positional).unwrap(),
4777            vec![
4778                Event::Arg {
4779                    arg: &ITEMS,
4780                    value: b"a",
4781                    delimit: true,
4782                },
4783                Event::Arg {
4784                    arg: &AFTER,
4785                    value: b"tail",
4786                    delimit: true,
4787                },
4788            ]
4789        );
4790    }
4791
4792    #[test]
4793    fn a_non_variadic_flag_leaves_the_next_word_alone() {
4794        // The counterpart to the test above: a flag that takes one value must not
4795        // swallow the word after it, which would silently steal a positional.
4796        let a = argv(["--jobs", "8", "keep-me"]);
4797        assert_eq!(
4798            parse(&ROOT, &a).unwrap(),
4799            vec![
4800                Event::Flag {
4801                    flag: &JOBS,
4802                    value: Some(b"8"),
4803                    negated: false
4804                },
4805                Event::Arg {
4806                    arg: &FILE,
4807                    value: b"keep-me",
4808                    delimit: true,
4809                },
4810            ]
4811        );
4812    }
4813
4814    #[test]
4815    fn double_dash_seen_means_a_separator_was_typed() {
4816        static FILES: Arg = Arg {
4817            key: 23,
4818            name: "files",
4819            double_dash: DoubleDash::Automatic,
4820            ..Arg::VAR
4821        };
4822        static AUTO: Command = Command {
4823            name: "ex",
4824            flags: &[&FORCE],
4825            args: &[&FILES],
4826            ..Command::EMPTY
4827        };
4828
4829        let a = argv(["--", "x"]);
4830        let mut parser = Parser::new(&ROOT, &a);
4831        while parser.next_event().is_some() {}
4832        assert!(parser.double_dash_seen(), "a real separator was consumed");
4833
4834        // `automatic` stops flag interpretation without a separator being typed,
4835        // and reporting one would be a lie to any caller that forwards argv.
4836        let a = argv(["x", "--force"]);
4837        let mut parser = Parser::new(&AUTO, &a);
4838        while parser.next_event().is_some() {}
4839        assert!(
4840            !parser.double_dash_seen(),
4841            "automatic mode must not claim a separator was given"
4842        );
4843    }
4844
4845    #[test]
4846    fn a_wrapper_still_forwards_a_help_flag() {
4847        // Supplying `--help` must not take the two forwarding mechanisms away from a wrapper,
4848        // which is the one place a CLI means to hand the token on rather than answer it.
4849        static ARGS: Arg = Arg {
4850            key: 24,
4851            name: "args",
4852            ..Arg::VAR
4853        };
4854        static WRAP: Command = Command {
4855            name: "wrap",
4856            args: &[&ARGS],
4857            ..Command::EMPTY
4858        };
4859
4860        // A typed separator: everything after it is a value, `--help` included.
4861        let a = argv(["--", "--help", "-h"]);
4862        assert_eq!(
4863            parse(&WRAP, &a).unwrap(),
4864            vec![
4865                Event::Arg {
4866                    arg: &ARGS,
4867                    value: b"--help",
4868                    delimit: true,
4869                },
4870                Event::Arg {
4871                    arg: &ARGS,
4872                    value: b"-h",
4873                    delimit: true,
4874                },
4875            ]
4876        );
4877
4878        // And `automatic`, for the wrapper whose caller should not have to type one: the
4879        // first value stops flag interpretation, so the flags after it forward.
4880        static AUTO_ARGS: Arg = Arg {
4881            key: 25,
4882            name: "args",
4883            double_dash: DoubleDash::Automatic,
4884            ..Arg::VAR
4885        };
4886        static AUTO_WRAP: Command = Command {
4887            name: "wrap",
4888            args: &[&AUTO_ARGS],
4889            ..Command::EMPTY
4890        };
4891
4892        let a = argv(["node", "--help"]);
4893        assert_eq!(
4894            parse(&AUTO_WRAP, &a).unwrap(),
4895            vec![
4896                Event::Arg {
4897                    arg: &AUTO_ARGS,
4898                    value: b"node",
4899                    delimit: true,
4900                },
4901                Event::Arg {
4902                    arg: &AUTO_ARGS,
4903                    value: b"--help",
4904                    delimit: true,
4905                },
4906            ]
4907        );
4908
4909        // Before either takes effect, though, the wrapper's own help is what `--help` asks
4910        // for — `mise run --help` is a question about `run`, not a value for it.
4911        let a = argv(["--help"]);
4912        assert_eq!(
4913            parse(&AUTO_WRAP, &a).unwrap(),
4914            vec![Event::Flag {
4915                flag: &HELP_LONG,
4916                value: None,
4917                negated: false
4918            }]
4919        );
4920    }
4921
4922    #[test]
4923    fn double_dash_required_arg() {
4924        static CMD: Arg = Arg {
4925            key: 20,
4926            name: "cmd",
4927            double_dash: DoubleDash::Required,
4928            ..Arg::REQUIRED
4929        };
4930        static EXEC: Command = Command {
4931            name: "ex",
4932            args: &[&CMD],
4933            ..Command::EMPTY
4934        };
4935
4936        let a = argv(["--", "ls"]);
4937        assert_eq!(
4938            parse(&EXEC, &a).unwrap(),
4939            vec![Event::Arg {
4940                arg: &CMD,
4941                value: b"ls",
4942                delimit: true,
4943            }]
4944        );
4945
4946        let a = argv(["ls"]);
4947        assert_eq!(
4948            parse(&EXEC, &a),
4949            Err(Error::ArgRequiresDoubleDash { arg: &CMD })
4950        );
4951    }
4952
4953    #[test]
4954    fn double_dash_preserve_keeps_the_separator() {
4955        static ARGS: Arg = Arg {
4956            key: 21,
4957            name: "args",
4958            double_dash: DoubleDash::Preserve,
4959            ..Arg::VAR
4960        };
4961        static WRAP: Command = Command {
4962            name: "ex",
4963            args: &[&ARGS],
4964            ..Command::EMPTY
4965        };
4966
4967        let a = argv(["a", "--", "b"]);
4968        let values: Vec<&[u8]> = parse(&WRAP, &a)
4969            .unwrap()
4970            .iter()
4971            .filter_map(|e| match e {
4972                Event::Arg { value, .. } => Some(*value),
4973                _ => None,
4974            })
4975            .collect();
4976        assert_eq!(values, vec![&b"a"[..], &b"--"[..], &b"b"[..]]);
4977    }
4978
4979    #[test]
4980    fn double_dash_automatic_stops_flag_interpretation() {
4981        static FILES: Arg = Arg {
4982            key: 22,
4983            name: "files",
4984            double_dash: DoubleDash::Automatic,
4985            ..Arg::VAR
4986        };
4987        static AUTO: Command = Command {
4988            name: "ex",
4989            flags: &[&FORCE],
4990            args: &[&FILES],
4991            ..Command::EMPTY
4992        };
4993
4994        // The flag before the first value is still a flag; the one after it is a
4995        // value.
4996        let a = argv(["-f", "one", "--force"]);
4997        assert_eq!(
4998            parse(&AUTO, &a).unwrap(),
4999            vec![
5000                Event::Flag {
5001                    flag: &FORCE,
5002                    value: None,
5003                    negated: false
5004                },
5005                Event::Arg {
5006                    arg: &FILES,
5007                    value: b"one",
5008                    delimit: true,
5009                },
5010                Event::Arg {
5011                    arg: &FILES,
5012                    value: b"--force",
5013                    delimit: true,
5014                },
5015            ]
5016        );
5017    }
5018
5019    #[test]
5020    fn too_many_words() {
5021        static ONE: Command = Command {
5022            name: "ex",
5023            args: &[&FILE],
5024            ..Command::EMPTY
5025        };
5026        let a = argv(["a", "b"]);
5027        assert_eq!(parse(&ONE, &a), Err(Error::UnexpectedArg { token: b"b" }));
5028    }
5029
5030    #[test]
5031    fn unknown_letter_rejects_the_whole_bundle() {
5032        // `-f` is real and `-z` is not. The first event must be the error: if the
5033        // flag event came out first, a caller would have applied `-f` from a
5034        // command line that was rejected.
5035        let a = argv(["-fz"]);
5036        let mut parser = Parser::new(&STRICT, &a);
5037        assert_eq!(
5038            parser.next_event(),
5039            Some(Err(Error::UnknownFlag { token: b"-fz" })),
5040            "an unknown letter must reject the token before any of it is applied"
5041        );
5042        assert!(parser.next_event().is_none());
5043
5044        // Leniently, the same token is a value — and `-f` is *not* applied, since
5045        // the token was never a bundle at all.
5046        let a = argv(["-fz"]);
5047        assert_eq!(
5048            parse(&ROOT, &a).unwrap(),
5049            vec![Event::Arg {
5050                arg: &FILE,
5051                value: b"-fz",
5052                delimit: true,
5053            }]
5054        );
5055    }
5056
5057    #[test]
5058    fn unknown_short_error_names_the_whole_token() {
5059        for (tokens, want) in [(["-z"], &b"-z"[..]), (["-fz"], &b"-fz"[..])] {
5060            let a = argv(tokens);
5061            assert_eq!(
5062                parse(&STRICT, &a),
5063                Err(Error::UnknownFlag { token: want }),
5064                "{tokens:?}"
5065            );
5066        }
5067    }
5068
5069    #[test]
5070    fn errors_are_terminal() {
5071        let a = argv(["--wat", "--force"]);
5072        let mut parser = Parser::new(&STRICT, &a);
5073        assert!(parser.next_event().unwrap().is_err());
5074        assert!(parser.next_event().is_none());
5075    }
5076
5077    #[test]
5078    fn non_utf8_values_still_parse() {
5079        // A value that is not valid UTF-8 binds; only converting it fails, and
5080        // only if a caller asks.
5081        let raw = OsStr::new("--force");
5082        let a = [raw];
5083        assert!(parse(&ROOT, &a).is_ok());
5084
5085        assert!(as_str(b"ok").is_ok());
5086        assert!(as_str(&[0xff, 0xfe]).is_err());
5087    }
5088
5089    #[test]
5090    fn a_multicall_applet_is_the_basename_unless_it_is_the_dispatcher() {
5091        assert_eq!(multicall_basename("/usr/bin/ls"), "ls");
5092        assert_eq!(multicall_basename(r"C:\busybox\ls.exe"), "ls");
5093        assert_eq!(
5094            multicall_applet("/usr/bin/ls", "busybox", Some("busybox")),
5095            Some("ls")
5096        );
5097        assert_eq!(
5098            multicall_applet("/usr/bin/busybox", "busybox", Some("busybox")),
5099            None
5100        );
5101        assert_eq!(
5102            multicall_applet("ls.exe", "busybox", Some("busybox")),
5103            Some("ls")
5104        );
5105        assert_eq!(
5106            multicall_applet("/usr/bin/busybox", "BusyBox", Some("/opt/bin/busybox")),
5107            None
5108        );
5109        assert_eq!(
5110            multicall_applet("busybox.exe", "BusyBox", Some("busybox.exe")),
5111            None
5112        );
5113    }
5114
5115    #[test]
5116    fn a_spec_request_is_the_first_word_and_nothing_else() {
5117        let request = [OsStr::new(SPEC_REQUEST)];
5118        assert!(is_spec_request(&ROOT, &request));
5119
5120        // Anywhere but the front it is an ordinary value, which is what makes the endpoint
5121        // safe for a CLI whose arguments are arbitrary text.
5122        let later = ["install", SPEC_REQUEST].map(OsStr::new);
5123        assert!(!is_spec_request(&ROOT, &later));
5124        assert!(!is_spec_request(&ROOT, &[]));
5125        assert!(!is_spec_request(&ROOT, &[OsStr::new("--help")]));
5126    }
5127
5128    #[test]
5129    fn a_declared_command_of_that_name_keeps_it() {
5130        static DECLARED: Command = Command {
5131            name: SPEC_REQUEST,
5132            key: 200,
5133            ..Command::EMPTY
5134        };
5135        static ALIASED: Command = Command {
5136            name: "describe",
5137            aliases: &[SPEC_REQUEST],
5138            key: 201,
5139            ..Command::EMPTY
5140        };
5141        static DECLARES_IT: Command = Command {
5142            name: "ex",
5143            subcommands: &[&DECLARED],
5144            ..Command::EMPTY
5145        };
5146        static ALIASES_IT: Command = Command {
5147            name: "ex",
5148            subcommands: &[&ALIASED],
5149            ..Command::EMPTY
5150        };
5151
5152        let request = [OsStr::new(SPEC_REQUEST)];
5153        assert!(!is_spec_request(&DECLARES_IT, &request));
5154        // An alias selects a command just as its name does, so it wins here too.
5155        assert!(!is_spec_request(&ALIASES_IT, &request));
5156    }
5157}