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