bon_macros/
lib.rs

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