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