bon_macros/builder/builder_gen/
models.rs1use 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 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) namespace: GenericsNamespace,
167
168 pub(super) members: Vec<Member>,
169
170 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 pub(super) orig_item_vis: syn::Visibility,
204
205 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 let vis = state_mod
285 .vis
286 .map(SpannedKey::into_value)
287 .unwrap_or_else(|| syn::Visibility::Inherited);
288
289 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}