Skip to main content

bon_macros/builder/builder_gen/
models.rs

1use super::member::Member;
2use super::top_level_config::{DerivesConfig, GenericsConfig, 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    /// Namespace for generating unique identifiers.
166    pub(super) namespace: GenericsNamespace,
167
168    pub(super) members: Vec<Member>,
169
170    /// Lint suppressions from the original item that will be inherited by all items
171    /// generated by the macro. If the original syntax used `#[expect(...)]`,
172    /// then it must be represented as `#[allow(...)]` here.
173    pub(super) allow_attrs: Vec<syn::Attribute>,
174    pub(super) const_: Option<syn::Token![const]>,
175    pub(super) on: Vec<OnConfig>,
176
177    pub(super) generics: Generics,
178    pub(super) generics_config: Option<GenericsConfig>,
179
180    pub(super) assoc_method_ctx: Option<AssocMethodCtx>,
181
182    pub(super) builder_type: BuilderType,
183    pub(super) state_mod: StateMod,
184    pub(super) start_fn: StartFn,
185    pub(super) finish_fn: FinishFn,
186}
187
188pub(super) struct BuilderGenCtxParams<'a> {
189    pub(crate) bon: BonCratePath,
190    pub(super) namespace: Cow<'a, GenericsNamespace>,
191    pub(super) members: Vec<Member>,
192
193    pub(super) allow_attrs: Vec<syn::Attribute>,
194    pub(super) const_: Option<syn::Token![const]>,
195    pub(super) on: Vec<OnConfig>,
196
197    /// This is the visibility of the original item that the builder is generated for.
198    /// For example, the `struct` or `fn` item visibility that the `#[builder]` or
199    /// `#[derive(Builder)]` attribute is applied to.
200    ///
201    /// It is used as the default visibility for all the generated items unless
202    /// explicitly overridden at a more specific level.
203    pub(super) orig_item_vis: syn::Visibility,
204
205    /// Generics to apply to the builder type.
206    pub(super) generics: Generics,
207    pub(super) generics_config: Option<GenericsConfig>,
208
209    pub(super) assoc_method_ctx: Option<AssocMethodCtxParams>,
210
211    pub(super) builder_type: BuilderTypeParams,
212    pub(super) state_mod: ItemSigConfig,
213    pub(super) start_fn: StartFnParams,
214    pub(super) finish_fn: FinishFnParams,
215}
216
217impl BuilderGenCtx {
218    pub(super) fn new(params: BuilderGenCtxParams<'_>) -> Result<Self> {
219        let BuilderGenCtxParams {
220            bon,
221            namespace,
222            members,
223            allow_attrs,
224            const_,
225            on,
226            generics,
227            generics_config,
228            orig_item_vis,
229            assoc_method_ctx,
230            builder_type,
231            state_mod,
232            start_fn,
233            finish_fn,
234        } = params;
235
236        let builder_type = BuilderType {
237            ident: builder_type.ident,
238            vis: builder_type.vis.unwrap_or(orig_item_vis),
239            derives: builder_type.derives,
240            docs: builder_type.docs.unwrap_or_else(|| {
241                let doc = format!(
242                    "Use builder syntax to set the inputs and finish with [`{0}()`](Self::{0}()).",
243                    finish_fn.ident
244                );
245
246                vec![syn::parse_quote! {
247                    #[doc = #doc]
248                }]
249            }),
250        };
251
252        let state_mod = {
253            let is_ident_overridden = state_mod.name.is_some();
254            let ident = state_mod
255                .name
256                .map(SpannedKey::into_value)
257                .unwrap_or_else(|| builder_type.ident.pascal_to_snake_case());
258
259            if builder_type.ident == ident {
260                if is_ident_overridden {
261                    bail!(
262                        &ident,
263                        "the builder module name must be different from the builder type name"
264                    )
265                }
266
267                bail!(
268                    &builder_type.ident,
269                    "couldn't infer the builder module name that doesn't conflict with \
270                    the builder type name; by default, the builder module name is set \
271                    to a snake_case equivalent of the builder type name; the snake_case \
272                    conversion doesn't produce a different name for this builder type \
273                    name; consider using PascalCase for the builder type name or specify \
274                    a separate name for the builder module explicitly via \
275                    `#[builder(state_mod = {{new_name}})]`"
276                );
277            }
278
279            // The builder module is private by default, meaning all symbols under
280            // that module can't be accessed from outside the module where the builder
281            // is defined. This makes the builder type signature unnamable from outside
282            // the module where we output the builder. The users need to explicitly
283            // opt-in to make the builder module public.
284            let vis = state_mod
285                .vis
286                .map(SpannedKey::into_value)
287                .unwrap_or_else(|| syn::Visibility::Inherited);
288
289            // The visibility for child items is based on the visibility of the
290            // builder type itself, because the types and traits from this module
291            // are part of the builder's generic type state parameter signature.
292            let vis_child = builder_type.vis.clone().into_equivalent_in_child_module()?;
293            let vis_child_child = vis_child.clone().into_equivalent_in_child_module()?;
294
295            StateMod {
296                vis,
297                vis_child,
298                vis_child_child,
299
300                ident,
301
302                docs: state_mod
303                    .docs
304                    .map(SpannedKey::into_value)
305                    .unwrap_or_else(|| {
306                        let docs = format!(
307                            "Tools for manipulating the type state of [`{}`].\n\
308                            \n\
309                            See the [detailed guide](https://bon-rs.com/guide/typestate-api) \
310                            that describes how all the pieces here fit together.",
311                            builder_type.ident
312                        );
313
314                        vec![syn::parse_quote!(#[doc = #docs])]
315                    }),
316            }
317        };
318
319        let start_fn = StartFn {
320            ident: start_fn.ident,
321            vis: start_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
322            docs: start_fn.docs,
323            generics: start_fn.generics,
324            span: start_fn.span.unwrap_or_else(Span::call_site),
325        };
326
327        let finish_fn = FinishFn {
328            ident: finish_fn.ident,
329            vis: finish_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
330            attrs: finish_fn.attrs,
331            unsafety: finish_fn.unsafety,
332            asyncness: finish_fn.asyncness,
333            special_attrs: finish_fn.special_attrs,
334            body: finish_fn.body,
335            output: finish_fn.output,
336        };
337
338        let state_var = {
339            let possible_names = ["S", "State", "BuilderState"];
340            possible_names
341                .iter()
342                .find(|&&candidate| !namespace.idents.contains(candidate))
343                .map(|&name| syn::Ident::new(name, Span::call_site()))
344                .unwrap_or_else(|| namespace.unique_ident(format!("{}_", possible_names[0])))
345        };
346
347        let assoc_method_ctx = assoc_method_ctx.map(|ctx| {
348            let receiver = ctx.receiver.map(|receiver| {
349                let start_fn_arg_names = members
350                    .iter()
351                    .filter_map(Member::as_start_fn)
352                    .map(|member| member.ident.to_string())
353                    .collect();
354
355                let field_ident = crate::normalization::unique_name(
356                    &start_fn_arg_names,
357                    "self_receiver".to_owned(),
358                );
359
360                AssocMethodReceiverCtx {
361                    with_self_keyword: receiver.with_self_keyword,
362                    without_self_keyword: receiver.without_self_keyword,
363                    field_ident: syn::Ident::new(&field_ident, Span::call_site()),
364                }
365            });
366
367            AssocMethodCtx {
368                self_ty: ctx.self_ty,
369                receiver,
370            }
371        });
372
373        Ok(Self {
374            bon,
375            state_var,
376            namespace: namespace.into_owned(),
377            members,
378            allow_attrs,
379            const_,
380            on,
381            generics,
382            generics_config,
383            assoc_method_ctx,
384            builder_type,
385            state_mod,
386            start_fn,
387            finish_fn,
388        })
389    }
390}
391
392impl Generics {
393    pub(super) fn new(
394        decl_with_defaults: Vec<syn::GenericParam>,
395        where_clause: Option<syn::WhereClause>,
396    ) -> Self {
397        let decl_without_defaults = decl_with_defaults
398            .iter()
399            .cloned()
400            .map(|mut param| {
401                match &mut param {
402                    syn::GenericParam::Type(param) => {
403                        param.default = None;
404                    }
405                    syn::GenericParam::Const(param) => {
406                        param.default = None;
407                    }
408                    syn::GenericParam::Lifetime(_) => {}
409                }
410                param
411            })
412            .collect();
413
414        let args = decl_with_defaults
415            .iter()
416            .map(syn::GenericParam::to_generic_argument)
417            .collect();
418
419        Self {
420            where_clause,
421            decl_with_defaults,
422            decl_without_defaults,
423            args,
424        }
425    }
426
427    pub(super) fn where_clause_predicates(&self) -> impl Iterator<Item = &syn::WherePredicate> {
428        self.where_clause
429            .as_ref()
430            .into_iter()
431            .flat_map(|clause| &clause.predicates)
432    }
433}