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