Skip to main content

usage_argv/
lib.rs

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