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