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. A genuinely dynamic
222//! value uses `default_fn = function` instead; an optional `default_note = "..."` reaches help,
223//! while portable KDL deliberately declares no concrete default it could not reproduce.
224//!
225//! A completer is written as
226//!
227//! ```ignore
228//! fn tasks(partial: &<Tasks as CommandArgs>::Partial, ctx: &CompleteCtx<'_>) -> Vec<Candidate<'static>>
229//! ```
230//!
231//! and is handed its *own command's* half-parsed struct, so `tk tasks --file other.toml <TAB>`
232//! can be answered against that file — which a `run=` shelling out to a fixed command cannot see.
233//! The emitted spec gets a `run=` naming this binary, so everything that reads a spec still has
234//! one, generated from the function rather than declared beside it.
235//!
236//! `Cli::parse()` is the entry point that *is* the process: it prints a help page or a version
237//! and leaves, and on a failure it prints the message to stderr and exits 2 — clap's status, so a
238//! script checking for it keeps working. `Cli::parse_from(argv)` hands the error back instead,
239//! for a library embedding a CLI that wants to decide for itself.
240//!
241//! Declaring a `version` or `long_version` also gives the CLI `--version` and `-V`, as clap does.
242//! The parser supplies version and help entry points, and generated specs materialize their
243//! surviving spellings as `action` flags so metadata consumers see the same interface. Either
244//! spelling yields to a flag the CLI declares for itself. clap refuses that collision by
245//! panicking at startup; here the declaration simply wins and the other spelling still answers.
246//!
247//! On the struct itself: `bin`, `version`, `long_version`, `author`, `license`, `repository`,
248//! `source_code_link_template` — a tera template rendered with the command path as `path`,
249//! which generated markdown turns into a "view source" link — `about`,
250//! `long_about`, `before_help`, `after_help`,
251//! `visible_alias(es)`, hidden `alias(es)`, and `hide` may be declared on an
252//! `Args` struct and are inherited by every subcommand variant that mounts it —
253//! `verbatim_doc_comment` — preserve doc-comment line breaks and whitespace —
254//! `default_subcommand`, `multicall` — argv[0]'s basename selects a subcommand —
255//! `arg_required_else_help` — a selected command with no argv of its own shows short help —
256//! `disable_help_flag`, `disable_help_subcommand`, and `disable_version_flag` — remove the
257//! corresponding synthesized entry point so a field with `action = usage::ArgAction::Help`,
258//! `HelpShort`, `HelpLong`, `HelpAll`, or `Version` can relocate it —
259//! `next_line_help` — put descriptions below each entry — `flatten_help` — expand visible
260//! subcommands into the current help page —
261//! `dont_delimit_trailing_values` — preserve delimiters after the trailing boundary —
262//! `args_override_self = false` — reject repeated scalar flags instead of letting the later
263//! occurrence correct the earlier one —
264//! `min_usage_version` — the oldest `usage` that can read the emitted
265//! spec, declared rather than worked out — `effect` — what running this command does to the world, on an `Args`
266//! rather than on the root, which does nothing itself — `completion`, which adds the hidden command a generated shell
267//! script calls, and needs usage-argv's `complete` feature enabled where it is depended on —
268//! `settings`, for a CLI whose bound flags all live in a flattened group (see [Settings]) —
269//! and `run`, `run_with`, `run_async` and `run_async_with`, which write the forward from a
270//! container command to its subcommands (see [Dispatch](#dispatch)).
271//!
272//! [Settings]: #settings-and-the-flags-that-set-them
273//!
274//! Named fields accept metadata through `#[usage(...)]`.
275//!
276//! | option | meaning |
277//! | --- | --- |
278//! | `long`, `long = "x"` | a long form, defaulting to the field name |
279//! | `short`, `short = 'x'` | a short form, defaulting to the field's first letter |
280//! | `name = "x"` | the name used in the spec and in help output |
281//! | `negate = "--no-x"` | a second long form that sets a `bool` false |
282//! | `count` | count occurrences instead of collecting values |
283//! | `var` | the flag may be repeated, taking one value each time |
284//! | `variadic` | one occurrence keeps taking values, until a flag-like token or `--` |
285//! | `var_max = n` | how many values a variadic takes before the next field gets the rest |
286//! | `global` | subcommands inherit the flag |
287//! | `env = "X"` | an environment variable that can supply the value |
288//! | `env` | infer the environment variable from the field, using the command's `rename_all_env` policy |
289//! | `env_fallback("OLD_X", "OLDER_X")` | additional environment variables, consulted in declaration order |
290//! | `deprecated_env("LEGACY_X")` | deprecated aliases, consulted after ordinary fallbacks and labeled in help |
291//! | `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 |
292//! | `default_fn = function` | compute one typed default at parse time without claiming a concrete portable value |
293//! | `default_note = "x"` | describe a `default_fn` in help; the note is prose, not a value |
294//! | `help_heading = "x"` | the section to list this under in help output |
295//! | `note = "x"` | a semantic note shown in long help and generated documentation |
296//! | `warning = "x"` | a semantic warning shown in long help and generated documentation |
297//! | `display_order = n` | explicit help order; positional parsing still follows declaration order |
298//! | `verbatim_doc_comment` | preserve line breaks and whitespace in the doc comment instead of flowing its first paragraph |
299//! | `hide` | keep it out of help and completions |
300//! | `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 |
301//! | `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) |
302//! | `complete = my_fn` | a function that answers for this value when a shell asks |
303//! | `value_enum` | the words come from the field's type, which derives [`ValueEnum`] |
304//! | `arg_group` | the flags come from the field's type, which derives [`ArgGroup`]; `Vec<T>` preserves a `multiple` group's occurrence order |
305//! | `value_hint = usage::ValueHint::FilePath` | ask the shell for paths, executables, or forwarded command argv |
306//! | `extensions("toml", "yaml")` | limit a file-path hint to these extensions while retaining directories |
307//! | `arg` | force a field to be positional |
308//! | `value_name = "NAME"` | a positional name, or the placeholder for a flag value |
309//! | `choices("a", "b")` | accepted values; typed conversion still uses the field type's `FromStr` |
310//! | `visible_alias = "other"` | an advertised long alias; the plural array spelling also works |
311//! | `alias = "other"` | a hidden long alias; the plural array spelling also works |
312//! | `overrides = "--other"` | a flag this one displaces, the last given winning |
313//! | `conflicts = "--other"` | an argument this one cannot be given with |
314//! | `requires = "--other"` | a flag that must also be given when this one is |
315//! | `requires_if("value", "--other")` | a flag required when this one explicitly has `value` |
316//! | `requires_ifs(("a", "--x"), ("b", "--y"))` | several value-conditional requirements |
317//! | `group = "input"` | the group this argument is one of; see below |
318//! | `exclusive` | this flag has to be given on its own, positionals included |
319//! | `delimiter = ','` | one word becomes several values; the field has to be a `Vec` |
320//! | `allow_hyphen_values` | a flag's detached value may look like a flag, including `--` |
321//! | `allow_negative_numbers` | accept negative numeric tokens without accepting every dash-word |
322//! | `value_terminator = ";"` | end a variadic field without storing the terminator |
323//! | `require_equals` | `--flag=value` is accepted and `--flag value` is not |
324//! | `default_missing = "always"` | the value when the flag is given with none |
325//! | `required_if = "--other"` | a flag whose presence makes this one necessary |
326//! | `required_if_eq("mode", "remote")` | a matching explicit value makes this one necessary |
327//! | `required_if_eq_any = [("mode", "a"), ("mode", "b")]` | any matching value makes this one necessary |
328//! | `required_if_eq_all = [("mode", "a"), ("scope", "global")]` | every value condition must match |
329//! | `required_unless = "--other"` | a flag whose presence makes this one unnecessary |
330//! | `required_unless = ["stdin", "file"]` | any present argument makes this unnecessary |
331//! | `required_unless_all = ["stdin", "file"]` | every named argument must be present |
332//!
333//! These name a flag as `"--long"` or `"-s"`, and a positional by its bare name. They
334//! take several as a list: `conflicts("--file", "target")`. A selector naming no argument
335//! on the command
336//! is a compile error, which is the advantage of declaring a relationship in code: in a
337//! hand-written spec a typo'd selector is a relationship that quietly does not hold.
338//!
339//! A **group** is the one relationship that is not written flag-to-flag, because what it
340//! says is about the set: `required` means one of them is needed, and no rule on an
341//! individual flag expresses that. Membership goes on the fields and the properties on the
342//! struct, which may be left out entirely when the group is a plain "at most one":
343//!
344//! ```ignore
345//! #[derive(Cli)]
346//! #[usage(bin = "ex")]
347//! #[usage(group("input", required))]
348//! struct Ex {
349//! #[usage(long, group = "input")]
350//! file: Option<String>,
351//! #[usage(long, group = "input")]
352//! url: Option<String>,
353//! }
354//! ```
355//!
356//! `required` means at least one member is needed and `multiple` means more than one may
357//! be given, so a bare group is "at most one", `required` alone is "exactly one", and the
358//! two together are "at least one" — clap's two properties, read the same way. A group
359//! with one member, or a declaration no field joins, is a compile error.
360//!
361//! A group of valueless flags may instead be an enum deriving [`ArgGroup`], held by one
362//! field marked `arg_group`, so the code reading it matches on a variant rather than on
363//! which of several `bool`s is set. It lowers to the same `group` node and the same errors.
364//!
365//! These post-parse relationships work on flags and positionals. `overrides` remains a
366//! flag-only binding rule. An argument ID such as `"mode"`, as clap attributes commonly
367//! use, resolves to the same field as the portable `"--mode"` spelling and is emitted in
368//! canonical spec form. A required-unless declaration needs somewhere to put "absent",
369//! so it takes an `Option` rather than a bare `String`.
370//!
371//! A variant may hold its struct in a `Box`, as `Install(Box<Install>)`: an enum is as
372//! large as its biggest variant, so one command with thirty flags otherwise makes every
373//! invocation move that much stack. Nothing else changes — the box is how the variant
374//! holds the struct, not something the CLI has, and the spec cannot tell.
375//!
376//! A command takes `alias = "i"` for a name it should advertise and
377//! `alias_hidden = "add"` for one it should answer to quietly, each accepting several as a
378//! list. They may be written on the `Args` struct that owns the command or on its
379//! `Subcommands` variant; when both say some, the lists are joined. The parser matches both;
380//! the difference is only whether help and completions mention them.
381//! `help_heading = "Maintenance"` on a variant groups that command under a named section
382//! in its parent's help. `display_order = n` controls where it is presented within the
383//! section.
384//!
385//! # Settings and the flags that set them
386//!
387//! `setting = "key"` says which setting a flag sets. `Cli::parse_from_with_settings` then
388//! returns a `usage_config::CliLayer` beside the parsed struct — the command line as the
389//! highest layer of a resolution — and `Cli::SETTINGS_BINDINGS` lists every flag it binds,
390//! which `usage_config::Registry::drift` compares against the flags the *spec* declares. A
391//! flag documented as setting something and read by nothing fails a test rather than a user.
392//!
393//! The layer is built from what the parser saw rather than from the parsed struct, because a
394//! `bool` field is `false` whether the flag was left off or negated, and the command line
395//! outranks every file on the machine. So `--no-colour` contributes `false`, and a flag that
396//! was not given contributes nothing at all.
397//!
398//! A setting can be declared wherever a flag is: on the root, in a `#[usage(flatten)]` group,
399//! or on a subcommand's struct. A group hands its parent what it was given in
400//! `usage_argv::spec::SettingGiven` — a vocabulary that says nothing about types, since the
401//! registry is what decides them — and only the root turns that into a layer, so a program
402//! with no settings never mentions `usage-config`. A root that binds nothing itself but
403//! flattens a group that does declares `#[usage(settings)]`; leaving it off is a compile error
404//! naming the attribute, because the alternative is a documented flag that quietly sets
405//! nothing.
406//!
407//! A word is held as the bytes it arrived as and converted once, where the struct is built.
408//! So a value that is not valid UTF-8 is **reported** rather than quietly replaced with
409//! `U+FFFD` — which for a `PathBuf` meant a different file, silently. On Unix, `PathBuf` and
410//! `OsString` fields accept the bytes exactly through the safe `OsStringExt::from_vec`; on
411//! Windows, a value that cannot be converted safely is reported rather than reconstructed with
412//! `OsString::from_encoded_bytes_unchecked`.
413//!
414use proc_macro::TokenStream;
415use syn::{parse_macro_input, DeriveInput};
416
417mod case;
418mod codegen;
419mod config;
420mod crate_name;
421mod model;
422
423/// Compile a struct into a parser and a spec. See the [crate docs](crate).
424// Legacy helper names stay registered so the derive can reject them at their
425// source span with a `#[usage(...)]` migration message.
426#[proc_macro_derive(Cli, attributes(usage, command, arg, value, group))]
427pub fn derive_cli(input: TokenStream) -> TokenStream {
428 let input = parse_macro_input!(input as DeriveInput);
429 let parsed = model::Cli::from_input(&input)
430 .and_then(|cli| cli.check_position(&input.ident, true).map(|()| cli));
431 match parsed {
432 Ok(cli) => codegen::emit(&cli).into(),
433 // Reporting the error as tokens rather than panicking is what puts it on
434 // the offending line instead of on the derive.
435 Err(e) => e.to_compile_error().into(),
436 }
437}
438
439/// Compile a struct into one subcommand's flags and arguments.
440///
441/// Used on the struct a [`Subcommands`] variant wraps. It generates the same
442/// tables and metadata as [`Cli`], minus the program-level parts a subcommand does
443/// not have — a name, a version, an entry point — plus the trait a parent uses to
444/// route events into it.
445#[proc_macro_derive(Args, attributes(usage, command, arg, value, group))]
446pub fn derive_args(input: TokenStream) -> TokenStream {
447 let input = parse_macro_input!(input as DeriveInput);
448 // `restart_token` and `mount` are per-command and belong here; `default_subcommand` is
449 // declared once for the whole spec and does not.
450 let parsed = model::Cli::from_input(&input).and_then(|mut cli| {
451 cli.composable = true;
452 cli.check_position(&input.ident, false).map(|()| cli)
453 });
454 match parsed {
455 Ok(cli) => codegen::emit_args(&cli).into(),
456 Err(e) => e.to_compile_error().into(),
457 }
458}
459
460/// Compile an enum into a set of subcommands.
461///
462/// Each variant may wrap a struct deriving [`Args`] or declare its fields inline.
463/// A field holding this enum is marked `#[usage(subcommand)]`.
464///
465/// `#[usage(run)]`, `#[usage(run_with)]`, `#[usage(run_async)]` or `#[usage(run_async_with)]` on
466/// the enum also writes the `match` that hands the selected command to its implementation; see
467/// the [crate docs](crate#dispatch).
468#[proc_macro_derive(Subcommands, attributes(usage, command, arg, value, group))]
469pub fn derive_subcommands(input: TokenStream) -> TokenStream {
470 let input = parse_macro_input!(input as DeriveInput);
471 match model::Subcommands::from_input(&input) {
472 Ok(subs) => codegen::emit_subcommands(&subs).into(),
473 Err(e) => e.to_compile_error().into(),
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::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 match config::Config::from_input(&input) {
505 Ok(config) => config::emit(&config).into(),
506 Err(e) => e.to_compile_error().into(),
507 }
508}
509
510/// Compile an enum into the words one value may be.
511///
512/// What a CLI calls an enum — `--shell bash` — and what the spec calls `choices`. The
513/// variant's name in kebab-case is the word, unless `name` says otherwise:
514///
515/// ```ignore
516/// #[derive(usage::ValueEnum)]
517/// enum Shell {
518/// /// Bourne Again shell.
519/// Bash,
520/// #[usage(alias = "shell-z")]
521/// Zsh,
522/// #[usage(name = "pwsh", visible_alias = "powershell", hide = true)]
523/// PowerShell,
524/// }
525/// ```
526///
527/// `#[usage(ignore_case)]` on the enum applies to canonical words and aliases.
528/// A variant's doc comment becomes its per-value help. `help = "..."` overrides
529/// it, `hide` keeps the value accepted while omitting it from help and completion,
530/// `alias` is hidden, and `visible_alias` is advertised alongside the canonical word.
531///
532/// The derive binds canonical words and aliases directly to their variants; a separate
533/// [`FromStr`](std::str::FromStr) implementation is not required. Variant `cfg` and
534/// `cfg_attr` attributes are copied to their entries in the static word tables.
535///
536/// A field holding one says `value_enum`, which is what puts the words in the spec — so
537/// help, completions and the check that rejects a wrong word all read the same list, and
538/// none of them can drift from the type.
539#[proc_macro_derive(ValueEnum, attributes(usage, command, arg, value, group))]
540pub fn derive_value_enum(input: TokenStream) -> TokenStream {
541 let input = parse_macro_input!(input as DeriveInput);
542 match model::ValueEnum::from_input(&input) {
543 Ok(value_enum) => codegen::emit_value_enum(&value_enum).into(),
544 Err(e) => e.to_compile_error().into(),
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::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::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 match model::ArgGroup::from_input(&input) {
601 Ok(group) => codegen::emit_arg_group(&group).into(),
602 Err(e) => e.to_compile_error().into(),
603 }
604}