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) must_use: Option<syn::Attribute>,
61 pub(super) body: Box<dyn FinishFnBody>,
62 pub(super) output: syn::ReturnType,
63}
64
65pub(super) struct FinishFnParams {
66 pub(super) ident: syn::Ident,
67
68 pub(super) vis: Option<syn::Visibility>,
70
71 pub(super) attrs: Vec<syn::Attribute>,
72 pub(super) unsafety: Option<syn::Token![unsafe]>,
73 pub(super) asyncness: Option<syn::Token![async]>,
74 pub(super) must_use: Option<syn::Attribute>,
75 pub(super) body: Box<dyn FinishFnBody>,
76 pub(super) output: syn::ReturnType,
77}
78
79pub(super) struct StartFn {
80 pub(super) ident: syn::Ident,
81 pub(super) vis: syn::Visibility,
82
83 pub(super) docs: Vec<syn::Attribute>,
84
85 pub(super) generics: Option<Generics>,
87}
88
89pub(super) struct StartFnParams {
90 pub(super) ident: syn::Ident,
91
92 pub(super) vis: Option<syn::Visibility>,
94
95 pub(super) docs: Vec<syn::Attribute>,
96
97 pub(super) generics: Option<Generics>,
99}
100
101pub(super) struct BuilderType {
102 pub(super) ident: syn::Ident,
103
104 pub(super) vis: syn::Visibility,
106
107 pub(super) derives: DerivesConfig,
108 pub(super) docs: Vec<syn::Attribute>,
109}
110
111pub(super) struct BuilderTypeParams {
112 pub(super) ident: syn::Ident,
113 pub(super) vis: Option<syn::Visibility>,
114 pub(super) derives: DerivesConfig,
115 pub(super) docs: Option<Vec<syn::Attribute>>,
116}
117
118pub(super) struct StateMod {
119 pub(super) ident: syn::Ident,
120
121 pub(super) vis: syn::Visibility,
123
124 pub(super) vis_child: syn::Visibility,
127
128 pub(super) vis_child_child: syn::Visibility,
131
132 pub(super) docs: Vec<syn::Attribute>,
133}
134
135pub(super) struct Generics {
136 pub(super) where_clause: Option<syn::WhereClause>,
137
138 pub(super) decl_with_defaults: Vec<syn::GenericParam>,
142
143 pub(super) decl_without_defaults: Vec<syn::GenericParam>,
146
147 pub(super) args: Vec<syn::GenericArgument>,
150}
151
152pub(crate) struct BuilderGenCtx {
153 pub(super) bon: BonCratePath,
154
155 pub(super) state_var: syn::Ident,
157
158 pub(super) members: Vec<Member>,
159
160 pub(super) allow_attrs: Vec<syn::Attribute>,
164 pub(super) on: Vec<OnConfig>,
165
166 pub(super) generics: Generics,
167
168 pub(super) assoc_method_ctx: Option<AssocMethodCtx>,
169
170 pub(super) builder_type: BuilderType,
171 pub(super) state_mod: StateMod,
172 pub(super) start_fn: StartFn,
173 pub(super) finish_fn: FinishFn,
174}
175
176pub(super) struct BuilderGenCtxParams<'a> {
177 pub(crate) bon: BonCratePath,
178 pub(super) namespace: Cow<'a, GenericsNamespace>,
179 pub(super) members: Vec<Member>,
180
181 pub(super) allow_attrs: Vec<syn::Attribute>,
182 pub(super) on: Vec<OnConfig>,
183
184 pub(super) orig_item_vis: syn::Visibility,
191
192 pub(super) generics: Generics,
194
195 pub(super) assoc_method_ctx: Option<AssocMethodCtxParams>,
196
197 pub(super) builder_type: BuilderTypeParams,
198 pub(super) state_mod: ItemSigConfig,
199 pub(super) start_fn: StartFnParams,
200 pub(super) finish_fn: FinishFnParams,
201}
202
203impl BuilderGenCtx {
204 pub(super) fn new(params: BuilderGenCtxParams<'_>) -> Result<Self> {
205 let BuilderGenCtxParams {
206 bon,
207 namespace,
208 members,
209 allow_attrs,
210 on,
211 generics,
212 orig_item_vis,
213 assoc_method_ctx,
214 builder_type,
215 state_mod,
216 start_fn,
217 finish_fn,
218 } = params;
219
220 let builder_type = BuilderType {
221 ident: builder_type.ident,
222 vis: builder_type.vis.unwrap_or(orig_item_vis),
223 derives: builder_type.derives,
224 docs: builder_type.docs.unwrap_or_else(|| {
225 let doc = format!(
226 "Use builder syntax to set the inputs and finish with [`{0}()`](Self::{0}()).",
227 finish_fn.ident
228 );
229
230 vec![syn::parse_quote! {
231 #[doc = #doc]
232 }]
233 }),
234 };
235
236 let state_mod = {
237 let is_ident_overridden = state_mod.name.is_some();
238 let ident = state_mod
239 .name
240 .map(SpannedKey::into_value)
241 .unwrap_or_else(|| builder_type.ident.pascal_to_snake_case());
242
243 if builder_type.ident == ident {
244 if is_ident_overridden {
245 bail!(
246 &ident,
247 "the builder module name must be different from the builder type name"
248 )
249 }
250
251 bail!(
252 &builder_type.ident,
253 "couldn't infer the builder module name that doesn't conflict with \
254 the builder type name; by default, the builder module name is set \
255 to a snake_case equivalent of the builder type name; the snake_case \
256 conversion doesn't produce a different name for this builder type \
257 name; consider using PascalCase for the builder type name or specify \
258 a separate name for the builder module explicitly via \
259 `#[builder(state_mod = {{new_name}})]`"
260 );
261 }
262
263 let vis = state_mod
269 .vis
270 .map(SpannedKey::into_value)
271 .unwrap_or_else(|| syn::Visibility::Inherited);
272
273 let vis_child = builder_type.vis.clone().into_equivalent_in_child_module()?;
277 let vis_child_child = vis_child.clone().into_equivalent_in_child_module()?;
278
279 StateMod {
280 vis,
281 vis_child,
282 vis_child_child,
283
284 ident,
285
286 docs: state_mod
287 .docs
288 .map(SpannedKey::into_value)
289 .unwrap_or_else(|| {
290 let docs = format!(
291 "Tools for manipulating the type state of [`{}`].\n\
292 \n\
293 See the [detailed guide](https://bon-rs.com/guide/typestate-api) \
294 that describes how all the pieces here fit together.",
295 builder_type.ident
296 );
297
298 vec![syn::parse_quote!(#[doc = #docs])]
299 }),
300 }
301 };
302
303 let start_fn = StartFn {
304 ident: start_fn.ident,
305 vis: start_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
306 docs: start_fn.docs,
307 generics: start_fn.generics,
308 };
309
310 let finish_fn = FinishFn {
311 ident: finish_fn.ident,
312 vis: finish_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
313 attrs: finish_fn.attrs,
314 unsafety: finish_fn.unsafety,
315 asyncness: finish_fn.asyncness,
316 must_use: finish_fn.must_use,
317 body: finish_fn.body,
318 output: finish_fn.output,
319 };
320
321 let state_var = {
322 let possible_names = ["S", "State", "BuilderState"];
323 possible_names
324 .iter()
325 .find(|&&candidate| !namespace.idents.contains(candidate))
326 .map(|&name| syn::Ident::new(name, Span::call_site()))
327 .unwrap_or_else(|| namespace.unique_ident(format!("{}_", possible_names[0])))
328 };
329
330 let assoc_method_ctx = assoc_method_ctx.map(|ctx| {
331 let receiver = ctx.receiver.map(|receiver| {
332 let start_fn_arg_names = members
333 .iter()
334 .filter_map(Member::as_start_fn)
335 .map(|member| member.ident.to_string())
336 .collect();
337
338 let field_ident = crate::normalization::unique_name(
339 &start_fn_arg_names,
340 "self_receiver".to_owned(),
341 );
342
343 AssocMethodReceiverCtx {
344 with_self_keyword: receiver.with_self_keyword,
345 without_self_keyword: receiver.without_self_keyword,
346 field_ident: syn::Ident::new(&field_ident, Span::call_site()),
347 }
348 });
349
350 AssocMethodCtx {
351 self_ty: ctx.self_ty,
352 receiver,
353 }
354 });
355
356 Ok(Self {
357 bon,
358 state_var,
359 members,
360 allow_attrs,
361 on,
362 generics,
363 assoc_method_ctx,
364 builder_type,
365 state_mod,
366 start_fn,
367 finish_fn,
368 })
369 }
370}
371
372impl Generics {
373 pub(super) fn new(
374 decl_with_defaults: Vec<syn::GenericParam>,
375 where_clause: Option<syn::WhereClause>,
376 ) -> Self {
377 let decl_without_defaults = decl_with_defaults
378 .iter()
379 .cloned()
380 .map(|mut param| {
381 match &mut param {
382 syn::GenericParam::Type(param) => {
383 param.default = None;
384 }
385 syn::GenericParam::Const(param) => {
386 param.default = None;
387 }
388 syn::GenericParam::Lifetime(_) => {}
389 }
390 param
391 })
392 .collect();
393
394 let args = decl_with_defaults
395 .iter()
396 .map(syn::GenericParam::to_generic_argument)
397 .collect();
398
399 Self {
400 where_clause,
401 decl_with_defaults,
402 decl_without_defaults,
403 args,
404 }
405 }
406
407 pub(super) fn where_clause_predicates(&self) -> impl Iterator<Item = &syn::WherePredicate> {
408 self.where_clause
409 .as_ref()
410 .into_iter()
411 .flat_map(|clause| &clause.predicates)
412 }
413}