bon_macros/builder/builder_gen/
models.rs1use 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 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 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 pub(super) self_ty: Box<syn::Type>,
32
33 pub(super) receiver: Option<AssocMethodReceiverCtx>,
36}
37
38pub(super) struct AssocMethodCtxParams {
39 pub(super) self_ty: Box<syn::Type>,
42
43 pub(super) receiver: Option<AssocMethodReceiverCtxParams>,
46}
47
48pub(super) struct FinishFn {
49 pub(super) ident: syn::Ident,
50
51 pub(super) vis: syn::Visibility,
53
54 pub(super) attrs: Vec<syn::Attribute>,
56
57 pub(super) unsafety: Option<syn::Token![unsafe]>,
58 pub(super) asyncness: Option<syn::Token![async]>,
59 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 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 pub(super) generics: Option<Generics>,
88
89 pub(super) span: Span,
91}
92
93pub(super) struct StartFnParams {
94 pub(super) ident: syn::Ident,
95
96 pub(super) vis: Option<syn::Visibility>,
98
99 pub(super) docs: Vec<syn::Attribute>,
100
101 pub(super) generics: Option<Generics>,
103 pub(super) span: Option<Span>,
106}
107
108pub(super) struct BuilderType {
109 pub(super) ident: syn::Ident,
110
111 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 pub(super) vis: syn::Visibility,
130
131 pub(super) vis_child: syn::Visibility,
134
135 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 pub(super) decl_with_defaults: Vec<syn::GenericParam>,
149
150 pub(super) decl_without_defaults: Vec<syn::GenericParam>,
153
154 pub(super) args: Vec<syn::GenericArgument>,
157}
158
159pub(crate) struct BuilderGenCtx {
160 pub(super) bon: BonCratePath,
161
162 pub(super) state_var: syn::Ident,
164
165 pub(super) members: Vec<Member>,
166
167 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 pub(super) orig_item_vis: syn::Visibility,
200
201 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 let vis = state_mod
279 .vis
280 .map(SpannedKey::into_value)
281 .unwrap_or_else(|| syn::Visibility::Inherited);
282
283 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}