click_derive/lib.rs
1//! Derive macros for click-rs CLI library.
2//!
3//! This crate provides procedural macros for automatically generating
4//! `CommandLike` implementations from Rust structs.
5//!
6//! # Example
7//!
8//! ```ignore
9//! use click_derive::Command;
10//!
11//! #[derive(Command)]
12//! #[command(name = "greet")]
13//! /// A friendly greeter
14//! struct Greet {
15//! /// Name to greet
16//! #[argument]
17//! name: String,
18//!
19//! /// Number of times to greet
20//! #[option(short, long, default = 1)]
21//! count: i32,
22//! }
23//!
24//! impl Greet {
25//! fn run(&self, _ctx: &click::Context) -> click::Result<()> {
26//! for _ in 0..self.count {
27//! println!("Hello, {}!", self.name);
28//! }
29//! Ok(())
30//! }
31//! }
32//! ```
33
34use proc_macro::TokenStream;
35use syn::{parse_macro_input, DeriveInput, ItemFn};
36
37mod attrs;
38mod command;
39mod function;
40mod group;
41
42use command::expand_command;
43use function::{expand_command_fn, expand_group_fn};
44use group::expand_group;
45
46/// Derive macro for creating CLI commands from structs.
47///
48/// # Container Attributes
49///
50/// - `#[command(name = "...")]` - Set the command name (defaults to struct name in kebab-case)
51/// - `#[command(help = "...")]` - Set the help text (defaults to doc comment)
52/// - `#[command(run)]` - Wire `Self::run(&self|self, &Context) -> Result<()>` as callback
53/// - `#[command(hidden)]` - Hide the command from help
54/// - `#[command(no_args_is_help)]` - Show help when no arguments provided
55///
56/// # Field Attributes
57///
58/// ## Options
59///
60/// - `#[option(short, long)]` - Create option with short (-n) and long (--name) flags
61/// - `#[option(short = 'n')]` - Specify custom short flag
62/// - `#[option(long = "name")]` - Specify custom long flag name
63/// - `#[option(help = "...")]` - Set help text
64/// - `#[option(default = value)]` - Set default value
65/// - `#[option(required)]` - Mark as required
66/// - `#[option(count)]` - Count occurrences (-v -v -v = 3)
67/// - `#[option(flag)]` - Boolean flag (no value)
68/// - `#[option(nargs = -1)]` - Variadic option values
69/// - `#[option(type = click::PathType::new())]` - Explicit converter expression
70/// - `#[option(validate = my_validator)]` - Declarative validation (`fn(&T) -> Result<(), String>`)
71/// - `#[option(envvar = "VAR")]` - Read from environment variable
72/// - `#[option(dest = "name")]` - Override destination parameter name
73/// - `#[option(shell_complete = my_completer)]` - Custom value completion callback
74///
75/// ## Arguments
76///
77/// - `#[argument]` - Positional argument
78/// - `#[argument(help = "...")]` - Set help text
79/// - `#[argument(required = false)]` - Optional argument
80/// - `#[argument(multiple)]` - Accept multiple values
81/// - `#[argument(nargs = -1)]` - Variadic argument values
82/// - `#[argument(type = click::FileType::new())]` - Explicit converter expression
83/// - `#[argument(validate = my_validator)]` - Declarative validation (`fn(&T) -> Result<(), String>`)
84/// - `#[argument(shell_complete = my_completer)]` - Custom completion callback
85///
86/// # Example
87///
88/// ```ignore
89/// #[derive(Command)]
90/// #[command(name = "greet")]
91/// struct Greet {
92/// #[option(short, long)]
93/// name: String,
94///
95/// #[option(short, long, default = 1)]
96/// count: i32,
97///
98/// #[argument]
99/// target: String,
100/// }
101/// ```
102#[proc_macro_derive(
103 Command,
104 attributes(
105 command,
106 option,
107 argument,
108 pass_context,
109 pass_obj,
110 version_option,
111 help_option,
112 confirmation_option,
113 password_option
114 )
115)]
116pub fn derive_command(input: TokenStream) -> TokenStream {
117 let input = parse_macro_input!(input as DeriveInput);
118 expand_command(input)
119 .unwrap_or_else(|e| e.to_compile_error())
120 .into()
121}
122
123/// Derive macro for creating CLI command groups from structs.
124///
125/// # Container Attributes
126///
127/// - `#[group(name = "...")]` - Set the group name
128/// - `#[group(help = "...")]` - Set the help text
129/// - `#[group(run)]` - Wire `Self::run(&self|self, &Context) -> Result<()>` as callback
130/// - `#[group(chain)]` - Enable command chaining
131/// - `#[group(invoke_without_command)]` - Run callback even without subcommand
132///
133/// # Field Attributes
134///
135/// - `#[subcommand]` - Mark field as a subcommand
136///
137/// # Example
138///
139/// ```ignore
140/// #[derive(Group)]
141/// #[group(name = "cli")]
142/// struct Cli {
143/// #[option(short, long)]
144/// verbose: bool,
145///
146/// #[subcommand]
147/// command: Commands,
148/// }
149///
150/// enum Commands {
151/// Add(AddCmd),
152/// Remove(RemoveCmd),
153/// }
154/// ```
155#[proc_macro_derive(
156 Group,
157 attributes(
158 group,
159 option,
160 argument,
161 subcommand,
162 pass_context,
163 pass_obj,
164 help_option
165 )
166)]
167pub fn derive_group(input: TokenStream) -> TokenStream {
168 let input = parse_macro_input!(input as DeriveInput);
169 expand_group(input)
170 .unwrap_or_else(|e| e.to_compile_error())
171 .into()
172}
173
174/// Attribute macro for function-first command definitions.
175///
176/// The annotated function parameters must use click parameter attributes such as
177/// `#[option(...)]` or `#[argument(...)]`. The macro generates:
178/// - a hidden derive-backed struct that carries the parameter metadata
179/// - a `<function_name>_command()` helper that builds a `click::Command`
180///
181/// # Example
182///
183/// ```ignore
184/// #[click::command(name = "hello")]
185/// fn hello(#[argument] name: String, #[option(short, long)] loud: bool) -> click::Result<()> {
186/// if loud { println!("HELLO, {}!", name); } else { println!("Hello, {}!", name); }
187/// Ok(())
188/// }
189///
190/// let cmd = hello_command();
191/// ```
192#[proc_macro_attribute]
193pub fn command(args: TokenStream, input: TokenStream) -> TokenStream {
194 let args = proc_macro2::TokenStream::from(args);
195 let input = parse_macro_input!(input as ItemFn);
196 expand_command_fn(args, input)
197 .unwrap_or_else(|e| e.to_compile_error())
198 .into()
199}
200
201/// Attribute macro for function-first group definitions.
202///
203/// Supports all `#[group(...)]` options accepted by the derive macro, plus:
204/// - `commands = [a, b, c]` where each entry resolves to `<name>_command()` (or can be explicit calls)
205/// - `groups = [nested]` where each entry resolves to `<name>_group()` (or can be explicit calls)
206///
207/// # Example
208///
209/// ```ignore
210/// #[click::group(name = "cli", commands = [hello], groups = [admin])]
211/// fn cli(#[option(short, long)] verbose: bool) -> click::Result<()> {
212/// if verbose { println!("verbose mode"); }
213/// Ok(())
214/// }
215///
216/// let grp = cli_group();
217/// ```
218#[proc_macro_attribute]
219pub fn group(args: TokenStream, input: TokenStream) -> TokenStream {
220 let args = proc_macro2::TokenStream::from(args);
221 let input = parse_macro_input!(input as ItemFn);
222 expand_group_fn(args, input)
223 .unwrap_or_else(|e| e.to_compile_error())
224 .into()
225}