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