usage_derive/lib.rs
1//! A derive that compiles a CLI definition into parse tables and a spec.
2//!
3//! `#[derive(usage::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::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. An enum may say several.
149//!
150//! The output type is the first variant's, and each of the others is required to agree, so a
151//! command returning something else is reported on the command. A `#[usage(run)]` *struct* gets
152//! a forward to its own subcommands, which is all it can get: it holds one field, its
153//! subcommands and not in an `Option`, since anything else would mean dropping what the struct
154//! declared or deciding what no command means. A variant holding nothing, holding its fields
155//! inline, or holding an `external_subcommand`'s argv cannot be dispatched — there is no type to
156//! implement the trait for — and says so where it is declared.
157//!
158//! Nothing about any of this reaches the spec, the parse tables, or help: which Rust function
159//! carries out a command is not part of what the CLI *is*. `#[usage(skip)]` follows the same
160//! rule.
161//!
162//! # What is decided after the parse
163//!
164//! The parser binds tokens. Whether what it bound is *acceptable* needs to know the
165//! declared type, so the generated code checks that once the last token has been
166//! read, in an order that is deliberate:
167//!
168//! 1. **The environment** fills what argv left out, for a field with `env`.
169//! 2. **Required-ness**, which the type states: a `String` has nowhere to put
170//! "absent", so it must be given — unless a default or the environment already
171//! filled it.
172//! 3. **`choices`, `validate`, and `var_min`/`var_max`**, which judge a value however it
173//! arrived, including from the environment or a default.
174//!
175//! Only the command that actually ran is judged. A flag that `install` requires says
176//! nothing about an invocation of `run`.
177//!
178//! Bounds constrain the values a field was *given*: an unused optional flag is
179//! absent, not a violation, or `var_min` would be a second way to spell
180//! required-ness and there would be no way to say "at least two, if you use it".
181//!
182//! `validate` is a portable [expr](https://expr-lang.org/) expression with one string
183//! variable, `value`. It must return a boolean. `validate_error` supplies the message
184//! shown when it returns false.
185//!
186//! Contradictions are refused at compile time rather than at run time — `choices` on
187//! a `bool`, a `var_min` above its `var_max`, a bound on something that is not a
188//! `Vec`, or a default that is not one of the choices.
189//!
190//! # Declaring
191//!
192//! A field with `long` or `short` is a flag; anything else is a positional
193//! argument. Help text comes from the doc comment: the first paragraph is the
194//! short form, and the whole comment is the long form.
195//!
196//! A field's **type** says how many values it takes and what they become. `bool` is a
197//! switch and an unsigned integer with `count` counts occurrences; everything else holds
198//! values, built with `FromStr`:
199//!
200//! | type | means |
201//! | --- | --- |
202//! | `T` | one value, required — the type has nowhere to put "absent" |
203//! | `Option<T>` | one value, or nothing |
204//! | `Vec<T>` | several, empty when none arrived |
205//! | `Option<Vec<T>>` | several, and `None` when the flag was never given at all |
206//!
207//! So `Option<PathBuf>`, `Vec<ToolArg>` and `Option<usize>` all work, and a type that no
208//! single word could become is a compile error naming that type. The conversion's error
209//! type has to implement `Display`, since what it says is what the user reads — a type
210//! whose error does not is also a compile error, and also names the type. The parse itself still
211//! binds text — a word's meaning is decided once, where the struct is built — and a value
212//! that will not convert becomes [`Error::InvalidValue`](usage_argv::Error::InvalidValue),
213//! carrying the offending text and whatever the type's own conversion said about it.
214//!
215//! Metadata that already has a Rust source of truth may remain an expression. Command help
216//! fields such as `about` and `after_long_help` accept expressions usable as `&'static str`.
217//! A computed `version` additionally declares `version_spec = "..."`, and a typed field
218//! default declares both `default_value_t = EXPR` and `default = "..."`: runtime behavior
219//! evaluates the expression while portable KDL uses the explicit literal.
220//!
221//! A completer is written as
222//!
223//! ```ignore
224//! fn tasks(partial: &<Tasks as CommandArgs>::Partial, ctx: &CompleteCtx<'_>) -> Vec<Candidate<'static>>
225//! ```
226//!
227//! and is handed its *own command's* half-parsed struct, so `tk tasks --file other.toml <TAB>`
228//! can be answered against that file — which a `run=` shelling out to a fixed command cannot see.
229//! The emitted spec gets a `run=` naming this binary, so everything that reads a spec still has
230//! one, generated from the function rather than declared beside it.
231//!
232//! `Cli::parse()` is the entry point that *is* the process: it prints a help page or a version
233//! and leaves, and on a failure it prints the message to stderr and exits 2 — clap's status, so a
234//! script checking for it keeps working. `Cli::parse_from(argv)` hands the error back instead,
235//! for a library embedding a CLI that wants to decide for itself.
236//!
237//! Declaring a `version` or `long_version` also gives the CLI `--version` and `-V`, as clap does — supplied by
238//! the parser rather than listed in the spec, exactly as `--help` is, and yielding to either
239//! spelling the CLI declares for itself. clap refuses that collision by panicking at startup;
240//! here the declaration simply wins and the other spelling still answers.
241//!
242//! On the struct itself: `bin`, `version`, `long_version`, `author`, `license`, `repository`,
243//! `source_code_link_template` — a tera template rendered with the command path as `path`,
244//! which generated markdown turns into a "view source" link — `about`,
245//! `long_about`, `before_help`, `after_help`,
246//! clap-compatible `visible_alias(es)`, hidden `alias(es)`, and `hide` may stay on an
247//! `Args` struct and are inherited by every subcommand variant that mounts it —
248//! clap's `#[group(required = ..., multiple = ...)]` may also stay on an `Args`
249//! struct; its direct flags and positionals become the implicit group members —
250//! `verbatim_doc_comment` — preserve doc-comment line breaks and whitespace —
251//! `default_subcommand`, `multicall` — argv[0]'s basename selects a subcommand —
252//! `arg_required_else_help` — a selected command with no argv of its own shows short help —
253//! `disable_help_flag`, `disable_help_subcommand`, and `disable_version_flag` — remove the
254//! corresponding synthesized entry point so a field with `action = usage::ArgAction::Help`,
255//! `HelpShort`, `HelpLong`, `HelpAll`, or `Version` can relocate it —
256//! `next_line_help` — put descriptions below each entry — `flatten_help` — expand visible
257//! subcommands into the current help page —
258//! `dont_delimit_trailing_values` — preserve delimiters after the trailing boundary —
259//! `args_override_self = false` — reject repeated scalar flags instead of letting the later
260//! occurrence correct the earlier one —
261//! `min_usage_version` — the oldest `usage` that can read the emitted
262//! spec, declared rather than worked out — `effect` — what running this command does to the world, on an `Args`
263//! rather than on the root, which does nothing itself — `completion`, which adds the hidden command a generated shell
264//! script calls, and needs usage-argv's `complete` feature enabled where it is depended on —
265//! `settings`, for a CLI whose bound flags all live in a flattened group (see [Settings]) —
266//! and `run`, `run_with`, `run_async` and `run_async_with`, which write the forward from a
267//! container command to its subcommands (see [Dispatch](#dispatch)).
268//!
269//! [Settings]: #settings-and-the-flags-that-set-them
270//!
271//! Named fields accept both `#[usage(...)]` and clap-compatible `#[arg(...)]`.
272//! Lossless clap spellings such as `id` and visible or hidden aliases may therefore stay in
273//! place while a CLI changes derives.
274//!
275//! | option | meaning |
276//! | --- | --- |
277//! | `long`, `long = "x"` | a long form, defaulting to the field name |
278//! | `short`, `short = 'x'` | a short form, defaulting to the field's first letter |
279//! | `name = "x"` | the name used in the spec and in help output |
280//! | `negate = "--no-x"` | a second long form that sets a `bool` false |
281//! | `count` | count occurrences instead of collecting values |
282//! | `var` | the flag may be repeated, taking one value each time |
283//! | `variadic` | one occurrence keeps taking values, until a flag-like token or `--` |
284//! | `var_max = n` | how many values a variadic takes before the next field gets the rest |
285//! | `global` | subcommands inherit the flag |
286//! | `env = "X"` | an environment variable that can supply the value |
287//! | `env` | infer the environment variable from the field, using the command's `rename_all_env` policy |
288//! | `env_fallback("OLD_X", "OLDER_X")` | additional environment variables, consulted in declaration order |
289//! | `deprecated_env("LEGACY_X")` | deprecated aliases, consulted after ordinary fallbacks and labeled in help |
290//! | `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 |
291//! | `help_heading = "x"` | the section to list this under in help output |
292//! | `display_order = n` | explicit help order; positional parsing still follows declaration order |
293//! | `verbatim_doc_comment` | preserve line breaks and whitespace in the doc comment instead of flowing its first paragraph |
294//! | `hide` | keep it out of help and completions |
295//! | `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 |
296//! | `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) |
297//! | `complete = my_fn` | a function that answers for this value when a shell asks |
298//! | `value_enum` | the words come from the field's type, which derives [`ValueEnum`] |
299//! | `arg_group` | the flags come from the field's type, which derives [`ArgGroup`]; at most one may be given |
300//! | `value_hint = usage::ValueHint::FilePath` | ask the shell for paths, executables, or forwarded command argv |
301//! | `arg` | force a field to be positional |
302//! | `id = "name"` | clap-compatible spelling for the field identity / positional name |
303//! | `value_name = "NAME"` | clap-compatible positional name, or the placeholder for a flag value |
304//! | `default_value = "x"` | clap-compatible spelling for a portable `default` |
305//! | `default_missing_value = "x"` | clap-compatible spelling for `default_missing` |
306//! | `default_value_if("other", predicate, "x")` | clap-compatible conditional default for portable presence and equality predicates |
307//! | `value_delimiter = ','` | clap-compatible spelling for `delimiter` |
308//! | `value_parser = ["a", "b"]` | clap-compatible literal choice list; typed parsers remain the field type's `FromStr` |
309//! | `last` | clap-compatible spelling for a positional requiring `--` |
310//! | `visible_alias = "other"` | clap-compatible advertised long alias; the plural array spelling also works |
311//! | `alias = "other"` | clap-compatible hidden long alias; the plural array spelling also works |
312//! | `overrides = "--other"` | a flag this one displaces, the last given winning |
313//! | `overrides_with = "other"` | clap-compatible spelling; `overrides_with_all = ["a", "b"]` also works |
314//! | `conflicts = "--other"` | an argument this one cannot be given with |
315//! | `requires = "--other"` | a flag that must also be given when this one is |
316//! | `requires_if("value", "--other")` | a flag required when this one explicitly has `value` |
317//! | `requires_ifs(("a", "--x"), ("b", "--y"))` | several value-conditional requirements |
318//! | `group = "input"` | the group this argument is one of; see below |
319//! | `exclusive` | this flag has to be given on its own, positionals included |
320//! | `delimiter = ','` | one word becomes several values; the field has to be a `Vec` |
321//! | `allow_hyphen_values` | a flag's detached value may look like a flag, including `--` |
322//! | `allow_negative_numbers` | accept negative numeric tokens without accepting every dash-word |
323//! | `value_terminator = ";"` | end a variadic field without storing the terminator |
324//! | `require_equals` | `--flag=value` is accepted and `--flag value` is not |
325//! | `default_missing = "always"` | the value when the flag is given with none |
326//! | `required_if = "--other"` | a flag whose presence makes this one necessary |
327//! | `required_if_eq("mode", "remote")` | a matching explicit value makes this one necessary |
328//! | `required_if_eq_any = [("mode", "a"), ("mode", "b")]` | any matching value makes this one necessary |
329//! | `required_if_eq_all = [("mode", "a"), ("scope", "global")]` | every value condition must match |
330//! | `required_unless = "--other"` | a flag whose presence makes this one unnecessary |
331//! | `required_unless_present_any = ["stdin", "file"]` | any present argument makes this unnecessary |
332//! | `required_unless_present_all = ["stdin", "file"]` | every named argument must be present |
333//!
334//! These name a flag as `"--long"` or `"-s"`, and a positional by its bare name. They
335//! take several as a list: `conflicts("--file", "target")`. A selector naming no argument
336//! on the command
337//! is a compile error, which is the advantage of declaring a relationship in code: in a
338//! hand-written spec a typo'd selector is a relationship that quietly does not hold.
339//!
340//! A **group** is the one relationship that is not written flag-to-flag, because what it
341//! says is about the set: `required` means one of them is needed, and no rule on an
342//! individual flag expresses that. Membership goes on the fields and the properties on the
343//! struct, which may be left out entirely when the group is a plain "at most one":
344//!
345//! ```ignore
346//! #[derive(Cli)]
347//! #[usage(bin = "ex")]
348//! #[usage(group("input", required))]
349//! struct Ex {
350//! #[usage(long, group = "input")]
351//! file: Option<String>,
352//! #[usage(long, group = "input")]
353//! url: Option<String>,
354//! }
355//! ```
356//!
357//! `required` means at least one member is needed and `multiple` means more than one may
358//! be given, so a bare group is "at most one", `required` alone is "exactly one", and the
359//! two together are "at least one" — clap's two properties, read the same way. A group
360//! with one member, or a declaration no field joins, is a compile error.
361//!
362//! A group of valueless flags may instead be an enum deriving [`ArgGroup`], held by one
363//! field marked `arg_group`, so the code reading it matches on a variant rather than on
364//! which of several `bool`s is set. It lowers to the same `group` node and the same errors.
365//!
366//! These post-parse relationships work on flags and positionals. `overrides` remains a
367//! flag-only binding rule. An argument ID such as `"mode"`, as clap attributes commonly
368//! use, resolves to the same field as the portable `"--mode"` spelling and is emitted in
369//! canonical spec form. A required-unless declaration needs somewhere to put "absent",
370//! so it takes an `Option` rather than a bare `String`.
371//!
372//! A variant may hold its struct in a `Box`, as `Install(Box<Install>)`: an enum is as
373//! large as its biggest variant, so one command with thirty flags otherwise makes every
374//! invocation move that much stack. Nothing else changes — the box is how the variant
375//! holds the struct, not something the CLI has, and the spec cannot tell.
376//!
377//! A command takes `alias = "i"` for a name it should advertise and
378//! `alias_hidden = "add"` for one it should answer to quietly, each accepting several as a
379//! list. They may be written on the `Args` struct that owns the command or on its
380//! `Subcommands` variant; when both say some, the lists are joined. The parser matches both;
381//! the difference is only whether help and completions mention them.
382//! `help_heading = "Maintenance"` on a variant groups that command under a named section
383//! in its parent's help. `display_order = n` controls where it is presented within the
384//! section.
385//!
386//! # Settings and the flags that set them
387//!
388//! `setting = "key"` says which setting a flag sets. `Cli::parse_from_with_settings` then
389//! returns a `usage_config::CliLayer` beside the parsed struct — the command line as the
390//! highest layer of a resolution — and `Cli::SETTINGS_BINDINGS` lists every flag it binds,
391//! which `usage_config::Registry::drift` compares against the flags the *spec* declares. A
392//! flag documented as setting something and read by nothing fails a test rather than a user.
393//!
394//! The layer is built from what the parser saw rather than from the parsed struct, because a
395//! `bool` field is `false` whether the flag was left off or negated, and the command line
396//! outranks every file on the machine. So `--no-colour` contributes `false`, and a flag that
397//! was not given contributes nothing at all.
398//!
399//! A setting can be declared wherever a flag is: on the root, in a `#[usage(flatten)]` group,
400//! or on a subcommand's struct. A group hands its parent what it was given in
401//! `usage_argv::spec::SettingGiven` — a vocabulary that says nothing about types, since the
402//! registry is what decides them — and only the root turns that into a layer, so a program
403//! with no settings never mentions `usage-config`. A root that binds nothing itself but
404//! flattens a group that does declares `#[usage(settings)]`; leaving it off is a compile error
405//! naming the attribute, because the alternative is a documented flag that quietly sets
406//! nothing.
407//!
408//! A word is held as the bytes it arrived as and converted once, where the struct is built.
409//! So a value that is not valid UTF-8 is **reported** rather than quietly replaced with
410//! `U+FFFD` — which for a `PathBuf` meant a different file, silently. On Unix, `PathBuf` and
411//! `OsString` fields accept the bytes exactly through the safe `OsStringExt::from_vec`; on
412//! Windows, a value that cannot be converted safely is reported rather than reconstructed with
413//! `OsString::from_encoded_bytes_unchecked`.
414//!
415use proc_macro::TokenStream;
416use syn::{parse_macro_input, DeriveInput};
417
418mod case;
419mod codegen;
420mod config;
421mod crate_name;
422mod model;
423
424/// Compile a struct into a parser and a spec. See the [crate docs](crate).
425#[proc_macro_derive(Cli, attributes(usage, command, arg, group))]
426pub fn derive_cli(input: TokenStream) -> TokenStream {
427 let input = parse_macro_input!(input as DeriveInput);
428 let parsed = model::Cli::from_input(&input)
429 .and_then(|cli| cli.check_position(&input.ident, true).map(|()| cli));
430 match parsed {
431 Ok(cli) => codegen::emit(&cli).into(),
432 // Reporting the error as tokens rather than panicking is what puts it on
433 // the offending line instead of on the derive.
434 Err(e) => e.to_compile_error().into(),
435 }
436}
437
438/// Compile a struct into one subcommand's flags and arguments.
439///
440/// Used on the struct a [`Subcommands`] variant wraps. It generates the same
441/// tables and metadata as [`Cli`], minus the program-level parts a subcommand does
442/// not have — a name, a version, an entry point — plus the trait a parent uses to
443/// route events into it.
444#[proc_macro_derive(Args, attributes(usage, command, arg, group))]
445pub fn derive_args(input: TokenStream) -> TokenStream {
446 let input = parse_macro_input!(input as DeriveInput);
447 // `restart_token` and `mount` are per-command and belong here; `default_subcommand` is
448 // declared once for the whole spec and does not.
449 let parsed = model::Cli::from_input(&input).and_then(|mut cli| {
450 cli.composable = true;
451 cli.check_position(&input.ident, false).map(|()| cli)
452 });
453 match parsed {
454 Ok(cli) => codegen::emit_args(&cli).into(),
455 Err(e) => e.to_compile_error().into(),
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/// clap-style. 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))]
468pub fn derive_subcommands(input: TokenStream) -> TokenStream {
469 let input = parse_macro_input!(input as DeriveInput);
470 match model::Subcommands::from_input(&input) {
471 Ok(subs) => codegen::emit_subcommands(&subs).into(),
472 Err(e) => e.to_compile_error().into(),
473 }
474}
475
476/// Compile a settings struct into its own registry, reader, and spec `config` block.
477///
478/// The struct the CLI already holds its settings in becomes the declaration: field types are
479/// the settings' types, doc comments are their help, and `#[usage(...)]` carries what a spec's
480/// `prop` node would — `env`, `default`, `merge`, `scope`, `choices`, `source` bindings.
481/// The derive generates `SETTINGS_PROPS`, `SETTINGS_REGISTRY`, `SETTINGS_SPEC`,
482/// `read(&Resolved)`, and `spec_kdl()`, so the registry, the reader, and the documentation
483/// cannot drift from the struct or from each other. The whole field vocabulary is in the
484/// guide: <https://usage.jdx.dev/rust/settings>.
485///
486/// ```ignore
487/// #[derive(usage::Config)]
488/// struct Settings {
489/// /// How many jobs to run at once
490/// #[usage(env = "EX_JOBS", default = 4, cli("--jobs", "-j"))]
491/// jobs: u64,
492/// #[usage(flatten)]
493/// task: TaskSettings,
494/// }
495/// ```
496///
497/// A group flattens into another with `#[usage(flatten)]`, declaring its dotted keys under
498/// its own `#[usage(prefix = "task")]`. The joined registry refuses duplicate keys at
499/// compile time.
500#[proc_macro_derive(Config, attributes(usage))]
501pub fn derive_config(input: TokenStream) -> TokenStream {
502 let input = parse_macro_input!(input as DeriveInput);
503 match config::Config::from_input(&input) {
504 Ok(config) => config::emit(&config).into(),
505 Err(e) => e.to_compile_error().into(),
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::ValueEnum)]
516/// enum Shell {
517/// /// Bourne Again shell.
518/// Bash,
519/// #[value(alias = "shell-z")]
520/// Zsh,
521/// #[value(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, value))]
539pub fn derive_value_enum(input: TokenStream) -> TokenStream {
540 let input = parse_macro_input!(input as DeriveInput);
541 match model::ValueEnum::from_input(&input) {
542 Ok(value_enum) => codegen::emit_value_enum(&value_enum).into(),
543 Err(e) => e.to_compile_error().into(),
544 }
545}
546
547/// Compile an enum into a set of flags at most one of which may be given.
548///
549/// clap's most-requested derive ergonomic (clap#2621): mutually exclusive flags as enum
550/// variants, so the code that reads them matches on a type rather than on which of several
551/// `bool`s is set. Each variant is one switch, named by its own name in kebab-case:
552///
553/// ```ignore
554/// #[derive(usage::ArgGroup)]
555/// #[usage(name = "format")]
556/// enum Format {
557/// /// Print JSON
558/// Json,
559/// /// Print YAML
560/// Yaml,
561/// #[usage(short = 'p', long = "plain")]
562/// PlainText,
563/// }
564/// ```
565///
566/// Only a variant's doc comment becomes that switch's help; the enum's own docs are not
567/// read, because a group has no help of its own — the members do.
568///
569/// A field holds one and says `arg_group`. `Option<Format>` is a group that may be left alone
570/// and a bare `Format` is one that has to be given — the same rule every other field's type is
571/// read by, and the only spelling of required-ness a group has, since there is no default
572/// variant:
573///
574/// ```ignore
575/// #[derive(usage::Cli)]
576/// #[usage(bin = "ex")]
577/// struct Ex {
578/// #[usage(arg_group)]
579/// format: Option<Format>,
580/// }
581/// ```
582///
583/// Nothing new reaches the spec: the enum lowers to the `group` node and the flags it names,
584/// so `--json --yaml` is the same [`Error::ConflictingFlags`](usage_argv::Error::ConflictingFlags)
585/// a hand-written group produces, and a missing member of a required one is the same
586/// [`Error::MissingGroup`](usage_argv::Error::MissingGroup). A member taking a value stays a
587/// hand-written `conflicts` set, where the values have somewhere to land.
588///
589/// A variant's doc comment becomes its help. `help = "..."`, `long_help = "..."`, `hide`, and
590/// `short = 'x'` are the rest of what a switch has; `cfg` and `cfg_attr` are copied to the
591/// variant's entries in the static tables, as [`ValueEnum`] copies them.
592#[proc_macro_derive(ArgGroup, attributes(usage, command, arg, group))]
593pub fn derive_arg_group(input: TokenStream) -> TokenStream {
594 let input = parse_macro_input!(input as DeriveInput);
595 match model::ArgGroup::from_input(&input) {
596 Ok(group) => codegen::emit_arg_group(&group).into(),
597 Err(e) => e.to_compile_error().into(),
598 }
599}