Skip to main content

winnow_args_derive/
lib.rs

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