Skip to main content

winnow_args_derive/
lib.rs

1//! The derives of winnow-args: `Args`, `Subcommand`, `ValueEnum` and `Occurrence`.
2//!
3//! Each is documented with every attribute it accepts. `#[winnow_args(...)]` is
4//! `#[arg(...)]` under another name, for a type whose other derives claim `arg`.
5//!
6//! What `#[derive(Args)]` generates is one loop:
7//!
8//! 1. read the next item of the command line (a flag, a word, `--`);
9//! 2. `match` its name against the struct's flags, and store the value in a
10//!    local variable for that field;
11//! 3. give a plain word to the next positional field, in declaration order,
12//!    unless it names a subcommand, which then parses the rest;
13//! 4. when the words run out, apply defaults, check the rules, build the struct.
14//!
15//! The names are patterns of a `match`, which rustc compiles: no list of
16//! parsers is walked, whatever the number of flags.
17//!
18//! # Documentation
19//!
20//! When the environment variable `WINNOW_ARGS_SPEC` holds the absolute path of
21//! a directory, each derive also writes `CRATE/FILE/Type.toml` under it,
22//! `FILE` being the type's source file: what help knows of the type, with a
23//! flatten or a subcommand as the other type's path, as it is written. A
24//! binary or an example writes under `CRATE-bin`, apart from the library of
25//! its package. `winnow-args-spec` reads them back, for `winnow-args-man` and
26//! `winnow-args-markdown`. `cargo check` is enough, and Cargo compiles the
27//! crate again when the variable changes.
28//!
29//! Two types of one name are told apart by their modules, which a fragment
30//! records: those of the source file, then the `mod name { }` blocks the file
31//! has around the type (the file is read for them). `add::Opts` is the `Opts`
32//! of a module `add`, and a bare `Opts` what the file's `use` lines bring in
33//! under that name, or the one the file declares. Where that
34//! does not say which, `winnow-args-spec` asks for a name:
35//! `#[arg(spec = "AddOpts")]` on the type files it under that name, and the
36//! same on a field or a variant that holds the type finds it there. Two types
37//! of one name in one file need it, and the build with the variable set
38//! says so. It also fails where the path a field writes does not lead to its
39//! type (an alias, a module under another name, a `spec` not repeated), so a
40//! page never describes another type.
41//!
42//! Set the variable for the documentation build alone, not in a shell: a
43//! test or a doctest would write its types beside the program's.
44//! rust-analyzer's macro server writes nothing.
45//!
46//! A value's choices are found under the name of its type. Where that name is
47//! an alias, or the type implements `FromArg` by hand, the build with the
48//! variable set fails at the field until `spec = "Name"` or `choices(...)`
49//! states them there, and fails again if the choices stated are not what the
50//! type accepts.
51
52use proc_macro::TokenStream;
53use proc_macro2::{Span, TokenStream as TokenStream2};
54use quote::{format_ident, quote};
55use syn::{
56    Data, DeriveInput, Fields, GenericArgument, Ident, LitByteStr, LitChar, LitStr, PathArguments,
57    Type, ext::IdentExt as _, parse_macro_input, spanned::Spanned as _,
58};
59
60mod spec;
61
62/// Derive `Args` for a struct with named fields: the generated loop parses the whole command line.
63///
64/// Every attribute is `#[arg(...)]` (or `#[winnow_args(...)]`); a field with none is `--field-name`.
65/// Doc comments are the help text. On a terminal a `` `quoted` ``, `*emphasized*` or `**strong**` span
66/// of one is painted, marks kept.
67///
68/// ```ignore
69/// use winnow_args::Args;
70///
71/// /// Copy files.
72/// #[derive(Args, Debug)]
73/// #[arg(name = "cp", version)]
74/// struct Cp {
75///     /// Explain what is done; repeat for more.
76///     #[arg(short, long, count)]
77///     verbose: u8,
78///     /// Overwrite without asking.
79///     #[arg(short, long, negate)]
80///     force: bool,
81///     /// Attributes to keep.
82///     #[arg(long, delimiter = ',')]
83///     preserve: Vec<String>,
84///     /// Jobs to run.
85///     #[arg(short, long, env = "CP_JOBS", default = "1")]
86///     jobs: usize,
87///     #[arg(positional)]
88///     source: String,
89///     #[arg(positional)]
90///     dest: Option<String>,
91/// }
92///
93/// let cp = Cp::parse_from(["-vvf", "--preserve=mode,links", "a", "--no-force", "b"])?;
94/// assert_eq!((cp.verbose, cp.force, cp.jobs), (2, false, 1));
95/// assert_eq!(cp.preserve, ["mode", "links"]);
96/// assert_eq!((cp.source.as_str(), cp.dest.as_deref()), ("a", Some("b")));
97/// # Ok::<(), winnow_args::Error>(())
98/// ```
99///
100/// What a field's type means. A repeated single-value flag keeps the last value.
101/// `values` and `delimiter` change what a `Vec` takes.
102///
103/// | Type | Flag | Positional |
104/// |---|---|---|
105/// | `bool` | a switch | |
106/// | integer, with `count` | how many times it was given | |
107/// | `T` | one value; an error if absent and without `env` or `default` | a required word |
108/// | `Option<T>` | one value, or `None`; with `require_equals`, a bare flag is `None` | an optional word |
109/// | `Vec<T>` | one value per occurrence | every word left |
110/// | `Option<bool>`, with `plus` | `-x` is `Some(true)`, `+x` `Some(false)` | |
111///
112/// # Struct options
113///
114/// Naming and help:
115///
116/// - `name = "…"`: the program name in the usage line (`argv[0]` when empty) and in completion scripts, which need it.
117/// - `version`, `version = "…"`: supply `-V`/`--version`; bare, the crate's `CARGO_PKG_VERSION`.
118/// - `about`, `long_about`, `after_help`, `after_long_help`: help text; the first two default to the doc comment.
119/// - `disable_help_flag`, `disable_help_short` (`--help` only), `disable_version_flag`,
120///   `disable_help_subcommand`: do not supply that item.
121/// - `arg_required_else_help`: no arguments at all is a request for help.
122///
123/// Grammar:
124///
125/// - `unknown_flags = "value"`: a flag-like word naming no flag is a positional value; `"error"` is the default.
126/// - `long_only`: a long name may be spelled with one dash (GNU `getopt_long_only`).
127/// - `plus_options`: `+abc` is a bundle of `+` options, for fields with `plus`.
128/// - `restart_token = "…"`: a word that starts a new run of positionals; flags resume and keep their values.
129/// - `default_subcommand = "…"`: the subcommand for a line whose first word names none; needs a `subcommand` field.
130/// - `default_subcommand_flags`: a flag this struct does not take selects the default subcommand too, which
131///   reads it as its own (`tool -p pkg` is `tool run -p pkg`). Every later word is then the subcommand's,
132///   one that names another subcommand included, and so is every later flag: one of this struct's that is
133///   not `global` goes before (`tool -qp pkg`, not `tool -pq pkg`).
134/// - `group("name", required, multiple)`: declare a group; fields join it with `group = "name"`.
135///   `required` means at least one member. Without `multiple`, a second member conflicts.
136/// - `spec = "…"`: the name the documentation files the type under, in place of its own. A field or a variant
137///   that holds the type states it too. See [Documentation](crate#documentation).
138///
139/// # Field roles
140///
141/// A field is a flag unless it is one of these. Most programs need `positional`, `subcommand` and
142/// `flatten`. `sequence` and `unknown` are for tools whose flags mean something by their order
143/// (see `Occurrence`). `flatten`, `sequence`, `unknown` and `skip` take no other option, except
144/// `sequence, unknown`.
145///
146/// - `positional`: a plain word. Required `T`, then `Option<T>`, then one `Vec<T>` last (it takes
147///   every word left). Any other order is a compile error.
148/// - `subcommand`: a `Subcommand` enum, `E` or `Option<E>`; takes no other option.
149/// - `flatten`: another flags-only `Args` struct, parsed as if declared here (nested too). A flag spelled in
150///   both is a compile error. The flattened struct has no positionals, subcommand, `sequence`, `unknown`,
151///   `keywords` or generics, nor `unknown_flags = "value"`; the parent no generics. A `global` flag of
152///   the flattened struct is global from the struct that flattens it.
153/// - `sequence`: a `Vec` of an `Occurrence` enum: its flags and words, kept in order, its flags in help.
154///   One per struct; a spelling it shares with the struct is a compile error.
155/// - `sequence, unknown`: the same, with unknown flags offered to the enum's `unknown` variant.
156/// - `unknown`: a `Vec<T>` of the flag-like words no flag takes, whole. One per struct.
157/// - `skip`: left at `Default`, never parsed.
158///
159/// # Flag names
160///
161/// - `short`, `short = 'x'`: `-x`; bare, the field name's first letter. A second `short` is another letter.
162/// - `long`, `long = "name"`: `--name`, matched in full; bare, the field name in kebab-case. A second `long` is another name.
163/// - `alias = "…"`, `alias("…", …)`: more long names, not shown in help.
164/// - `negate`, `negate = "no-name"`: `--no-name` sets a `bool` false; bare, `--no-<long>`.
165/// - `two_dashes`: under `long_only`, the long name is never spelled with one dash (`--omagic`).
166/// - `prefix`: an ASCII `short` that always takes the rest of its word (`-lfoo`); a value flag.
167/// - `plus = 'c'`: also spelled `+c` in a `plus_options` struct; on an `Option<bool>` with `short = 'c'`,
168///   `-c` is `Some(true)` and `+c` `Some(false)`.
169/// - `global`: also accepted after a subcommand word, at any depth.
170///
171/// # Values
172///
173/// A value is any `T: FromArg`; the field's type says how many, as the table above has it.
174///
175/// - `count`: an integer counting occurrences.
176/// - `value_name = "…"`: the placeholder in help.
177/// - `delimiter = ','`: split each value of a `Vec` field.
178/// - `values = N`: each occurrence of a `Vec` flag takes `N` words, whatever they look like.
179/// - `values = 1..`, `values = 2..=3`: at least and at most that many words an occurrence; those after
180///   the first are taken while they are not flag-like. Positionals after such a flag need a flag or `--`
181///   before them.
182/// - `value_terminator = ";"`: the word that ends such a run, itself dropped.
183/// - `choices("a", "b")`: the only values accepted.
184/// - `env = "VAR"`, `default = "…"`: fallbacks after the command line, in that order; an `Option<T>`
185///   field then holds `Some`. A switch's variable is true unless empty, `0`, `false`, `no` or `off`.
186///   An unparseable `count` variable is left unset, not an error.
187/// - `default_fn = path`: a function returning the field's value (for a `Vec`, the list), called when
188///   nothing else gave one; `default_note = "…"` is what help shows for it.
189/// - `default_if("--flag", "v")`: the default when `--flag` was given; `default_if("--flag", "x", "v")`
190///   when its value is `x`, whose type must be `PartialEq`. Tried after `env` and before `default`,
191///   in order; the selector is as for the rules below.
192/// - `default_missing = "…"`: the value of a flag given without one, which makes its value optional.
193/// - `require_equals`: the value only attached (`--name=v`, `-nv`); with `default_missing`, a bare flag
194///   leaves the next word alone, and on an `Option` field a bare flag is `None`.
195/// - `keep_equals`: a short flag's attached value keeps a leading `=` (`-L=dir`).
196/// - `allow_hyphen_values`: the next word is the value, flag-like or `--` included.
197/// - `allow_negative_numbers`: a negative number is a value, for a flag or a positional.
198/// - `keywords`: on a flag of a type deriving `Args`, each value is `--value` of that type (`-z now`).
199/// - `spec = "…"`: the name the documentation finds the field's type under, when it is not the type's own:
200///   the type states a `spec`, or is named here through an alias. Also on a `flatten` or `sequence` field.
201///
202/// # Positionals
203///
204/// - `double_dash = "…"`: how `--` treats it: `"optional"` (default), `"required"` (only words after it),
205///   `"automatic"` (flags stop once it has a value), `"preserve"` (a `--` reaching it is a value).
206/// - `stop_flags`: once it has a value, flags stop, whatever its `double_dash`.
207///
208/// # Rules
209///
210/// Selectors name another field: `"--long"`, `"-s"`, a positional's name or the field's name.
211///
212/// - `required`: the field must hold a value once defaults are applied. A bare `Vec` may be empty without it.
213/// - `required_unless("…", …)`: required unless one of them has a value.
214/// - `conflicts("…", …)`: not supplied together with these.
215/// - `overrides("…", …)`: the last of this and these supplied wins.
216/// - `requires("…", …)`: these must have a value when this is supplied.
217/// - `group = "name"`: a member of a struct-level group.
218///
219/// # Help
220///
221/// - `help = "…"`, `long_help = "…"`: replace the doc comment's first paragraph and whole text.
222/// - `help_heading = "…"`: list it under that heading instead of "Options".
223/// - `hint = "dir"`: what the value is, for the shell that completes it: `"file"`, `"dir"`, `"command"`,
224///   or `"none"` for one no shell can know. Unstated, the type says (`FromArg::HINT`): a file for a
225///   `PathBuf`, a `String` or an `OsString`, nothing for any other.
226/// - `complete = path::to::function`: a `fn(&str) -> Vec<winnow_args::complete::Candidate>` that answers
227///   what the value can be, given what is typed of it, when a shell completes it. It answers alone, so
228///   not beside `hint`, and each candidate is one plain word: the scripts quote nothing for the shell.
229/// - `hide`: leave it out of help and completion.
230#[proc_macro_derive(Args, attributes(arg, winnow_args))]
231pub fn derive_args(input: TokenStream) -> TokenStream {
232    let input = parse_macro_input!(input as DeriveInput);
233    finish(expand(&input))
234}
235
236/// The expansion, or its error, and what tells Cargo to compile the crate
237/// again when `WINNOW_ARGS_SPEC` changes.
238fn finish(expanded: syn::Result<TokenStream2>) -> TokenStream {
239    let expanded = expanded.unwrap_or_else(syn::Error::into_compile_error);
240    quote! {
241        #expanded
242        const _: ::core::option::Option<&str> = ::core::option_env!("WINNOW_ARGS_SPEC");
243    }
244    .into()
245}
246
247/// Derive `Subcommand` (and `Args`, so the enum can be the whole command line) for an enum of subcommands.
248///
249/// A variant is a unit, or holds one `Args` type, which may be `Box`ed; a parent struct holds the enum in
250/// a `subcommand` field. The doc comments are the help text. As a whole command line the enum has no
251/// name, version or about: wrap it in a struct for those, and for completion scripts.
252///
253/// ```ignore
254/// use winnow_args::{Args, Subcommand};
255///
256/// #[derive(Args, Debug, PartialEq)]
257/// struct Add {
258///     #[arg(positional)]
259///     paths: Vec<String>,
260/// }
261///
262/// #[derive(Subcommand, Debug, PartialEq)]
263/// enum Command {
264///     /// Stage files.
265///     Add(Add),
266///     /// Show what changed.
267///     #[arg(alias = "st")]
268///     Status,
269/// }
270///
271/// #[derive(Args, Debug)]
272/// struct Git {
273///     /// Say less.
274///     #[arg(short, long, global)]
275///     quiet: bool,
276///     #[arg(subcommand)]
277///     command: Command,
278/// }
279///
280/// let git = Git::parse_from(["add", "-q", "a", "b"])?;
281/// assert!(git.quiet);
282/// assert_eq!(git.command, Command::Add(Add { paths: vec!["a".into(), "b".into()] }));
283/// assert_eq!(Git::parse_from(["st"])?.command, Command::Status);
284///
285/// match git.command {
286///     Command::Add(Add { paths }) => println!("staging {paths:?}"),
287///     Command::Status => println!("on branch main"),
288/// }
289/// # Ok::<(), winnow_args::Error>(())
290/// ```
291///
292/// - Enum: `rename_all = "…"`: the case of variant names, one of `"kebab-case"` (default), `"lowercase"`,
293///   `"UPPERCASE"`, `"snake_case"` and `"verbatim"`.
294/// - Enum: `spec = "…"`: the name the documentation files the enum under, in place of its own.
295/// - Enum: `run`, `run_async`: implement [`Run`](https://docs.rs/winnow-args/latest/winnow_args/run/trait.Run.html)
296///   or `RunAsync` for the enum, by calling it on the type the selected variant holds. Every variant holds
297///   a type, each implements the trait, and all produce the same `Output`; two variants may hold one type.
298///   The enum then implements it for every context, so it takes no implementation of its own.
299/// - Variant: `name = "…"`: the word that selects it.
300/// - Variant: `spec = "…"`: the name the documentation finds the variant's type under, when it is not the type's own.
301/// - Variant: `alias = "…"`, `alias("…", …)`: other words, shown in help; `alias_hidden` is the same, not shown.
302/// - Variant: `hide`: leave it out of the list in help and completion.
303/// - Variant: `help = "…"`: the one-line description; `long_help = "…"` is accepted and unused in the list.
304#[proc_macro_derive(Subcommand, attributes(arg, winnow_args))]
305pub fn derive_subcommand(input: TokenStream) -> TokenStream {
306    let input = parse_macro_input!(input as DeriveInput);
307    finish(expand_subcommand(&input))
308}
309
310/// One `match` on the subcommand's name; the variant's `Args` parses the rest.
311fn expand_subcommand(input: &DeriveInput) -> syn::Result<TokenStream2> {
312    let Data::Enum(data) = &input.data else {
313        return Err(syn::Error::new(
314            input.span(),
315            "`Subcommand` can only be derived for enums",
316        ));
317    };
318    let mut runs = None;
319    let case = rename_all(input, Some(&mut runs))?;
320    let mut names: Vec<(String, &Ident)> = Vec::new();
321    let mut arms = Vec::new();
322    let mut patterns = Vec::new();
323    let mut subs = Vec::new();
324    // Each variant and the type it holds, for `run`.
325    let mut held: Vec<(&Ident, Type, bool)> = Vec::new();
326    let spec_on = spec::enabled();
327    let filed = filed(input)?;
328    let identity = spec::identity(input.ident.span(), &filed);
329    let mut spec_variants = Vec::new();
330    let mut spec_checks = Vec::new();
331    for variant in &data.variants {
332        let ident = &variant.ident;
333        let info = variant_names(variant, &mut names, case)?;
334        let pattern = byte_patterns(&info.names);
335        let primary = LitStr::new(&info.names[0], Span::call_site());
336        let inner = match &variant.fields {
337            Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
338                let ty = &fields.unnamed[0].ty;
339                Some(boxed(ty).unwrap_or(ty).clone())
340            }
341            _ => None,
342        };
343        match &inner {
344            Some(ty) => {
345                let is_boxed = matches!(&variant.fields, Fields::Unnamed(f) if boxed(&f.unnamed[0].ty).is_some());
346                held.push((ident, ty.clone(), is_boxed));
347            }
348            None if runs.is_some() => {
349                return Err(syn::Error::new(
350                    ident.span(),
351                    "`run` calls the type a variant holds, and this variant holds none",
352                ));
353            }
354            None => {}
355        }
356        // The whole doc comment, when the description is its first paragraph.
357        let unit_long_about = inner.is_none().then(|| {
358            let (doc, long_doc) = docs(&variant.attrs);
359            if doc == info.about {
360                long_doc
361            } else {
362                info.about.clone()
363            }
364        });
365        let command = match (&inner, &unit_long_about) {
366            (Some(ty), _) => quote!(<#ty as ::winnow_args::Args>::HELP),
367            (None, long_about) => {
368                let long_about = text(long_about.as_deref().unwrap_or_default());
369                let about = text(&info.about);
370                quote!(&::winnow_args::help::Command {
371                    name: "",
372                    about: #about,
373                    long_about: #long_about,
374                    after_help: "",
375                    after_long_help: "",
376                    items: &[],
377                    subcommands: &[],
378                    subcommand_required: false,
379                    help_flag: true,
380                    help_short: true,
381                    version: ::core::option::Option::None,
382                })
383            }
384        };
385        let about = match (&inner, info.about.is_empty()) {
386            (Some(ty), true) => quote!(<#ty as ::winnow_args::Args>::HELP.about),
387            _ => text(&info.about),
388        };
389        let shown = &info.names[1..=info.shown];
390        let all = &info.names;
391        let hide = info.hide;
392        if spec_on {
393            let ty = match &inner {
394                Some(ty) => {
395                    let name = spec_type(spec_name(&variant.attrs)?, ty)?;
396                    spec_checks.extend(spec_filed(&input.generics, quote!(Args), ty, &name));
397                    Some(name)
398                }
399                None => None,
400            };
401            spec_variants.push(spec::VariantDoc {
402                name: info.names[0].clone(),
403                aliases: shown.to_vec(),
404                about: info.about.clone(),
405                long_about: unit_long_about.unwrap_or_default(),
406                hide: info.hide,
407                ty,
408            });
409        }
410        subs.push(quote! {
411            ::winnow_args::help::Sub {
412                name: #primary,
413                aliases: &[#(#shown),*],
414                names: &[#(#all),*],
415                command: #command,
416                about: #about,
417                hide: #hide,
418            }
419        });
420        // Read here, so a payload only printed is not dead code.
421        let read_payload = quote! {
422            |__item| match __item {
423                Self::#ident(__read) => {
424                    let _ = __read;
425                }
426                #[allow(unreachable_patterns)]
427                _ => {}
428            }
429        };
430        let parse = match &variant.fields {
431            Fields::Unit => {
432                let index = subs.len() - 1;
433                quote! {
434                    __wa::finish_with(
435                        __input,
436                        __globals,
437                        <Self as ::winnow_args::Args>::HELP.subcommands[#index].command,
438                    )
439                    .map(|()| Self::#ident)
440                }
441            }
442            Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
443                let ty = &fields.unnamed[0].ty;
444                match boxed(ty) {
445                    Some(inner) => quote! {
446                        <#inner as ::winnow_args::Args>::parse_argv_with(__input, __globals)
447                            .map(|v| Self::#ident(::std::boxed::Box::new(v)))
448                            .inspect(#read_payload)
449                    },
450                    None => quote! {
451                        <#ty as ::winnow_args::Args>::parse_argv_with(__input, __globals)
452                            .map(Self::#ident)
453                            .inspect(#read_payload)
454                    },
455                }
456            }
457            _ => {
458                return Err(syn::Error::new(
459                    variant.span(),
460                    "a subcommand variant is a unit or holds one `Args` type",
461                ));
462            }
463        };
464        arms.push(quote!(#pattern => (#parse).map_err(|e| e.within(#primary)),));
465        patterns.push(pattern);
466    }
467    let (about_text, long_about_text) = docs(&input.attrs);
468    let (about, long_about) = (text(&about_text), text(&long_about_text));
469    if spec_on {
470        spec::write_subcommands(
471            input.ident.span(),
472            &filed,
473            &about_text,
474            &long_about_text,
475            &spec_variants,
476        )?;
477    }
478
479    let name = &input.ident;
480    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
481    let run_impl = runs.map(|runs| run_impl(input, runs, &held));
482    Ok(quote! {
483        #(#spec_checks)*
484        #run_impl
485        impl #impl_generics ::winnow_args::Subcommand for #name #ty_generics #where_clause {
486            const SPEC: &'static str = #identity;
487
488            #[inline]
489            fn has(__name: &[u8]) -> bool {
490                matches!(__name, #(#patterns)|*)
491            }
492
493            fn parse_subcommand(
494                __name: &[u8],
495                __input: &mut ::winnow_args::Argv<'_>,
496                __globals: &mut dyn ::winnow_args::Globals,
497            ) -> ::core::result::Result<Self, ::winnow_args::Error> {
498                use ::winnow_args::__private as __wa;
499                match __name {
500                    #(#arms)*
501                    _ => ::core::result::Result::Err(__wa::Error::unexpected_arg(
502                        __input.offset(),
503                        ::std::string::String::from_utf8_lossy(__name),
504                    )),
505                }
506            }
507        }
508
509        impl #impl_generics ::winnow_args::Args for #name #ty_generics #where_clause {
510            const SPEC: &'static str = #identity;
511
512            // Compiled here and never inlined, so a program in another crate has
513            // no copy of the help.
514            #[inline(never)]
515            fn help_data() -> &'static ::winnow_args::help::Command {
516                <Self as ::winnow_args::Args>::HELP
517            }
518
519            const HELP: &'static ::winnow_args::help::Command = &::winnow_args::help::Command {
520                name: "",
521                about: #about,
522                long_about: #long_about,
523                after_help: "",
524                after_long_help: "",
525                items: &[],
526                subcommands: &[#(#subs),*],
527                subcommand_required: true,
528                help_flag: true,
529                help_short: true,
530                version: ::core::option::Option::None,
531            };
532
533            fn parse_argv_with(
534                __input: &mut ::winnow_args::Argv<'_>,
535                __globals: &mut dyn ::winnow_args::Globals,
536            ) -> ::core::result::Result<Self, ::winnow_args::Error> {
537                use ::winnow_args::__private as __wa;
538                let __help = <Self as ::winnow_args::Args>::HELP;
539                // Inherited global flags may come before the subcommand's name.
540                loop {
541                    if __input.is_empty() {
542                        return ::core::result::Result::Err(
543                            __wa::Error::missing_subcommand(__input.offset()),
544                        );
545                    }
546                    let __arg = __wa::arg(__input)?;
547                    match __arg {
548                        __wa::Arg::Word(__word) => {
549                            if <Self as __wa::Subcommand>::has(&**__word.value) {
550                                return <Self as __wa::Subcommand>::parse_subcommand(
551                                    &**__word.value, __input, __globals,
552                                );
553                            }
554                            if &**__word.value == b"help" {
555                                return ::core::result::Result::Err(__wa::help_word(__help, __input));
556                            }
557                        }
558                        __wa::Arg::Long(_) | __wa::Arg::Short(_)
559                            if __globals.bind(&__arg, __input)? =>
560                        {
561                            continue;
562                        }
563                        __wa::Arg::Long(__flag) if __flag.name == b"help" => {
564                            return ::core::result::Result::Err(__wa::Error::help(__help, true));
565                        }
566                        __wa::Arg::Short(__flag) if __flag.letter == 'h' => {
567                            return ::core::result::Result::Err(__wa::Error::help(__help, false));
568                        }
569                        _ => {}
570                    }
571                    return ::core::result::Result::Err(__arg.unexpected());
572                }
573            }
574        }
575    })
576}
577
578/// `Run` or `RunAsync` for a `Subcommand` enum: the command it holds is run,
579/// with whatever context it takes. Every variant's type produces the same.
580fn run_impl(input: &DeriveInput, runs: Runs, held: &[(&Ident, Type, bool)]) -> TokenStream2 {
581    if held.is_empty() {
582        // An enum with no variant holds nothing to run: it is never built.
583        return quote!();
584    }
585    let name = &input.ident;
586    let (_, ty_generics, where_clause) = input.generics.split_for_impl();
587    // The enum's own parameters, without the defaults an `impl` may not state.
588    let mut generics = input.generics.clone();
589    for param in &mut generics.params {
590        match param {
591            syn::GenericParam::Type(param) => (param.eq_token, param.default) = (None, None),
592            syn::GenericParam::Const(param) => (param.eq_token, param.default) = (None, None),
593            syn::GenericParam::Lifetime(_) => {}
594        }
595    }
596    let params = generics.params.iter();
597    let (the_trait, method) = match runs {
598        Runs::Sync => (quote!(::winnow_args::run::Run), quote!(run)),
599        Runs::Async => (quote!(::winnow_args::run::RunAsync), quote!(run_async)),
600    };
601    // One output for all: a type two variants hold is then bound twice alike.
602    let bounds = held
603        .iter()
604        .map(|(_, ty, _)| quote!(#ty: #the_trait<__C, Output = __O>));
605    let own_bounds = where_clause
606        .iter()
607        .flat_map(|clause| clause.predicates.iter());
608    let arms = held.iter().map(|(ident, ty, is_boxed)| {
609        let command = match is_boxed {
610            true => quote!(*__command),
611            false => quote!(__command),
612        };
613        let call = quote!(<#ty as #the_trait<__C>>::#method(#command, __context));
614        match runs {
615            Runs::Sync => quote!(Self::#ident(__command) => #call,),
616            Runs::Async => quote!(Self::#ident(__command) => #call.await,),
617        }
618    });
619    let function = match runs {
620        Runs::Sync => quote!(fn run(self, __context: __C) -> Self::Output),
621        Runs::Async => quote!(async fn run_async(self, __context: __C) -> Self::Output),
622    };
623    quote! {
624        #[automatically_derived]
625        // Whether the future is `Send` is the commands' to say, not this `match`'s.
626        #[allow(clippy::future_not_send)]
627        impl<#(#params,)* __C, __O> #the_trait<__C> for #name #ty_generics
628        where
629            #(#own_bounds,)*
630            #(#bounds,)*
631        {
632            type Output = __O;
633
634            #function {
635                match self {
636                    #(#arms)*
637                }
638            }
639        }
640    }
641}
642
643/// Derive `Occurrence` for an enum: one variant per flag or positional, kept
644/// in command-line order by an `#[arg(sequence)]` field of an `Args` struct.
645///
646/// A variant is a unit (a switch) or holds one value type; a `Spanned<T>` also records the offset and
647/// whether the value was attached. A variant with no `short` or `long` is `--variant-name`. Doc comments
648/// are the help text. A spelling used twice, here or in the parent, is a compile error.
649///
650/// ```ignore
651/// use winnow_args::{Args, Occurrence};
652///
653/// #[derive(Occurrence, Debug, PartialEq)]
654/// enum Item {
655///     /// Link what follows only if needed.
656///     #[arg(long)]
657///     AsNeeded,
658///     /// Search for a library.
659///     #[arg(short = 'l', prefix)]
660///     Library(String),
661///     #[arg(positional, value_name = "FILE")]
662///     Input(String),
663/// }
664///
665/// #[derive(Args, Debug)]
666/// struct Ld {
667///     #[arg(short, long)]
668///     output: Option<String>,
669///     #[arg(sequence)]
670///     items: Vec<Item>,
671/// }
672///
673/// let ld = Ld::parse_from(["a.o", "--as-needed", "-lm", "-o", "out", "b.o"])?;
674/// assert_eq!(ld.output.as_deref(), Some("out"));
675/// assert_eq!(
676///     ld.items,
677///     [
678///         Item::Input("a.o".into()),
679///         Item::AsNeeded,
680///         Item::Library("m".into()),
681///         Item::Input("b.o".into()),
682///     ]
683/// );
684/// # Ok::<(), winnow_args::Error>(())
685/// ```
686///
687/// Enum options, the default of every value variant:
688///
689/// - `allow_hyphen_values`, `keep_equals`: as for a field.
690///
691/// Variant names:
692///
693/// - `short = 'x'`, `long`, `long = "name"`: as for a field; a second `long` is another name.
694/// - `alias = "…"`, `alias("…", …)`: more long names.
695/// - `two_dashes`, `prefix`: as for a field.
696///
697/// Variant values, as for a field:
698///
699/// - `value_name = "…"`, `require_equals`, `keep_equals`, `allow_hyphen_values`, `allow_negative_numbers`,
700///   `default_missing = "…"`.
701///
702/// Variant roles, at most one of each in an enum:
703///
704/// - `positional`: each plain word is that variant; its `value_name` defaults to the variant's name.
705/// - `unknown`: takes a flag no variant names, whole, for a parent with `sequence, unknown`.
706/// - `bundle`: takes a word of several short flags (`-sS`), whole, ahead of its letters.
707/// - `skip`: built by the program, never parsed.
708#[proc_macro_derive(Occurrence, attributes(arg, winnow_args))]
709pub fn derive_occurrence(input: TokenStream) -> TokenStream {
710    let input = parse_macro_input!(input as DeriveInput);
711    finish(expand_occurrence(&input))
712}
713
714/// Derive `FromArg` for an enum of unit variants, matched on the value's bytes; help lists them as possible values.
715///
716/// ```ignore
717/// use winnow_args::{Args, ErrorKind, ValueEnum};
718///
719/// #[derive(ValueEnum, Debug, PartialEq)]
720/// enum Color {
721///     Always,
722///     #[arg(alias = "no")]
723///     Never,
724///     #[arg(name = "auto")]
725///     WhenTerminal,
726/// }
727///
728/// #[derive(Args, Debug)]
729/// struct Cli {
730///     #[arg(long)]
731///     color: Option<Color>,
732/// }
733///
734/// assert_eq!(Cli::parse_from(["--color=auto"])?.color, Some(Color::WhenTerminal));
735/// assert_eq!(Cli::parse_from(["--color", "no"])?.color, Some(Color::Never));
736/// let error = Cli::parse_from(["--color=red"]).unwrap_err();
737/// assert_eq!(error.kind(), ErrorKind::InvalidChoice);
738/// # Ok::<(), winnow_args::Error>(())
739/// ```
740///
741/// - Enum: `rename_all = "…"`: the case of variant names, one of `"kebab-case"` (default), `"lowercase"`,
742///   `"UPPERCASE"`, `"snake_case"` and `"verbatim"`.
743/// - Enum: `spec = "…"`: the name the documentation files the choices under, in place of the enum's own.
744/// - Variant: `name = "…"`: the value that selects it.
745/// - Variant: `alias = "…"`, `alias("…", …)`: other accepted values; `alias_hidden` is the same, not listed.
746/// - Variant: `hide`: accepted, but named neither in help nor in errors.
747/// - Variant: `help = "…"`, `long_help = "…"`: accepted and unused.
748#[proc_macro_derive(ValueEnum, attributes(arg, winnow_args))]
749pub fn derive_value_enum(input: TokenStream) -> TokenStream {
750    let input = parse_macro_input!(input as DeriveInput);
751    finish(expand_value_enum(&input))
752}
753
754/// `from_arg`: a `match` on the long name and one on the letter, each arm
755/// building its variant; `from_word`: the positional variant, if any.
756fn expand_occurrence(input: &DeriveInput) -> syn::Result<TokenStream2> {
757    let Data::Enum(data) = &input.data else {
758        return Err(syn::Error::new(
759            input.span(),
760            "`Occurrence` can only be derived for enums",
761        ));
762    };
763    let (mut long_arms, mut short_arms) = (Vec::new(), Vec::new());
764    let (mut single_dash, mut prefixes) = (Vec::new(), Vec::new());
765    let mut letters = Vec::new();
766    let (mut word, mut unknown, mut bundle) = (None, None, None);
767    let mut seen = std::collections::HashMap::new();
768    // What help lists: one row a variant, in declaration order.
769    let mut help_items = Vec::new();
770    let spec_on = spec::enabled();
771    let filed = filed(input)?;
772    let identity = spec::identity(input.ident.span(), &filed);
773    let mut spec_rows = Vec::new();
774    let mut spec_checks = Vec::new();
775    // `allow_hyphen_values` and `keep_equals` on the enum: every value
776    // variant's default.
777    let (mut enum_hyphen_values, mut enum_keep_equals) = (false, false);
778    for attr in input.attrs.iter().filter(|a| is_ours(a)) {
779        attr.parse_nested_meta(|meta| {
780            if meta.path.is_ident("allow_hyphen_values") {
781                enum_hyphen_values = true;
782                Ok(())
783            } else if meta.path.is_ident("keep_equals") {
784                enum_keep_equals = true;
785                Ok(())
786            } else if skip_spec(&meta)? {
787                Ok(())
788            } else {
789                Err(meta.error("expected `allow_hyphen_values`, `keep_equals` or `spec`"))
790            }
791        })?;
792    }
793    for variant in &data.variants {
794        let ident = &variant.ident;
795        let (mut short, mut longs, mut positional) = (None, Vec::new(), false);
796        let (mut two_dashes, mut prefix, mut value_name) = (false, false, None);
797        let (mut hyphen_values, mut negative_numbers) = (enum_hyphen_values, false);
798        let (mut require_equals, mut default_missing) = (false, None);
799        let mut keep_equals = enum_keep_equals;
800        let (mut skip, mut is_unknown, mut is_bundle) = (false, false, false);
801        let stated = spec_name(&variant.attrs)?;
802        for attr in variant.attrs.iter().filter(|a| is_ours(a)) {
803            attr.parse_nested_meta(|meta| {
804                if meta.path.is_ident("allow_hyphen_values") {
805                    hyphen_values = true;
806                } else if meta.path.is_ident("allow_negative_numbers") {
807                    negative_numbers = true;
808                } else if meta.path.is_ident("require_equals") {
809                    require_equals = true;
810                } else if meta.path.is_ident("keep_equals") {
811                    keep_equals = true;
812                } else if meta.path.is_ident("default_missing") {
813                    default_missing = Some(meta.value()?.parse::<LitStr>()?.value());
814                } else if meta.path.is_ident("skip") {
815                    skip = true;
816                } else if meta.path.is_ident("bundle") {
817                    is_bundle = true;
818                } else if meta.path.is_ident("unknown") {
819                    is_unknown = true;
820                } else if meta.path.is_ident("short") {
821                    if short.is_some() {
822                        return Err(meta.error("an `Occurrence` variant has one `short`"));
823                    }
824                    short = Some(meta.value()?.parse::<LitChar>()?.value());
825                } else if meta.path.is_ident("long") {
826                    longs.push(if meta.input.peek(syn::Token![=]) {
827                        meta.value()?.parse::<LitStr>()?.value()
828                    } else {
829                        kebab_case(&ident.to_string())
830                    });
831                } else if meta.path.is_ident("alias") {
832                    longs.extend(aliases(&meta)?);
833                } else if meta.path.is_ident("positional") {
834                    positional = true;
835                } else if meta.path.is_ident("two_dashes") {
836                    two_dashes = true;
837                } else if meta.path.is_ident("prefix") {
838                    prefix = true;
839                } else if meta.path.is_ident("value_name") {
840                    value_name = Some(meta.value()?.parse::<LitStr>()?.value());
841                } else if skip_spec(&meta)? {
842                } else {
843                    return Err(meta.error(
844                        "expected `short`, `long`, `alias`, `positional`, `two_dashes`, `prefix`, \
845                         `value_name`, `allow_hyphen_values`, `allow_negative_numbers`, \
846                         `require_equals`, `keep_equals`, `default_missing`, `skip`, `unknown` or `bundle`",
847                    ));
848                }
849                Ok(())
850            })?;
851        }
852        let takes = match &variant.fields {
853            Fields::Unit => None,
854            Fields::Unnamed(f) if f.unnamed.len() == 1 => Some(&f.unnamed[0].ty),
855            _ => {
856                return Err(syn::Error::new(
857                    variant.span(),
858                    "an `Occurrence` variant is a unit (a switch) or holds one value",
859                ));
860            }
861        };
862        if skip {
863            // Built by the program itself (from a `-z` keyword, say), never parsed.
864            continue;
865        }
866        // A `Spanned<T>` value also records where it was and whether it was attached.
867        let spanned = takes.and_then(|ty| {
868            last_segment(ty)
869                .filter(|s| s.ident == "Spanned")
870                .and_then(inner)
871        });
872        // An `Option<T>` field takes `T`, and a bare flag is `None` under
873        // `require_equals`, not a converted default.
874        let optional = takes.and_then(|ty| {
875            last_segment(ty)
876                .filter(|s| s.ident == "Option")
877                .and_then(inner)
878        });
879        let wrap = |value: TokenStream2, offset: TokenStream2, attached: TokenStream2| {
880            if spanned.is_some() {
881                quote! {
882                    ::winnow_args::value::Spanned {
883                        value: #value,
884                        offset: #offset,
885                        attached: #attached,
886                    }
887                }
888            } else {
889                value
890            }
891        };
892        if positional || is_unknown || is_bundle {
893            let Some(ty) = takes else {
894                return Err(syn::Error::new(
895                    variant.span(),
896                    "a positional, `unknown` or `bundle` variant holds its value",
897                ));
898            };
899            let ty = spanned.unwrap_or(ty);
900            let slot = if positional {
901                &mut word
902            } else if is_unknown {
903                &mut unknown
904            } else {
905                &mut bundle
906            };
907            if slot.is_some() {
908                return Err(syn::Error::new(
909                    variant.span(),
910                    "at most one positional, one `unknown` and one `bundle` variant",
911                ));
912            }
913            let name = value_name.unwrap_or_else(|| ident.to_string().to_uppercase());
914            if positional {
915                help_items.push(occurrence_item(
916                    variant,
917                    None,
918                    &[],
919                    Some(&name),
920                    Some(ty),
921                    false,
922                    false,
923                ));
924                if spec_on {
925                    spec_checks.extend(spec_check(&input.generics, Some(ty), stated.as_ref()));
926                    spec_rows.push(occurrence_spec(
927                        variant,
928                        spec::ItemDoc {
929                            value_name: Some(name.clone()),
930                            choices_ty: choices_name(stated.as_ref(), ty),
931                            ..spec::ItemDoc::default()
932                        },
933                    ));
934                }
935            }
936            let value = wrap(
937                quote!(__word.convert::<#ty>(#name)?),
938                quote!(__word.offset),
939                quote!(false),
940            );
941            *slot = Some(quote!({
942                let __item = Self::#ident(#value);
943                // Read here, so a value only printed is not dead code.
944                match &__item {
945                    Self::#ident(__read) => {
946                        let _ = __read;
947                    }
948                    #[allow(unreachable_patterns)]
949                    _ => {}
950                }
951                ::core::result::Result::Ok(::core::option::Option::Some(__item))
952            }));
953            continue;
954        }
955        if short.is_none() && longs.is_empty() {
956            longs.push(kebab_case(&ident.to_string()));
957        }
958        let spelled = short
959            .map(|c| format!("-{c}"))
960            .into_iter()
961            .chain(longs.iter().map(|l| format!("--{l}")));
962        for name in spelled {
963            if let Some(other) = seen.insert(name.clone(), ident.clone()) {
964                return Err(syn::Error::new(
965                    variant.span(),
966                    format!("`{name}` is already used by `{other}`"),
967                ));
968            }
969        }
970        let value = takes.map(|ty| spanned.or(optional).unwrap_or(ty));
971        help_items.push(occurrence_item(
972            variant,
973            short,
974            &longs,
975            takes
976                .is_some()
977                .then(|| value_name.as_deref().unwrap_or("VALUE")),
978            value,
979            require_equals,
980            default_missing.is_some(),
981        ));
982        if spec_on {
983            let value_ty = takes.map(|ty| spanned.or(optional).unwrap_or(ty));
984            spec_checks.extend(spec_check(&input.generics, value_ty, stated.as_ref()));
985            spec_rows.push(occurrence_spec(
986                variant,
987                spec::ItemDoc {
988                    short,
989                    long: longs.first().cloned(),
990                    aliases: longs.iter().skip(1).cloned().collect(),
991                    value_name: value_ty
992                        .map(|_| value_name.clone().unwrap_or_else(|| "VALUE".to_owned())),
993                    choices_ty: value_ty.and_then(|ty| choices_name(stated.as_ref(), ty)),
994                    require_equals,
995                    optional_value: default_missing.is_some(),
996                    two_dashes,
997                    prefix,
998                    ..spec::ItemDoc::default()
999                },
1000            ));
1001        }
1002        let body = match takes {
1003            None => quote! {
1004                __arg.check_switch()?;
1005                ::core::result::Result::Ok(::core::option::Option::Some(Self::#ident))
1006            },
1007            Some(ty) => {
1008                let ty = spanned.or(optional).unwrap_or(ty);
1009                let options = quote! {
1010                    ::winnow_args::token::ValueOptions {
1011                        negative_numbers: #negative_numbers,
1012                        hyphen_values: #hyphen_values,
1013                        require_equals: #require_equals,
1014                        keep_equals: #keep_equals,
1015                    }
1016                };
1017                let attached = spanned
1018                    .is_some()
1019                    .then(|| quote!(let __attached = __arg.has_attached_value(__input);));
1020                let value = wrap(
1021                    quote!(__arg.convert::<#ty>(__value)?),
1022                    quote!(__arg.offset()),
1023                    quote!(__attached),
1024                );
1025                let item = if optional.is_some() && require_equals && default_missing.is_none() {
1026                    quote! {
1027                        if let ::core::option::Option::Some(__value) =
1028                            __arg.read_value_opt_with(__input, #options)
1029                        {
1030                            Self::#ident(::core::option::Option::Some(#value))
1031                        } else {
1032                            Self::#ident(::core::option::Option::None)
1033                        }
1034                    }
1035                } else {
1036                    let read = match &default_missing {
1037                        Some(missing) => {
1038                            let bytes = LitByteStr::new(missing.as_bytes(), Span::call_site());
1039                            quote! {
1040                                __arg.read_value_or_with(
1041                                    __input,
1042                                    #options,
1043                                    ::winnow_args::__private::BStr::new(#bytes),
1044                                )
1045                            }
1046                        }
1047                        None => quote!(__arg.read_value_with(__input, #options)?),
1048                    };
1049                    quote! {{
1050                        let __value = #read;
1051                        Self::#ident(#value)
1052                    }}
1053                };
1054                quote! {
1055                    #attached
1056                    let __item = #item;
1057                    // Read here, so a value only printed is not dead code.
1058                    match &__item {
1059                        Self::#ident(__read) => {
1060                            let _ = __read;
1061                        }
1062                        #[allow(unreachable_patterns)]
1063                        _ => {}
1064                    }
1065                    ::core::result::Result::Ok(::core::option::Option::Some(__item))
1066                }
1067            }
1068        };
1069        if !longs.is_empty() {
1070            let pattern = byte_patterns(&longs);
1071            long_arms.push(quote!(#pattern => { #body }));
1072            if !two_dashes {
1073                single_dash.extend(longs.iter().cloned());
1074            }
1075        }
1076        if let Some(c) = short {
1077            if prefix {
1078                if !c.is_ascii() || takes.is_none() {
1079                    return Err(syn::Error::new(
1080                        variant.span(),
1081                        "`prefix` is for a value-taking variant with an ASCII `short` letter",
1082                    ));
1083                }
1084                prefixes.push(c as u8);
1085            }
1086            let letter = LitChar::new(c, Span::call_site());
1087            let takes_value = takes.is_some();
1088            letters.push(quote!(#letter => ::core::option::Option::Some(#takes_value),));
1089            short_arms.push(quote!(#letter => { #body }));
1090        }
1091    }
1092    let has_positional = word.is_some();
1093    let word =
1094        word.unwrap_or_else(|| quote!(::core::result::Result::Ok(::core::option::Option::None)));
1095    let bundle = bundle.map(|body| {
1096        quote! {
1097            const BUNDLES: bool = true;
1098
1099            fn from_bundle(
1100                __word: &::winnow_args::token::Word<'_>,
1101            ) -> ::core::result::Result<::core::option::Option<Self>, ::winnow_args::Error> {
1102                #body
1103            }
1104        }
1105    });
1106    let unknown = unknown.map(|body| {
1107        quote! {
1108            fn from_unknown(
1109                __word: &::winnow_args::token::Word<'_>,
1110            ) -> ::core::result::Result<::core::option::Option<Self>, ::winnow_args::Error> {
1111                #body
1112            }
1113        }
1114    });
1115    let is_long = matches_name(&single_dash);
1116    let name = &input.ident;
1117    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
1118    if spec_on {
1119        spec::write_occurrence(input.ident.span(), &filed, &spec_rows)?;
1120    }
1121    Ok(quote! {
1122        #(#spec_checks)*
1123        impl #impl_generics ::winnow_args::Occurrence for #name #ty_generics #where_clause {
1124            const SPEC: &'static str = #identity;
1125
1126            const PREFIXES: &'static [u8] = &[#(#prefixes),*];
1127
1128            const POSITIONAL: bool = #has_positional;
1129
1130            const ITEMS: &'static [::winnow_args::help::Item] = &[#(#help_items),*];
1131
1132            fn from_arg<'__i>(
1133                __arg: &::winnow_args::Arg<'__i>,
1134                __input: &mut ::winnow_args::Argv<'__i>,
1135            ) -> ::core::result::Result<::core::option::Option<Self>, ::winnow_args::Error> {
1136                let _ = &__input;
1137                match __arg {
1138                    ::winnow_args::Arg::Long(__flag) => match __flag.name {
1139                        #(#long_arms)*
1140                        _ => ::core::result::Result::Ok(::core::option::Option::None),
1141                    },
1142                    ::winnow_args::Arg::Short(__flag) if !__flag.plus => match __flag.letter {
1143                        #(#short_arms)*
1144                        _ => ::core::result::Result::Ok(::core::option::Option::None),
1145                    },
1146                    _ => ::core::result::Result::Ok(::core::option::Option::None),
1147                }
1148            }
1149
1150            fn from_word(
1151                __word: &::winnow_args::token::Word<'_>,
1152            ) -> ::core::result::Result<::core::option::Option<Self>, ::winnow_args::Error> {
1153                let _ = __word;
1154                #word
1155            }
1156
1157            fn is_long(__name: &[u8]) -> bool {
1158                #is_long
1159            }
1160
1161            #unknown
1162
1163            #bundle
1164
1165            fn short(__letter: char) -> ::core::option::Option<bool> {
1166                match __letter {
1167                    #(#letters)*
1168                    _ => ::core::option::Option::None,
1169                }
1170            }
1171        }
1172    })
1173}
1174
1175/// An `Occurrence` variant's row in help: a flag by `short` and `longs`, or
1176/// the positional when it has neither.
1177fn occurrence_item(
1178    variant: &syn::Variant,
1179    short: Option<char>,
1180    longs: &[String],
1181    value_name: Option<&str>,
1182    value: Option<&Type>,
1183    require_equals: bool,
1184    optional_value: bool,
1185) -> TokenStream2 {
1186    let (doc, long_doc) = docs(&variant.attrs);
1187    let (help, long_help) = (text(&doc), text(&long_doc));
1188    // What the value's type says of it, when the variant holds one.
1189    let (choices, hint) = match value {
1190        Some(ty) => (
1191            quote!(<#ty as ::winnow_args::FromArg>::CHOICES),
1192            quote!(<#ty as ::winnow_args::FromArg>::HINT),
1193        ),
1194        None => (quote!(&[]), quote!(::winnow_args::complete::Hint::Nothing)),
1195    };
1196    let positional = short.is_none() && longs.is_empty();
1197    let short = opt_char(short);
1198    let long = opt_str(longs.first().map(String::as_str));
1199    let aliases = longs.iter().skip(1);
1200    let value_name = opt_str(value_name);
1201    quote!(::winnow_args::help::Item {
1202        short: #short,
1203        long: #long,
1204        aliases: &[#(#aliases),*],
1205        negate: ::core::option::Option::None,
1206        value_name: #value_name,
1207        help: #help,
1208        long_help: #long_help,
1209        heading: ::core::option::Option::None,
1210        hide: false,
1211        positional: #positional,
1212        required: false,
1213        multiple: #positional,
1214        trailing: false,
1215        default: ::core::option::Option::None,
1216        env: ::core::option::Option::None,
1217        choices: #choices,
1218        require_equals: #require_equals,
1219        global: false,
1220        more_shorts: &[],
1221        plus: ::core::option::Option::None,
1222        optional_value: #optional_value,
1223        values: 1,
1224        more_values: false,
1225        hint: #hint,
1226        complete: ::core::option::Option::None,
1227    })
1228}
1229
1230/// `spec = "Name"` among the options of a type, a field or a variant: the
1231/// name the documentation files the type under, or finds it under.
1232fn spec_name(attrs: &[syn::Attribute]) -> syn::Result<Option<String>> {
1233    let mut name = None;
1234    for attr in attrs.iter().filter(|a| is_ours(a)) {
1235        // The parser of each derive has already rejected what it does not know.
1236        let _ = attr.parse_nested_meta(|meta| {
1237            if meta.path.is_ident("spec") {
1238                name = Some(meta.value()?.parse::<LitStr>()?);
1239            } else if meta.input.peek(syn::Token![=]) {
1240                meta.value()?.parse::<syn::Expr>()?;
1241            } else if meta.input.peek(syn::token::Paren) {
1242                let _content;
1243                syn::parenthesized!(_content in meta.input);
1244                _content.parse::<TokenStream2>()?;
1245            }
1246            Ok(())
1247        });
1248    }
1249    let Some(name) = name else {
1250        return Ok(None);
1251    };
1252    let text = name.value();
1253    if text.is_empty() || text.contains(['/', '\\', ':', '.']) {
1254        return Err(syn::Error::new(
1255            name.span(),
1256            "`spec` is a name as a type's is: a file is named after it",
1257        ));
1258    }
1259    Ok(Some(text))
1260}
1261
1262/// Reads past `spec = "Name"`, which [`spec_name`] reads.
1263fn skip_spec(meta: &syn::meta::ParseNestedMeta<'_>) -> syn::Result<bool> {
1264    if !meta.path.is_ident("spec") {
1265        return Ok(false);
1266    }
1267    meta.value()?.parse::<LitStr>()?;
1268    Ok(true)
1269}
1270
1271/// The name a derived type is filed under: its `spec`, or its own.
1272fn filed(input: &DeriveInput) -> syn::Result<String> {
1273    Ok(spec_name(&input.attrs)?.unwrap_or_else(|| input.ident.unraw().to_string()))
1274}
1275
1276/// A type as it is written, `add::Opts`: its modules tell two types of one
1277/// name apart. `crate`, `self` and `super` are no module's name. `self` and
1278/// `super` are written as the modules they are, from `crate`; where those
1279/// are not known, as `crate` alone, which says the type is this crate's.
1280///
1281/// A bare name is the path its file's `use` brings it in by, when one does.
1282/// One the file neither declares nor brings in by name is `?::Opts`: it may
1283/// be any type of that name, or none the documentation knows.
1284fn type_path(ty: &Type) -> Option<String> {
1285    let Type::Path(path) = boxed(ty).unwrap_or(ty) else {
1286        return None;
1287    };
1288    if path.qself.is_some() {
1289        return None;
1290    }
1291    let mut names: Vec<String> = path
1292        .path
1293        .segments
1294        .iter()
1295        .map(|segment| segment.ident.unraw().to_string())
1296        .collect();
1297    if let [name] = &names[..] {
1298        match spec::origin(ty.span(), name) {
1299            spec::Origin::Here => {}
1300            spec::Origin::Imported(from) => names = from,
1301            spec::Origin::Unknown => names.insert(0, "?".to_owned()),
1302        }
1303    }
1304    let rooted = names
1305        .iter()
1306        .take_while(|name| matches!(name.as_str(), "crate" | "self" | "super"))
1307        .count();
1308    if rooted > 0 {
1309        let supers = names[..rooted]
1310            .iter()
1311            .filter(|name| *name == "super")
1312            .count();
1313        let relative = names[0] != "crate";
1314        let modules = relative.then(|| spec::above(supers)).flatten();
1315        let root = ["crate".to_owned()].into_iter();
1316        names.splice(..rooted, root.chain(modules.unwrap_or_default()));
1317    }
1318    (names.len() > usize::from(rooted > 0)).then(|| names.join("::"))
1319}
1320
1321/// The name `ty` is looked for under: the one stated where it is referred
1322/// to, or its own.
1323fn spec_type(stated: Option<String>, ty: &Type) -> syn::Result<String> {
1324    stated.or_else(|| type_path(ty)).ok_or_else(|| {
1325        syn::Error::new(
1326            ty.span(),
1327            "this type needs a name when `WINNOW_ARGS_SPEC` is set: state it, as `spec = \"Name\"`",
1328        )
1329    })
1330}
1331
1332/// A documentation build fails where a type is looked for under a name it is
1333/// not filed under, so that a page never describes another type of that name.
1334/// A generic type is not checked: a constant cannot name its parameters.
1335fn spec_filed(
1336    generics: &syn::Generics,
1337    kind: TokenStream2,
1338    ty: &Type,
1339    name: &str,
1340) -> Option<TokenStream2> {
1341    if !generics.params.is_empty() {
1342        return None;
1343    }
1344    let ty = boxed(ty).unwrap_or(ty);
1345    let name = name.strip_prefix("?::").unwrap_or(name);
1346    Some(quote::quote_spanned! {ty.span()=>
1347        const _: () = ::core::assert!(
1348            ::winnow_args::__private::filed_as(<#ty as ::winnow_args::#kind>::SPEC, #name),
1349            "the documentation does not find this type by this path: it is named through an alias or a re-export, its module goes by another name here, or it states a `spec`. State the name it is filed under, as `spec = \"Name\"`",
1350        );
1351    })
1352}
1353
1354/// The type of a field's value.
1355fn value_type(field: &Field) -> Option<&Type> {
1356    if !field.takes_value() || field.keywords || matches!(field.role, Role::Subcommand) {
1357        return None;
1358    }
1359    match &field.kind {
1360        Kind::Optional(ty) | Kind::Required(ty) | Kind::Many(ty) => Some(ty),
1361        Kind::Switch | Kind::Count(_) => None,
1362    }
1363}
1364
1365/// The type of a field's value, where its choices are looked for: none when
1366/// the field states them.
1367fn choices_type(field: &Field) -> Option<&Type> {
1368    value_type(field).filter(|_| field.choices.is_none())
1369}
1370
1371/// A documentation build fails where the choices a field states are not
1372/// those of its type, so that a list written twice cannot drift.
1373fn spec_check_stated(generics: &syn::Generics, field: &Field) -> Option<TokenStream2> {
1374    let stated = field.choices.as_ref()?;
1375    let ty = value_type(field).filter(|_| generics.params.is_empty())?;
1376    Some(quote::quote_spanned! {ty.span()=>
1377        const _: () = ::core::assert!(
1378            ::winnow_args::__private::stated_choices::<#ty>(&[#(#stated),*]),
1379            "the choices stated on this field are not those of its type, in number, spelling or order",
1380        );
1381    })
1382}
1383
1384/// The name the value type's choices are looked for under: the field's
1385/// `spec`, or the type's own. Only a `ValueEnum` has a fragment under it.
1386fn choices_link(field: &Field) -> Option<String> {
1387    choices_name(field.spec.as_ref(), choices_type(field)?)
1388}
1389
1390fn choices_name(stated: Option<&String>, ty: &Type) -> Option<String> {
1391    stated.cloned().or_else(|| type_path(ty))
1392}
1393
1394/// A documentation build fails where a value has choices its fragment cannot
1395/// name, so that a page never leaves them out. A generic type is not checked:
1396/// a constant cannot name its parameters.
1397fn spec_check(
1398    generics: &syn::Generics,
1399    ty: Option<&Type>,
1400    stated: Option<&String>,
1401) -> Option<TokenStream2> {
1402    let ty = ty.filter(|_| generics.params.is_empty())?;
1403    let name = choices_name(stated, ty)?;
1404    let name = name.strip_prefix("?::").unwrap_or(&name);
1405    Some(quote::quote_spanned! {ty.span()=>
1406        const _: () = ::core::assert!(
1407            ::winnow_args::__private::documented_choices::<#ty>(#name),
1408            "the documentation cannot find the choices of this value: its type is named through an alias or a re-export, states a `spec`, or implements `FromArg` by hand. State the name they are filed under, as `spec = \"Name\"`, or the choices, as `choices(\"a\", \"b\")`",
1409        );
1410    })
1411}
1412
1413/// A bare `version` becomes `env!("CARGO_PKG_VERSION")` in help. Cargo has
1414/// already set that variable for the crate being compiled.
1415fn package_version(version: &Option<TokenStream2>, enabled: bool) -> Option<String> {
1416    if !enabled {
1417        return None;
1418    }
1419    let tokens = version.as_ref()?;
1420    if let Ok(lit) = syn::parse2::<LitStr>(tokens.clone()) {
1421        let text = lit.value();
1422        return (!text.is_empty()).then_some(text);
1423    }
1424    std::env::var("CARGO_PKG_VERSION")
1425        .ok()
1426        .filter(|text| !text.is_empty())
1427}
1428
1429fn spec_item(field: &Field) -> syn::Result<spec::ItemDoc> {
1430    let longs = field.longs();
1431    let keywords = if field.keywords {
1432        match &field.kind {
1433            Kind::Required(ty) => Some(spec_type(field.spec.clone(), ty)?),
1434            _ => {
1435                return Err(syn::Error::new(
1436                    field.ident.span(),
1437                    "`keywords` needs a named type",
1438                ));
1439            }
1440        }
1441    } else {
1442        None
1443    };
1444    let required = (matches!(field.kind, Kind::Required(_))
1445        && field.default.is_none()
1446        && field.default_fn.is_none()
1447        && field.env.is_none()
1448        && !field.keywords)
1449        || field.required;
1450    Ok(spec::ItemDoc {
1451        short: field.short(),
1452        more_shorts: field.short_aliases.clone(),
1453        plus: field.plus,
1454        long: longs.first().map(|name| (*name).to_owned()),
1455        aliases: longs
1456            .iter()
1457            .skip(1)
1458            .map(|name| (*name).to_owned())
1459            .collect(),
1460        negate: field.negate.clone(),
1461        value_name: if field.is_positional() {
1462            Some(field.display())
1463        } else {
1464            field.value_name.clone()
1465        },
1466        help: field.help.clone(),
1467        long_help: field.long_help.clone(),
1468        heading: field.heading.clone(),
1469        hide: field.hide,
1470        positional: field.is_positional(),
1471        required,
1472        multiple: matches!(field.kind, Kind::Many(_) | Kind::Count(_)),
1473        trailing: field.double_dash() == DoubleDash::Required,
1474        default: field.default.clone().or_else(|| field.default_note.clone()),
1475        env: field.env.clone(),
1476        choices: field.choices.clone().unwrap_or_default(),
1477        choices_ty: choices_link(field),
1478        require_equals: field.require_equals,
1479        global: field.is_global(),
1480        optional_value: field.default_missing.is_some()
1481            || field.require_equals && matches!(field.kind, Kind::Optional(_)),
1482        values: field.values,
1483        more_values: field.values_max.is_some(),
1484        two_dashes: field.two_dashes,
1485        prefix: field.prefix,
1486        stop_flags: field.stop_flags,
1487        keywords,
1488    })
1489}
1490
1491fn occurrence_spec(variant: &syn::Variant, mut item: spec::ItemDoc) -> spec::ItemDoc {
1492    let (help, long_help) = docs(&variant.attrs);
1493    item.help = help;
1494    item.long_help = long_help;
1495    item.positional = item.short.is_none() && item.long.is_none();
1496    item.multiple = item.positional;
1497    item
1498}
1499
1500/// `FromArg` as one `match` on the value's bytes.
1501fn expand_value_enum(input: &DeriveInput) -> syn::Result<TokenStream2> {
1502    let Data::Enum(data) = &input.data else {
1503        return Err(syn::Error::new(
1504            input.span(),
1505            "`ValueEnum` can only be derived for enums",
1506        ));
1507    };
1508    let case = rename_all(input, None)?;
1509    let mut names: Vec<(String, &Ident)> = Vec::new();
1510    let mut arms = Vec::new();
1511    let mut visible = Vec::new();
1512    let spec_on = spec::enabled();
1513    let filed = filed(input)?;
1514    let identity = spec::identity(input.ident.span(), &filed);
1515    let mut spec_choices = Vec::new();
1516    for variant in &data.variants {
1517        if !matches!(variant.fields, Fields::Unit) {
1518            return Err(syn::Error::new(
1519                variant.span(),
1520                "a `ValueEnum` variant holds no data",
1521            ));
1522        }
1523        let Variant {
1524            names: spellings,
1525            hide,
1526            ..
1527        } = variant_names(variant, &mut names, case)?;
1528        let name = LitStr::new(&spellings[0], Span::call_site());
1529        if !hide {
1530            if spec_on {
1531                spec_choices.push(spellings[0].clone());
1532            }
1533            visible.push(name);
1534        }
1535        let pattern = byte_patterns(&spellings);
1536        let ident = &variant.ident;
1537        arms.push(quote!(#pattern => ::core::result::Result::Ok(Self::#ident),));
1538    }
1539    let name = &input.ident;
1540    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
1541    if spec_on {
1542        spec::write_choices(input.ident.span(), &filed, &spec_choices)?;
1543    }
1544    Ok(quote! {
1545        impl #impl_generics ::winnow_args::FromArg for #name #ty_generics #where_clause {
1546            const CHOICES: &'static [&'static str] = &[#(#visible),*];
1547            const SPEC: &'static str = #identity;
1548
1549            fn from_arg(
1550                __value: &::winnow_args::__private::BStr,
1551            ) -> ::core::result::Result<Self, ::winnow_args::__private::BoxError> {
1552                match &**__value {
1553                    #(#arms)*
1554                    _ => ::core::result::Result::Err(::std::boxed::Box::new(
1555                        ::winnow_args::ChoiceError { choices: Self::CHOICES },
1556                    )),
1557                }
1558            }
1559        }
1560    })
1561}
1562
1563/// Help prose, left out of binaries built without winnow-args' `help-text`.
1564fn text(prose: &str) -> TokenStream2 {
1565    if prose.is_empty() {
1566        quote!("")
1567    } else {
1568        quote!(::winnow_args::__text!(#prose))
1569    }
1570}
1571
1572/// A variant's spellings and help.
1573struct Variant {
1574    /// `name` (or kebab-case) first, then shown aliases, then hidden ones.
1575    names: Vec<String>,
1576    /// How many of `names` after the first are shown in help.
1577    shown: usize,
1578    hide: bool,
1579    about: String,
1580}
1581
1582/// How a variant's name is spelled when it gives none: an enum's
1583/// `#[arg(rename_all = "…")]`.
1584#[derive(Clone, Copy)]
1585enum Case {
1586    /// `DryRun` → `dry-run` (the default).
1587    Kebab,
1588    /// `DryRun` → `dryrun`.
1589    Lower,
1590    /// `DryRun` → `DRYRUN`.
1591    Upper,
1592    /// `DryRun` → `dry_run`.
1593    Snake,
1594    /// `DryRun` → `DryRun`.
1595    Verbatim,
1596}
1597
1598impl Case {
1599    fn apply(self, ident: &str) -> String {
1600        let ident = ident.trim_start_matches("r#");
1601        match self {
1602            Case::Kebab => kebab_case(ident),
1603            Case::Lower => ident.to_lowercase(),
1604            Case::Upper => ident.to_uppercase(),
1605            Case::Snake => kebab_case(ident).replace('-', "_"),
1606            Case::Verbatim => ident.to_owned(),
1607        }
1608    }
1609}
1610
1611/// How a `Subcommand` enum runs the command it holds: `#[arg(run)]` or
1612/// `#[arg(run_async)]`.
1613#[derive(Clone, Copy, PartialEq)]
1614enum Runs {
1615    Sync,
1616    Async,
1617}
1618
1619/// An enum's `#[arg(rename_all = "…")]`, kebab-case if absent, and for a
1620/// `Subcommand` its `run` or `run_async`.
1621fn rename_all(input: &DeriveInput, runs: Option<&mut Option<Runs>>) -> syn::Result<Case> {
1622    let mut case = Case::Kebab;
1623    let mut found = None;
1624    let may_run = runs.is_some();
1625    for attr in input.attrs.iter().filter(|a| is_ours(a)) {
1626        attr.parse_nested_meta(|meta| {
1627            if skip_spec(&meta)? {
1628                return Ok(());
1629            }
1630            let run = match () {
1631                () if meta.path.is_ident("run") => Some(Runs::Sync),
1632                () if meta.path.is_ident("run_async") => Some(Runs::Async),
1633                () => None,
1634            };
1635            if let (Some(run), true) = (run, may_run) {
1636                if found.replace(run).is_some() {
1637                    return Err(meta.error("`run` or `run_async`, once"));
1638                }
1639                return Ok(());
1640            }
1641            if !meta.path.is_ident("rename_all") {
1642                return Err(meta.error(match may_run {
1643                    true => "expected `rename_all`, `run`, `run_async` or `spec`",
1644                    false => "expected `rename_all` or `spec`",
1645                }));
1646            }
1647            let lit = meta.value()?.parse::<LitStr>()?;
1648            case = match lit.value().as_str() {
1649                "kebab-case" => Case::Kebab,
1650                "lowercase" => Case::Lower,
1651                "UPPERCASE" => Case::Upper,
1652                "snake_case" => Case::Snake,
1653                "verbatim" => Case::Verbatim,
1654                _ => {
1655                    return Err(syn::Error::new(
1656                        lit.span(),
1657                        "expected \"kebab-case\", \"lowercase\", \"UPPERCASE\", \"snake_case\" or \"verbatim\"",
1658                    ));
1659                }
1660            };
1661            Ok(())
1662        })?;
1663    }
1664    if let Some(runs) = runs {
1665        *runs = found;
1666    }
1667    Ok(case)
1668}
1669
1670/// A variant's spellings, checked against `seen` for duplicates, and its help.
1671fn variant_names<'a>(
1672    variant: &'a syn::Variant,
1673    seen: &mut Vec<(String, &'a Ident)>,
1674    case: Case,
1675) -> syn::Result<Variant> {
1676    let holds_type = !matches!(variant.fields, Fields::Unit);
1677    let (mut name, mut alias, mut hidden, mut hide) = (None, Vec::new(), Vec::new(), false);
1678    let mut help: Option<String> = None;
1679    for attr in variant.attrs.iter().filter(|a| is_ours(a)) {
1680        attr.parse_nested_meta(|meta| {
1681            if meta.path.is_ident("name") {
1682                name = Some(meta.value()?.parse::<LitStr>()?.value());
1683            } else if meta.path.is_ident("alias") {
1684                alias.extend(aliases(&meta)?);
1685            } else if meta.path.is_ident("alias_hidden") {
1686                hidden.extend(aliases(&meta)?);
1687            } else if meta.path.is_ident("hide") {
1688                hide = true;
1689            } else if meta.path.is_ident("help") {
1690                help = Some(meta.value()?.parse::<LitStr>()?.value());
1691            } else if meta.path.is_ident("long_help") {
1692                // The listing shows one line; the subcommand's own help has the rest.
1693                meta.value()?.parse::<LitStr>()?;
1694            } else if skip_spec(&meta)? {
1695                if !holds_type {
1696                    return Err(meta
1697                        .error("`spec` names the type a variant holds, and this one holds none"));
1698                }
1699            } else {
1700                return Err(meta.error(
1701                    "expected `name`, `alias`, `alias_hidden`, `hide`, `help` or `long_help`",
1702                ));
1703            }
1704            Ok(())
1705        })?;
1706    }
1707    let ident = &variant.ident;
1708    let shown = alias.len();
1709    let names: Vec<String> =
1710        std::iter::once(name.unwrap_or_else(|| case.apply(&ident.to_string())))
1711            .chain(alias)
1712            .chain(hidden)
1713            .collect();
1714    for spelling in &names {
1715        if let Some((_, other)) = seen.iter().find(|(n, _)| n == spelling) {
1716            return Err(syn::Error::new(
1717                ident.span(),
1718                format!("`{spelling}` is already used by `{other}`"),
1719            ));
1720        }
1721        seen.push((spelling.clone(), ident));
1722    }
1723    Ok(Variant {
1724        names,
1725        shown,
1726        hide,
1727        about: help
1728            .map(|h| {
1729                h.split("\n\n")
1730                    .next()
1731                    .unwrap_or_default()
1732                    .replace('\n', " ")
1733            })
1734            .unwrap_or_else(|| docs(&variant.attrs).0),
1735    })
1736}
1737
1738/// A doc comment as help: its first paragraph, and the whole of it.
1739fn docs(attrs: &[syn::Attribute]) -> (String, String) {
1740    let lines: Vec<String> = attrs
1741        .iter()
1742        .filter(|a| a.path().is_ident("doc"))
1743        .filter_map(|a| match &a.meta {
1744            syn::Meta::NameValue(nv) => match &nv.value {
1745                syn::Expr::Lit(syn::ExprLit {
1746                    lit: syn::Lit::Str(s),
1747                    ..
1748                }) => Some(s.value()),
1749                _ => None,
1750            },
1751            _ => None,
1752        })
1753        .map(|line| {
1754            line.strip_prefix(' ')
1755                .unwrap_or(&line)
1756                .trim_end()
1757                .to_owned()
1758        })
1759        .collect();
1760    let long = lines.join("\n").trim().to_owned();
1761    let short = long
1762        .split("\n\n")
1763        .next()
1764        .unwrap_or_default()
1765        .replace('\n', " ");
1766    (short, long)
1767}
1768
1769/// `Some("s")` or `None`, spelled out.
1770fn opt_str(s: Option<&str>) -> TokenStream2 {
1771    match s {
1772        Some(s) => quote!(::core::option::Option::Some(#s)),
1773        None => quote!(::core::option::Option::None),
1774    }
1775}
1776
1777/// `Some('c')` or `None`, spelled out.
1778fn opt_char(c: Option<char>) -> TokenStream2 {
1779    match c {
1780        Some(c) => quote!(::core::option::Option::Some(#c)),
1781        None => quote!(::core::option::Option::None),
1782    }
1783}
1784
1785/// `b"a" | b"b"`.
1786fn byte_patterns<S: AsRef<str>>(names: &[S]) -> TokenStream2 {
1787    let literals = names
1788        .iter()
1789        .map(|n| LitByteStr::new(n.as_ref().as_bytes(), Span::call_site()));
1790    quote!(#(#literals)|*)
1791}
1792
1793/// Whether `__name` is one of `names`: `matches!(__name, b"a" | b"b")`.
1794fn matches_name(names: &[String]) -> TokenStream2 {
1795    if names.is_empty() {
1796        quote!(false)
1797    } else {
1798        let names = byte_patterns(names);
1799        quote!(matches!(__name, #names))
1800    }
1801}
1802
1803/// `alias = "x"` or `alias("x", "y")`.
1804fn aliases(meta: &syn::meta::ParseNestedMeta<'_>) -> syn::Result<Vec<String>> {
1805    if meta.input.peek(syn::Token![=]) {
1806        return Ok(vec![meta.value()?.parse::<LitStr>()?.value()]);
1807    }
1808    let content;
1809    syn::parenthesized!(content in meta.input);
1810    let names = syn::punctuated::Punctuated::<LitStr, syn::Token![,]>::parse_terminated(&content)?;
1811    Ok(names.iter().map(LitStr::value).collect())
1812}
1813
1814/// `DryRun` → `dry-run`.
1815fn kebab_case(ident: &str) -> String {
1816    let mut out = String::new();
1817    for (i, c) in ident.trim_start_matches("r#").chars().enumerate() {
1818        if c.is_uppercase() {
1819            if i > 0 {
1820                out.push('-');
1821            }
1822            out.extend(c.to_lowercase());
1823        } else {
1824            out.push(c);
1825        }
1826    }
1827    out
1828}
1829
1830/// An attribute for these derives: `#[arg(…)]`, or `#[winnow_args(…)]` where
1831/// another derive claims `arg`.
1832fn is_ours(attr: &syn::Attribute) -> bool {
1833    attr.path().is_ident("arg") || attr.path().is_ident("winnow_args")
1834}
1835
1836/// Whether `ty` is `bool`.
1837fn is_bool(ty: &Type) -> bool {
1838    last_segment(ty).is_some_and(|s| s.ident == "bool" && s.arguments.is_none())
1839}
1840
1841/// Whether a field is `#[arg(sequence)]`: occurrences of an `Occurrence` enum.
1842fn is_sequence(f: &syn::Field) -> bool {
1843    has_flag(f, "sequence")
1844}
1845
1846/// Whether a field is `#[arg(flatten)]`: another struct's flags, parsed as ours.
1847fn is_flatten(f: &syn::Field) -> bool {
1848    has_flag(f, "flatten")
1849}
1850
1851/// Whether a field is `#[arg(unknown)]`: the flags no one declared, whole.
1852fn is_unknown(f: &syn::Field) -> bool {
1853    has_flag(f, "unknown") && !is_sequence(f)
1854}
1855
1856/// Whether a field is `#[arg(skip)]`: left at its default, not parsed.
1857fn is_skipped(f: &syn::Field) -> bool {
1858    has_flag(f, "skip")
1859}
1860
1861/// Whether one of the field's attributes is the bare word `name`.
1862fn has_flag(f: &syn::Field, name: &str) -> bool {
1863    f.attrs.iter().filter(|a| is_ours(a)).any(|a| {
1864        let mut found = false;
1865        let _ = a.parse_nested_meta(|meta| {
1866            if meta.path.is_ident(name) {
1867                found = true;
1868            } else if meta.input.peek(syn::Token![=]) {
1869                meta.value()?.parse::<syn::Expr>()?;
1870            } else if meta.input.peek(syn::token::Paren) {
1871                let _content;
1872                syn::parenthesized!(_content in meta.input);
1873            }
1874            Ok(())
1875        });
1876        found
1877    })
1878}
1879
1880/// A `skip`, `flatten`, `sequence` or `unknown` field takes no other option
1881/// (`sequence, unknown` aside): none of them would be read.
1882fn role_alone(f: &syn::Field) -> syn::Result<()> {
1883    let role = ["skip", "flatten", "sequence", "unknown"]
1884        .into_iter()
1885        .find(|role| has_flag(f, role));
1886    let Some(role) = role else {
1887        return Ok(());
1888    };
1889    for attr in f.attrs.iter().filter(|a| is_ours(a)) {
1890        attr.parse_nested_meta(|meta| {
1891            let alone =
1892                meta.path.is_ident(role) || (role == "sequence" && meta.path.is_ident("unknown"));
1893            // The name the documentation finds a flattened struct or a
1894            // sequence enum under.
1895            let named = matches!(role, "flatten" | "sequence") && skip_spec(&meta)?;
1896            if alone || named {
1897                Ok(())
1898            } else {
1899                Err(meta.error(format!("a `{role}` field takes no other `arg` options")))
1900            }
1901        })?;
1902    }
1903    Ok(())
1904}
1905
1906/// `T` in `Box<T>`.
1907fn boxed(ty: &Type) -> Option<&Type> {
1908    let last = last_segment(ty)?;
1909    if last.ident == "Box" {
1910        inner(last)
1911    } else {
1912        None
1913    }
1914}
1915
1916enum Kind {
1917    /// `bool`: present or not.
1918    Switch,
1919    /// `#[arg(count)]` on an integer: how many times it was given.
1920    Count(Type),
1921    /// `Option<T>`: one value, may be absent.
1922    Optional(Type),
1923    /// `T`: one value, required.
1924    Required(Type),
1925    /// `Vec<T>`: a repeatable flag's values, or every remaining word.
1926    Many(Type),
1927}
1928
1929enum Role {
1930    Flag {
1931        short: Option<char>,
1932        long: Option<String>,
1933        /// More long names, matched like `long`.
1934        aliases: Vec<String>,
1935        /// Also accepted after a subcommand word, at any depth.
1936        global: bool,
1937    },
1938    Positional {
1939        name: String,
1940        double_dash: DoubleDash,
1941    },
1942    Subcommand,
1943}
1944
1945/// A positional's relation to `--`.
1946#[derive(Clone, Copy, PartialEq, Eq)]
1947enum DoubleDash {
1948    /// Words fill it on either side of `--`.
1949    Optional,
1950    /// Only words after `--` fill it, and they all do.
1951    Required,
1952    /// Once it has a value, flags stop as if `--` had been typed.
1953    Automatic,
1954    /// A `--` reaching it is one of its values, and stops nothing.
1955    Preserve,
1956}
1957
1958struct Field {
1959    ident: Ident,
1960    /// The name the documentation finds the field's type under, when it is
1961    /// not the type's own.
1962    spec: Option<String>,
1963    kind: Kind,
1964    role: Role,
1965    /// Splits each value of a `Vec` field.
1966    delimiter: Option<u8>,
1967    /// The only values accepted, checked before conversion.
1968    choices: Option<Vec<String>>,
1969    /// Environment variable consulted when the command line gave nothing.
1970    env: Option<String>,
1971    /// Value used when neither the command line nor the environment did.
1972    default: Option<String>,
1973    /// Selectors that may not be supplied together with this field.
1974    conflicts: Vec<String>,
1975    /// Selectors this field replaces, and that replace it: the last one given wins.
1976    overrides: Vec<String>,
1977    /// Selectors that must have a value when this field is supplied.
1978    requires: Vec<String>,
1979    /// Must end up with a value (for `Option` and `Vec` fields).
1980    required: bool,
1981    /// Required unless one of these selectors has a value.
1982    required_unless: Vec<String>,
1983    /// The struct-level group this field belongs to.
1984    group: Option<String>,
1985    /// The value of a flag given without one.
1986    default_missing: Option<String>,
1987    /// Help: first paragraph, whole text, section, hidden.
1988    help: String,
1989    long_help: String,
1990    heading: Option<String>,
1991    hide: bool,
1992    /// A flag's value placeholder.
1993    value_name: Option<String>,
1994    /// What the value is, for a shell that completes it: a variant of `Hint`.
1995    /// Unstated, it is what the value's type says.
1996    hint: Option<syn::Ident>,
1997    /// The function a shell's completion of the value asks.
1998    complete: Option<syn::Path>,
1999    /// A negative number is a value: a flag's detached value, or a positional's word.
2000    negative_numbers: bool,
2001    /// A flag's detached value may be any word, flag-like or `--`.
2002    hyphen_values: bool,
2003    /// A flag's value must be attached.
2004    require_equals: bool,
2005    /// A short option's attached value keeps a leading `=` (GNU ld's `-L=dir`).
2006    keep_equals: bool,
2007    /// The long spelling that sets a `bool` false; the slot is then an
2008    /// `Option<bool>` until the end, so a default fills only what was not given.
2009    negate: Option<String>,
2010    /// Never spelled with one dash under `long_only`: `--omagic` (ld).
2011    two_dashes: bool,
2012    /// Words each occurrence takes (a `Vec` flag): `-platform_version macos 11.0 12.0`.
2013    values: usize,
2014    /// `values = a..` or `a..=b`: up to this many words in all (`usize::MAX`:
2015    /// no limit), the ones after the first only while they are not flag-like.
2016    values_max: Option<usize>,
2017    /// The word that ends a variable run of values, itself dropped.
2018    value_terminator: Option<String>,
2019    /// A function giving the value when nothing else did.
2020    default_fn: Option<syn::ExprPath>,
2021    /// What help says of `default_fn`'s value.
2022    default_note: Option<String>,
2023    /// `(selector, the value it must have, this field's default then)`.
2024    default_ifs: Vec<(String, Option<String>, String)>,
2025    /// Once this positional has a value, flags stop (whatever its `double_dash`).
2026    stop_flags: bool,
2027    /// The letter of its `+c` spelling, in a `plus_options` struct.
2028    plus: Option<char>,
2029    /// An `Option<bool>` with `short` and the same `plus` letter: `-c` is
2030    /// `Some(true)`, `+c` is `Some(false)`.
2031    tristate: bool,
2032    /// Its values are words of a vocabulary of their own, the field's type
2033    /// (`Args`): `-z now -z max-page-size=4096` (ld).
2034    keywords: bool,
2035    /// More letters it answers to, from a second `short` (`kill -l`/`-L`).
2036    short_aliases: Vec<char>,
2037    /// A short letter that always takes the rest of its word (`-lfoo`), so
2038    /// under `long_only` no long name starting with it is tried with one dash.
2039    prefix: bool,
2040}
2041
2042impl Field {
2043    /// This field's entry in `help::Command::items`.
2044    fn help_item(&self) -> TokenStream2 {
2045        let short = opt_char(self.short());
2046        let long = opt_str(self.longs().first().copied());
2047        let aliases = self.longs().into_iter().skip(1);
2048        let negate = opt_str(self.negate.as_deref());
2049        let require_equals = self.require_equals;
2050        let global = self.is_global();
2051        let positional = self.is_positional();
2052        let trailing = self.double_dash() == DoubleDash::Required;
2053        let display = self.display();
2054        let value_name = if positional {
2055            opt_str(Some(&display))
2056        } else {
2057            opt_str(self.value_name.as_deref())
2058        };
2059        let (help, long_help) = (text(&self.help), text(&self.long_help));
2060        let heading = opt_str(self.heading.as_deref());
2061        let hide = self.hide;
2062        let more_shorts = self.short_aliases.iter();
2063        let plus = opt_char(self.plus);
2064        let optional_value = self.default_missing.is_some()
2065            || require_equals && matches!(self.kind, Kind::Optional(_));
2066        let values = self.values;
2067        let more_values = self.values_max.is_some();
2068        let hint = match (&self.hint, &self.kind, &self.role) {
2069            (Some(hint), _, _) => quote!(::winnow_args::complete::Hint::#hint),
2070            (None, Kind::Optional(ty) | Kind::Required(ty) | Kind::Many(ty), role)
2071                if !matches!(role, Role::Subcommand) && !self.keywords && !self.tristate =>
2072            {
2073                quote!(<#ty as ::winnow_args::FromArg>::HINT)
2074            }
2075            (None, _, _) => quote!(::winnow_args::complete::Hint::Nothing),
2076        };
2077        let complete = match &self.complete {
2078            Some(function) => quote!(::core::option::Option::Some(#function)),
2079            None => quote!(::core::option::Option::None),
2080        };
2081        let required = matches!(self.kind, Kind::Required(_))
2082            && self.default.is_none()
2083            && self.default_fn.is_none()
2084            && self.env.is_none()
2085            && !self.keywords
2086            || self.required;
2087        let multiple = matches!(self.kind, Kind::Many(_) | Kind::Count(_));
2088        let default = opt_str(self.default.as_deref().or(self.default_note.as_deref()));
2089        let env = opt_str(self.env.as_deref());
2090        // Declared `choices`, else whatever fixed set the value type has.
2091        let choices = match (&self.choices, &self.kind, &self.role) {
2092            (Some(choices), _, _) => quote!(&[#(#choices),*]),
2093            (None, _, Role::Subcommand) | (None, Kind::Switch | Kind::Count(_), _) => quote!(&[]),
2094            (None, _, _) if self.keywords || self.tristate => quote!(&[]),
2095            (None, Kind::Optional(ty) | Kind::Required(ty) | Kind::Many(ty), _) => {
2096                quote!(<#ty as ::winnow_args::FromArg>::CHOICES)
2097            }
2098        };
2099        quote! {
2100            ::winnow_args::help::Item {
2101                short: #short,
2102                long: #long,
2103                aliases: &[#(#aliases),*],
2104                negate: #negate,
2105                value_name: #value_name,
2106                help: #help,
2107                long_help: #long_help,
2108                heading: #heading,
2109                hide: #hide,
2110                positional: #positional,
2111                required: #required,
2112                multiple: #multiple,
2113                trailing: #trailing,
2114                default: #default,
2115                env: #env,
2116                choices: #choices,
2117                require_equals: #require_equals,
2118                global: #global,
2119                more_shorts: &[#(#more_shorts),*],
2120                plus: #plus,
2121                optional_value: #optional_value,
2122                values: #values,
2123                more_values: #more_values,
2124                hint: #hint,
2125                complete: #complete,
2126            }
2127        }
2128    }
2129
2130    /// How this flag takes a detached value.
2131    fn value_options(&self) -> TokenStream2 {
2132        let (negative_numbers, hyphen_values, require_equals, keep_equals) = (
2133            self.negative_numbers,
2134            self.hyphen_values,
2135            self.require_equals,
2136            self.keep_equals,
2137        );
2138        quote! {
2139            __wa::ValueOptions {
2140                negative_numbers: #negative_numbers,
2141                hyphen_values: #hyphen_values,
2142                require_equals: #require_equals,
2143                keep_equals: #keep_equals,
2144            }
2145        }
2146    }
2147
2148    /// Reading this flag's value: `read_value`, or `read_value_or` its `default_missing`.
2149    fn read(&self) -> TokenStream2 {
2150        let (negative_numbers, hyphen_values, require_equals, keep_equals) = (
2151            self.negative_numbers,
2152            self.hyphen_values,
2153            self.require_equals,
2154            self.keep_equals,
2155        );
2156        let options = self.value_options();
2157        match &self.default_missing {
2158            None if !negative_numbers && !hyphen_values && !require_equals && !keep_equals => {
2159                quote!(__arg.read_value(__input)?)
2160            }
2161            None => quote!(__arg.read_value_with(__input, #options)?),
2162            Some(missing) => {
2163                let bytes = LitByteStr::new(missing.as_bytes(), Span::call_site());
2164                quote!(__arg.read_value_or_with(__input, #options, __wa::BStr::new(#bytes)))
2165            }
2166        }
2167    }
2168
2169    /// Reject `value` (a `&BStr`) outside `choices`, reported by `error(cause)`.
2170    fn check(
2171        &self,
2172        value: TokenStream2,
2173        error: impl FnOnce(TokenStream2) -> TokenStream2,
2174    ) -> TokenStream2 {
2175        let Some(choices) = &self.choices else {
2176            return quote!();
2177        };
2178        let pattern = byte_patterns(choices);
2179        let listed = choices.iter().map(|c| LitStr::new(c, Span::call_site()));
2180        let cause = quote!(::winnow_args::ChoiceError { choices: &[#(#listed),*] });
2181        let error = error(cause);
2182        quote! {
2183            if !matches!(&**#value, #pattern) {
2184                return ::core::result::Result::Err(#error);
2185            }
2186        }
2187    }
2188
2189    /// A flag's value converted to `ty`, checked against `choices` first.
2190    fn flag_value(&self, ty: &Type, value: TokenStream2) -> TokenStream2 {
2191        let check = self.check(value.clone(), |cause| {
2192            quote!(__wa::Error::invalid_value(__arg.offset(), __arg.spelling(), #value, #cause))
2193        });
2194        quote!({ #check __arg.convert::<#ty>(#value)? })
2195    }
2196
2197    /// `value` (a `&BStr` from the environment or a default) converted to `ty`;
2198    /// errors name it by `source`.
2199    fn source_value(&self, ty: &Type, value: TokenStream2, source: &str) -> TokenStream2 {
2200        let source = LitStr::new(source, Span::call_site());
2201        let check = self.check(
2202            value.clone(),
2203            |cause| quote!(__wa::Error::invalid_value(__input.offset(), #source, #value, #cause)),
2204        );
2205        quote!({
2206            #check
2207            <#ty as __wa::FromArg>::from_arg(#value).map_err(|cause| {
2208                __wa::Error::invalid_value(__input.offset(), #source, #value, cause)
2209            })?
2210        })
2211    }
2212
2213    /// Whether the field holds a value. Read before defaults this is "supplied"
2214    /// (command line or environment); after them it is "has a value".
2215    fn has(&self) -> TokenStream2 {
2216        let slot = slot(&self.ident);
2217        if self.keywords {
2218            return quote!((!#slot.is_empty()));
2219        }
2220        match &self.kind {
2221            Kind::Switch if self.negate.is_some() => {
2222                quote!((#slot == ::core::option::Option::Some(true)))
2223            }
2224            Kind::Switch => quote!(#slot),
2225            Kind::Count(_) => quote!((#slot != 0)),
2226            Kind::Optional(_) | Kind::Required(_) => quote!(#slot.is_some()),
2227            Kind::Many(_) => quote!((!#slot.is_empty())),
2228        }
2229    }
2230
2231    /// Store `value` (a `&BStr`), converted, in the slot of a field that takes
2232    /// values; `source` names where it came from in an error.
2233    fn assign(&self, value: TokenStream2, source: &str) -> TokenStream2 {
2234        let slot = slot(&self.ident);
2235        match &self.kind {
2236            Kind::Optional(ty) | Kind::Required(ty) => {
2237                let value = self.source_value(ty, value, source);
2238                quote!(#slot = ::core::option::Option::Some(#value);)
2239            }
2240            Kind::Many(ty) => match self.delimiter {
2241                None => {
2242                    let value = self.source_value(ty, value, source);
2243                    quote!(#slot.push(#value);)
2244                }
2245                Some(d) => {
2246                    let piece = self.source_value(ty, quote!(__piece), source);
2247                    quote!(for __piece in __wa::split(#value, #d) { #slot.push(#piece); })
2248                }
2249            },
2250            Kind::Switch | Kind::Count(_) => unreachable!("a switch or a count takes no value"),
2251        }
2252    }
2253
2254    /// Whether the field was not given, for a fallback to fill it.
2255    fn unset(&self, displaced: bool) -> TokenStream2 {
2256        let slot = slot(&self.ident);
2257        if self.negate.is_some() {
2258            return quote!(#slot.is_none());
2259        }
2260        let has = self.has();
2261        let not_displaced = displaced.then(|| {
2262            let displaced = displaced_flag(&self.ident);
2263            quote!(&& !#displaced)
2264        });
2265        quote!(!#has #not_displaced)
2266    }
2267
2268    /// Fill the field from its environment variable (`env`) or its default when
2269    /// the command line left it unset and no `overrides` displaced it.
2270    fn fallback(&self, env: bool, displaced: bool) -> TokenStream2 {
2271        let slot = slot(&self.ident);
2272        if self.negate.is_some() {
2273            return self.negatable_fallback(env);
2274        }
2275        let unset = self.unset(displaced);
2276        let assign = |value: TokenStream2, source: &str| self.assign(value, source);
2277        if !env {
2278            if let Some(function) = &self.default_fn {
2279                let set = match &self.kind {
2280                    Kind::Many(_) => quote!(#slot = #function();),
2281                    _ => quote!(#slot = ::core::option::Option::Some(#function());),
2282                };
2283                return quote!(if #unset { #set });
2284            }
2285            return self
2286                .default
2287                .as_ref()
2288                .map(|value| {
2289                    let bytes = LitByteStr::new(value.as_bytes(), Span::call_site());
2290                    let assign = assign(quote!(__wa::BStr::new(#bytes)), &self.display());
2291                    quote!(if #unset { #assign })
2292                })
2293                .unwrap_or_default();
2294        }
2295        self.env
2296            .as_ref()
2297            .map(|var| {
2298                let name = LitStr::new(var, Span::call_site());
2299                let apply = match &self.kind {
2300                    Kind::Switch => quote!(#slot = __wa::env::truthy(&__raw);),
2301                    Kind::Count(ty) => quote! {
2302                        if let ::core::option::Option::Some(::core::result::Result::Ok(__n)) =
2303                            __raw.to_str().map(str::parse::<#ty>)
2304                        {
2305                            #slot = __n;
2306                        }
2307                    },
2308                    _ => {
2309                        let assign = assign(quote!(__value), &format!("${var}"));
2310                        quote! {
2311                            let __value = __wa::BStr::new(__raw.as_encoded_bytes());
2312                            #assign
2313                        }
2314                    }
2315                };
2316                quote! {
2317                    if #unset {
2318                        if let ::core::option::Option::Some(__raw) = __wa::env::var(#name) {
2319                            #apply
2320                        }
2321                    }
2322                }
2323            })
2324            .unwrap_or_default()
2325    }
2326
2327    /// A negatable switch's fallback: only when neither spelling was given.
2328    fn negatable_fallback(&self, env: bool) -> TokenStream2 {
2329        let slot = slot(&self.ident);
2330        let value = if env {
2331            let Some(var) = &self.env else {
2332                return quote!();
2333            };
2334            let name = LitStr::new(var, Span::call_site());
2335            quote! {
2336                if let ::core::option::Option::Some(__raw) = __wa::env::var(#name) {
2337                    #slot = ::core::option::Option::Some(__wa::env::truthy(&__raw));
2338                }
2339            }
2340        } else {
2341            match (&self.default_fn, &self.default) {
2342                (Some(function), _) => quote!(#slot = ::core::option::Option::Some(#function());),
2343                (None, Some(default)) => {
2344                    let default = default == "true";
2345                    quote!(#slot = ::core::option::Option::Some(#default);)
2346                }
2347                (None, None) => return quote!(),
2348            }
2349        };
2350        quote!(if #slot.is_none() { #value })
2351    }
2352
2353    /// A positional word converted to `ty`, checked against `choices` first.
2354    fn word_value(&self, ty: &Type, word: TokenStream2, display: &LitStr) -> TokenStream2 {
2355        let check = self.check(
2356            quote!(#word.value),
2357            |cause| quote!(__wa::Error::invalid_value(#word.offset, #display, #word.value, #cause)),
2358        );
2359        quote!({ #check #word.convert::<#ty>(#display)? })
2360    }
2361
2362    /// How errors name the field: its long flag, else its short one, else its value name.
2363    fn display(&self) -> String {
2364        match &self.role {
2365            Role::Flag { long: Some(l), .. } => format!("--{l}"),
2366            Role::Flag { short: Some(c), .. } => format!("-{c}"),
2367            Role::Flag { .. } => match self.plus {
2368                Some(c) => format!("+{c}"),
2369                None => unreachable!("every flag has a name"),
2370            },
2371            Role::Positional { name, .. } => name.clone(),
2372            Role::Subcommand => "<COMMAND>".to_owned(),
2373        }
2374    }
2375
2376    /// The pattern of every letter it answers to: `'l' | 'L'`.
2377    fn short_pattern(&self) -> Option<TokenStream2> {
2378        let first = self.short()?;
2379        let letters = std::iter::once(first)
2380            .chain(self.short_aliases.iter().copied())
2381            .map(|c| LitChar::new(c, Span::call_site()));
2382        Some(quote!(#(#letters)|*))
2383    }
2384
2385    /// Every letter it answers to.
2386    fn shorts(&self) -> Vec<char> {
2387        self.short()
2388            .into_iter()
2389            .chain(self.short_aliases.iter().copied())
2390            .collect()
2391    }
2392
2393    fn short(&self) -> Option<char> {
2394        match self.role {
2395            Role::Flag { short, .. } => short,
2396            Role::Positional { .. } | Role::Subcommand => None,
2397        }
2398    }
2399
2400    /// Every long spelling: the name, then its aliases.
2401    fn longs(&self) -> Vec<&str> {
2402        match &self.role {
2403            Role::Flag { long, aliases, .. } => {
2404                long.iter().chain(aliases).map(String::as_str).collect()
2405            }
2406            Role::Positional { .. } | Role::Subcommand => Vec::new(),
2407        }
2408    }
2409
2410    /// The pattern matching any long spelling, if there is one.
2411    fn long_pattern(&self) -> Option<TokenStream2> {
2412        let longs = self.longs();
2413        (!longs.is_empty()).then(|| byte_patterns(&longs))
2414    }
2415
2416    /// Whether a flag reads a value (rather than being a switch or a count).
2417    fn takes_value(&self) -> bool {
2418        !self.tristate && !matches!(self.kind, Kind::Switch | Kind::Count(_))
2419    }
2420
2421    fn is_positional(&self) -> bool {
2422        matches!(self.role, Role::Positional { .. })
2423    }
2424
2425    fn is_global(&self) -> bool {
2426        matches!(self.role, Role::Flag { global: true, .. })
2427    }
2428
2429    /// A positional's relation to `--`; `Optional` for anything else.
2430    fn double_dash(&self) -> DoubleDash {
2431        match self.role {
2432            Role::Positional { double_dash, .. } => double_dash,
2433            _ => DoubleDash::Optional,
2434        }
2435    }
2436}
2437
2438fn expand(input: &DeriveInput) -> syn::Result<TokenStream2> {
2439    let Data::Struct(data) = &input.data else {
2440        return Err(syn::Error::new(
2441            input.span(),
2442            "`Args` can only be derived for structs",
2443        ));
2444    };
2445    let Fields::Named(named) = &data.fields else {
2446        return Err(syn::Error::new(
2447            data.fields.span(),
2448            "`Args` needs a struct with named fields",
2449        ));
2450    };
2451    for f in &named.named {
2452        role_alone(f)?;
2453    }
2454    let fields = named
2455        .named
2456        .iter()
2457        .filter(|f| !is_skipped(f) && !is_sequence(f) && !is_unknown(f) && !is_flatten(f))
2458        .map(field)
2459        .collect::<syn::Result<Vec<_>>>()?;
2460    let skipped: Vec<&Ident> = named
2461        .named
2462        .iter()
2463        .filter(|f| is_skipped(f) && !is_sequence(f) && !is_unknown(f) && !is_flatten(f))
2464        .filter_map(|f| f.ident.as_ref())
2465        .collect();
2466    // `#[arg(sequence)] items: Vec<T>`: occurrences of `T`'s flags and
2467    // positional, in order.
2468    let sequences: Vec<&syn::Field> = named.named.iter().filter(|f| is_sequence(f)).collect();
2469    if let Some(extra) = sequences.get(1) {
2470        return Err(syn::Error::new(
2471            extra.span(),
2472            "a struct has at most one `sequence` field",
2473        ));
2474    }
2475    // `#[arg(sequence, unknown)]`: unknown flags join the sequence, through
2476    // the enum's `#[arg(unknown)]` variant.
2477    let sequence_unknown = sequences.first().is_some_and(|f| has_flag(f, "unknown"));
2478    let sequence = vec_field(
2479        sequences.first().copied(),
2480        "a `sequence` field is a `Vec<T>` of an `Occurrence` enum",
2481    )?;
2482    let sequence_ty = sequence.as_ref().map(|(_, ty)| ty);
2483    // `#[arg(unknown)] unknown: Vec<T>`: flag-like words naming no flag.
2484    let unknowns: Vec<&syn::Field> = named.named.iter().filter(|f| is_unknown(f)).collect();
2485    if let Some(extra) = unknowns.get(1) {
2486        return Err(syn::Error::new(
2487            extra.span(),
2488            "a struct has at most one `unknown` field",
2489        ));
2490    }
2491    let unknown = vec_field(
2492        unknowns.first().copied(),
2493        "an `unknown` field is a `Vec<T>`",
2494    )?;
2495    // `#[arg(flatten)] common: T`: `T`'s flags, parsed as if declared here.
2496    let flattens: Vec<(Ident, Type)> = named
2497        .named
2498        .iter()
2499        .filter(|f| is_flatten(f))
2500        .map(|f| (f.ident.clone().expect("named field"), f.ty.clone()))
2501        .collect();
2502    if !flattens.is_empty() && !input.generics.params.is_empty() {
2503        return Err(syn::Error::new(
2504            input.generics.span(),
2505            "`flatten` in a generic struct is not supported yet",
2506        ));
2507    }
2508    check_duplicates(&fields)?;
2509    // A `double_dash = "required"` positional is outside the ordinary sequence:
2510    // every word after `--` goes to it, and no word before.
2511    let positionals: Vec<&Field> = fields
2512        .iter()
2513        .filter(|f| f.is_positional() && f.double_dash() != DoubleDash::Required)
2514        .collect();
2515    check_positional_order(&positionals)?;
2516    let trailing: Vec<&Field> = fields
2517        .iter()
2518        .filter(|f| f.double_dash() == DoubleDash::Required)
2519        .collect();
2520    if let Some(extra) = trailing.get(1) {
2521        return Err(syn::Error::new(
2522            extra.ident.span(),
2523            "a struct has at most one `double_dash = \"required\"` positional",
2524        ));
2525    }
2526    let trailing = trailing.first().copied();
2527    // The positionals a `--` fills while they are next, by position.
2528    let preserving: Vec<usize> = positionals
2529        .iter()
2530        .enumerate()
2531        .filter(|(_, f)| f.double_dash() == DoubleDash::Preserve)
2532        .map(|(i, _)| i)
2533        .collect();
2534    if let (Some(t), false) = (trailing, preserving.is_empty()) {
2535        return Err(syn::Error::new(
2536            t.ident.span(),
2537            "a `double_dash = \"preserve\"` positional keeps the `--` this one waits for",
2538        ));
2539    }
2540    let StructOptions {
2541        groups,
2542        restart_token,
2543        default_subcommand,
2544        default_subcommand_flags,
2545        arg_required_else_help,
2546        name: program_name,
2547        version,
2548        about,
2549        long_about,
2550        after_help,
2551        after_long_help,
2552        disable_help_flag,
2553        disable_help_short,
2554        disable_version_flag,
2555        disable_help_subcommand,
2556        unknown_flags_value,
2557        long_only,
2558        plus_options,
2559    } = struct_options(input)?;
2560    let (doc_about, doc_long_about) = docs(&input.attrs);
2561    let about = about.unwrap_or(doc_about);
2562    let long_about = long_about.unwrap_or(doc_long_about);
2563    let (about_text, long_about_text) = (about, long_about);
2564    let (after_text, after_long_text) = (after_help, after_long_help);
2565    let (about, long_about) = (text(&about_text), text(&long_about_text));
2566    let (after_help, after_long_help) = (text(&after_text), text(&after_long_text));
2567    let rules = Rules::new(&fields, &groups)?;
2568
2569    let name = &input.ident;
2570    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
2571
2572    // Each field's slot: its name, type and starting value.
2573    let slot_parts: Vec<(Ident, TokenStream2, TokenStream2)> = fields
2574        .iter()
2575        .map(|f| {
2576            let none = quote!(::core::option::Option::None);
2577            let new = quote!(::std::vec::Vec::new());
2578            let (ty, init) = match &f.kind {
2579                // Borrows the command line, so never in a `Flatten` slot struct.
2580                _ if f.keywords => (quote!(::std::vec::Vec<&__wa::BStr>), new),
2581                Kind::Switch if f.negate.is_some() => (quote!(::core::option::Option<bool>), none),
2582                Kind::Switch => (quote!(bool), quote!(false)),
2583                Kind::Count(ty) => (quote!(#ty), quote!(0)),
2584                Kind::Optional(ty) | Kind::Required(ty) => {
2585                    (quote!(::core::option::Option<#ty>), none)
2586                }
2587                Kind::Many(ty) => (quote!(::std::vec::Vec<#ty>), new),
2588            };
2589            (slot(&f.ident), ty, init)
2590        })
2591        .collect();
2592    let slots = slot_parts
2593        .iter()
2594        .map(|(ident, ty, init)| quote!(let mut #ident: #ty = #init;));
2595
2596    // What storing one flag occurrence looks like; the same for both spellings.
2597    let store = |f: &Field| {
2598        let ident = slot(&f.ident);
2599        let displace = rules.displace(&fields, f);
2600        let stored = match &f.kind {
2601            _ if f.keywords => {
2602                let read = f.read();
2603                quote! {
2604                    let __value = #read;
2605                    #ident.push(__value);
2606                }
2607            }
2608            _ if f.tristate || f.negate.is_some() => quote! {
2609                __arg.check_switch()?;
2610                #ident = ::core::option::Option::Some(true);
2611            },
2612            Kind::Switch => quote! {
2613                __arg.check_switch()?;
2614                #ident = true;
2615            },
2616            Kind::Count(_) => quote! {
2617                __arg.check_switch()?;
2618                #ident = #ident.saturating_add(1);
2619            },
2620            Kind::Required(ty) => {
2621                let value = f.flag_value(ty, quote!(__value));
2622                let read = f.read();
2623                quote! {
2624                    let __value = #read;
2625                    #ident = ::core::option::Option::Some(#value);
2626                }
2627            }
2628            Kind::Optional(ty) => {
2629                let value = f.flag_value(ty, quote!(__value));
2630                match &f.default_missing {
2631                    // `require_equals` is getopt's `optional_argument`: a bare
2632                    // flag holds `None`. `default_missing` names what a bare
2633                    // flag holds instead.
2634                    Some(_) => {
2635                        let read = f.read();
2636                        quote! {
2637                            let __value = #read;
2638                            #ident = ::core::option::Option::Some(#value);
2639                        }
2640                    }
2641                    None if f.require_equals => {
2642                        let options = f.value_options();
2643                        quote! {
2644                            if let ::core::option::Option::Some(__value) =
2645                                __arg.read_value_opt_with(__input, #options)
2646                            {
2647                                #ident = ::core::option::Option::Some(#value);
2648                            }
2649                        }
2650                    }
2651                    None => {
2652                        let read = f.read();
2653                        quote! {
2654                            let __value = #read;
2655                            #ident = ::core::option::Option::Some(#value);
2656                        }
2657                    }
2658                }
2659            }
2660            Kind::Many(ty) => match f.delimiter {
2661                None => {
2662                    let value = f.flag_value(ty, quote!(__value));
2663                    let read = f.read();
2664                    let more = match f.values_max {
2665                        // `values = N`: the words after the first, whatever they look like.
2666                        None => (f.values > 1).then(|| {
2667                            let more = f.values - 1;
2668                            quote! {
2669                                for _ in 0..#more {
2670                                    let __value = __arg.read_next(__input)?;
2671                                    #ident.push(#value);
2672                                }
2673                            }
2674                        }),
2675                        // `values = a..=b`: more words until one is flag-like or the terminator.
2676                        Some(max) => {
2677                            let (min, options) = (f.values, f.value_options());
2678                            let terminator = match &f.value_terminator {
2679                                Some(word) => {
2680                                    let word = LitByteStr::new(word.as_bytes(), Span::call_site());
2681                                    quote!(::core::option::Option::Some(__wa::BStr::new(#word)))
2682                                }
2683                                None => quote!(::core::option::Option::None),
2684                            };
2685                            Some(quote! {
2686                                let mut __taken = 1usize;
2687                                while __taken < #max {
2688                                    let ::core::option::Option::Some(__value) =
2689                                        __arg.read_more(__input, #options, #terminator)
2690                                    else {
2691                                        break;
2692                                    };
2693                                    #ident.push(#value);
2694                                    __taken += 1;
2695                                }
2696                                __arg.check_values(__taken, #min)?;
2697                            })
2698                        }
2699                    };
2700                    quote! {
2701                        let __value = #read;
2702                        #ident.push(#value);
2703                        #more
2704                    }
2705                }
2706                Some(d) => {
2707                    let value = f.flag_value(ty, quote!(__piece));
2708                    let read = f.read();
2709                    quote! {
2710                        for __piece in __wa::split(#read, #d) {
2711                            #ident.push(#value);
2712                        }
2713                    }
2714                }
2715            },
2716        };
2717        quote!(#stored #displace)
2718    };
2719
2720    let negated = |f: &Field| {
2721        let no = LitByteStr::new(f.negate.as_ref()?.as_bytes(), Span::call_site());
2722        let ident = slot(&f.ident);
2723        let displace = rules.displace(&fields, f);
2724        Some((
2725            quote!(#no),
2726            quote! {
2727                __arg.check_switch()?;
2728                #ident = ::core::option::Option::Some(false);
2729                #displace
2730            },
2731        ))
2732    };
2733    // A `+c` occurrence: a tristate's `Some(false)`, else what `-c` stores.
2734    let plus = |f: &Field| {
2735        let letter = LitChar::new(f.plus?, Span::call_site());
2736        let body = if f.tristate {
2737            let ident = slot(&f.ident);
2738            let displace = rules.displace(&fields, f);
2739            quote!(#ident = ::core::option::Option::Some(false); #displace)
2740        } else {
2741            store(f)
2742        };
2743        Some((quote!(#letter), body))
2744    };
2745    // The long (negations last), short and `+` arms of the fields `keep`
2746    // selects, each body ending in `tail`.
2747    type Part<'a> = &'a dyn Fn(&Field) -> Option<(TokenStream2, TokenStream2)>;
2748    let arms = |keep: &dyn Fn(&Field) -> bool, tail: TokenStream2| {
2749        let of = |part: Part<'_>| -> Vec<TokenStream2> {
2750            fields
2751                .iter()
2752                .filter(|f| keep(f))
2753                .filter_map(part)
2754                .map(|(pattern, body)| quote!(#pattern => { #body #tail }))
2755                .collect()
2756        };
2757        let mut long = of(&|f| Some((f.long_pattern()?, store(f))));
2758        long.extend(of(&negated));
2759        let short = of(&|f| Some((f.short_pattern()?, store(f))));
2760        (long, short, of(&plus))
2761    };
2762    let (long_arms, short_arms, plus_arms) = arms(&|_| true, quote!());
2763
2764    let unexpected = quote!(return ::core::result::Result::Err(__arg.unexpected()));
2765    let subcommands: Vec<&Field> = fields
2766        .iter()
2767        .filter(|f| matches!(f.role, Role::Subcommand))
2768        .collect();
2769    if let Some(extra) = subcommands.get(1) {
2770        return Err(syn::Error::new(
2771            extra.ident.span(),
2772            "a struct has at most one subcommand field",
2773        ));
2774    }
2775    let subcommand = subcommands.first();
2776    // A word routes to a subcommand only before any positional is filled.
2777    let track_filled = subcommand.is_some() && !positionals.is_empty();
2778    let filled = track_filled.then(|| quote!(__filled = true;));
2779
2780    // A word no ordinary positional takes: before `--`, it would have reached
2781    // the `double_dash = "required"` positional if there is one.
2782    let overflow = match trailing {
2783        Some(t) => {
2784            let display = LitStr::new(&t.display(), Span::call_site());
2785            quote! {
2786                return ::core::result::Result::Err(
2787                    __wa::Error::requires_double_dash(__word.offset, #display),
2788                )
2789            }
2790        }
2791        None => unexpected.clone(),
2792    };
2793    let positional_match = if positionals.is_empty() {
2794        quote!({ #overflow; })
2795    } else {
2796        let arms = positionals.iter().enumerate().map(|(i, f)| {
2797            let ident = slot(&f.ident);
2798            let display = LitStr::new(&f.display(), Span::call_site());
2799            let stop = (f.double_dash() == DoubleDash::Automatic || f.stop_flags)
2800                .then(|| quote!(__input.stop_flags();));
2801            match &f.kind {
2802                Kind::Optional(ty) | Kind::Required(ty) => {
2803                    let value = f.word_value(ty, quote!(__word), &display);
2804                    quote! {
2805                        #i => {
2806                            #ident = ::core::option::Option::Some(#value);
2807                            __position += 1;
2808                            #filled
2809                            #stop
2810                        }
2811                    }
2812                }
2813                Kind::Many(ty) => {
2814                    let push = match f.delimiter {
2815                        None => {
2816                            let value = f.word_value(ty, quote!(__word), &display);
2817                            // Sized once, for at most every word left: the
2818                            // usual tail of operands then needs no regrowth.
2819                            quote! {
2820                                if #ident.capacity() == 0 {
2821                                    #ident.reserve_exact(__input.words_left() + 1);
2822                                }
2823                                #ident.push(#value);
2824                            }
2825                        }
2826                        Some(d) => {
2827                            let value = f.word_value(ty, quote!(__piece), &display);
2828                            quote! {
2829                                for __piece in __word.split(#d) {
2830                                    #ident.push(#value);
2831                                }
2832                            }
2833                        }
2834                    };
2835                    quote! {
2836                        #i => {
2837                            #push
2838                            #filled
2839                            #stop
2840                        }
2841                    }
2842                }
2843                Kind::Switch | Kind::Count(_) => {
2844                    unreachable!("rejected for positionals in `field`")
2845                }
2846            }
2847        });
2848        quote! {
2849            match __position {
2850                #(#arms)*
2851                _ => { #overflow; }
2852            }
2853        }
2854    };
2855    let positional_match = match trailing {
2856        None => positional_match,
2857        Some(t) => {
2858            let ident = slot(&t.ident);
2859            let display = LitStr::new(&t.display(), Span::call_site());
2860            let store = match &t.kind {
2861                Kind::Many(ty) => match t.delimiter {
2862                    None => {
2863                        let value = t.word_value(ty, quote!(__word), &display);
2864                        quote!(#ident.push(#value);)
2865                    }
2866                    Some(d) => {
2867                        let value = t.word_value(ty, quote!(__piece), &display);
2868                        quote!(for __piece in __word.split(#d) { #ident.push(#value); })
2869                    }
2870                },
2871                Kind::Optional(ty) | Kind::Required(ty) => {
2872                    let value = t.word_value(ty, quote!(__word), &display);
2873                    quote! {
2874                        if #ident.is_some() { #unexpected; }
2875                        #ident = ::core::option::Option::Some(#value);
2876                    }
2877                }
2878                Kind::Switch | Kind::Count(_) => {
2879                    unreachable!("rejected for positionals in `field`")
2880                }
2881            };
2882            quote! {
2883                if __word.after_separator {
2884                    #store
2885                } else {
2886                    #positional_match
2887                }
2888            }
2889        }
2890    };
2891    let routes = subcommand.map(|f| {
2892        let ident = slot(&f.ident);
2893        let ty = subcommand_type(f);
2894        let not_filled = track_filled.then(|| quote!(!__filled &&));
2895        let (global_longs, global_shorts, global_plus) = arms(
2896            &Field::is_global,
2897            quote!(return ::core::result::Result::Ok(true);),
2898        );
2899        // The subcommand sees this struct's globals first, those of the structs
2900        // it flattens, then its ancestors'.
2901        let flat_globals = flattens.iter().map(|(ident, ty)| {
2902            let slot = format_ident!("__flat_{}", ident);
2903            quote! {
2904                if <#ty as __wa::Flatten>::bind_global(&mut #slot, __arg, __input)? {
2905                    return ::core::result::Result::Ok(true);
2906                }
2907            }
2908        });
2909        let flat_shorts = flattens
2910            .iter()
2911            .map(|(_, ty)| quote!(.or_else(|| <#ty as __wa::Flatten>::global_short(__c))));
2912        let (inherit, handler) = if fields.iter().any(Field::is_global) || !flattens.is_empty() {
2913            let shorts = fields.iter().filter(|f| f.is_global()).flat_map(|f| {
2914                let takes_value = f.takes_value();
2915                f.shorts().into_iter().map(move |c| {
2916                    let c = LitChar::new(c, Span::call_site());
2917                    quote!((#c, #takes_value))
2918                })
2919            });
2920            let setup = quote! {
2921                let mut __inherit = __wa::inherit(
2922                    &[#(#shorts),*],
2923                    |__c| ::core::option::Option::<bool>::None #(#flat_shorts)*,
2924                    __globals,
2925                    |__arg, __input, __parent| {
2926                    match __arg {
2927                        __wa::Arg::Long(__flag) => match __flag.name {
2928                            #(#global_longs)*
2929                            _ => {}
2930                        },
2931                        __wa::Arg::Short(__flag) if __flag.plus => match __flag.letter {
2932                            #(#global_plus)*
2933                            _ => {}
2934                        },
2935                        __wa::Arg::Short(__flag) => match __flag.letter {
2936                            #(#global_shorts)*
2937                            _ => {}
2938                        },
2939                        _ => {}
2940                    }
2941                    #(#flat_globals)*
2942                    __parent.bind(__arg, __input)
2943                });
2944            };
2945            (setup, quote!(&mut __inherit))
2946        } else {
2947            (quote!(), quote!(&mut *__globals))
2948        };
2949        // `help a b`: the help of a subcommand, unless one is actually named `help`.
2950        let help_word = (!disable_help_subcommand).then(|| {
2951            quote! {
2952                if &**__word.value == b"help" {
2953                    return ::core::result::Result::Err(
2954                        __wa::help_word(<Self as ::winnow_args::Args>::HELP, __input),
2955                    );
2956                }
2957            }
2958        });
2959        // Any other word selects the default subcommand, which reads it again as its own.
2960        let fallback = default_subcommand.as_ref().map(|name| {
2961            let name = LitByteStr::new(name.as_bytes(), Span::call_site());
2962            quote! {
2963                let __sub = {
2964                    #inherit
2965                    *__input = __start;
2966                    <#ty as __wa::Subcommand>::parse_subcommand(#name, __input, #handler)?
2967                };
2968                #ident = ::core::option::Option::Some(__sub);
2969                break;
2970            }
2971        });
2972        // A flag this struct does not take selects it too, and is read again as its own.
2973        let flag_fallback = fallback.as_ref().filter(|_| default_subcommand_flags).map(
2974            |fallback| match track_filled {
2975                true => quote!(if __filled { #unexpected; } #fallback),
2976                false => fallback.clone(),
2977            },
2978        );
2979        let route = quote! {
2980            if #not_filled !__word.after_separator {
2981                if <#ty as __wa::Subcommand>::has(&**__word.value) {
2982                    let __sub = {
2983                        #inherit
2984                        <#ty as __wa::Subcommand>::parse_subcommand(
2985                            &**__word.value, __input, #handler,
2986                        )?
2987                    };
2988                    #ident = ::core::option::Option::Some(__sub);
2989                    break;
2990                }
2991                #help_word
2992                #fallback
2993            }
2994        };
2995        (route, flag_fallback)
2996    });
2997    let (route, flag_fallback) = match routes {
2998        Some((route, flag_fallback)) => (Some(route), flag_fallback),
2999        None => (None, None),
3000    };
3001    // A new invocation of the same command: positionals start over, flags keep theirs.
3002    let restart = restart_token.map(|token| {
3003        let token = LitByteStr::new(token.as_bytes(), Span::call_site());
3004        let clears = fields.iter().filter(|f| f.is_positional()).map(|f| {
3005            let slot = slot(&f.ident);
3006            match f.kind {
3007                Kind::Many(_) => quote!(#slot.clear();),
3008                _ => quote!(#slot = ::core::option::Option::None;),
3009            }
3010        });
3011        let position = (!positionals.is_empty()).then(|| quote!(__position = 0;));
3012        let filled = track_filled.then(|| quote!(__filled = false;));
3013        quote! {
3014            if &**__word.value == #token {
3015                #(#clears)*
3016                #position
3017                #filled
3018                __input.resume_flags();
3019                continue;
3020            }
3021        }
3022    });
3023    let sequence_word = sequence_ty.map(|ty| {
3024        quote! {
3025            if let ::core::option::Option::Some(__item) =
3026                <#ty as ::winnow_args::Occurrence>::from_word(&__word)?
3027            {
3028                __sequence.push(__item);
3029                continue;
3030            }
3031        }
3032    });
3033    let word_arm = quote! {
3034        __wa::Arg::Word(__word) => {
3035            #restart
3036            #route
3037            #sequence_word
3038            #positional_match
3039        }
3040    };
3041    let separator_arm = if preserving.is_empty() {
3042        quote!(__wa::Arg::Separator { .. } => {})
3043    } else {
3044        quote! {
3045            __wa::Arg::Separator { offset: __offset } => {
3046                if matches!(__position, #(#preserving)|*) {
3047                    __input.resume_flags();
3048                    let __word = __wa::Word {
3049                        value: __wa::BStr::new(b"--"),
3050                        offset: __offset,
3051                        after_separator: false,
3052                    };
3053                    #positional_match
3054                }
3055            }
3056        }
3057    };
3058    let position = (!positionals.is_empty()).then(|| quote!(let mut __position: usize = 0;));
3059    let filled_slot = track_filled.then(|| quote!(let mut __filled = false;));
3060
3061    // A negative number is a word where the positional next to fill opts in,
3062    // unless it spells a declared digit short exactly (`-0` for fd's `--print0`).
3063    let opted: Vec<usize> = positionals
3064        .iter()
3065        .enumerate()
3066        .filter(|(_, f)| f.negative_numbers)
3067        .map(|(i, _)| i)
3068        .collect();
3069    let (unknown_slot, unknown_build) = match &unknown {
3070        Some((ident, ty)) => (
3071            quote!(let mut __unknown: ::std::vec::Vec<#ty> = ::std::vec::Vec::new();),
3072            quote!(#ident: __unknown,),
3073        ),
3074        None => (quote!(), quote!()),
3075    };
3076    let (sequence_slot, sequence_arg, sequence_build) = match &sequence {
3077        Some((ident, ty)) => (
3078            // Room for one item a word: a linker's line is mostly inputs.
3079            quote! {
3080                let mut __sequence: ::std::vec::Vec<#ty> =
3081                    ::std::vec::Vec::with_capacity(__input.words_left());
3082            },
3083            quote! {
3084                if let ::core::option::Option::Some(__item) =
3085                    <#ty as ::winnow_args::Occurrence>::from_arg(&__arg, __input)?
3086                {
3087                    __sequence.push(__item);
3088                } else
3089            },
3090            quote!(#ident: __sequence,),
3091        ),
3092        None => (quote!(), quote!(), quote!()),
3093    };
3094    // A word of several short flags (`-sS`), where the enum wants to know:
3095    // once its first letter is read, whoever took it, the `bundle` item goes
3096    // before the items of its letters.
3097    let bundle_start = sequence_ty.map(|_| {
3098        quote! {
3099            let __bundle_word = __input.front();
3100            let __bundle_start = __input.mode() == ::winnow_args::stream::Mode::Word;
3101            let __bundle_at = __sequence.len();
3102        }
3103    });
3104    let bundle_end = sequence_ty.map(|ty| {
3105        quote! {
3106            if <#ty as ::winnow_args::Occurrence>::BUNDLES
3107                && __bundle_start
3108                && matches!(__arg, __wa::Arg::Short(__flag) if !__flag.plus)
3109                && __input.mode() == ::winnow_args::stream::Mode::Bundle
3110            {
3111                let __word = __wa::Word {
3112                    value: __wa::BStr::new(__bundle_word),
3113                    offset: __arg.offset(),
3114                    after_separator: false,
3115                };
3116                if let ::core::option::Option::Some(__bundle) =
3117                    <#ty as ::winnow_args::Occurrence>::from_bundle(&__word)?
3118                {
3119                    __sequence.insert(__bundle_at, __bundle);
3120                }
3121            }
3122        }
3123    });
3124    // `#[arg(flatten)]` fields: their slots, the flags offered to them after
3125    // ours, and their values built at the end.
3126    let flat_slot = |ident: &Ident| format_ident!("__flat_{}", ident);
3127    let flatten_slots = flattens.iter().map(|(ident, ty)| {
3128        let slot = flat_slot(ident);
3129        quote! {
3130            let mut #slot = <<#ty as __wa::Flatten>::Slots as ::core::default::Default>::default();
3131        }
3132    });
3133    let flatten_arg: TokenStream2 = flattens
3134        .iter()
3135        .map(|(ident, ty)| {
3136            let slot = flat_slot(ident);
3137            quote!(if <#ty as __wa::Flatten>::bind(&mut #slot, &__arg, __input)? {} else)
3138        })
3139        .collect();
3140    let flatten_build = flattens.iter().map(|(ident, ty)| {
3141        let slot = flat_slot(ident);
3142        quote!(#ident: <#ty as __wa::Flatten>::finish(#slot, __input)?,)
3143    });
3144    let flatten_shorts: Vec<TokenStream2> = flattens
3145        .iter()
3146        .map(|(_, ty)| {
3147            quote! {
3148                __c if <#ty as __wa::Flatten>::short(__c).is_some() => {
3149                    <#ty as __wa::Flatten>::short(__c)
3150                }
3151            }
3152        })
3153        .collect();
3154    let flatten_longs: Vec<TokenStream2> = flattens
3155        .iter()
3156        .map(|(_, ty)| quote!(|| <#ty as __wa::Flatten>::is_long(__name)))
3157        .collect();
3158    // Whether each of our letters takes a value, as `short` answers.
3159    let own_shorts: Vec<TokenStream2> = fields
3160        .iter()
3161        .filter_map(|f| {
3162            let letters = f.short_pattern()?;
3163            let takes_value = f.takes_value();
3164            Some(quote!(#letters => ::core::option::Option::Some(#takes_value),))
3165        })
3166        .collect();
3167    // The long names one dash may spell under `long_only`, and the letters
3168    // that never start one.
3169    let own_longs: Vec<String> = fields
3170        .iter()
3171        .filter(|f| !f.two_dashes)
3172        .flat_map(|f| spellings(f).into_iter().map(str::to_owned))
3173        .collect();
3174    let prefixes: Vec<u8> = fields
3175        .iter()
3176        .filter(|f| f.prefix)
3177        .filter_map(|f| f.short().map(|c| c as u8))
3178        .collect();
3179    let flatten_prefixes = flattens.iter().map(|(_, ty)| {
3180        quote!(&& !__name.first().is_some_and(|__c| <#ty as __wa::Flatten>::is_prefix(*__c)))
3181    });
3182    let lexer = if plus_options {
3183        quote!(__wa::arg_plus)
3184    } else {
3185        quote!(__wa::arg)
3186    };
3187    if !plus_options && let Some(f) = fields.iter().find(|f| f.plus.is_some()) {
3188        return Err(syn::Error::new(
3189            f.ident.span(),
3190            "`plus` needs `#[arg(plus_options)]` on the struct",
3191        ));
3192    }
3193    // A `+x` this struct does not take selects the default subcommand as a flag does.
3194    let plus_unknown = match &flag_fallback {
3195        Some(fallback) => quote!(#fallback),
3196        None => quote!(#unexpected;),
3197    };
3198    let plus_match = plus_options.then(|| {
3199        quote! {
3200            __wa::Arg::Short(__flag) if __flag.plus => match __flag.letter {
3201                #(#plus_arms)*
3202                _ => #flatten_arg if !__globals.bind(&__arg, __input)? { #plus_unknown }
3203            },
3204        }
3205    });
3206    let lex = if opted.is_empty() {
3207        quote!(#lexer(__input)?)
3208    } else {
3209        let digits: Vec<LitByteStr> = fields
3210            .iter()
3211            .filter_map(|f| f.short().filter(char::is_ascii_digit))
3212            .map(|c| LitByteStr::new(format!("-{c}").as_bytes(), Span::call_site()))
3213            .collect();
3214        let not_a_short =
3215            (!digits.is_empty()).then(|| quote!(&&!matches!(__input.front(), #(#digits)|*)));
3216        quote! {
3217            match matches!(__position, #(#opted)|*) #not_a_short {
3218                true => match __wa::number(__input) {
3219                    ::core::result::Result::Ok(__word) => __wa::Arg::Word(__word),
3220                    ::core::result::Result::Err(_) => #lexer(__input)?,
3221                },
3222                false => #lexer(__input)?,
3223            }
3224        }
3225    };
3226    let start = default_subcommand
3227        .is_some()
3228        .then(|| quote!(let __start = *__input;));
3229    let help_on_empty = arg_required_else_help.then(|| {
3230        quote! {
3231            if __input.is_empty() {
3232                return ::core::result::Result::Err(
3233                    __wa::Error::bare_help(<Self as ::winnow_args::Args>::HELP),
3234                );
3235            }
3236        }
3237    });
3238    if default_subcommand.is_some() && subcommand.is_none() {
3239        return Err(syn::Error::new(
3240            input.ident.span(),
3241            "`default_subcommand` needs an `#[arg(subcommand)]` field",
3242        ));
3243    }
3244    if default_subcommand_flags && default_subcommand.is_none() {
3245        return Err(syn::Error::new(
3246            input.ident.span(),
3247            "`default_subcommand_flags` needs `default_subcommand`",
3248        ));
3249    }
3250    if default_subcommand_flags && (unknown_flags_value || unknown.is_some() || sequence_unknown) {
3251        return Err(syn::Error::new(
3252            input.ident.span(),
3253            "`default_subcommand_flags` hands an unknown flag to the default subcommand: \
3254             this struct cannot keep unknown flags too",
3255        ));
3256    }
3257    // `-h`/`--help` and `-V`/`--version`, unless declared or disabled: arms after
3258    // the struct's own, so a CLI naming its own `--help` keeps it.
3259    let declares_long = |name: &str| fields.iter().any(|f| f.longs().contains(&name));
3260    let declares_short = |c: char| fields.iter().any(|f| f.shorts().contains(&c));
3261    let help = quote!(<Self as ::winnow_args::Args>::HELP);
3262    let help_long = (!disable_help_flag && !declares_long("help")).then(
3263        || quote!(b"help" => return ::core::result::Result::Err(__wa::Error::help(#help, true)),),
3264    );
3265    let help_short = (!disable_help_flag && !disable_help_short && !declares_short('h')).then(
3266        || quote!('h' => return ::core::result::Result::Err(__wa::Error::help(#help, false)),),
3267    );
3268    let with_version = version.is_some() && !disable_version_flag;
3269    let version_long = (with_version && !declares_long("version")).then(
3270        || quote!(b"version" => return ::core::result::Result::Err(__wa::Error::version(#help)),),
3271    );
3272    let version_short = (with_version && !declares_short('V'))
3273        .then(|| quote!('V' => return ::core::result::Result::Err(__wa::Error::version(#help)),));
3274    // The row help prints is `--help`'s: a struct with its own has none.
3275    let help_flag = help_long.is_some();
3276    let help_short_flag = help_short.is_some();
3277    let version_short_flag = version_short.is_some();
3278    // A flattened struct or a sequence enum may declare `-h`, `--help`, `-V`
3279    // or `--version` itself (ld's `-h SONAME`): the built-in ones are then
3280    // tried after them, not as arms before.
3281    let late = !flattens.is_empty() || sequence.is_some();
3282    let late_if = |on: bool, test: TokenStream2, error: TokenStream2| {
3283        (late && on).then(|| quote!(if #test { return ::core::result::Result::Err(#error); } else))
3284    };
3285    let late_long: TokenStream2 = [
3286        late_if(
3287            help_long.is_some(),
3288            quote!(__flag.name == b"help"),
3289            quote!(__wa::Error::help(#help, true)),
3290        ),
3291        late_if(
3292            version_long.is_some(),
3293            quote!(__flag.name == b"version"),
3294            quote!(__wa::Error::version(#help)),
3295        ),
3296    ]
3297    .into_iter()
3298    .flatten()
3299    .collect();
3300    let late_short: TokenStream2 = [
3301        late_if(
3302            help_short.is_some(),
3303            quote!(__flag.letter == 'h'),
3304            quote!(__wa::Error::help(#help, false)),
3305        ),
3306        late_if(
3307            version_short.is_some(),
3308            quote!(__flag.letter == 'V'),
3309            quote!(__wa::Error::version(#help)),
3310        ),
3311    ]
3312    .into_iter()
3313    .flatten()
3314    .collect();
3315    let keep = |arm: Option<TokenStream2>| arm.filter(|_| !late);
3316    let (help_long, help_short) = (keep(help_long), keep(help_short));
3317    let (version_long, version_short) = (keep(version_long), keep(version_short));
3318
3319    // `long_only`: `-name` is `--name` for the long names that may take one
3320    // dash, tried before the word is read as short letters.
3321    // `unknown_flags = "value"`: a flag-like word naming no flag goes to the
3322    // positionals whole, never to a subcommand. A bundle of several letters is
3323    // checked before any binds; a long word or a single letter once no arm or
3324    // global took it.
3325    // An `unknown` field takes those words instead of the positionals.
3326    // Only the first letter of a word reaches the short arm's sink: the bundle
3327    // check vouched for the letters of a longer one.
3328    let (lenient_bundle, save_token, unknown_flag) = if unknown_flags_value
3329        || unknown.is_some()
3330        || sequence_unknown
3331    {
3332        // With nothing to take a word, `positional_match` always returns.
3333        let next = (!positionals.is_empty() || trailing.is_some()).then(|| quote!(continue;));
3334        let (sink, sink_next) = match (&unknown, sequence_ty.filter(|_| sequence_unknown)) {
3335            (_, Some(ty)) => (
3336                quote! {
3337                    match <#ty as ::winnow_args::Occurrence>::from_unknown(&__word)? {
3338                        ::core::option::Option::Some(__item) => __sequence.push(__item),
3339                        ::core::option::Option::None => {
3340                            return ::core::result::Result::Err(__wa::Error::unknown_flag(
3341                                __word.offset,
3342                                ::std::string::String::from_utf8_lossy(&__word.value),
3343                            ));
3344                        }
3345                    }
3346                },
3347                quote!(continue;),
3348            ),
3349            (Some((_, ty)), None) => (
3350                quote!(__unknown.push(__word.convert::<#ty>("<UNKNOWN>")?);),
3351                quote!(continue;),
3352            ),
3353            (None, None) => (
3354                quote! {
3355                    let __arg = __wa::Arg::Word(__word);
3356                    #positional_match
3357                },
3358                quote!(#next),
3359            ),
3360        };
3361        let theirs = match sequence_ty {
3362            Some(ty) => quote! {
3363                __c => match <#ty as ::winnow_args::Occurrence>::short(__c) {
3364                    ::core::option::Option::None => __globals.short(__c),
3365                    __known => __known,
3366                },
3367            },
3368            None => quote!(_ => __globals.short(__c),),
3369        };
3370        // Known letters whether their arms come first or late.
3371        let builtin = help_short_flag
3372            .then(|| quote!('h' => ::core::option::Option::Some(false),))
3373            .into_iter()
3374            .chain(version_short_flag.then(|| quote!('V' => ::core::option::Option::Some(false),)));
3375        (
3376            quote! {
3377                if let ::core::option::Option::Some(__word) = __wa::unknown_bundle(__input, |__c| match __c {
3378                    #(#own_shorts)*
3379                    #(#builtin)*
3380                    #(#flatten_shorts)*
3381                    #theirs
3382                }) {
3383                    #sink
3384                    #sink_next
3385                }
3386            },
3387            quote!(let __token = __input.front();),
3388            quote! {
3389                let __word = __wa::Word {
3390                    value: __wa::BStr::new(__token),
3391                    offset: __flag.offset,
3392                    after_separator: false,
3393                };
3394                #sink
3395            },
3396        )
3397    } else {
3398        let unknown_flag = match &flag_fallback {
3399            Some(fallback) => quote!(#fallback),
3400            None => quote!(#unexpected;),
3401        };
3402        (quote!(), quote!(), unknown_flag)
3403    };
3404    let mut names = Vec::new();
3405    if long_only {
3406        names.clone_from(&own_longs);
3407        names.extend(help_long.is_some().then(|| "help".to_owned()));
3408        names.extend(version_long.is_some().then(|| "version".to_owned()));
3409    }
3410    let long_only_lex =
3411        long_only && (!names.is_empty() || sequence_ty.is_some() || !flattens.is_empty());
3412    let lex = if long_only_lex {
3413        let own = matches_name(&names);
3414        let (their_names, their_prefixes) = match sequence_ty {
3415            Some(ty) => (
3416                quote!(|| <#ty as ::winnow_args::Occurrence>::is_long(__name)),
3417                quote! {
3418                    && !__name.first().is_some_and(|__c| {
3419                        <#ty as ::winnow_args::Occurrence>::PREFIXES.contains(__c)
3420                    })
3421                },
3422            ),
3423            None => (quote!(), quote!()),
3424        };
3425        let not_prefixed = (!prefixes.is_empty()).then(
3426            || quote!(!matches!(__name.first(), ::core::option::Option::Some(#(#prefixes)|*)) &&),
3427        );
3428        quote! {
3429            match __wa::long_only(__input, |__name| {
3430                #not_prefixed (#own #their_names #(#flatten_longs)*) #their_prefixes #(#flatten_prefixes)*
3431            }) {
3432                ::core::option::Option::Some(__arg) => __arg,
3433                ::core::option::Option::None => {
3434                    #lenient_bundle
3435                    #lex
3436                }
3437            }
3438        }
3439    } else {
3440        lex
3441    };
3442    let lenient_bundle = if long_only_lex {
3443        quote!()
3444    } else {
3445        lenient_bundle
3446    };
3447
3448    let help_version = match &version {
3449        Some(v) if with_version => quote!(::core::option::Option::Some(#v)),
3450        _ => quote!(::core::option::Option::None),
3451    };
3452    let help_items = fields
3453        .iter()
3454        .filter(|f| !matches!(f.role, Role::Subcommand))
3455        .map(|f| f.help_item());
3456    let (help_subcommands, subcommand_required) = match subcommand {
3457        Some(f) => {
3458            let ty = subcommand_type(f);
3459            (
3460                quote!(<#ty as ::winnow_args::Args>::HELP.subcommands),
3461                matches!(f.kind, Kind::Required(_)),
3462            )
3463        }
3464        None => (quote!(&[]), false),
3465    };
3466    let displaced = rules.displaced_slots(&fields);
3467    let env_fallbacks: Vec<TokenStream2> = fields
3468        .iter()
3469        .map(|f| f.fallback(true, rules.is_displaced(&fields, f)))
3470        .collect();
3471    let default_fallbacks: Vec<TokenStream2> = fields
3472        .iter()
3473        .map(|f| f.fallback(false, rules.is_displaced(&fields, f)))
3474        .collect();
3475    let exclusive = rules.exclusive(&fields, &groups);
3476    let supplied = rules.supplied(&fields);
3477    let default_ifs = rules.default_ifs(&fields);
3478    let required = rules.required(&fields, &groups);
3479    let build = fields.iter().map(|f| {
3480        let ident = &f.ident;
3481        let slot = slot(ident);
3482        match &f.kind {
3483            Kind::Required(ty) if f.keywords => {
3484                let display = LitStr::new(&f.display(), Span::call_site());
3485                quote!(#ident: __wa::keywords::<#ty>(&#slot, #display, __input.offset())?)
3486            }
3487            Kind::Switch if f.negate.is_some() => quote!(#ident: #slot.unwrap_or(false)),
3488            Kind::Switch | Kind::Count(_) | Kind::Optional(_) | Kind::Many(_) => {
3489                quote!(#ident: #slot)
3490            }
3491            Kind::Required(_) => {
3492                let display = LitStr::new(&f.display(), Span::call_site());
3493                let error = match f.role {
3494                    Role::Positional { .. } => {
3495                        quote!(__wa::Error::missing_argument(__input.offset(), #display))
3496                    }
3497                    Role::Flag { .. } => {
3498                        quote!(__wa::Error::missing_required(__input.offset(), #display))
3499                    }
3500                    Role::Subcommand => quote!(__wa::Error::missing_subcommand(__input.offset())),
3501                };
3502                quote! {
3503                    #ident: match #slot {
3504                        ::core::option::Option::Some(v) => v,
3505                        ::core::option::Option::None => {
3506                            return ::core::result::Result::Err(#error);
3507                        }
3508                    }
3509                }
3510            }
3511        }
3512    });
3513    let build: Vec<TokenStream2> = build.collect();
3514    // Every field read once after building, so a flag accepted and ignored on
3515    // purpose (`pwd -L`) is not dead code in the user's crate. No runtime work
3516    // after optimization.
3517    let field_reads: Vec<TokenStream2> = fields
3518        .iter()
3519        .map(|f| &f.ident)
3520        .chain(skipped.iter().copied())
3521        .chain(sequence.iter().chain(&unknown).map(|(ident, _)| ident))
3522        .chain(flattens.iter().map(|(ident, _)| ident))
3523        .map(|ident| quote!(let _ = &__built.#ident;))
3524        .collect();
3525    let flatten_build: Vec<TokenStream2> = flatten_build.collect();
3526    // Flags only: no positionals, subcommand, `sequence`, `unknown`,
3527    // keywords or generics.
3528    let flattenable = positionals.is_empty()
3529        && trailing.is_none()
3530        && subcommand.is_none()
3531        && sequence.is_none()
3532        && unknown.is_none()
3533        && !unknown_flags_value
3534        && input.generics.params.is_empty()
3535        && !fields.iter().any(|f| f.keywords);
3536    let flatten_impl = flattenable.then(|| {
3537        let vis = &input.vis;
3538        let slots_name = format_ident!("__{}WinnowArgsSlots", name);
3539        let displaced_idents: Vec<Ident> = fields
3540            .iter()
3541            .filter(|f| rules.is_displaced(&fields, f))
3542            .map(|f| displaced_flag(&f.ident))
3543            .collect();
3544        let nested: Vec<(Ident, &Type)> = flattens
3545            .iter()
3546            .map(|(ident, ty)| (flat_slot(ident), ty))
3547            .collect();
3548        let decls = slot_parts.iter().map(|(ident, ty, _)| quote!(#ident: #ty,));
3549        let inits = slot_parts
3550            .iter()
3551            .map(|(ident, _, init)| quote!(#ident: #init,));
3552        let all: Vec<&Ident> = slot_parts
3553            .iter()
3554            .map(|(ident, _, _)| ident)
3555            .chain(&displaced_idents)
3556            .chain(nested.iter().map(|(n, _)| n))
3557            .collect();
3558        let names: Vec<TokenStream2> = all.iter().map(|ident| quote!(#ident,)).collect();
3559        let mutable: Vec<TokenStream2> = all.iter().map(|ident| quote!(mut #ident,)).collect();
3560        let displaced_decls = displaced_idents.iter().map(|d| quote!(#d: bool,));
3561        let displaced_inits = displaced_idents.iter().map(|d| quote!(#d: false,));
3562        let nested_decls = nested
3563            .iter()
3564            .map(|(n, ty)| quote!(#n: <#ty as ::winnow_args::__private::Flatten>::Slots,));
3565        let nested_inits = nested
3566            .iter()
3567            .map(|(n, _)| quote!(#n: ::core::default::Default::default(),));
3568        let (bind_long, bind_short, bind_plus) = arms(&|_| true, quote!(true));
3569        // The same for the `global` ones alone: what a subcommand of the
3570        // struct that flattens these is offered.
3571        let (global_long, global_short, global_plus) = arms(&Field::is_global, quote!(true));
3572        let global_nested: TokenStream2 = nested
3573            .iter()
3574            .map(|(n, ty)| {
3575                quote!(if <#ty as __wa::Flatten>::bind_global(&mut #n, &__arg, __input)? { true } else)
3576            })
3577            .collect();
3578        let global_letters = fields.iter().filter(|f| f.is_global()).filter_map(|f| {
3579            let letters = f.short_pattern()?;
3580            let takes_value = f.takes_value();
3581            Some(quote!(#letters => ::core::option::Option::Some(#takes_value),))
3582        });
3583        let nested_global_letters = nested
3584            .iter()
3585            .map(|(_, ty)| quote!(.or_else(|| <#ty as __wa::Flatten>::global_short(__c))));
3586        let bind_nested: TokenStream2 = nested
3587            .iter()
3588            .map(|(n, ty)| {
3589                quote!(if <#ty as __wa::Flatten>::bind(&mut #n, &__arg, __input)? { true } else)
3590            })
3591            .collect();
3592        // A `+x` word is offered to the `+` arms only, here and below.
3593        let plus = if plus_options {
3594            quote! {
3595                __wa::Arg::Short(__flag) if __flag.plus => match __flag.letter {
3596                    #(#bind_plus)*
3597                    _ => #bind_nested { false }
3598                },
3599            }
3600        } else {
3601            quote!(__wa::Arg::Short(__flag) if __flag.plus => #bind_nested { false },)
3602        };
3603        let own_longs = matches_name(&own_longs);
3604        let own_prefixes = if prefixes.is_empty() {
3605            quote!(false)
3606        } else {
3607            quote!(matches!(__c, #(#prefixes)|*))
3608        };
3609        let nested_prefixes = nested
3610            .iter()
3611            .map(|(_, ty)| quote!(|| <#ty as __wa::Flatten>::is_prefix(__c)));
3612        quote! {
3613            #[doc(hidden)]
3614            #[allow(clippy::all, clippy::pedantic, clippy::nursery, clippy::restriction)]
3615            #vis struct #slots_name {
3616                #(#decls)*
3617                #(#displaced_decls)*
3618                #(#nested_decls)*
3619            }
3620
3621            #[automatically_derived]
3622            impl ::core::default::Default for #slots_name {
3623                fn default() -> Self {
3624                    Self {
3625                        #(#inits)*
3626                        #(#displaced_inits)*
3627                        #(#nested_inits)*
3628                    }
3629                }
3630            }
3631
3632            #[automatically_derived]
3633            #[allow(
3634                unused_mut,
3635                unused_variables,
3636                unreachable_code,
3637                clippy::all,
3638                clippy::pedantic,
3639                clippy::nursery,
3640                clippy::restriction
3641            )]
3642            impl ::winnow_args::__private::Flatten for #name {
3643                type Slots = #slots_name;
3644
3645                fn bind<'__i>(
3646                    __slots: &mut #slots_name,
3647                    __arg: &::winnow_args::Arg<'__i>,
3648                    __input: &mut ::winnow_args::Argv<'__i>,
3649                ) -> ::core::result::Result<bool, ::winnow_args::Error> {
3650                    use ::winnow_args::__private as __wa;
3651                    let __arg = *__arg;
3652                    let #slots_name { #(#mutable)* } = ::core::mem::take(__slots);
3653                    let __bound = match __arg {
3654                        __wa::Arg::Long(__flag) => match __flag.name {
3655                            #(#bind_long)*
3656                            _ => #bind_nested { false }
3657                        },
3658                        #plus
3659                        __wa::Arg::Short(__flag) => match __flag.letter {
3660                            #(#bind_short)*
3661                            _ => #bind_nested { false }
3662                        },
3663                        _ => false,
3664                    };
3665                    *__slots = #slots_name { #(#names)* };
3666                    ::core::result::Result::Ok(__bound)
3667                }
3668
3669                fn short(__c: char) -> ::core::option::Option<bool> {
3670                    use ::winnow_args::__private as __wa;
3671                    match __c {
3672                        #(#own_shorts)*
3673                        #(#flatten_shorts)*
3674                        _ => ::core::option::Option::None,
3675                    }
3676                }
3677
3678                fn bind_global<'__i>(
3679                    __slots: &mut #slots_name,
3680                    __arg: &::winnow_args::Arg<'__i>,
3681                    __input: &mut ::winnow_args::Argv<'__i>,
3682                ) -> ::core::result::Result<bool, ::winnow_args::Error> {
3683                    use ::winnow_args::__private as __wa;
3684                    let __arg = *__arg;
3685                    let #slots_name { #(#mutable)* } = ::core::mem::take(__slots);
3686                    let __bound = match __arg {
3687                        __wa::Arg::Long(__flag) => match __flag.name {
3688                            #(#global_long)*
3689                            _ => #global_nested { false }
3690                        },
3691                        __wa::Arg::Short(__flag) if __flag.plus => match __flag.letter {
3692                            #(#global_plus)*
3693                            _ => #global_nested { false }
3694                        },
3695                        __wa::Arg::Short(__flag) => match __flag.letter {
3696                            #(#global_short)*
3697                            _ => #global_nested { false }
3698                        },
3699                        _ => false,
3700                    };
3701                    *__slots = #slots_name { #(#names)* };
3702                    ::core::result::Result::Ok(__bound)
3703                }
3704
3705                fn global_short(__c: char) -> ::core::option::Option<bool> {
3706                    use ::winnow_args::__private as __wa;
3707                    let __own = match __c {
3708                        #(#global_letters)*
3709                        _ => ::core::option::Option::None,
3710                    };
3711                    __own #(#nested_global_letters)*
3712                }
3713
3714                fn is_prefix(__c: u8) -> bool {
3715                    use ::winnow_args::__private as __wa;
3716                    #own_prefixes #(#nested_prefixes)*
3717                }
3718
3719                fn is_long(__name: &[u8]) -> bool {
3720                    use ::winnow_args::__private as __wa;
3721                    #own_longs #(#flatten_longs)*
3722                }
3723
3724                fn finish(
3725                    __slots: #slots_name,
3726                    __input: &::winnow_args::Argv<'_>,
3727                ) -> ::core::result::Result<Self, ::winnow_args::Error> {
3728                    use ::winnow_args::__private as __wa;
3729                    let #slots_name { #(#mutable)* } = __slots;
3730                    #(#env_fallbacks)*
3731                    #exclusive
3732                    #supplied
3733                    #default_ifs
3734                    #(#default_fallbacks)*
3735                    #required
3736                    let __built = Self {
3737                        #(#build,)*
3738                        #(#skipped: ::core::default::Default::default(),)*
3739                        #(#flatten_build)*
3740                    };
3741                    #(#field_reads)*
3742                    ::core::result::Result::Ok(__built)
3743                }
3744            }
3745        }
3746    });
3747    let own_items = quote!(&[#(#help_items),*]);
3748    // A sequence enum with a positional variant takes every word.
3749    let positional_clash = sequence_ty
3750        .filter(|_| !positionals.is_empty() || trailing.is_some())
3751        .map(|ty| {
3752            quote! {
3753                const _: () = assert!(
3754                    !<#ty as ::winnow_args::Occurrence>::POSITIONAL,
3755                    "the sequence enum has a positional variant, which takes every word: this struct's positionals would never be filled",
3756                );
3757            }
3758        });
3759    // Ours, then the flattened structs', then the sequence enum's.
3760    let more: Vec<TokenStream2> = flattens
3761        .iter()
3762        .map(|(_, ty)| quote!(<#ty as ::winnow_args::Args>::HELP.items))
3763        .chain(
3764            sequence_ty
3765                .iter()
3766                .map(|ty| quote!(<#ty as ::winnow_args::Occurrence>::ITEMS)),
3767        )
3768        .collect();
3769    let items = if more.is_empty() {
3770        own_items
3771    } else {
3772        quote!({
3773            const __OWN: &[::winnow_args::help::Item] = #own_items;
3774            const _: () = assert!(
3775                !::winnow_args::help::items_clash(&[__OWN, #(#more),*]),
3776                "a flattened struct or the sequence enum declares a flag that this struct, or another of them, also declares",
3777            );
3778            #positional_clash
3779            const __N: usize = __OWN.len() #(+ #more.len())*;
3780            const __ALL: [::winnow_args::help::Item; __N] = ::winnow_args::__private::concat_items::<__N>(
3781                &[__OWN, #(#more),*],
3782            );
3783            &__ALL
3784        })
3785    };
3786    let mut spec_checks = Vec::new();
3787    let filed = filed(input)?;
3788    let identity = spec::identity(input.ident.span(), &filed);
3789    if spec::enabled() {
3790        spec_checks.extend(fields.iter().filter_map(|field| {
3791            spec_check(&input.generics, choices_type(field), field.spec.as_ref())
3792                .or_else(|| spec_check_stated(&input.generics, field))
3793        }));
3794        // The name stated on a `flatten` or `sequence` field, which `field`
3795        // does not read.
3796        let stated = |ident: &Ident| {
3797            let field = named.named.iter().find(|f| f.ident.as_ref() == Some(ident));
3798            field.map_or(Ok(None), |f| spec_name(&f.attrs))
3799        };
3800        let mut link = |kind: TokenStream2, stated: Option<String>, ty: &Type| {
3801            let name = spec_type(stated, ty)?;
3802            spec_checks.extend(spec_filed(&input.generics, kind, ty, &name));
3803            syn::Result::Ok(name)
3804        };
3805        for field in fields.iter().filter(|field| field.keywords) {
3806            if let Kind::Required(ty) = &field.kind {
3807                link(quote!(Args), field.spec.clone(), ty)?;
3808            }
3809        }
3810        let items = fields
3811            .iter()
3812            .filter(|field| !matches!(field.role, Role::Subcommand))
3813            .map(spec_item)
3814            .collect::<syn::Result<Vec<_>>>()?;
3815        let flatten = flattens
3816            .iter()
3817            .map(|(ident, ty)| link(quote!(Args), stated(ident)?, ty))
3818            .collect::<syn::Result<Vec<_>>>()?;
3819        let sequence = sequence
3820            .as_ref()
3821            .map(|(ident, ty)| link(quote!(Occurrence), stated(ident)?, ty))
3822            .transpose()?;
3823        let subcommand_ty = subcommand
3824            .map(|field| {
3825                link(
3826                    quote!(Subcommand),
3827                    field.spec.clone(),
3828                    subcommand_type(field),
3829                )
3830            })
3831            .transpose()?;
3832        spec::write_args(
3833            input.ident.span(),
3834            &spec::ArgsDoc {
3835                ident: filed.clone(),
3836                name: program_name.clone(),
3837                about: about_text,
3838                long_about: long_about_text,
3839                after_help: after_text,
3840                after_long_help: after_long_text,
3841                package_version: package_version(&version, with_version),
3842                items,
3843                flatten,
3844                sequence,
3845                subcommand: subcommand_ty,
3846                subcommand_required,
3847                help_flag,
3848                help_short: help_short_flag,
3849                long_only,
3850                unknown_flags_value,
3851            },
3852        )?;
3853    }
3854
3855    Ok(quote! {
3856        #(#spec_checks)*
3857        impl #impl_generics ::winnow_args::Args for #name #ty_generics #where_clause {
3858            const SPEC: &'static str = #identity;
3859
3860            // Compiled here and never inlined, so a program in another crate has
3861            // no copy of the help.
3862            #[inline(never)]
3863            fn help_data() -> &'static ::winnow_args::help::Command {
3864                <Self as ::winnow_args::Args>::HELP
3865            }
3866
3867            const HELP: &'static ::winnow_args::help::Command = &::winnow_args::help::Command {
3868                name: #program_name,
3869                about: #about,
3870                long_about: #long_about,
3871                after_help: #after_help,
3872                after_long_help: #after_long_help,
3873                items: #items,
3874                subcommands: #help_subcommands,
3875                subcommand_required: #subcommand_required,
3876                help_flag: #help_flag,
3877                help_short: #help_short_flag,
3878                version: #help_version,
3879            };
3880
3881            fn parse_argv_with(
3882                __input: &mut ::winnow_args::Argv<'_>,
3883                __globals: &mut dyn ::winnow_args::Globals,
3884            ) -> ::core::result::Result<Self, ::winnow_args::Error> {
3885                use ::winnow_args::__private as __wa;
3886                #(#slots)*
3887                #sequence_slot
3888                #unknown_slot
3889                #(#flatten_slots)*
3890                #(#displaced)*
3891                #position
3892                #filled_slot
3893                #help_on_empty
3894                while !__input.is_empty() {
3895                    #start
3896                    #lenient_bundle
3897                    #save_token
3898                    #bundle_start
3899                    let __arg = #lex;
3900                    match __arg {
3901                        __wa::Arg::Long(__flag) => match __flag.name {
3902                            #(#long_arms)*
3903                            #help_long
3904                            #version_long
3905                            _ => #sequence_arg #flatten_arg #late_long if !__globals.bind(&__arg, __input)? { #unknown_flag },
3906                        },
3907                        #plus_match
3908                        __wa::Arg::Short(__flag) => match __flag.letter {
3909                            #(#short_arms)*
3910                            #help_short
3911                            #version_short
3912                            _ => #sequence_arg #flatten_arg #late_short if !__globals.bind(&__arg, __input)? { #unknown_flag },
3913                        },
3914                        #separator_arm
3915                        #word_arm
3916                    }
3917                    #bundle_end
3918                }
3919                #(#env_fallbacks)*
3920                #exclusive
3921                #supplied
3922                #default_ifs
3923                #(#default_fallbacks)*
3924                #required
3925                let __built = Self {
3926                    #(#build,)*
3927                    #(#skipped: ::core::default::Default::default(),)*
3928                    #sequence_build
3929                    #unknown_build
3930                    #(#flatten_build)*
3931                };
3932                #(#field_reads)*
3933                ::core::result::Result::Ok(__built)
3934            }
3935        }
3936
3937        #flatten_impl
3938    })
3939}
3940
3941/// A struct-level `group("name", required, multiple)`.
3942struct Group {
3943    name: String,
3944    required: bool,
3945    multiple: bool,
3946}
3947
3948/// `#[arg(...)]` on the struct: options declared there rather than on a field.
3949#[derive(Default)]
3950struct StructOptions {
3951    groups: Vec<Group>,
3952    /// The word that starts a new invocation of this command.
3953    restart_token: Option<String>,
3954    /// The subcommand a word naming none selects.
3955    default_subcommand: Option<String>,
3956    /// A flag this struct does not take selects it too.
3957    default_subcommand_flags: bool,
3958    /// A bare invocation asks for help.
3959    arg_required_else_help: bool,
3960    /// The program name for the usage line; `argv[0]` when empty.
3961    name: String,
3962    /// `-V`/`--version` text: a literal, or `CARGO_PKG_VERSION`.
3963    version: Option<TokenStream2>,
3964    /// Help text around the lists; `about`/`long_about` default to the doc comment.
3965    about: Option<String>,
3966    long_about: Option<String>,
3967    after_help: String,
3968    after_long_help: String,
3969    disable_help_flag: bool,
3970    /// Help is `--help` alone, `-h` left undeclared (bash's builtins).
3971    disable_help_short: bool,
3972    disable_version_flag: bool,
3973    disable_help_subcommand: bool,
3974    /// An unknown flag-like word is a positional value.
3975    unknown_flags_value: bool,
3976    /// GNU's `getopt_long_only`: a long name may be spelled with one dash.
3977    long_only: bool,
3978    /// `+abc` is a bundle of `+` options (`set +eu`), for fields with `plus`.
3979    plus_options: bool,
3980}
3981
3982fn struct_options(input: &DeriveInput) -> syn::Result<StructOptions> {
3983    let mut options = StructOptions::default();
3984    for attr in input.attrs.iter().filter(|a| is_ours(a)) {
3985        attr.parse_nested_meta(|meta| {
3986            if skip_spec(&meta)? {
3987                return Ok(());
3988            }
3989            if meta.path.is_ident("restart_token") {
3990                options.restart_token = Some(meta.value()?.parse::<LitStr>()?.value());
3991                return Ok(());
3992            }
3993            if meta.path.is_ident("default_subcommand") {
3994                options.default_subcommand = Some(meta.value()?.parse::<LitStr>()?.value());
3995                return Ok(());
3996            }
3997            if meta.path.is_ident("default_subcommand_flags") {
3998                options.default_subcommand_flags = true;
3999                return Ok(());
4000            }
4001            if meta.path.is_ident("arg_required_else_help") {
4002                options.arg_required_else_help = true;
4003                return Ok(());
4004            }
4005            if meta.path.is_ident("long_only") {
4006                options.long_only = true;
4007                return Ok(());
4008            }
4009            if meta.path.is_ident("plus_options") {
4010                options.plus_options = true;
4011                return Ok(());
4012            }
4013            if meta.path.is_ident("unknown_flags") {
4014                let mode = meta.value()?.parse::<LitStr>()?;
4015                options.unknown_flags_value = match mode.value().as_str() {
4016                    "value" => true,
4017                    "error" => false,
4018                    _ => {
4019                        return Err(syn::Error::new(
4020                            mode.span(),
4021                            "expected \"value\" or \"error\"",
4022                        ));
4023                    }
4024                };
4025                return Ok(());
4026            }
4027            let text = |meta: &syn::meta::ParseNestedMeta<'_>| -> syn::Result<String> {
4028                Ok(meta.value()?.parse::<LitStr>()?.value())
4029            };
4030            if meta.path.is_ident("name") {
4031                options.name = text(&meta)?;
4032                return Ok(());
4033            }
4034            if meta.path.is_ident("version") {
4035                options.version = Some(if meta.input.peek(syn::Token![=]) {
4036                    let version = meta.value()?.parse::<LitStr>()?;
4037                    quote!(#version)
4038                } else {
4039                    quote!(::core::env!("CARGO_PKG_VERSION"))
4040                });
4041                return Ok(());
4042            }
4043            for (key, slot) in [
4044                ("about", &mut options.about),
4045                ("long_about", &mut options.long_about),
4046            ] {
4047                if meta.path.is_ident(key) {
4048                    *slot = Some(text(&meta)?);
4049                    return Ok(());
4050                }
4051            }
4052            for (key, slot) in [
4053                ("after_help", &mut options.after_help),
4054                ("after_long_help", &mut options.after_long_help),
4055            ] {
4056                if meta.path.is_ident(key) {
4057                    *slot = text(&meta)?;
4058                    return Ok(());
4059                }
4060            }
4061            for (key, slot) in [
4062                ("disable_help_flag", &mut options.disable_help_flag),
4063                ("disable_help_short", &mut options.disable_help_short),
4064                ("disable_version_flag", &mut options.disable_version_flag),
4065                (
4066                    "disable_help_subcommand",
4067                    &mut options.disable_help_subcommand,
4068                ),
4069            ] {
4070                if meta.path.is_ident(key) {
4071                    *slot = true;
4072                    return Ok(());
4073                }
4074            }
4075            if !meta.path.is_ident("group") {
4076                return Err(meta.error(
4077                    "expected `group(\"name\", ...)`, `restart_token`, `default_subcommand`, \
4078                     `default_subcommand_flags`, `arg_required_else_help`, `unknown_flags`, `long_only`, `plus_options`, `name`, `version`, `about`, \
4079                     `long_about`, `after_help`, `after_long_help` or a `disable_*` option",
4080                ));
4081            }
4082            let content;
4083            syn::parenthesized!(content in meta.input);
4084            let name = content.parse::<LitStr>()?.value();
4085            let (mut required, mut multiple) = (false, false);
4086            while !content.is_empty() {
4087                content.parse::<syn::Token![,]>()?;
4088                let flag = content.parse::<Ident>()?;
4089                match flag.to_string().as_str() {
4090                    "required" => required = true,
4091                    "multiple" => multiple = true,
4092                    _ => {
4093                        return Err(syn::Error::new(
4094                            flag.span(),
4095                            "expected `required` or `multiple`",
4096                        ));
4097                    }
4098                }
4099            }
4100            options.groups.push(Group {
4101                name,
4102                required,
4103                multiple,
4104            });
4105            Ok(())
4106        })?;
4107    }
4108    Ok(options)
4109}
4110
4111/// The relations between fields, resolved to field indices.
4112struct Rules {
4113    /// Pairs `(a, b)` that may not both be supplied; each unordered pair once.
4114    conflicts: Vec<(usize, usize)>,
4115    /// For each field, the fields it displaces when bound (symmetric).
4116    beats: Vec<Vec<usize>>,
4117    /// `(field, target)`: `field` supplied needs `target` to have a value.
4118    requires: Vec<(usize, usize)>,
4119    /// `(field, others)`: `field` needs a value unless one of `others` has one.
4120    required_unless: Vec<(usize, Vec<usize>)>,
4121    /// Members of each struct-level group, in `groups` order.
4122    members: Vec<Vec<usize>>,
4123    /// `(field, other, the value `other` must have, the field's default then)`.
4124    default_ifs: Vec<(usize, usize, Option<String>, String)>,
4125}
4126
4127impl Rules {
4128    fn new(fields: &[Field], groups: &[Group]) -> syn::Result<Self> {
4129        let resolve = |from: &Field, selector: &str| -> syn::Result<usize> {
4130            fields
4131                .iter()
4132                .position(
4133                    |f| match (selector.strip_prefix("--"), selector.strip_prefix('-')) {
4134                        (Some(long), _) => f.longs().contains(&long),
4135                        (None, Some(short)) => {
4136                            let mut chars = short.chars();
4137                            chars
4138                                .next()
4139                                .is_some_and(|c| chars.next().is_none() && f.shorts().contains(&c))
4140                        }
4141                        (None, None) => {
4142                            matches!(&f.role, Role::Positional { name, .. } if name == selector)
4143                                || f.ident == selector
4144                        }
4145                    },
4146                )
4147                .ok_or_else(|| {
4148                    // A rule is about a flag having a value, whichever way it was spelled.
4149                    let negated = selector
4150                        .strip_prefix("--")
4151                        .and_then(|long| fields.iter().find(|f| f.negate.as_deref() == Some(long)));
4152                    let message = match negated {
4153                        Some(f) => format!(
4154                            "`{selector}` is the negation of `{}`: name that flag instead",
4155                            f.display()
4156                        ),
4157                        None => format!("`{selector}` names no flag or positional of this struct"),
4158                    };
4159                    syn::Error::new(from.ident.span(), message)
4160                })
4161        };
4162        let mut rules = Rules {
4163            conflicts: Vec::new(),
4164            beats: vec![Vec::new(); fields.len()],
4165            requires: Vec::new(),
4166            required_unless: Vec::new(),
4167            members: vec![Vec::new(); groups.len()],
4168            default_ifs: Vec::new(),
4169        };
4170        for (i, f) in fields.iter().enumerate() {
4171            for selector in &f.conflicts {
4172                let j = resolve(f, selector)?;
4173                let pair = (i.min(j), i.max(j));
4174                if !rules.conflicts.contains(&pair) {
4175                    rules.conflicts.push(pair);
4176                }
4177            }
4178            for selector in &f.overrides {
4179                let j = resolve(f, selector)?;
4180                if !rules.beats[i].contains(&j) {
4181                    rules.beats[i].push(j);
4182                }
4183                if !rules.beats[j].contains(&i) {
4184                    rules.beats[j].push(i);
4185                }
4186            }
4187            for selector in &f.requires {
4188                rules.requires.push((i, resolve(f, selector)?));
4189            }
4190            for (selector, equals, value) in &f.default_ifs {
4191                let other = resolve(f, selector)?;
4192                let comparable = match &fields[other].kind {
4193                    Kind::Count(_) => false,
4194                    Kind::Switch => equals.as_deref().is_none_or(is_bool_word),
4195                    _ => !fields[other].keywords,
4196                };
4197                if equals.is_some() && !comparable {
4198                    return Err(syn::Error::new(
4199                        f.ident.span(),
4200                        format!(
4201                            "`{selector}` has no value to compare: use `default_if(\"{selector}\", \"…\")`"
4202                        ),
4203                    ));
4204                }
4205                rules
4206                    .default_ifs
4207                    .push((i, other, equals.clone(), value.clone()));
4208            }
4209            if !f.required_unless.is_empty() {
4210                let others = f
4211                    .required_unless
4212                    .iter()
4213                    .map(|selector| resolve(f, selector))
4214                    .collect::<syn::Result<_>>()?;
4215                rules.required_unless.push((i, others));
4216            }
4217            if let Some(group) = &f.group {
4218                let g = groups
4219                    .iter()
4220                    .position(|g| &g.name == group)
4221                    .ok_or_else(|| {
4222                        syn::Error::new(
4223                            f.ident.span(),
4224                            format!("no `#[arg(group(\"{group}\"))]` on the struct"),
4225                        )
4226                    })?;
4227                rules.members[g].push(i);
4228            }
4229        }
4230        if let Some((g, _)) = groups
4231            .iter()
4232            .zip(&rules.members)
4233            .find(|(g, members)| g.required && members.is_empty())
4234        {
4235            return Err(syn::Error::new(
4236                Span::call_site(),
4237                format!("group `{}` is required, but no field is in it", g.name),
4238            ));
4239        }
4240        Ok(rules)
4241    }
4242
4243    /// Whether an `overrides` can unset `f`, so its fallbacks must check.
4244    fn is_displaced(&self, fields: &[Field], f: &Field) -> bool {
4245        !self.beats[index_of(fields, f)].is_empty()
4246    }
4247
4248    /// `let mut __displaced_x = false;` for every field an `overrides` can unset.
4249    fn displaced_slots(&self, fields: &[Field]) -> Vec<TokenStream2> {
4250        fields
4251            .iter()
4252            .enumerate()
4253            .filter(|(i, _)| !self.beats[*i].is_empty())
4254            .map(|(_, f)| {
4255                let displaced = displaced_flag(&f.ident);
4256                quote!(let mut #displaced = false;)
4257            })
4258            .collect()
4259    }
4260
4261    /// After binding `f`: unset what it beats, and mark `f` as standing.
4262    fn displace(&self, fields: &[Field], f: &Field) -> TokenStream2 {
4263        let i = index_of(fields, f);
4264        if self.beats[i].is_empty() {
4265            return quote!();
4266        }
4267        let standing = displaced_flag(&f.ident);
4268        let clears = self.beats[i].iter().map(|&j| {
4269            let other = &fields[j];
4270            let slot = slot(&other.ident);
4271            let displaced = displaced_flag(&other.ident);
4272            let clear = match &other.kind {
4273                _ if other.keywords => quote!(#slot.clear();),
4274                Kind::Switch if other.negate.is_some() => {
4275                    quote!(#slot = ::core::option::Option::None;)
4276                }
4277                Kind::Switch => quote!(#slot = false;),
4278                Kind::Count(_) => quote!(#slot = 0;),
4279                Kind::Optional(_) | Kind::Required(_) => {
4280                    quote!(#slot = ::core::option::Option::None;)
4281                }
4282                Kind::Many(_) => quote!(#slot.clear();),
4283            };
4284            quote!(#clear #displaced = true;)
4285        });
4286        quote!(#standing = false; #(#clears)*)
4287    }
4288
4289    /// Conflicts and at-most-one groups, judged on what was supplied.
4290    fn exclusive(&self, fields: &[Field], groups: &[Group]) -> TokenStream2 {
4291        let pairs = self.conflicts.iter().map(|&(a, b)| {
4292            let (fa, fb) = (&fields[a], &fields[b]);
4293            let (has_a, has_b) = (fa.has(), fb.has());
4294            let (name_a, name_b) = (fa.display(), fb.display());
4295            quote! {
4296                if #has_a && #has_b {
4297                    return ::core::result::Result::Err(
4298                        __wa::Error::conflict(__input.offset(), #name_a, #name_b),
4299                    );
4300                }
4301            }
4302        });
4303        let at_most_one = groups.iter().zip(&self.members).filter(|(g, _)| !g.multiple).map(|(_, members)| {
4304            let checks = members.iter().map(|&m| {
4305                let has = fields[m].has();
4306                let name = fields[m].display();
4307                quote! {
4308                    if #has {
4309                        if let ::core::option::Option::Some(__first) = __first {
4310                            return ::core::result::Result::Err(
4311                                __wa::Error::conflict(__input.offset(), __first, #name),
4312                            );
4313                        }
4314                        __first = ::core::option::Option::Some(#name);
4315                    }
4316                }
4317            });
4318            quote! {{
4319                let mut __first: ::core::option::Option<&'static str> = ::core::option::Option::None;
4320                #(#checks)*
4321            }}
4322        });
4323        quote!(#(#pairs)* #(#at_most_one)*)
4324    }
4325
4326    /// `default_if`: each condition judged on what was supplied, then the first
4327    /// that holds for a field still without a value gives it one.
4328    fn default_ifs(&self, fields: &[Field]) -> TokenStream2 {
4329        let (mut conditions, mut fills) = (Vec::new(), Vec::new());
4330        for (n, (field, other, equals, value)) in self.default_ifs.iter().enumerate() {
4331            let (f, other) = (&fields[*field], &fields[*other]);
4332            let holds = format_ident!("__default_if_{n}");
4333            let other_slot = slot(&other.ident);
4334            let is = |ty: &Type, equals: &str| {
4335                let literal = LitByteStr::new(equals.as_bytes(), Span::call_site());
4336                quote! {
4337                    |__v| <#ty as ::winnow_args::FromArg>::from_arg(__wa::BStr::new(#literal))
4338                        .is_ok_and(|__literal| __literal == *__v)
4339                }
4340            };
4341            let condition = match (equals, &other.kind) {
4342                (None, _) => other.has(),
4343                (Some(equals), Kind::Switch) => {
4344                    let equals = equals == "true";
4345                    match other.negate {
4346                        Some(_) => quote!((#other_slot == ::core::option::Option::Some(#equals))),
4347                        None => quote!((#other_slot == #equals)),
4348                    }
4349                }
4350                (Some(equals), Kind::Optional(ty) | Kind::Required(ty)) => {
4351                    let is = is(ty, equals);
4352                    quote!(#other_slot.as_ref().is_some_and(#is))
4353                }
4354                (Some(equals), Kind::Many(ty)) => {
4355                    let is = is(ty, equals);
4356                    quote!(#other_slot.iter().any(#is))
4357                }
4358                (Some(_), Kind::Count(_)) => unreachable!("refused when the rules were read"),
4359            };
4360            conditions.push(quote!(let #holds = #condition;));
4361            let unset = f.unset(self.is_displaced(fields, f));
4362            let slot = slot(&f.ident);
4363            let fill = match &f.kind {
4364                Kind::Switch => {
4365                    let value = value == "true";
4366                    match f.negate {
4367                        Some(_) => quote!(#slot = ::core::option::Option::Some(#value);),
4368                        None => quote!(#slot = #value;),
4369                    }
4370                }
4371                _ => {
4372                    let bytes = LitByteStr::new(value.as_bytes(), Span::call_site());
4373                    f.assign(quote!(__wa::BStr::new(#bytes)), &f.display())
4374                }
4375            };
4376            fills.push(quote!(if #holds && #unset { #fill }));
4377        }
4378        quote!(#(#conditions)* #(#fills)*)
4379    }
4380
4381    /// Whether each `requires`-declaring field was supplied, before defaults fill it.
4382    fn supplied(&self, fields: &[Field]) -> TokenStream2 {
4383        let mut seen = Vec::new();
4384        let snapshots = self.requires.iter().filter_map(|&(i, _)| {
4385            if seen.contains(&i) {
4386                return None;
4387            }
4388            seen.push(i);
4389            let supplied = format_ident!("__supplied_{}", fields[i].ident);
4390            let has = fields[i].has();
4391            Some(quote!(let #supplied = #has;))
4392        });
4393        quote!(#(#snapshots)*)
4394    }
4395
4396    /// `required`, `required_unless`, required groups and `requires`, judged on
4397    /// what has a value, defaults included.
4398    fn required(&self, fields: &[Field], groups: &[Group]) -> TokenStream2 {
4399        let missing = |f: &Field| {
4400            let name = f.display();
4401            match f.role {
4402                Role::Positional { .. } => {
4403                    quote!(__wa::Error::missing_argument(__input.offset(), #name))
4404                }
4405                _ => quote!(__wa::Error::missing_required(__input.offset(), #name)),
4406            }
4407        };
4408        // A flag another one overrode was given its answer: it is not missing.
4409        let displaced = |f: &Field| {
4410            self.is_displaced(fields, f).then(|| {
4411                let flag = displaced_flag(&f.ident);
4412                quote!(&& !#flag)
4413            })
4414        };
4415        let plain = fields.iter().filter(|f| f.required).map(|f| {
4416            let (has, displaced) = (f.has(), displaced(f));
4417            let error = missing(f);
4418            quote!(if !#has #displaced { return ::core::result::Result::Err(#error); })
4419        });
4420        let unless = self.required_unless.iter().map(|(i, others)| {
4421            let (has, displaced) = (fields[*i].has(), displaced(&fields[*i]));
4422            let others = others.iter().map(|&j| fields[j].has());
4423            let error = missing(&fields[*i]);
4424            quote! {
4425                if !#has #displaced #(&& !#others)* {
4426                    return ::core::result::Result::Err(#error);
4427                }
4428            }
4429        });
4430        let one_of = groups
4431            .iter()
4432            .zip(&self.members)
4433            .filter(|(g, _)| g.required)
4434            .map(|(g, members)| {
4435                let name = &g.name;
4436                let has = members.iter().map(|&m| fields[m].has());
4437                let names = members.iter().map(|&m| fields[m].display());
4438                quote! {
4439                    if #(!#has)&&* {
4440                        return ::core::result::Result::Err(
4441                            __wa::Error::missing_one_of(__input.offset(), #name, &[#(#names),*]),
4442                        );
4443                    }
4444                }
4445            });
4446        let requires = self.requires.iter().map(|&(i, j)| {
4447            let supplied = format_ident!("__supplied_{}", fields[i].ident);
4448            let has = fields[j].has();
4449            let (by, target) = (fields[i].display(), fields[j].display());
4450            quote! {
4451                if #supplied && !#has {
4452                    return ::core::result::Result::Err(
4453                        __wa::Error::required_by(__input.offset(), #target, #by),
4454                    );
4455                }
4456            }
4457        });
4458        quote!(#(#plain)* #(#unless)* #(#one_of)* #(#requires)*)
4459    }
4460}
4461
4462/// A `Vec<T>` field's name and `T`; `shape` is the error for any other type.
4463fn vec_field(f: Option<&syn::Field>, shape: &str) -> syn::Result<Option<(Ident, Type)>> {
4464    let Some(f) = f else {
4465        return Ok(None);
4466    };
4467    let ty = last_segment(&f.ty)
4468        .filter(|s| s.ident == "Vec")
4469        .and_then(inner)
4470        .ok_or_else(|| syn::Error::new(f.ty.span(), shape))?;
4471    Ok(Some((f.ident.clone().expect("named field"), ty.clone())))
4472}
4473
4474/// The enum of a subcommand field.
4475fn subcommand_type(f: &Field) -> &Type {
4476    match &f.kind {
4477        Kind::Optional(ty) | Kind::Required(ty) => ty,
4478        _ => unreachable!("rejected for subcommands in `field`"),
4479    }
4480}
4481
4482fn displaced_flag(ident: &Ident) -> Ident {
4483    format_ident!("__displaced_{}", ident)
4484}
4485
4486fn index_of(fields: &[Field], f: &Field) -> usize {
4487    fields
4488        .iter()
4489        .position(|g| g.ident == f.ident)
4490        .expect("a field of this struct")
4491}
4492
4493fn slot(ident: &Ident) -> Ident {
4494    format_ident!("__slot_{}", ident)
4495}
4496
4497fn field(f: &syn::Field) -> syn::Result<Field> {
4498    let ident = f.ident.clone().expect("named field");
4499    let bare = ident.to_string().trim_start_matches("r#").to_owned();
4500
4501    let mut short = None;
4502    let mut long = None;
4503    let mut positional = false;
4504    let mut count = false;
4505    let mut subcommand = false;
4506    let mut global = false;
4507    let mut alias = Vec::new();
4508    let mut delimiter = None;
4509    let mut choices = None;
4510    let mut env = None;
4511    let mut default = None;
4512    let mut double_dash = None;
4513    let mut default_missing = None;
4514    let mut negative_numbers = false;
4515    let mut hyphen_values = false;
4516    let mut require_equals = false;
4517    let mut keep_equals = false;
4518    let (mut two_dashes, mut prefix) = (false, false);
4519    let mut values = 1;
4520    let (mut values_max, mut value_terminator) = (None, None);
4521    let (mut default_fn, mut default_note, mut default_ifs) = (None, None, Vec::new());
4522    let mut stop_flags = false;
4523    let mut plus: Option<char> = None;
4524    let mut keywords = false;
4525    let mut short_aliases = Vec::new();
4526    let mut negate: Option<Option<String>> = None;
4527    let (doc_help, doc_long_help) = docs(&f.attrs);
4528    let (mut help, mut long_help, mut heading, mut hide) = (None, None, None, false);
4529    let (mut conflicts, mut overrides, mut requires) = (Vec::new(), Vec::new(), Vec::new());
4530    let (mut required, mut required_unless, mut group) = (false, Vec::new(), None);
4531    let mut value_name = None;
4532    let (mut hint, mut complete) = (None, None);
4533    for attr in f.attrs.iter().filter(|a| is_ours(a)) {
4534        attr.parse_nested_meta(|meta| {
4535            if meta.path.is_ident("hint") {
4536                let name = meta.value()?.parse::<LitStr>()?;
4537                let variant = match name.value().as_str() {
4538                    "file" => "Files",
4539                    "dir" => "Dirs",
4540                    "command" => "Commands",
4541                    "none" => "Nothing",
4542                    _ => {
4543                        return Err(syn::Error::new(
4544                            name.span(),
4545                            "a hint is \"file\", \"dir\", \"command\" or \"none\"",
4546                        ));
4547                    }
4548                };
4549                hint = Some(syn::Ident::new(variant, name.span()));
4550            } else if meta.path.is_ident("complete") {
4551                complete = Some(meta.value()?.parse::<syn::Path>()?);
4552            } else if meta.path.is_ident("short") {
4553                let c = if meta.input.peek(syn::Token![=]) {
4554                    meta.value()?.parse::<LitChar>()?.value()
4555                } else {
4556                    bare.chars()
4557                        .next()
4558                        .ok_or_else(|| meta.error("cannot infer a short name"))?
4559                };
4560                // A second `short` is another letter, as `long` is another name.
4561                match short {
4562                    None => short = Some(c),
4563                    Some(_) => short_aliases.push(c),
4564                }
4565            } else if meta.path.is_ident("long") {
4566                let name = if meta.input.peek(syn::Token![=]) {
4567                    meta.value()?.parse::<LitStr>()?.value()
4568                } else {
4569                    bare.replace('_', "-")
4570                };
4571                // A second `long` is another spelling.
4572                match long {
4573                    None => long = Some(name),
4574                    Some(_) => alias.push(name),
4575                }
4576            } else if meta.path.is_ident("positional") {
4577                positional = true;
4578            } else if meta.path.is_ident("count") {
4579                count = true;
4580            } else if meta.path.is_ident("subcommand") {
4581                subcommand = true;
4582            } else if meta.path.is_ident("global") {
4583                global = true;
4584            } else if meta.path.is_ident("alias") {
4585                alias.extend(aliases(&meta)?);
4586            } else if meta.path.is_ident("help") {
4587                help = Some(meta.value()?.parse::<LitStr>()?.value());
4588            } else if meta.path.is_ident("long_help") {
4589                long_help = Some(meta.value()?.parse::<LitStr>()?.value());
4590            } else if meta.path.is_ident("help_heading") {
4591                heading = Some(meta.value()?.parse::<LitStr>()?.value());
4592            } else if meta.path.is_ident("hide") {
4593                hide = true;
4594            } else if meta.path.is_ident("negate") {
4595                negate = Some(if meta.input.peek(syn::Token![=]) {
4596                    let name = meta.value()?.parse::<LitStr>()?.value();
4597                    // A leading `--` is not part of the name.
4598                    Some(name.strip_prefix("--").unwrap_or(&name).to_owned())
4599                } else {
4600                    None
4601                });
4602            } else if meta.path.is_ident("keywords") {
4603                keywords = true;
4604            } else if skip_spec(&meta)? {
4605            } else if meta.path.is_ident("plus") {
4606                plus = Some(meta.value()?.parse::<LitChar>()?.value());
4607            } else if meta.path.is_ident("stop_flags") {
4608                stop_flags = true;
4609            } else if meta.path.is_ident("values") {
4610                let count = meta.value()?.parse::<syn::Expr>()?;
4611                (values, values_max) = value_count(&count)?;
4612            } else if meta.path.is_ident("value_terminator") {
4613                value_terminator = Some(meta.value()?.parse::<LitStr>()?.value());
4614            } else if meta.path.is_ident("default_fn") {
4615                default_fn = Some(meta.value()?.parse::<syn::ExprPath>()?);
4616            } else if meta.path.is_ident("default_note") {
4617                default_note = Some(meta.value()?.parse::<LitStr>()?.value());
4618            } else if meta.path.is_ident("default_if") {
4619                let content;
4620                syn::parenthesized!(content in meta.input);
4621                let list =
4622                    syn::punctuated::Punctuated::<LitStr, syn::Token![,]>::parse_terminated(&content)?;
4623                let mut words = list.iter().map(LitStr::value);
4624                default_ifs.push(match (words.next(), words.next(), words.next(), words.next()) {
4625                    (Some(selector), Some(value), None, _) => (selector, None, value),
4626                    (Some(selector), Some(equals), Some(value), None) => {
4627                        (selector, Some(equals), value)
4628                    }
4629                    _ => {
4630                        return Err(meta.error(
4631                            "expected `default_if(\"--flag\", \"value\")` or \
4632                             `default_if(\"--flag\", \"its value\", \"value\")`",
4633                        ));
4634                    }
4635                });
4636            } else if meta.path.is_ident("two_dashes") {
4637                two_dashes = true;
4638            } else if meta.path.is_ident("prefix") {
4639                prefix = true;
4640            } else if meta.path.is_ident("require_equals") {
4641                require_equals = true;
4642            } else if meta.path.is_ident("keep_equals") {
4643                keep_equals = true;
4644            } else if meta.path.is_ident("allow_hyphen_values") {
4645                hyphen_values = true;
4646            } else if meta.path.is_ident("allow_negative_numbers") {
4647                negative_numbers = true;
4648            } else if meta.path.is_ident("default_missing") {
4649                default_missing = Some(meta.value()?.parse::<LitStr>()?.value());
4650            } else if meta.path.is_ident("double_dash") {
4651                let mode = meta.value()?.parse::<LitStr>()?;
4652                double_dash = Some(match mode.value().as_str() {
4653                    "required" => DoubleDash::Required,
4654                    "automatic" => DoubleDash::Automatic,
4655                    "optional" => DoubleDash::Optional,
4656                    "preserve" => DoubleDash::Preserve,
4657                    _ => {
4658                        return Err(syn::Error::new(
4659                            mode.span(),
4660                            "expected \"required\", \"automatic\", \"optional\" or \"preserve\"",
4661                        ));
4662                    }
4663                });
4664            } else if meta.path.is_ident("conflicts") {
4665                conflicts.extend(aliases(&meta)?);
4666            } else if meta.path.is_ident("overrides") {
4667                overrides.extend(aliases(&meta)?);
4668            } else if meta.path.is_ident("requires") {
4669                requires.extend(aliases(&meta)?);
4670            } else if meta.path.is_ident("required_unless") {
4671                required_unless.extend(aliases(&meta)?);
4672            } else if meta.path.is_ident("required") {
4673                required = true;
4674            } else if meta.path.is_ident("group") {
4675                group = Some(meta.value()?.parse::<LitStr>()?.value());
4676            } else if meta.path.is_ident("env") {
4677                env = Some(meta.value()?.parse::<LitStr>()?.value());
4678            } else if meta.path.is_ident("default") {
4679                default = Some(meta.value()?.parse::<LitStr>()?.value());
4680            } else if meta.path.is_ident("choices") {
4681                let content;
4682                syn::parenthesized!(content in meta.input);
4683                let list =
4684                    syn::punctuated::Punctuated::<LitStr, syn::Token![,]>::parse_terminated(&content)?;
4685                choices = Some(list.iter().map(LitStr::value).collect());
4686            } else if meta.path.is_ident("delimiter") {
4687                let c = meta.value()?.parse::<LitChar>()?;
4688                if !c.value().is_ascii() {
4689                    return Err(syn::Error::new(c.span(), "a delimiter is one ASCII character"));
4690                }
4691                delimiter = Some(c.value() as u8);
4692            } else if meta.path.is_ident("value_name") {
4693                value_name = Some(meta.value()?.parse::<LitStr>()?.value());
4694            } else {
4695                return Err(meta.error(
4696                    "unknown `arg` option; expected one of `short`, `long`, `alias`, `global`, `count`, \
4697                     `positional`, `value_name`, `double_dash`, `subcommand`, `flatten`, `sequence`, `unknown`, `skip`, \
4698                     `delimiter`, `choices`, `env`, `default`, `default_missing`, `allow_negative_numbers`, \
4699                     `allow_hyphen_values`, `require_equals`, `keep_equals`, `negate`, `two_dashes`, `prefix`, `values`, \
4700                     `value_terminator`, `default_fn`, `default_note`, `default_if`, \
4701                     `stop_flags`, `plus`, `keywords`, `conflicts`, `overrides`, `requires`, `required`, `required_unless`, \
4702                     `group`, `help`, `long_help`, `help_heading`, `hide`, `hint`, `complete`",
4703                ));
4704            }
4705            Ok(())
4706        })?;
4707    }
4708
4709    let error = |msg: String| Err(syn::Error::new(f.span(), msg));
4710    if delimiter.is_some() && (count || !matches!(kind(&f.ty), Kind::Many(_))) {
4711        return error("`delimiter` needs a `Vec<T>` field to put the pieces in".into());
4712    }
4713    let kind = if count {
4714        Kind::Count(f.ty.clone())
4715    } else {
4716        kind(&f.ty)
4717    };
4718    if default_missing.is_some()
4719        && (positional || subcommand || matches!(kind, Kind::Switch | Kind::Count(_)))
4720    {
4721        return error("`default_missing` is for flags that take a value".into());
4722    }
4723    if (hyphen_values || require_equals)
4724        && (positional || subcommand || matches!(kind, Kind::Switch | Kind::Count(_)))
4725    {
4726        return error(
4727            "`allow_hyphen_values` and `require_equals` are for flags that take a value".into(),
4728        );
4729    }
4730    if negative_numbers && (subcommand || matches!(kind, Kind::Switch | Kind::Count(_))) {
4731        return error("`allow_negative_numbers` is for fields that take a value".into());
4732    }
4733    let tristate =
4734        plus.is_some() && plus == short && matches!(&kind, Kind::Optional(ty) if is_bool(ty));
4735    if plus.is_some()
4736        && (positional || subcommand || !tristate && matches!(kind, Kind::Switch | Kind::Count(_)))
4737    {
4738        return error(
4739            "`plus` is for a flag taking a value, or an `Option<bool>` with the same `short` letter".into(),
4740        );
4741    }
4742    if keywords
4743        && (positional
4744            || subcommand
4745            || !matches!(kind, Kind::Required(_))
4746            || env.is_some()
4747            || default.is_some()
4748            || default_missing.is_some()
4749            || choices.is_some()
4750            || delimiter.is_some())
4751    {
4752        return error(
4753            "`keywords` is for a flag whose type derives `Args`, without `env`, `default`, \
4754             `default_missing`, `choices` or `delimiter`"
4755                .into(),
4756        );
4757    }
4758    if stop_flags && !positional {
4759        return error("`stop_flags` is for positional fields".into());
4760    }
4761    if double_dash.is_some() && !positional {
4762        return error("`double_dash` is for positional fields".into());
4763    }
4764    if (values > 1 || values_max.is_some())
4765        && (positional
4766            || subcommand
4767            || !matches!(kind, Kind::Many(_))
4768            || delimiter.is_some()
4769            || default_missing.is_some())
4770    {
4771        return error(
4772            "`values` is for a `Vec<T>` flag, without `delimiter` or `default_missing`".into(),
4773        );
4774    }
4775    if values_max.is_some() && require_equals {
4776        return error("a range of `values` takes detached words: not with `require_equals`".into());
4777    }
4778    if value_terminator.is_some() && values_max.is_none() {
4779        return error("`value_terminator` ends a range of `values` (`values = 1..`)".into());
4780    }
4781    let valueless = matches!(kind, Kind::Switch | Kind::Count(_)) && negate.is_none();
4782    if default_fn.is_some() && (default.is_some() || subcommand || keywords || valueless) {
4783        return error(
4784            "`default_fn` is for a field that takes a value, or `negate`, without `default`".into(),
4785        );
4786    }
4787    if default_note.is_some() && default_fn.is_none() {
4788        return error("`default_note` describes a `default_fn`".into());
4789    }
4790    if !default_ifs.is_empty() && (subcommand || keywords || matches!(kind, Kind::Count(_))) {
4791        return error("`default_if` is for a switch or a field that takes a value".into());
4792    }
4793    if let (Kind::Switch, Some((.., value))) =
4794        (&kind, default_ifs.iter().find(|d| !is_bool_word(&d.2)))
4795    {
4796        return error(format!(
4797            "a switch's `default_if` value is `true` or `false`, not `{value}`"
4798        ));
4799    }
4800    if two_dashes && (long.is_none() && alias.is_empty() || positional || subcommand) {
4801        return error("`two_dashes` is for flags with a long name".into());
4802    }
4803    if prefix
4804        && (short.is_none_or(|c| !c.is_ascii())
4805            || positional
4806            || subcommand
4807            || matches!(kind, Kind::Switch | Kind::Count(_)))
4808    {
4809        return error("`prefix` is for a value-taking flag with an ASCII `short` letter".into());
4810    }
4811    let flag_value_name =
4812        (!positional && !subcommand && !matches!(kind, Kind::Switch | Kind::Count(_)))
4813            .then(|| value_name.clone().unwrap_or_else(|| bare.to_uppercase()));
4814    let role = if subcommand {
4815        if positional || count || global || short.is_some() || long.is_some() || !alias.is_empty() {
4816            return error("a subcommand field takes no other `arg` options".into());
4817        }
4818        if !matches!(kind, Kind::Optional(_) | Kind::Required(_)) {
4819            return error("a subcommand field is `E` or `Option<E>`".into());
4820        }
4821        Role::Subcommand
4822    } else if positional {
4823        if short.is_some() || long.is_some() || global || !alias.is_empty() {
4824            return error("a positional field has no `short`, `long`, `alias` or `global`".into());
4825        }
4826        if matches!(kind, Kind::Switch | Kind::Count(_)) {
4827            return error("a positional field cannot be `bool` or `count`".into());
4828        }
4829        Role::Positional {
4830            double_dash: double_dash.unwrap_or(DoubleDash::Optional),
4831            name: value_name.unwrap_or_else(|| bare.to_uppercase()),
4832        }
4833    } else {
4834        // Like bpaf: a flag with no names is `--field-name` (unless it is
4835        // spelled only `+c`).
4836        if short.is_none() && long.is_none() && plus.is_none() {
4837            long = Some(bare.replace('_', "-"));
4838        }
4839        for l in long.iter().chain(&alias) {
4840            if l.is_empty() || l.starts_with('-') || l.contains('=') {
4841                return error(format!("`{l}` is not a usable long name"));
4842            }
4843        }
4844        if let Some(c) = short
4845            && (c == '-' || c == '=')
4846        {
4847            return error(format!("`{c}` is not a usable short name"));
4848        }
4849        Role::Flag {
4850            short,
4851            long,
4852            aliases: alias,
4853            global,
4854        }
4855    };
4856
4857    if choices.is_some()
4858        && (matches!(kind, Kind::Switch | Kind::Count(_)) || matches!(role, Role::Subcommand))
4859    {
4860        return error("`choices` needs a field that takes a value".into());
4861    }
4862    if (env.is_some() || default.is_some()) && matches!(role, Role::Subcommand) {
4863        return error("a subcommand field takes no `env` or `default`".into());
4864    }
4865    let negate = match negate {
4866        None => None,
4867        Some(_) if !matches!(kind, Kind::Switch) || !matches!(role, Role::Flag { .. }) => {
4868            return error("`negate` is for `bool` flags".into());
4869        }
4870        Some(Some(name)) => Some(name),
4871        Some(None) => match &role {
4872            Role::Flag { long: Some(l), .. } => Some(format!("no-{l}")),
4873            _ => return error("`negate` without a name needs a `long` to prefix".into()),
4874        },
4875    };
4876    if let Some(no) = &negate
4877        && (no.is_empty() || no.starts_with('-') || no.contains('='))
4878    {
4879        return error(format!("`{no}` is not a usable long name"));
4880    }
4881    match (&default, &negate) {
4882        (Some(d), Some(_)) if d != "true" && d != "false" => {
4883            return error("a negatable flag's `default` is \"true\" or \"false\"".into());
4884        }
4885        (Some(_), None) if matches!(kind, Kind::Switch | Kind::Count(_)) => {
4886            return error("`default` needs a field that takes a value, or `negate`".into());
4887        }
4888        _ => {}
4889    }
4890    if (hint.is_some() || complete.is_some())
4891        && (matches!(kind, Kind::Switch | Kind::Count(_))
4892            || !matches!(role, Role::Flag { .. } | Role::Positional { .. }))
4893    {
4894        return error("`hint` and `complete` are for a field that takes a value".into());
4895    }
4896    if hint.is_some() && complete.is_some() {
4897        return error("`complete` answers alone: `hint` beside it would never be asked".into());
4898    }
4899    let spec = spec_name(&f.attrs)?;
4900    if spec.is_some() && matches!(kind, Kind::Switch | Kind::Count(_)) {
4901        return error("`spec` names the type of a field's value, and this field has none".into());
4902    }
4903    Ok(Field {
4904        ident,
4905        spec,
4906        kind,
4907        role,
4908        delimiter,
4909        choices,
4910        env,
4911        default,
4912        conflicts,
4913        overrides,
4914        requires,
4915        required,
4916        required_unless,
4917        group,
4918        default_missing,
4919        long_help: long_help.or_else(|| help.clone()).unwrap_or(doc_long_help),
4920        help: help.unwrap_or(doc_help),
4921        heading,
4922        hide,
4923        value_name: if tristate { None } else { flag_value_name },
4924        hint,
4925        complete,
4926        negative_numbers,
4927        hyphen_values,
4928        require_equals,
4929        keep_equals,
4930        negate,
4931        two_dashes,
4932        prefix,
4933        values,
4934        values_max,
4935        value_terminator,
4936        default_fn,
4937        default_note,
4938        default_ifs,
4939        stop_flags,
4940        plus,
4941        tristate,
4942        keywords,
4943        short_aliases,
4944    })
4945}
4946
4947fn is_bool_word(word: &str) -> bool {
4948    matches!(word, "true" | "false")
4949}
4950
4951/// `values = 3`, `values = 1..`, `values = 1..=3`: the least, and for a range
4952/// the most (`usize::MAX` when open).
4953fn value_count(count: &syn::Expr) -> syn::Result<(usize, Option<usize>)> {
4954    let number = |e: &syn::Expr| match e {
4955        syn::Expr::Lit(syn::ExprLit {
4956            lit: syn::Lit::Int(n),
4957            ..
4958        }) => n.base10_parse::<usize>(),
4959        _ => Err(syn::Error::new(e.span(), "expected a number")),
4960    };
4961    let (min, max) = match count {
4962        syn::Expr::Range(range) => {
4963            let min = range.start.as_deref().map(number).transpose()?.unwrap_or(1);
4964            let max = match (&range.end, &range.limits) {
4965                (None, _) => usize::MAX,
4966                (Some(end), syn::RangeLimits::Closed(_)) => number(end)?,
4967                (Some(end), syn::RangeLimits::HalfOpen(_)) => number(end)?.saturating_sub(1),
4968            };
4969            (min, Some(max))
4970        }
4971        exact => (number(exact)?, None),
4972    };
4973    if min == 0 || max.is_some_and(|max| max < min) {
4974        return Err(syn::Error::new(
4975            count.span(),
4976            "`values` is at least 1, and a range does not end below its start",
4977        ));
4978    }
4979    Ok((min, max))
4980}
4981
4982/// Every long spelling of `f`, its negation included.
4983fn spellings(f: &Field) -> Vec<&str> {
4984    let mut longs = f.longs();
4985    longs.extend(f.negate.as_deref());
4986    longs
4987}
4988
4989fn check_duplicates(fields: &[Field]) -> syn::Result<()> {
4990    for (i, a) in fields.iter().enumerate() {
4991        for b in &fields[..i] {
4992            let theirs = b.shorts();
4993            let clash = a
4994                .shorts()
4995                .into_iter()
4996                .find(|c| theirs.contains(c))
4997                .map(|c| format!("-{c}"))
4998                .or_else(|| {
4999                    let longs = spellings(b);
5000                    spellings(a)
5001                        .into_iter()
5002                        .find(|l| longs.contains(l))
5003                        .map(|l| format!("--{l}"))
5004                });
5005            if let Some(name) = clash {
5006                return Err(syn::Error::new(
5007                    a.ident.span(),
5008                    format!("`{name}` is already used by `{}`", b.ident),
5009                ));
5010            }
5011        }
5012    }
5013    Ok(())
5014}
5015
5016/// Required positionals come first, then optional ones, then at most one `Vec`,
5017/// last: otherwise which word fills which field would depend on lookahead.
5018fn check_positional_order(positionals: &[&Field]) -> syn::Result<()> {
5019    let rank = |f: &Field| match f.kind {
5020        Kind::Required(_) => 0,
5021        Kind::Optional(_) => 1,
5022        Kind::Many(_) | Kind::Switch | Kind::Count(_) => 2,
5023    };
5024    for pair in positionals.windows(2) {
5025        if rank(pair[1]) < rank(pair[0]) || rank(pair[0]) == 2 {
5026            return Err(syn::Error::new(
5027                pair[1].ident.span(),
5028                "positionals must be required, then optional, then one `Vec` last",
5029            ));
5030        }
5031    }
5032    Ok(())
5033}
5034
5035fn last_segment(ty: &Type) -> Option<&syn::PathSegment> {
5036    match ty {
5037        Type::Path(path) if path.qself.is_none() => path.path.segments.last(),
5038        _ => None,
5039    }
5040}
5041
5042fn inner(segment: &syn::PathSegment) -> Option<&Type> {
5043    match &segment.arguments {
5044        PathArguments::AngleBracketed(args) => match args.args.first()? {
5045            GenericArgument::Type(inner) => Some(inner),
5046            _ => None,
5047        },
5048        _ => None,
5049    }
5050}
5051
5052fn kind(ty: &Type) -> Kind {
5053    if is_bool(ty) {
5054        return Kind::Switch;
5055    }
5056    if let Some(last) = last_segment(ty)
5057        && let Some(inner) = inner(last)
5058    {
5059        if last.ident == "Option" {
5060            return Kind::Optional(inner.clone());
5061        }
5062        if last.ident == "Vec" {
5063            return Kind::Many(inner.clone());
5064        }
5065    }
5066    Kind::Required(ty.clone())
5067}