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