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 ty = &receiver.without_self_keyword;
41 quote! {
42 #private_field_attrs
43 __unsafe_private_receiver: #ty,
44 }
45 });
46
47 let must_use_message = format!(
48 "the builder does nothing until you call `{}()` on it to finish building",
49 self.finish_fn.ident
50 );
51
52 let allows = super::allow_warnings_on_member_types();
53
54 let mut start_fn_arg_types = self
55 .start_fn_args()
56 .map(|member| &member.base.ty.norm)
57 .peekable();
58
59 let start_fn_args_field = start_fn_arg_types.peek().is_some().then(|| {
60 quote! {
61 #private_field_attrs
62 __unsafe_private_start_fn_args: (#(#start_fn_arg_types,)*),
63 }
64 });
65
66 let named_members_types = self.named_members().map(NamedMember::underlying_norm_ty);
67
68 let docs = &self.builder_type.docs;
69 let state_var = &self.state_var;
70
71 let custom_fields_idents = self.custom_fields().map(|field| &field.ident);
72 let custom_fields_types = self.custom_fields().map(|field| &field.norm_ty);
73
74 quote! {
75 #[must_use = #must_use_message]
76 #(#docs)*
77 #allows
78 #[allow(
79 // We use `__private` prefix for all fields intentionally to hide them
80 clippy::struct_field_names,
81
82 // This lint doesn't emerge until you manually expand the macro. Just
83 // because `bon` developers need to expand the macros a lot it makes
84 // sense to just silence it to avoid some noise. This lint is triggered
85 // by the big PhantomData type generated by the macro
86 clippy::type_complexity
87 )]
88 #builder_vis struct #builder_ident<
89 #(#generics_decl,)*
90 // Having the `State` trait bound on the struct declaration is important
91 // for future proofing. It will allow us to use this bound in the `Drop`
92 // implementation of the builder if we ever add one. @Veetaha already did
93 // some experiments with `MaybeUninit` that requires a custom drop impl,
94 // so this could be useful in the future.
95 //
96 // On the flip side, if we have a custom `Drop` impl, then partially moving
97 // the builder will be impossible. So.. it's a trade-off, and it's probably
98 // not a big deal to remove this bound from here if we feel like it.
99 #state_var: #state_mod::State = #state_mod::Empty
100 >
101 #where_clause
102 {
103 #private_field_attrs
104 __unsafe_private_phantom: #phantom_data,
105
106 #receiver_field
107 #start_fn_args_field
108
109 #( #custom_fields_idents: #custom_fields_types, )*
110
111 #private_field_attrs
112 __unsafe_private_named: (
113 #(
114 ::core::option::Option<#named_members_types>,
115 )*
116 ),
117 }
118 }
119 }
120}