bon_macros/builder/builder_gen/
models.rs1use super::member::Member;
2use super::top_level_config::{DerivesConfig, OnConfig};
3use crate::normalization::GenericsNamespace;
4use crate::parsing::{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
20pub(super) struct AssocMethodCtx {
21 pub(super) self_ty: Box<syn::Type>,
24
25 pub(super) receiver: Option<AssocMethodReceiverCtx>,
28}
29
30pub(super) struct FinishFn {
31 pub(super) ident: syn::Ident,
32
33 pub(super) vis: syn::Visibility,
35
36 pub(super) attrs: Vec<syn::Attribute>,
38
39 pub(super) unsafety: Option<syn::Token![unsafe]>,
40 pub(super) asyncness: Option<syn::Token![async]>,
41 pub(super) must_use: Option<syn::Attribute>,
43 pub(super) body: Box<dyn FinishFnBody>,
44 pub(super) output: syn::ReturnType,
45}
46
47pub(super) struct FinishFnParams {
48 pub(super) ident: syn::Ident,
49
50 pub(super) vis: Option<syn::Visibility>,
52
53 pub(super) attrs: Vec<syn::Attribute>,
54 pub(super) unsafety: Option<syn::Token![unsafe]>,
55 pub(super) asyncness: Option<syn::Token![async]>,
56 pub(super) must_use: Option<syn::Attribute>,
57 pub(super) body: Box<dyn FinishFnBody>,
58 pub(super) output: syn::ReturnType,
59}
60
61pub(super) struct StartFn {
62 pub(super) ident: syn::Ident,
63 pub(super) vis: syn::Visibility,
64
65 pub(super) docs: Vec<syn::Attribute>,
66
67 pub(super) generics: Option<Generics>,
69}
70
71pub(super) struct StartFnParams {
72 pub(super) ident: syn::Ident,
73
74 pub(super) vis: Option<syn::Visibility>,
76
77 pub(super) docs: Vec<syn::Attribute>,
78
79 pub(super) generics: Option<Generics>,
81}
82
83pub(super) struct BuilderType {
84 pub(super) ident: syn::Ident,
85
86 pub(super) vis: syn::Visibility,
88
89 pub(super) derives: DerivesConfig,
90 pub(super) docs: Vec<syn::Attribute>,
91}
92
93pub(super) struct BuilderTypeParams {
94 pub(super) ident: syn::Ident,
95 pub(super) vis: Option<syn::Visibility>,
96 pub(super) derives: DerivesConfig,
97 pub(super) docs: Option<Vec<syn::Attribute>>,
98}
99
100pub(super) struct StateMod {
101 pub(super) ident: syn::Ident,
102
103 pub(super) vis: syn::Visibility,
105
106 pub(super) vis_child: syn::Visibility,
109
110 pub(super) vis_child_child: syn::Visibility,
113
114 pub(super) docs: Vec<syn::Attribute>,
115}
116
117pub(super) struct Generics {
118 pub(super) where_clause: Option<syn::WhereClause>,
119
120 pub(super) decl_with_defaults: Vec<syn::GenericParam>,
124
125 pub(super) decl_without_defaults: Vec<syn::GenericParam>,
128
129 pub(super) args: Vec<syn::GenericArgument>,
132}
133
134pub(crate) struct BuilderGenCtx {
135 pub(super) bon: syn::Path,
137
138 pub(super) state_var: syn::Ident,
140
141 pub(super) members: Vec<Member>,
142
143 pub(super) allow_attrs: Vec<syn::Attribute>,
147 pub(super) on: Vec<OnConfig>,
148
149 pub(super) generics: Generics,
150
151 pub(super) assoc_method_ctx: Option<AssocMethodCtx>,
152
153 pub(super) builder_type: BuilderType,
154 pub(super) state_mod: StateMod,
155 pub(super) start_fn: StartFn,
156 pub(super) finish_fn: FinishFn,
157}
158
159pub(super) struct BuilderGenCtxParams<'a> {
160 pub(crate) bon: Option<syn::Path>,
161 pub(super) namespace: Cow<'a, GenericsNamespace>,
162 pub(super) members: Vec<Member>,
163
164 pub(super) allow_attrs: Vec<syn::Attribute>,
165 pub(super) on: Vec<OnConfig>,
166
167 pub(super) orig_item_vis: syn::Visibility,
174
175 pub(super) generics: Generics,
177
178 pub(super) assoc_method_ctx: Option<AssocMethodCtx>,
179
180 pub(super) builder_type: BuilderTypeParams,
181 pub(super) state_mod: ItemSigConfig,
182 pub(super) start_fn: StartFnParams,
183 pub(super) finish_fn: FinishFnParams,
184}
185
186impl BuilderGenCtx {
187 pub(super) fn new(params: BuilderGenCtxParams<'_>) -> Result<Self> {
188 let BuilderGenCtxParams {
189 bon,
190 namespace,
191 members,
192 allow_attrs,
193 on,
194 generics,
195 orig_item_vis,
196 assoc_method_ctx,
197 builder_type,
198 state_mod,
199 start_fn,
200 finish_fn,
201 } = params;
202
203 let builder_type = BuilderType {
204 ident: builder_type.ident,
205 vis: builder_type.vis.unwrap_or(orig_item_vis),
206 derives: builder_type.derives,
207 docs: builder_type.docs.unwrap_or_else(|| {
208 let doc = format!(
209 "Use builder syntax to set the inputs and finish with [`{0}()`](Self::{0}()).",
210 finish_fn.ident
211 );
212
213 vec![syn::parse_quote! {
214 #[doc = #doc]
215 }]
216 }),
217 };
218
219 let state_mod = {
220 let is_ident_overridden = state_mod.name.is_some();
221 let ident = state_mod
222 .name
223 .map(SpannedKey::into_value)
224 .unwrap_or_else(|| builder_type.ident.pascal_to_snake_case());
225
226 if builder_type.ident == ident {
227 if is_ident_overridden {
228 bail!(
229 &ident,
230 "the builder module name must be different from the builder type name"
231 )
232 }
233
234 bail!(
235 &builder_type.ident,
236 "couldn't infer the builder module name that doesn't conflict with \
237 the builder type name; by default, the builder module name is set \
238 to a snake_case equivalent of the builder type name; the snake_case \
239 conversion doesn't produce a different name for this builder type \
240 name; consider using PascalCase for the builder type name or specify \
241 a separate name for the builder module explicitly via \
242 `#[builder(state_mod = {{new_name}})]`"
243 );
244 }
245
246 let vis = state_mod
252 .vis
253 .map(SpannedKey::into_value)
254 .unwrap_or_else(|| syn::Visibility::Inherited);
255
256 let vis_child = builder_type.vis.clone().into_equivalent_in_child_module()?;
260 let vis_child_child = vis_child.clone().into_equivalent_in_child_module()?;
261
262 StateMod {
263 vis,
264 vis_child,
265 vis_child_child,
266
267 ident,
268
269 docs: state_mod
270 .docs
271 .map(SpannedKey::into_value)
272 .unwrap_or_else(|| {
273 let docs = format!(
274 "Tools for manipulating the type state of [`{}`].\n\
275 \n\
276 See the [detailed guide](https://bon-rs.com/guide/typestate-api) \
277 that describes how all the pieces here fit together.",
278 builder_type.ident
279 );
280
281 vec![syn::parse_quote!(#[doc = #docs])]
282 }),
283 }
284 };
285
286 let start_fn = StartFn {
287 ident: start_fn.ident,
288 vis: start_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
289 docs: start_fn.docs,
290 generics: start_fn.generics,
291 };
292
293 let finish_fn = FinishFn {
294 ident: finish_fn.ident,
295 vis: finish_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
296 attrs: finish_fn.attrs,
297 unsafety: finish_fn.unsafety,
298 asyncness: finish_fn.asyncness,
299 must_use: finish_fn.must_use,
300 body: finish_fn.body,
301 output: finish_fn.output,
302 };
303
304 let state_var = {
305 let possible_names = ["S", "State", "BuilderState"];
306 possible_names
307 .iter()
308 .find(|&&candidate| !namespace.idents.contains(candidate))
309 .map(|&name| syn::Ident::new(name, Span::call_site()))
310 .unwrap_or_else(|| namespace.unique_ident(format!("{}_", possible_names[0])))
311 };
312
313 Ok(Self {
314 bon: bon.unwrap_or_else(|| syn::parse_quote!(::bon)),
315 state_var,
316 members,
317 allow_attrs,
318 on,
319 generics,
320 assoc_method_ctx,
321 builder_type,
322 state_mod,
323 start_fn,
324 finish_fn,
325 })
326 }
327}
328
329impl Generics {
330 pub(super) fn new(
331 decl_with_defaults: Vec<syn::GenericParam>,
332 where_clause: Option<syn::WhereClause>,
333 ) -> Self {
334 let decl_without_defaults = decl_with_defaults
335 .iter()
336 .cloned()
337 .map(|mut param| {
338 match &mut param {
339 syn::GenericParam::Type(param) => {
340 param.default = None;
341 }
342 syn::GenericParam::Const(param) => {
343 param.default = None;
344 }
345 syn::GenericParam::Lifetime(_) => {}
346 }
347 param
348 })
349 .collect();
350
351 let args = decl_with_defaults
352 .iter()
353 .map(syn::GenericParam::to_generic_argument)
354 .collect();
355
356 Self {
357 where_clause,
358 decl_with_defaults,
359 decl_without_defaults,
360 args,
361 }
362 }
363
364 pub(super) fn where_clause_predicates(&self) -> impl Iterator<Item = &syn::WherePredicate> {
365 self.where_clause
366 .as_ref()
367 .into_iter()
368 .flat_map(|clause| &clause.predicates)
369 }
370}