bon_macros/builder/builder_gen/
models.rs

1use super::member::Member;
2use super::top_level_config::{DerivesConfig, OnConfig};
3use crate::normalization::GenericsNamespace;
4use crate::parsing::{ItemSigConfig, SpannedKey};
5use crate::util::prelude::*;
6use std::borrow::Cow;
7
8pub(super) trait FinishFnBody {
9    /// Generate the `finish` function body from the ready-made variables.
10    /// The generated function body may assume that there are variables
11    /// named the same as the members in scope.
12    fn generate(&self, ctx: &BuilderGenCtx) -> TokenStream;
13}
14
15pub(super) struct AssocMethodReceiverCtx {
16    pub(super) with_self_keyword: syn::Receiver,
17    pub(super) without_self_keyword: Box<syn::Type>,
18}
19
20pub(super) struct AssocMethodCtx {
21    /// The `Self` type of the impl block. It doesn't contain any nested
22    /// `Self` keywords in it. This is prohibited by Rust's syntax itself.
23    pub(super) self_ty: Box<syn::Type>,
24
25    /// Present only if the method has a receiver, i.e. `self` or `&self` or
26    /// `&mut self` or `self: ExplicitType`.
27    pub(super) receiver: Option<AssocMethodReceiverCtx>,
28}
29
30pub(super) struct FinishFn {
31    pub(super) ident: syn::Ident,
32
33    /// Visibility override specified by the user
34    pub(super) vis: syn::Visibility,
35
36    /// Additional attributes to apply to the item
37    pub(super) attrs: Vec<syn::Attribute>,
38
39    pub(super) unsafety: Option<syn::Token![unsafe]>,
40    pub(super) asyncness: Option<syn::Token![async]>,
41    /// <https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute>
42    pub(super) must_use: Option<syn::Attribute>,
43    pub(super) body: Box<dyn FinishFnBody>,
44    pub(super) output: syn::ReturnType,
45}
46
47pub(super) struct FinishFnParams {
48    pub(super) ident: syn::Ident,
49
50    /// Visibility override specified by the user
51    pub(super) vis: Option<syn::Visibility>,
52
53    pub(super) attrs: Vec<syn::Attribute>,
54    pub(super) unsafety: Option<syn::Token![unsafe]>,
55    pub(super) asyncness: Option<syn::Token![async]>,
56    pub(super) must_use: Option<syn::Attribute>,
57    pub(super) body: Box<dyn FinishFnBody>,
58    pub(super) output: syn::ReturnType,
59}
60
61pub(super) struct StartFn {
62    pub(super) ident: syn::Ident,
63    pub(super) vis: syn::Visibility,
64
65    pub(super) docs: Vec<syn::Attribute>,
66
67    /// Overrides the default generics
68    pub(super) generics: Option<Generics>,
69}
70
71pub(super) struct StartFnParams {
72    pub(super) ident: syn::Ident,
73
74    /// If present overrides the default visibility derived from the builder's type.
75    pub(super) vis: Option<syn::Visibility>,
76
77    pub(super) docs: Vec<syn::Attribute>,
78
79    /// Overrides the default generics
80    pub(super) generics: Option<Generics>,
81}
82
83pub(super) struct BuilderType {
84    pub(super) ident: syn::Ident,
85
86    /// Visibility of the builder module itself.
87    pub(super) vis: syn::Visibility,
88
89    pub(super) derives: DerivesConfig,
90    pub(super) docs: Vec<syn::Attribute>,
91}
92
93pub(super) struct BuilderTypeParams {
94    pub(super) ident: syn::Ident,
95    pub(super) vis: Option<syn::Visibility>,
96    pub(super) derives: DerivesConfig,
97    pub(super) docs: Option<Vec<syn::Attribute>>,
98}
99
100pub(super) struct StateMod {
101    pub(super) ident: syn::Ident,
102
103    /// Visibility of the builder module itself.
104    pub(super) vis: syn::Visibility,
105
106    /// Visibility equivalent to the [`Self::vis`], but for items
107    /// generated inside the builder child module.
108    pub(super) vis_child: syn::Visibility,
109
110    /// Visibility equivalent to the [`Self::vis_child`], but for items
111    /// generated inside one more level of nesting in the builder child module.
112    pub(super) vis_child_child: syn::Visibility,
113
114    pub(super) docs: Vec<syn::Attribute>,
115}
116
117pub(super) struct Generics {
118    pub(super) where_clause: Option<syn::WhereClause>,
119
120    /// Original generics that may contain default values in them. This is only
121    /// suitable for use in places where default values for generic parameters
122    /// are allowed.
123    pub(super) decl_with_defaults: Vec<syn::GenericParam>,
124
125    /// Generic parameters without default values in them. This is suitable for
126    /// use as generics in function signatures or impl blocks.
127    pub(super) decl_without_defaults: Vec<syn::GenericParam>,
128
129    /// Mirrors the `decl` representing how generic params should be represented
130    /// when these parameters are passed through as arguments in a turbofish.
131    pub(super) args: Vec<syn::GenericArgument>,
132}
133
134pub(crate) struct BuilderGenCtx {
135    /// Path to the `bon` crate.
136    pub(super) bon: syn::Path,
137
138    /// Name of the generic variable that holds the builder's state.
139    pub(super) state_var: syn::Ident,
140
141    pub(super) members: Vec<Member>,
142
143    /// Lint suppressions from the original item that will be inherited by all items
144    /// generated by the macro. If the original syntax used `#[expect(...)]`,
145    /// then it must be represented as `#[allow(...)]` here.
146    pub(super) allow_attrs: Vec<syn::Attribute>,
147    pub(super) on: Vec<OnConfig>,
148
149    pub(super) generics: Generics,
150
151    pub(super) assoc_method_ctx: Option<AssocMethodCtx>,
152
153    pub(super) builder_type: BuilderType,
154    pub(super) state_mod: StateMod,
155    pub(super) start_fn: StartFn,
156    pub(super) finish_fn: FinishFn,
157}
158
159pub(super) struct BuilderGenCtxParams<'a> {
160    pub(crate) bon: Option<syn::Path>,
161    pub(super) namespace: Cow<'a, GenericsNamespace>,
162    pub(super) members: Vec<Member>,
163
164    pub(super) allow_attrs: Vec<syn::Attribute>,
165    pub(super) on: Vec<OnConfig>,
166
167    /// This is the visibility of the original item that the builder is generated for.
168    /// For example, the `struct` or `fn` item visibility that the `#[builder]` or
169    /// `#[derive(Builder)]` attribute is applied to.
170    ///
171    /// It is used as the default visibility for all the generated items unless
172    /// explicitly overridden at a more specific level.
173    pub(super) orig_item_vis: syn::Visibility,
174
175    /// Generics to apply to the builder type.
176    pub(super) generics: Generics,
177
178    pub(super) assoc_method_ctx: Option<AssocMethodCtx>,
179
180    pub(super) builder_type: BuilderTypeParams,
181    pub(super) state_mod: ItemSigConfig,
182    pub(super) start_fn: StartFnParams,
183    pub(super) finish_fn: FinishFnParams,
184}
185
186impl BuilderGenCtx {
187    pub(super) fn new(params: BuilderGenCtxParams<'_>) -> Result<Self> {
188        let BuilderGenCtxParams {
189            bon,
190            namespace,
191            members,
192            allow_attrs,
193            on,
194            generics,
195            orig_item_vis,
196            assoc_method_ctx,
197            builder_type,
198            state_mod,
199            start_fn,
200            finish_fn,
201        } = params;
202
203        let builder_type = BuilderType {
204            ident: builder_type.ident,
205            vis: builder_type.vis.unwrap_or(orig_item_vis),
206            derives: builder_type.derives,
207            docs: builder_type.docs.unwrap_or_else(|| {
208                let doc = format!(
209                    "Use builder syntax to set the inputs and finish with [`{0}()`](Self::{0}()).",
210                    finish_fn.ident
211                );
212
213                vec![syn::parse_quote! {
214                    #[doc = #doc]
215                }]
216            }),
217        };
218
219        let state_mod = {
220            let is_ident_overridden = state_mod.name.is_some();
221            let ident = state_mod
222                .name
223                .map(SpannedKey::into_value)
224                .unwrap_or_else(|| builder_type.ident.pascal_to_snake_case());
225
226            if builder_type.ident == ident {
227                if is_ident_overridden {
228                    bail!(
229                        &ident,
230                        "the builder module name must be different from the builder type name"
231                    )
232                }
233
234                bail!(
235                    &builder_type.ident,
236                    "couldn't infer the builder module name that doesn't conflict with \
237                    the builder type name; by default, the builder module name is set \
238                    to a snake_case equivalent of the builder type name; the snake_case \
239                    conversion doesn't produce a different name for this builder type \
240                    name; consider using PascalCase for the builder type name or specify \
241                    a separate name for the builder module explicitly via \
242                    `#[builder(state_mod = {{new_name}})]`"
243                );
244            }
245
246            // The builder module is private by default, meaning all symbols under
247            // that module can't be accessed from outside the module where the builder
248            // is defined. This makes the builder type signature unnamable from outside
249            // the module where we output the builder. The users need to explicitly
250            // opt-in to make the builder module public.
251            let vis = state_mod
252                .vis
253                .map(SpannedKey::into_value)
254                .unwrap_or_else(|| syn::Visibility::Inherited);
255
256            // The visibility for child items is based on the visibility of the
257            // builder type itself, because the types and traits from this module
258            // are part of the builder's generic type state parameter signature.
259            let vis_child = builder_type.vis.clone().into_equivalent_in_child_module()?;
260            let vis_child_child = vis_child.clone().into_equivalent_in_child_module()?;
261
262            StateMod {
263                vis,
264                vis_child,
265                vis_child_child,
266
267                ident,
268
269                docs: state_mod
270                    .docs
271                    .map(SpannedKey::into_value)
272                    .unwrap_or_else(|| {
273                        let docs = format!(
274                            "Tools for manipulating the type state of [`{}`].\n\
275                            \n\
276                            See the [detailed guide](https://bon-rs.com/guide/typestate-api) \
277                            that describes how all the pieces here fit together.",
278                            builder_type.ident
279                        );
280
281                        vec![syn::parse_quote!(#[doc = #docs])]
282                    }),
283            }
284        };
285
286        let start_fn = StartFn {
287            ident: start_fn.ident,
288            vis: start_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
289            docs: start_fn.docs,
290            generics: start_fn.generics,
291        };
292
293        let finish_fn = FinishFn {
294            ident: finish_fn.ident,
295            vis: finish_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
296            attrs: finish_fn.attrs,
297            unsafety: finish_fn.unsafety,
298            asyncness: finish_fn.asyncness,
299            must_use: finish_fn.must_use,
300            body: finish_fn.body,
301            output: finish_fn.output,
302        };
303
304        let state_var = {
305            let possible_names = ["S", "State", "BuilderState"];
306            possible_names
307                .iter()
308                .find(|&&candidate| !namespace.idents.contains(candidate))
309                .map(|&name| syn::Ident::new(name, Span::call_site()))
310                .unwrap_or_else(|| namespace.unique_ident(format!("{}_", possible_names[0])))
311        };
312
313        Ok(Self {
314            bon: bon.unwrap_or_else(|| syn::parse_quote!(::bon)),
315            state_var,
316            members,
317            allow_attrs,
318            on,
319            generics,
320            assoc_method_ctx,
321            builder_type,
322            state_mod,
323            start_fn,
324            finish_fn,
325        })
326    }
327}
328
329impl Generics {
330    pub(super) fn new(
331        decl_with_defaults: Vec<syn::GenericParam>,
332        where_clause: Option<syn::WhereClause>,
333    ) -> Self {
334        let decl_without_defaults = decl_with_defaults
335            .iter()
336            .cloned()
337            .map(|mut param| {
338                match &mut param {
339                    syn::GenericParam::Type(param) => {
340                        param.default = None;
341                    }
342                    syn::GenericParam::Const(param) => {
343                        param.default = None;
344                    }
345                    syn::GenericParam::Lifetime(_) => {}
346                }
347                param
348            })
349            .collect();
350
351        let args = decl_with_defaults
352            .iter()
353            .map(syn::GenericParam::to_generic_argument)
354            .collect();
355
356        Self {
357            where_clause,
358            decl_with_defaults,
359            decl_without_defaults,
360            args,
361        }
362    }
363
364    pub(super) fn where_clause_predicates(&self) -> impl Iterator<Item = &syn::WherePredicate> {
365        self.where_clause
366            .as_ref()
367            .into_iter()
368            .flat_map(|clause| &clause.predicates)
369    }
370}