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