Skip to main content

bon_macros/
lib.rs

1#![doc = include_str!("../README.md")]
2#![expect(
3    clippy::redundant_pub_crate,
4    clippy::map_unwrap_or,
5    clippy::items_after_statements,
6    clippy::missing_const_for_fn,
7    clippy::option_if_let_else,
8    clippy::too_many_lines,
9    clippy::if_not_else,
10
11    // This crate doesn't expose complex public function signatures, any occurrences
12    // of `&Option<T>` are internal and they are likely just small private functions
13    // that take a reference to an `Option<T>` to make the callsite cleaner avoiding
14    // the unnecessary `Option::as_ref()` calls.
15    clippy::ref_option,
16)]
17
18mod bon;
19mod builder;
20mod collections;
21mod error;
22mod normalization;
23mod parsing;
24mod privatize;
25mod util;
26
27#[cfg(test)]
28mod tests;
29
30/// Generates a builder for the function or method it's placed on.
31///
32/// ## Quick examples
33///
34/// You can turn a function with positional parameters into a function with
35/// named parameters just by placing the `#[builder]` attribute on top of it.
36///
37/// ```rust ignore
38/// use bon::builder;
39///
40/// #[builder]
41/// fn greet(name: &str, level: Option<u32>) -> String {
42///     let level = level.unwrap_or(0);
43///
44///     format!("Hello {name}! Your level is {level}")
45/// }
46///
47/// let greeting = greet()
48///     .name("Bon")
49///     .level(24) // <- setting `level` is optional, we could omit it
50///     .call();
51///
52/// assert_eq!(greeting, "Hello Bon! Your level is 24");
53/// ```
54///
55/// You can also use the `#[builder]` attribute with associated methods:
56///
57/// ```rust ignore
58/// use bon::bon;
59///
60/// struct User {
61///     id: u32,
62///     name: String,
63/// }
64///
65/// #[bon] // <- this attribute is required on impl blocks that contain `#[builder]`
66/// impl User {
67///     #[builder]
68///     fn new(id: u32, name: String) -> Self {
69///         Self { id, name }
70///     }
71///
72///     #[builder]
73///     fn greet(&self, target: &str, level: Option<&str>) -> String {
74///         let level = level.unwrap_or("INFO");
75///         let name = &self.name;
76///
77///         format!("[{level}] {name} says hello to {target}")
78///     }
79/// }
80///
81/// // The method named `new` generates `builder()/build()` methods
82/// let user = User::builder()
83///     .id(1)
84///     .name("Bon".to_owned())
85///     .build();
86///
87/// // All other methods generate `method_name()/call()` methods
88/// let greeting = user
89///     .greet()
90///     .target("the world")
91///     // `level` is optional, we can omit it here
92///     .call();
93///
94/// assert_eq!(user.id, 1);
95/// assert_eq!(user.name, "Bon");
96/// assert_eq!(greeting, "[INFO] Bon says hello to the world");
97/// ```
98///
99/// The builder never panics. Any mistakes such as missing required fields
100/// or setting the same field twice will be reported as compile-time errors.
101///
102/// See the full documentation for more details:
103/// - [Guide](https://bon-rs.com/guide/overview)
104/// - [Attributes reference](https://bon-rs.com/reference/builder)
105#[proc_macro_attribute]
106pub fn builder(
107    params: proc_macro::TokenStream,
108    item: proc_macro::TokenStream,
109) -> proc_macro::TokenStream {
110    builder::generate_from_attr(params.into(), item.into()).into()
111}
112
113/// Derives a builder for the struct it's placed on.
114///
115/// ## Quick example
116///
117/// Add a `#[derive(Builder)]` attribute to your struct to generate a `builder()` method for it.
118///
119/// ```rust ignore
120/// use bon::{bon, builder, Builder};
121///
122/// #[derive(Builder)]
123/// struct User {
124///     name: String,
125///     is_admin: bool,
126///     level: Option<u32>,
127/// }
128///
129/// let user = User::builder()
130///     .name("Bon".to_owned())
131///     // `level` is optional, we could omit it here
132///     .level(24)
133///     // call setters in any order
134///     .is_admin(true)
135///     .build();
136///
137/// assert_eq!(user.name, "Bon");
138/// assert_eq!(user.level, Some(24));
139/// assert!(user.is_admin);
140/// ```
141///
142/// The builder never panics. Any mistakes such as missing required fields
143/// or setting the same field twice will be reported as compile-time errors.
144///
145/// See the full documentation for more details:
146/// - [Guide](https://bon-rs.com/guide/overview)
147/// - [Attributes reference](https://bon-rs.com/reference/builder)
148#[proc_macro_derive(Builder, attributes(builder))]
149pub fn derive_builder(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
150    builder::generate_from_derive(item.into()).into()
151}
152
153/// Companion macro for [`builder`]. You should place it on top of the `impl` block
154/// where you want to define methods with the [`builder`] macro.
155///
156/// It provides the necessary context to the [`builder`] macros on top of the functions
157/// inside of the `impl` block. You'll get compile errors without that context.
158///
159/// # Quick example
160///
161/// ```rust ignore
162/// use bon::bon;
163///
164/// struct User {
165///     id: u32,
166///     name: String,
167/// }
168///
169/// #[bon] // <- this attribute is required on impl blocks that contain `#[builder]`
170/// impl User {
171///     #[builder]
172///     fn new(id: u32, name: String) -> Self {
173///         Self { id, name }
174///     }
175///
176///     #[builder]
177///     fn greet(&self, target: &str, level: Option<&str>) -> String {
178///         let level = level.unwrap_or("INFO");
179///         let name = &self.name;
180///
181///         format!("[{level}] {name} says hello to {target}")
182///     }
183/// }
184///
185/// // The method named `new` generates `builder()/build()` methods
186/// let user = User::builder()
187///     .id(1)
188///     .name("Bon".to_owned())
189///     .build();
190///
191/// // All other methods generate `method_name()/call()` methods
192/// let greeting = user
193///     .greet()
194///     .target("the world")
195///     // `level` is optional, we can omit it here
196///     .call();
197///
198/// assert_eq!(user.id, 1);
199/// assert_eq!(user.name, "Bon");
200/// assert_eq!(greeting, "[INFO] Bon says hello to the world");
201/// ```
202///
203/// The builder never panics. Any mistakes such as missing required fields
204/// or setting the same field twice will be reported as compile-time errors.
205///
206/// For details on this macro [see the overview](https://bon-rs.com/guide/overview).
207///
208/// [`builder`]: macro@builder
209#[proc_macro_attribute]
210pub fn bon(
211    params: proc_macro::TokenStream,
212    item: proc_macro::TokenStream,
213) -> proc_macro::TokenStream {
214    bon::generate(params.into(), item.into()).into()
215}
216
217/// Creates any map-like collection that implements [`FromIterator<(K, V)>`].
218///
219/// It automatically converts each key and value to the target type using [`Into`].
220/// This way you can write a map of `String`s without the need to call `.to_owned()`
221/// or `.to_string()` on every string literal:
222///
223/// ```rust
224/// # use bon_macros as bon;
225/// # use std::collections::HashMap;
226/// let map: HashMap<String, String> = bon::map! {
227///     "key1": "value1",
228///     format!("key{}", 2): "value2",
229///     "key3": format!("value{}", 3),
230/// };
231/// ```
232///
233/// There is no separate variant for [`BTreeMap`] and [`HashMap`]. Instead, you
234/// should annotate the return type of this macro with the desired type or make
235/// sure the compiler can infer the collection type from other context.
236///
237/// # Compile errors
238///
239/// The macro conservatively rejects duplicate keys in the map with a compile error.
240/// This check works for very simple expressions that involve only literal values.
241///
242/// ```rust compile_fail
243/// # use bon_macros as bon;
244/// # use std::collections::HashMap;
245/// let map: HashMap<String, String> = bon::map! {
246///     "key1": "value1",
247///     "key2": "value2"
248///     "key1": "value3", // compile error: `duplicate key in the map`
249/// };
250/// ```
251///
252/// [`FromIterator<(K, V)>`]: https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html
253/// [`Into`]: https://doc.rust-lang.org/stable/std/convert/trait.Into.html
254/// [`BTreeMap`]: https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html
255/// [`HashMap`]: https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html
256#[proc_macro]
257pub fn map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
258    let entries = syn::parse_macro_input!(input with collections::map::parse_macro_input);
259
260    collections::map::generate(entries).into()
261}
262
263/// Creates any set-like collection that implements [`FromIterator<T>`].
264///
265/// It automatically converts each value to the target type using [`Into`].
266/// This way you can write a set of `String`s without the need to call `.to_owned()`
267/// or `.to_string()` on every string literal:
268///
269/// ```rust
270/// # use bon_macros as bon;
271/// # use std::collections::HashSet;
272/// let set: HashSet<String> = bon::set![
273///     "value1",
274///     format!("value{}", 2),
275///     "value3",
276/// ];
277/// ```
278///
279/// There is no separate variant for [`BTreeSet`] and [`HashSet`]. Instead, you
280/// should annotate the return type of this macro with the desired type or make
281/// sure the compiler can infer the collection type from other context.
282///
283/// # Compile errors
284///
285/// The macro conservatively rejects duplicate values in the set with a compile error.
286/// This check works for very simple expressions that involve only literal values.
287///
288/// ```rust compile_fail
289/// # use bon_macros as bon;
290/// # use std::collections::HashSet;
291/// let set: HashSet<String> = bon::set![
292///     "value1",
293///     "value2"
294///     "value1", // compile error: `duplicate value in the set`
295/// ];
296/// ```
297///
298/// [`FromIterator<T>`]: https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html
299/// [`Into`]: https://doc.rust-lang.org/stable/std/convert/trait.Into.html
300/// [`BTreeSet`]: https://doc.rust-lang.org/stable/std/collections/struct.BTreeSet.html
301/// [`HashSet`]: https://doc.rust-lang.org/stable/std/collections/struct.HashSet.html
302#[proc_macro]
303pub fn set(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
304    use syn::punctuated::Punctuated;
305
306    let entries = syn::parse_macro_input!(input with Punctuated::parse_terminated);
307
308    collections::set::generate(entries).into()
309}
310
311// Private implementation detail. Don't use it directly!
312// This attribute renames the function provided to it to `__orig_{fn_name}`
313#[doc(hidden)]
314#[proc_macro_attribute]
315pub fn __privatize(
316    _params: proc_macro::TokenStream,
317    input: proc_macro::TokenStream,
318) -> proc_macro::TokenStream {
319    privatize::privatize_fn(input.into()).into()
320}