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