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