Skip to main content

usage_derive/
lib.rs

1//! A derive that compiles a CLI definition into parse tables and a spec.
2//!
3//! `#[derive(usage_rs::Cli)]` reads a struct and emits three things: `static` parse
4//! tables for [usage-argv](https://docs.rs/usage-argv), `static` metadata for
5//! spec emission, and a parse function that assigns values straight into the
6//! struct's fields. Nothing is constructed at run time — there is no command tree
7//! to build before a parse can start — and a successful parse touches only the
8//! first of the three.
9//!
10//! Not compiled here, because this crate deliberately does not depend on
11//! usage-argv — see the note in its `Cargo.toml`. The same example runs as a test
12//! in `conformance/tests/derive.rs`, as `the_crate_level_example_from_the_docs`.
13//!
14//! ```ignore
15//! # use usage_derive::Cli;
16//! /// A tool that does things
17//! #[derive(Cli)]
18//! #[usage(bin = "ex", version = "1.0")]
19//! struct Cli {
20//!     /// How many jobs to run at once
21//!     #[usage(short = 'j', long, env = "EX_JOBS", default = "4")]
22//!     jobs: Option<String>,
23//!
24//!     /// Print more
25//!     #[usage(short = 'v', long, count)]
26//!     verbose: u8,
27//!
28//!     /// Colorize output
29//!     #[usage(long, negate = "--no-color", default = "true")]
30//!     color: bool,
31//!
32//!     /// Files to process
33//!     files: Vec<String>,
34//! }
35//!
36//! let argv = ["-j8", "--no-color", "a.txt"].map(std::ffi::OsStr::new);
37//! let cli = Cli::parse_from(&argv).unwrap();
38//! assert_eq!(cli.jobs.as_deref(), Some("8"));
39//! assert!(!cli.color);
40//! assert_eq!(cli.files, ["a.txt"]);
41//!
42//! // The same declaration is also the spec, which is what generates docs,
43//! // manpages, and completions.
44//! assert!(Cli::to_kdl().contains(r#"flag "-j --jobs""#));
45//! ```
46//!
47//! # Subcommands
48//!
49//! A field marked `subcommand` holds an enum whose variants each wrap a struct:
50//!
51//! ```ignore
52//! #[derive(Cli)]
53//! #[usage(bin = "ex")]
54//! struct Ex {
55//!     #[usage(short = 'v', long, global)]
56//!     verbose: bool,
57//!     #[usage(subcommand)]
58//!     command: Option<Commands>,
59//! }
60//!
61//! #[derive(Subcommands)]
62//! enum Commands {
63//!     /// Install a tool
64//!     Install(Install),
65//!     /// Run a task
66//!     #[usage(name = "run")]
67//!     RunTask(Run),
68//! }
69//!
70//! /// Install a tool
71//! #[derive(Args)]
72//! struct Install {
73//!     #[usage(short = 'f', long)]
74//!     force: bool,
75//!     tools: Vec<String>,
76//! }
77//! ```
78//!
79//! The three derives cannot see each other — a macro sees one item — so the tables
80//! are joined through two traits, [`usage_argv::spec::CommandArgs`] and
81//! [`usage_argv::spec::Subcommands`], whose associated consts a parent splices into
82//! its own `static` tables. Nothing is assembled at run time.
83//!
84//! A variant that holds nothing is a command with no flags and no arguments of its own:
85//!
86//! ```ignore
87//! #[derive(Subcommands)]
88//! enum Commands {
89//!     /// Install a tool
90//!     Install(Install),
91//!     /// Show who pays for this
92//!     #[usage(effect = "read")]
93//!     Sponsors,
94//! }
95//! ```
96//!
97//! `effect` goes on the variant there because there is no struct to put it on; everywhere else
98//! it belongs to the `Args`, and declaring it in both places is refused.
99//!
100//! A command type with no fields may keep Rust's unit-struct spelling:
101//!
102//! ```ignore
103//! #[derive(Args)]
104//! struct Sponsors;
105//! ```
106//!
107//! Tuple structs remain ambiguous: a derive cannot infer whether their unnamed field is a
108//! positional value or a flattened `Args` type. The diagnostic points wrapper migrations to a
109//! named field with `#[usage(flatten)]`.
110//!
111//! A command inside a command is not a special case: an `Args` struct carries a
112//! `subcommand` field exactly as the root does, to any depth, and generates the same
113//! code for it. mise reaches four levels, so one was never going to be enough.
114//!
115//! Keys carry a hash of the declaration they came from, which is how independently
116//! expanded macros avoid handing two fields the same one. A key chooses which arm to
117//! jump to and the arm then verifies the event came from its own table, so even two
118//! identical declarations in different modules cannot misbind — the event simply goes
119//! unclaimed, and `Spec::to_kdl` asserts the tree holds no duplicate keys, so a
120//! collision fails a test rather than quietly doing the wrong thing.
121//!
122//! # Dispatch
123//!
124//! `#[usage(run)]` on the enum writes the `match` that hands the selected command to the code
125//! that carries it out — one arm per variant, each calling
126//! [`Run::run`](usage_argv::Run::run) on the struct the variant holds:
127//!
128//! ```ignore
129//! #[derive(Subcommands)]
130//! #[usage(run)]
131//! enum Commands {
132//!     Install(Install),
133//!     Sponsors(Sponsors),
134//! }
135//!
136//! impl usage_rs::Run for Install {
137//!     type Output = miette::Result<()>;
138//!     fn run(self) -> Self::Output { install(&self.tools, self.force) }
139//! }
140//! ```
141//!
142//! Four attributes, one per trait, differing only in whether a command is handed a context and
143//! whether it is awaited: `run` for [`Run`](usage_argv::Run), `run_with` for
144//! [`RunWith`](usage_argv::RunWith), `run_async` for [`RunAsync`](usage_argv::RunAsync), and
145//! `run_async_with` for [`RunAsyncWith`](usage_argv::RunAsyncWith). A context is whatever the CLI
146//! has to give, and the generated dispatch is generic over it. The async pair's implementations
147//! are written `async fn` and the generated dispatch awaits the selected command, with no `Send`
148//! bound imposed either way. It builds that command's future on the heap and awaits the box, so
149//! an unoptimized build does not keep a stack slot for every command's future while running
150//! one. An enum may say several.
151//!
152//! The output type is the first variant's, and each of the others is required to agree, so a
153//! command returning something else is reported on the command. A `#[usage(run)]` *struct* that
154//! holds only its subcommands implements the trait as a forward; a root that also declares
155//! flags gets `run_command`, which moves the subcommand out and leaves the flags for the
156//! caller. A variant that holds nothing or its fields inline is dispatched through the
157//! `{Enum}{Variant}` struct the derive writes for it. An `external_subcommand` is dispatched
158//! by `external = fallback` on the enum. A command that should not wait when the rest of the
159//! enum does says `#[usage(run)]` on the variant; one that should not take the context says
160//! `#[usage(no_ctx)]`.
161//!
162//! Nothing about any of this reaches the spec, the parse tables, or help: which Rust function
163//! carries out a command is not part of what the CLI *is*. `#[usage(skip)]` follows the same
164//! rule.
165//!
166//! # What is decided after the parse
167//!
168//! The parser binds tokens. Whether what it bound is *acceptable* needs to know the
169//! declared type, so the generated code checks that once the last token has been
170//! read, in an order that is deliberate:
171//!
172//! 1. **The environment** fills what argv left out, for a field with `env`.
173//! 2. **Required-ness**, which the type states: a `String` has nowhere to put
174//!    "absent", so it must be given — unless a default or the environment already
175//!    filled it.
176//! 3. **`choices`, `validate`, and `var_min`/`var_max`**, which judge a value however it
177//!    arrived, including from the environment or a default.
178//!
179//! Only the command that actually ran is judged. A flag that `install` requires says
180//! nothing about an invocation of `run`.
181//!
182//! Bounds constrain the values a field was *given*: an unused optional flag is
183//! absent, not a violation, or `var_min` would be a second way to spell
184//! required-ness and there would be no way to say "at least two, if you use it".
185//!
186//! `validate` is a portable [expr](https://expr-lang.org/) expression with one string
187//! variable, `value`. It must return a boolean. `validate_error` supplies the message
188//! shown when it returns false.
189//!
190//! Contradictions are refused at compile time rather than at run time — `choices` on
191//! a `bool`, a `var_min` above its `var_max`, a bound on something that is not a
192//! `Vec`, or a default that is not one of the choices.
193//!
194//! # Declaring
195//!
196//! A field with `long` or `short` is a flag; anything else is a positional
197//! argument. Help text comes from the doc comment: the first paragraph is the
198//! short form, and the whole comment is the long form.
199//!
200//! A field's **type** says how many values it takes and what they become. `bool` is a
201//! switch and an unsigned integer with `count` counts occurrences; everything else holds
202//! values, built with `FromStr`:
203//!
204//! | type | means |
205//! | --- | --- |
206//! | `T` | one value, required — the type has nowhere to put "absent" |
207//! | `Option<T>` | one value, or nothing |
208//! | `Vec<T>` | several, empty when none arrived |
209//! | `Option<Vec<T>>` | several, and `None` when the flag was never given at all |
210//!
211//! So `Option<PathBuf>`, `Vec<ToolArg>` and `Option<usize>` all work, and a type that no
212//! single word could become is a compile error naming that type. The conversion's error
213//! type has to implement `Display`, since what it says is what the user reads — a type
214//! whose error does not is also a compile error, and also names the type. The parse itself still
215//! binds text — a word's meaning is decided once, where the struct is built — and a value
216//! that will not convert becomes [`Error::InvalidValue`](usage_argv::Error::InvalidValue),
217//! carrying the offending text and whatever the type's own conversion said about it.
218//!
219//! Metadata that already has a Rust source of truth may remain an expression. Command help
220//! fields such as `about` and `after_long_help` accept expressions usable as `&'static str`.
221//! A computed `version` additionally declares `version_spec = "..."`, and a typed field
222//! default declares both `default_value_t = EXPR` and `default = "..."`: runtime behavior
223//! evaluates the expression while portable KDL uses the explicit literal. A genuinely dynamic
224//! value uses `default_fn = function` instead; an optional `default_note = "..."` reaches help,
225//! while portable KDL deliberately declares no concrete default it could not reproduce.
226//!
227//! A completer is written as
228//!
229//! ```ignore
230//! fn tasks(partial: &<Tasks as CommandArgs>::Partial, ctx: &CompleteCtx<'_>) -> Vec<Candidate<'static>>
231//! ```
232//!
233//! and is handed its *own command's* half-parsed struct, so `tk tasks --file other.toml <TAB>`
234//! can be answered against that file — which a `run=` shelling out to a fixed command cannot see.
235//! The emitted spec gets a `run=` naming this binary, so everything that reads a spec still has
236//! one, generated from the function rather than declared beside it.
237//!
238//! `Cli::parse()` is the entry point that *is* the process: it prints a help page or a version
239//! and leaves, and on a failure it prints the message to stderr and exits 2 — clap's status, so a
240//! script checking for it keeps working. `Cli::parse_from(argv)` hands the error back instead,
241//! for a library embedding a CLI that wants to decide for itself.
242//!
243//! Declaring a `version` or `long_version` also gives the CLI `--version` and `-V`, as clap does.
244//! The parser supplies version and help entry points, and generated specs materialize their
245//! surviving spellings as `action` flags so metadata consumers see the same interface. Either
246//! spelling yields to a flag the CLI declares for itself. clap refuses that collision by
247//! panicking at startup; here the declaration simply wins and the other spelling still answers.
248//!
249//! On the struct itself: `bin`, `version`, `long_version`, `author`, `license`, `repository`,
250//! `source_code_link_template` — a tera template rendered with the command path as `path`,
251//! which generated markdown turns into a "view source" link — `about`,
252//! `long_about`, `before_help`, `after_help`,
253//! `visible_alias(es)`, hidden `alias(es)`, and `hide` may be declared on an
254//! `Args` struct and are inherited by every subcommand variant that mounts it —
255//! `verbatim_doc_comment` — preserve doc-comment line breaks and whitespace —
256//! `default_subcommand`, `default_subcommand_flags`, `default_subcommand_on_empty`, `default_subcommand_help`, `multicall` — argv[0]'s basename selects a subcommand —
257//! `arg_required_else_help` — a selected command with no argv of its own shows short help —
258//! `disable_help_flag`, `disable_help_subcommand`, and `disable_version_flag` — remove the
259//! corresponding synthesized entry point so a field with `action = usage_rs::ArgAction::Help`,
260//! `HelpShort`, `HelpLong`, `HelpAll`, or `Version` can relocate it —
261//! `next_line_help` — put descriptions below each entry — `flatten_help` — expand visible
262//! subcommands into the current help page —
263//! `dont_delimit_trailing_values` — preserve delimiters after the trailing boundary —
264//! `args_override_self = false` — reject repeated scalar flags instead of letting the later
265//! occurrence correct the earlier one —
266//! `min_usage_version` — the oldest `usage` that can read the emitted
267//! spec, declared rather than worked out — `effect` — what running this command does to the world, on an `Args`
268//! rather than on the root, which does nothing itself — `completion`, which adds the hidden command a generated shell
269//! script calls, and needs usage-argv's `complete` feature enabled where it is depended on —
270//! `settings`, for a CLI whose bound flags all live in a flattened group (see [Settings]) —
271//! and `run`, `run_with`, `run_async` and `run_async_with`, which write the forward from a
272//! container command to its subcommands (see [Dispatch](#dispatch)).
273//!
274//! [Settings]: #settings-and-the-flags-that-set-them
275//!
276//! Named fields accept metadata through `#[usage(...)]`.
277//!
278//! | option | meaning |
279//! | --- | --- |
280//! | `long`, `long = "x"` | a long form, defaulting to the field name |
281//! | `short`, `short = 'x'` | a short form, defaulting to the field's first letter |
282//! | `name = "x"` | the name used in the spec and in help output |
283//! | `negate = "--no-x"` | a second long form that sets a `bool` false |
284//! | `count` | count occurrences instead of collecting values |
285//! | `var` | the flag may be repeated, taking one value each time |
286//! | `variadic` | one occurrence keeps taking values, until a flag-like token or `--` |
287//! | `var_max = n` | how many values a variadic takes before the next field gets the rest |
288//! | `global` | subcommands inherit the flag |
289//! | `env = "X"` | an environment variable that can supply the value |
290//! | `env` | infer the environment variable from the field, using the command's `rename_all_env` policy |
291//! | `env_fallback("OLD_X", "OLDER_X")` | additional environment variables, consulted in declaration order |
292//! | `deprecated_env("LEGACY_X")` | deprecated aliases, consulted after ordinary fallbacks and labeled in help |
293//! | `default = "x"` | the value when the command line does not supply one; a `Vec` may be given several, and starts out holding all of them |
294//! | `default_fn = function` | compute one typed default at parse time without claiming a concrete portable value |
295//! | `default_note = "x"` | describe a `default_fn` in help; the note is prose, not a value |
296//! | `help_heading = "x"` | the section to list this under in help output |
297//! | `note = "x"` | a semantic note shown in long help and generated documentation |
298//! | `warning = "x"` | a semantic warning shown in long help and generated documentation |
299//! | `display_order = n` | explicit help order; positional parsing still follows declaration order |
300//! | `verbatim_doc_comment` | preserve line breaks and whitespace in the doc comment instead of flowing its first paragraph |
301//! | `hide` | keep it out of help and completions |
302//! | `effect = "write"` | what supplying this flag does to the world: `read`, `write` or `destructive`. Also goes on an `Args`, where it says what *running* the command does |
303//! | `double_dash = "…"` | how a positional relates to `--`: `optional` (the default), `required` (fillable only after one), `preserve` (the `--` is a value), `automatic` (filling it ends flag parsing, so a wrapper forwards) |
304//! | `complete = my_fn` | a function that answers for this value when a shell asks |
305//! | `value_enum` | the words come from the field's type, which derives [`ValueEnum`] |
306//! | `arg_group` | the flags come from the field's type, which derives [`ArgGroup`]; `Vec<T>` preserves a `multiple` group's occurrence order |
307//! | `value_hint = usage_rs::ValueHint::FilePath` | ask the shell for paths, executables, or forwarded command argv |
308//! | `extensions("toml", "yaml")` | limit a file-path hint to these extensions while retaining directories |
309//! | `arg` | force a field to be positional |
310//! | `value_name = "NAME"` | a positional name, or the placeholder for a flag value |
311//! | `choices("a", "b")` | accepted values; typed conversion still uses the field type's `FromStr` |
312//! | `visible_alias = "other"` | an advertised long alias; the plural array spelling also works |
313//! | `alias = "other"` | a hidden long alias; the plural array spelling also works |
314//! | `overrides = "--other"` | a flag this one displaces, the last given winning |
315//! | `conflicts = "--other"` | an argument this one cannot be given with |
316//! | `requires = "--other"` | a flag that must also be given when this one is |
317//! | `requires_if("value", "--other")` | a flag required when this one explicitly has `value` |
318//! | `requires_ifs(("a", "--x"), ("b", "--y"))` | several value-conditional requirements |
319//! | `group = "input"` | the group this argument is one of; see below |
320//! | `exclusive` | this flag has to be given on its own, positionals included |
321//! | `delimiter = ','` | one word becomes several values; the field has to be a `Vec` |
322//! | `allow_hyphen_values` | a flag's detached value may look like a flag, including `--` |
323//! | `allow_negative_numbers` | accept negative numeric tokens without accepting every dash-word |
324//! | `value_terminator = ";"` | end a variadic field without storing the terminator |
325//! | `require_equals` | `--flag=value` is accepted and `--flag value` is not |
326//! | `default_missing = "always"` | the value when the flag is given with none |
327//! | `required_if = "--other"` | a flag whose presence makes this one necessary |
328//! | `required_if_eq("mode", "remote")` | a matching explicit value makes this one necessary |
329//! | `required_if_eq_any = [("mode", "a"), ("mode", "b")]` | any matching value makes this one necessary |
330//! | `required_if_eq_all = [("mode", "a"), ("scope", "global")]` | every value condition must match |
331//! | `required_unless = "--other"` | a flag whose presence makes this one unnecessary |
332//! | `required_unless = ["stdin", "file"]` | any present argument makes this unnecessary |
333//! | `required_unless_all = ["stdin", "file"]` | every named argument must be present |
334//!
335//! These name a flag as `"--long"` or `"-s"`, and a positional by its bare name. They
336//! take several as a list: `conflicts("--file", "target")`. A selector naming no argument
337//! on the command
338//! is a compile error, which is the advantage of declaring a relationship in code: in a
339//! hand-written spec a typo'd selector is a relationship that quietly does not hold.
340//!
341//! A **group** is the one relationship that is not written flag-to-flag, because what it
342//! says is about the set: `required` means one of them is needed, and no rule on an
343//! individual flag expresses that. Membership goes on the fields and the properties on the
344//! struct, which may be left out entirely when the group is a plain "at most one":
345//!
346//! ```ignore
347//! #[derive(Cli)]
348//! #[usage(bin = "ex")]
349//! #[usage(group("input", required))]
350//! struct Ex {
351//!     #[usage(long, group = "input")]
352//!     file: Option<String>,
353//!     #[usage(long, group = "input")]
354//!     url: Option<String>,
355//! }
356//! ```
357//!
358//! `required` means at least one member is needed and `multiple` means more than one may
359//! be given, so a bare group is "at most one", `required` alone is "exactly one", and the
360//! two together are "at least one" — clap's two properties, read the same way. A group
361//! with one member, or a declaration no field joins, is a compile error.
362//!
363//! A group of valueless flags may instead be an enum deriving [`ArgGroup`], held by one
364//! field marked `arg_group`, so the code reading it matches on a variant rather than on
365//! which of several `bool`s is set. It lowers to the same `group` node and the same errors.
366//!
367//! These post-parse relationships work on flags and positionals. `overrides` remains a
368//! flag-only binding rule. An argument ID such as `"mode"`, as clap attributes commonly
369//! use, resolves to the same field as the portable `"--mode"` spelling and is emitted in
370//! canonical spec form. A required-unless declaration needs somewhere to put "absent",
371//! so it takes an `Option` rather than a bare `String`.
372//!
373//! A variant may hold its struct in a `Box`, as `Install(Box<Install>)`: an enum is as
374//! large as its biggest variant, so one command with thirty flags otherwise makes every
375//! invocation move that much stack. Nothing else changes — the box is how the variant
376//! holds the struct, not something the CLI has, and the spec cannot tell.
377//!
378//! A command takes `alias = "i"` for a name it should advertise and
379//! `alias_hidden = "add"` for one it should answer to quietly, each accepting several as a
380//! list. They may be written on the `Args` struct that owns the command or on its
381//! `Subcommands` variant; when both say some, the lists are joined. The parser matches both;
382//! the difference is only whether help and completions mention them.
383//! `help_heading = "Maintenance"` on a variant groups that command under a named section
384//! in its parent's help. `display_order = n` controls where it is presented within the
385//! section.
386//!
387//! # Settings and the flags that set them
388//!
389//! `setting = "key"` says which setting a flag sets. `Cli::parse_from_with_settings` then
390//! returns a `usage_config::CliLayer` beside the parsed struct — the command line as the
391//! highest layer of a resolution — and `Cli::SETTINGS_BINDINGS` lists every flag it binds,
392//! which `usage_config::Registry::drift` compares against the flags the *spec* declares. A
393//! flag documented as setting something and read by nothing fails a test rather than a user.
394//!
395//! The layer is built from what the parser saw rather than from the parsed struct, because a
396//! `bool` field is `false` whether the flag was left off or negated, and the command line
397//! outranks every file on the machine. So `--no-colour` contributes `false`, and a flag that
398//! was not given contributes nothing at all.
399//!
400//! A setting can be declared wherever a flag is: on the root, in a `#[usage(flatten)]` group,
401//! or on a subcommand's struct. A group hands its parent what it was given in
402//! `usage_argv::spec::SettingGiven` — a vocabulary that says nothing about types, since the
403//! registry is what decides them — and only the root turns that into a layer, so a program
404//! with no settings never mentions `usage-config`. A root that binds nothing itself but
405//! flattens a group that does declares `#[usage(settings)]`; leaving it off is a compile error
406//! naming the attribute, because the alternative is a documented flag that quietly sets
407//! nothing.
408//!
409//! A word is held as the bytes it arrived as and converted once, where the struct is built.
410//! So a value that is not valid UTF-8 is **reported** rather than quietly replaced with
411//! `U+FFFD` — which for a `PathBuf` meant a different file, silently. On Unix, `PathBuf` and
412//! `OsString` fields accept the bytes exactly through the safe `OsStringExt::from_vec`; on
413//! Windows, a value that cannot be converted safely is reported rather than reconstructed with
414//! `OsString::from_encoded_bytes_unchecked`.
415//!
416use proc_macro::TokenStream;
417use syn::{DeriveInput, parse_macro_input};
418
419mod case;
420mod codegen;
421mod config;
422mod facade;
423mod model;
424
425/// Compile a struct into a parser and a spec. See the [crate docs](crate).
426// Legacy helper names stay registered so the derive can reject them at their
427// source span with a `#[usage(...)]` migration message.
428#[proc_macro_derive(Cli, attributes(usage, command, arg, value, group))]
429pub fn derive_cli(input: TokenStream) -> TokenStream {
430    let input = parse_macro_input!(input as DeriveInput);
431    // Reporting an error as tokens rather than panicking is what puts it on
432    // the offending line instead of on the derive.
433    facade::expand(input, |input| {
434        let cli = model::Cli::from_input(input)?;
435        cli.check_position(&input.ident, true)?;
436        Ok(codegen::emit(&cli))
437    })
438}
439
440/// Compile a struct into one subcommand's flags and arguments.
441///
442/// Used on the struct a [`Subcommands`] variant wraps. It generates the same
443/// tables and metadata as [`Cli`], minus the program-level parts a subcommand does
444/// not have — a name, a version, an entry point — plus the trait a parent uses to
445/// route events into it.
446#[proc_macro_derive(Args, attributes(usage, command, arg, value, group))]
447pub fn derive_args(input: TokenStream) -> TokenStream {
448    let input = parse_macro_input!(input as DeriveInput);
449    // `restart_token` and `mount` are per-command and belong here; `default_subcommand` is
450    // declared once for the whole spec and does not.
451    facade::expand(input, |input| {
452        let mut cli = model::Cli::from_input(input)?;
453        cli.composable = true;
454        cli.check_position(&input.ident, false)?;
455        Ok(codegen::emit_args(&cli))
456    })
457}
458
459/// Compile an enum into a set of subcommands.
460///
461/// Each variant may wrap a struct deriving [`Args`] or declare its fields inline.
462/// A field holding this enum is marked `#[usage(subcommand)]`.
463///
464/// `#[usage(run)]`, `#[usage(run_with)]`, `#[usage(run_async)]` or `#[usage(run_async_with)]` on
465/// the enum also writes the `match` that hands the selected command to its implementation; see
466/// the [crate docs](crate#dispatch).
467#[proc_macro_derive(Subcommands, attributes(usage, command, arg, value, group))]
468pub fn derive_subcommands(input: TokenStream) -> TokenStream {
469    let input = parse_macro_input!(input as DeriveInput);
470    facade::expand(input, |input| {
471        Ok(codegen::emit_subcommands(&model::Subcommands::from_input(
472            input,
473        )?))
474    })
475}
476
477/// Compile a settings struct into its own registry, reader, and spec `config` block.
478///
479/// The struct the CLI already holds its settings in becomes the declaration: field types are
480/// the settings' types, doc comments are their help, and `#[usage(...)]` carries what a spec's
481/// `prop` node would — `env`, `default`, `merge`, `scope`, `choices`, `source` bindings.
482/// The derive generates `SETTINGS_PROPS`, `SETTINGS_REGISTRY`, `SETTINGS_SPEC`,
483/// `read(&Resolved)`, and `spec_kdl()`, so the registry, the reader, and the documentation
484/// cannot drift from the struct or from each other. The whole field vocabulary is in the
485/// guide: <https://usage.jdx.dev/rust/configuration>.
486///
487/// ```ignore
488/// #[derive(usage_rs::Config)]
489/// struct Settings {
490///     /// How many jobs to run at once
491///     #[usage(env = "EX_JOBS", default = 4, cli("--jobs", "-j"))]
492///     jobs: u64,
493///     #[usage(flatten)]
494///     task: TaskSettings,
495/// }
496/// ```
497///
498/// A group flattens into another with `#[usage(flatten)]`, declaring its dotted keys under
499/// its own `#[usage(prefix = "task")]`. The joined registry refuses duplicate keys at
500/// compile time.
501#[proc_macro_derive(Config, attributes(usage))]
502pub fn derive_config(input: TokenStream) -> TokenStream {
503    let input = parse_macro_input!(input as DeriveInput);
504    facade::expand(input, |input| {
505        Ok(config::emit(&config::Config::from_input(input)?))
506    })
507}
508
509/// Compile an enum into the words one value may be.
510///
511/// What a CLI calls an enum — `--shell bash` — and what the spec calls `choices`. The
512/// variant's name in kebab-case is the word, unless `name` says otherwise:
513///
514/// ```ignore
515/// #[derive(usage_rs::ValueEnum)]
516/// enum Shell {
517///     /// Bourne Again shell.
518///     Bash,
519///     #[usage(alias = "shell-z")]
520///     Zsh,
521///     #[usage(name = "pwsh", visible_alias = "powershell", hide = true)]
522///     PowerShell,
523/// }
524/// ```
525///
526/// `#[usage(ignore_case)]` on the enum applies to canonical words and aliases.
527/// A variant's doc comment becomes its per-value help. `help = "..."` overrides
528/// it, `hide` keeps the value accepted while omitting it from help and completion,
529/// `alias` is hidden, and `visible_alias` is advertised alongside the canonical word.
530///
531/// The derive binds canonical words and aliases directly to their variants; a separate
532/// [`FromStr`](std::str::FromStr) implementation is not required. Variant `cfg` and
533/// `cfg_attr` attributes are copied to their entries in the static word tables.
534///
535/// A field holding one says `value_enum`, which is what puts the words in the spec — so
536/// help, completions and the check that rejects a wrong word all read the same list, and
537/// none of them can drift from the type.
538#[proc_macro_derive(ValueEnum, attributes(usage, command, arg, value, group))]
539pub fn derive_value_enum(input: TokenStream) -> TokenStream {
540    let input = parse_macro_input!(input as DeriveInput);
541    facade::expand(input, |input| {
542        Ok(codegen::emit_value_enum(&model::ValueEnum::from_input(
543            input,
544        )?))
545    })
546}
547
548/// Compile an enum into a set of related flags.
549///
550/// Exclusive or ordered flags as enum variants, so the code that reads them matches on a type
551/// rather than on which of several fields is set. Each variant is one switch, named by its own
552/// name in kebab-case:
553///
554/// ```ignore
555/// #[derive(usage_rs::ArgGroup)]
556/// #[usage(name = "format")]
557/// enum Format {
558///     /// Print JSON
559///     Json,
560///     /// Print YAML
561///     Yaml,
562///     #[usage(short = 'p', long = "plain")]
563///     PlainText,
564/// }
565/// ```
566///
567/// Only a variant's doc comment becomes that switch's help; the enum's own docs are not
568/// read, because a group has no help of its own — the members do.
569///
570/// A field holds one and says `arg_group`. `Option<Format>` is a group that may be left alone
571/// and a bare `Format` is one that has to be given — the same rule every other field's type is
572/// read by, and the only spelling of required-ness a group has, since there is no default
573/// variant:
574///
575/// ```ignore
576/// #[derive(usage_rs::Cli)]
577/// #[usage(bin = "ex")]
578/// struct Ex {
579///     #[usage(arg_group)]
580///     format: Option<Format>,
581/// }
582/// ```
583///
584/// Nothing new reaches the spec: the enum lowers to the `group` node and the flags it names,
585/// so `--json --yaml` is the same [`Error::ConflictingFlags`](usage_argv::Error::ConflictingFlags)
586/// a hand-written group produces, and a missing member of a required one is the same
587/// [`Error::MissingGroup`](usage_argv::Error::MissingGroup). A tuple variant with one field is a
588/// value-taking member such as `Migrate(Source)`; `value_name` and `value_enum` describe that
589/// payload exactly as they do on an ordinary flag.
590///
591/// `#[usage(multiple)]` changes the group into an ordered instruction stream. Hold it as
592/// `Vec<Mode>` and every occurrence is returned in argv order, including interleaved variants.
593///
594/// A variant's doc comment becomes its help. `help = "..."`, `long_help = "..."`, `hide`, and
595/// `short = 'x'` are the rest of what a member has; `cfg` and `cfg_attr` are copied to the
596/// variant's entries in the static tables, as [`ValueEnum`] copies them.
597#[proc_macro_derive(ArgGroup, attributes(usage, command, arg, value, group))]
598pub fn derive_arg_group(input: TokenStream) -> TokenStream {
599    let input = parse_macro_input!(input as DeriveInput);
600    facade::expand(input, |input| {
601        Ok(codegen::emit_arg_group(&model::ArgGroup::from_input(
602            input,
603        )?))
604    })
605}