bon_macros/builder/builder_gen/builder_decl.rs
1use crate::builder::builder_gen::NamedMember;
2use crate::util::prelude::*;
3
4impl super::BuilderGenCtx {
5 pub(super) fn builder_decl(&self) -> TokenStream {
6 let builder_vis = &self.builder_type.vis;
7 let builder_ident = &self.builder_type.ident;
8 let generics_decl = &self.generics.decl_with_defaults;
9 let where_clause = &self.generics.where_clause;
10 let phantom_data = self.phantom_data();
11 let state_mod = &self.state_mod.ident;
12
13 // The fields can't be hidden using Rust's privacy syntax.
14 // The details about this are described in the blog post:
15 // https://bon-rs.com/blog/the-weird-of-function-local-types-in-rust.
16 //
17 // We could use `#[cfg(not(rust_analyzer))]` to hide the private fields in IDE.
18 // However, RA would then not be able to type-check the generated code, which
19 // may or may not be a problem, because the main thing is that the type signatures
20 // would still work in RA.
21 let private_field_attrs = {
22 // The message is defined separately to make it single-line in the
23 // generated code. This simplifies the task of removing unnecessary
24 // attributes from the generated code when preparing for demo purposes.
25 let deprecated_msg = "\
26 this field should not be used directly; it's an implementation detail, and \
27 if you access it directly, you may break some internal unsafe invariants; \
28 if you found yourself needing it, then you are probably doing something wrong; \
29 feel free to open an issue/discussion in our GitHub repository \
30 (https://github.com/elastio/bon) or ask for help in our Discord server \
31 (https://bon-rs.com/discord)";
32
33 quote! {
34 #[doc(hidden)]
35 #[deprecated = #deprecated_msg]
36 }
37 };
38
39 let receiver_field = self.receiver().map(|receiver| {
40 let ident = &receiver.field_ident;
41 let ty = &receiver.without_self_keyword;
42 quote! {
43 /// Value of `self` passed to the starting method, that created the builder
44 #ident: #ty,
45 }
46 });
47
48 let must_use_message = format!(
49 "the builder does nothing until you call `{}()` on it to finish building",
50 self.finish_fn.ident
51 );
52
53 let allows = super::allow_warnings_on_member_types();
54
55 let start_fn_args_fields = self.start_fn_args().map(|member| {
56 let ident = &member.ident;
57 let ty = &member.ty.norm;
58 let doc = format!(
59 "Value of `{ident}` passed as an argument to the starting function,
60 that created the builder",
61 );
62 quote! {
63 #[doc = #doc]
64 #ident: #ty
65 }
66 });
67
68 let named_members_types = self.named_members().map(NamedMember::underlying_norm_ty);
69
70 let docs = &self.builder_type.docs;
71 let state_var = &self.state_var;
72
73 let custom_fields_idents = self.custom_fields().map(|field| &field.ident);
74 let custom_fields_types = self.custom_fields().map(|field| &field.norm_ty);
75
76 quote! {
77 #[must_use = #must_use_message]
78 #(#docs)*
79 #allows
80 #[allow(
81 // We use `__private` prefix for all fields intentionally to hide them
82 clippy::struct_field_names,
83
84 // This lint doesn't emerge until you manually expand the macro. Just
85 // because `bon` developers need to expand the macros a lot it makes
86 // sense to just silence it to avoid some noise. This lint is triggered
87 // by the big PhantomData type generated by the macro
88 clippy::type_complexity
89 )]
90 #builder_vis struct #builder_ident<
91 #(#generics_decl,)*
92 // Having the `State` trait bound on the struct declaration is important
93 // for future proofing. It will allow us to use this bound in the `Drop`
94 // implementation of the builder if we ever add one. @Veetaha already did
95 // some experiments with `MaybeUninit` that requires a custom drop impl,
96 // so this could be useful in the future.
97 //
98 // On the flip side, if we have a custom `Drop` impl, then partially moving
99 // the builder will be impossible. So.. it's a trade-off, and it's probably
100 // not a big deal to remove this bound from here if we feel like it.
101 #state_var: #state_mod::State = #state_mod::Empty
102 >
103 #where_clause
104 {
105 #private_field_attrs
106 __unsafe_private_phantom: #phantom_data,
107
108 #receiver_field
109
110 #( #start_fn_args_fields, )*
111
112 #( #custom_fields_idents: #custom_fields_types, )*
113
114 #private_field_attrs
115 __unsafe_private_named: (
116 #(
117 ::core::option::Option<#named_members_types>,
118 )*
119 ),
120 }
121 }
122 }
123}