bon_macros/builder/builder_gen/
state_mod.rs1use super::BuilderGenCtx;
2use crate::util::prelude::*;
3
4pub(super) struct StateModGenCtx<'a> {
5 base: &'a BuilderGenCtx,
6 stateful_members_snake: Vec<&'a syn::Ident>,
7 stateful_members_pascal: Vec<&'a syn::Ident>,
8 sealed_item_decl: TokenStream,
9 sealed_item_impl: TokenStream,
10}
11
12impl<'a> StateModGenCtx<'a> {
13 pub(super) fn new(builder_gen: &'a BuilderGenCtx) -> Self {
14 Self {
15 base: builder_gen,
16
17 stateful_members_snake: builder_gen
18 .stateful_members()
19 .map(|member| &member.name.snake)
20 .collect(),
21
22 stateful_members_pascal: builder_gen
23 .stateful_members()
24 .map(|member| &member.name.pascal)
25 .collect(),
26
27 sealed_item_decl: quote! {
30 #[doc(hidden)]
31 const SEALED: sealed::Sealed;
32 },
33
34 sealed_item_impl: quote! {
35 const SEALED: sealed::Sealed = sealed::Sealed;
36 },
37 }
38 }
39
40 pub(super) fn state_mod(&self) -> TokenStream {
41 let bon = &self.base.bon;
42 let vis = &self.base.state_mod.vis;
43 let vis_child = &self.base.state_mod.vis_child;
44 let vis_child_child = &self.base.state_mod.vis_child_child;
45
46 let state_mod_docs = &self.base.state_mod.docs;
47 let state_mod_ident = &self.base.state_mod.ident;
48
49 let state_trait = self.state_trait();
50 let is_complete_trait = self.is_complete_trait();
51 let members_names_mod = self.members_names_mod();
52 let state_transitions = self.state_transitions();
53
54 quote! {
55 #[allow(
56 unnameable_types, unreachable_pub, clippy::redundant_pub_crate
68 )]
69 #( #state_mod_docs )*
70 #vis mod #state_mod_ident {
71 #[doc(inline)]
72 #vis_child use #bon::__::{IsSet, IsUnset};
73 use #bon::__::{Set, Unset};
74
75 mod sealed {
76 #vis_child_child struct Sealed;
77 }
78
79 #state_trait
80 #is_complete_trait
81 #members_names_mod
82 #state_transitions
83 }
84 }
85 }
86
87 fn state_transitions(&self) -> TokenStream {
88 let mut set_members_structs = Vec::with_capacity(self.stateful_members_snake.len());
92 let mut state_impls = Vec::with_capacity(self.stateful_members_snake.len());
93
94 let vis_child = &self.base.state_mod.vis_child;
95 let sealed_item_impl = &self.sealed_item_impl;
96
97 for member in self.base.stateful_members() {
98 let member_pascal = &member.name.pascal;
99
100 let docs = format!(
101 "Represents a [`State`] that has [`IsSet`] implemented for [`State::{member_pascal}`].\n\n\
102 The state for all other members is left the same as in the input state.",
103 );
104
105 let struct_ident = format_ident!("Set{}", member.name.pascal_str);
106
107 set_members_structs.push(quote! {
108 #[doc = #docs]
109 #vis_child struct #struct_ident<S: State = Empty>(
110 ::core::marker::PhantomData<fn() -> S>
115 );
116 });
117
118 let states = self.base.stateful_members().map(|other_member| {
119 if other_member.is(member) {
120 let member_snake = &member.name.snake;
121 quote! {
122 Set<members::#member_snake>
123 }
124 } else {
125 let member_pascal = &other_member.name.pascal;
126 quote! {
127 S::#member_pascal
128 }
129 }
130 });
131
132 let stateful_members_pascal = &self.stateful_members_pascal;
133
134 state_impls.push(quote! {
135 #[doc(hidden)]
136 impl<S: State> State for #struct_ident<S> {
137 #(
138 type #stateful_members_pascal = #states;
139 )*
140 #sealed_item_impl
141 }
142 });
143 }
144
145 let stateful_members_snake = &self.stateful_members_snake;
146 let stateful_members_pascal = &self.stateful_members_pascal;
147
148 quote! {
149 #vis_child struct Empty(());
153
154 #( #set_members_structs )*
155
156 #[doc(hidden)]
157 impl State for Empty {
158 #(
159 type #stateful_members_pascal = Unset<members::#stateful_members_snake>;
160 )*
161 #sealed_item_impl
162 }
163
164 #( #state_impls )*
165
166 }
167 }
168
169 fn state_trait(&self) -> TokenStream {
170 let assoc_types_docs = self.stateful_members_snake.iter().map(|member_snake| {
171 format!(
172 "Type state of the member `{member_snake}`.\n\
173 \n\
174 It can implement either [`IsSet`] or [`IsUnset`]",
175 )
176 });
177
178 let vis_child = &self.base.state_mod.vis_child;
179 let sealed_item_decl = &self.sealed_item_decl;
180 let stateful_members_pascal = &self.stateful_members_pascal;
181
182 let docs_suffix = if stateful_members_pascal.is_empty() {
183 ""
184 } else {
185 "\n\n\
186 You can use the associated types of this trait to control the state of individual members \
187 with the [`IsSet`] and [`IsUnset`] traits. You can change the state of the members with \
188 the `Set*` structs available in this module."
189 };
190
191 let docs = format!(
192 "Builder's type state specifies if members are set or not (unset).{docs_suffix}"
193 );
194
195 quote! {
196 #[doc = #docs]
197 #vis_child trait State: ::core::marker::Sized {
198 #(
199 #[doc = #assoc_types_docs]
200 type #stateful_members_pascal;
201 )*
202 #sealed_item_decl
203 }
204 }
205 }
206
207 fn is_complete_trait(&self) -> TokenStream {
208 let required_members_pascal = self
209 .base
210 .named_members()
211 .filter(|member| member.is_required())
212 .map(|member| &member.name.pascal)
213 .collect::<Vec<_>>();
214
215 let maybe_assoc_type_bounds = cfg!(feature = "implied-bounds").then(|| {
220 quote! {
221 < #( #required_members_pascal: IsSet, )* >
222 }
223 });
224
225 let vis_child = &self.base.state_mod.vis_child;
226 let sealed_item_decl = &self.sealed_item_decl;
227 let sealed_item_impl = &self.sealed_item_impl;
228
229 let builder_ident = &self.base.builder_type.ident;
230 let finish_fn = &self.base.finish_fn.ident;
231
232 let docs = format!(
233 "Marker trait that indicates that all required members are set.\n\n\
234 In this state, you can finish building by calling the method \
235 [`{builder_ident}::{finish_fn}()`](super::{builder_ident}::{finish_fn}())",
236 );
237
238 quote! {
239 #[doc = #docs]
240 #vis_child trait IsComplete: State #maybe_assoc_type_bounds {
241 #sealed_item_decl
242 }
243
244 #[doc(hidden)]
245 impl<S: State> IsComplete for S
246 where
247 #(
248 S::#required_members_pascal: IsSet,
249 )*
250 {
251 #sealed_item_impl
252 }
253 }
254 }
255
256 fn members_names_mod(&self) -> TokenStream {
257 let vis_child_child = &self.base.state_mod.vis_child_child;
258 let stateful_members_snake = &self.stateful_members_snake;
259
260 let deprecated_msg = "\
264 this should not be used directly; it is an implementation detail; \
265 use the Set* type aliases to control the \
266 state of members instead";
267
268 quote! {
269 #[deprecated = #deprecated_msg]
270 #[doc(hidden)]
271 #[allow(non_camel_case_types)]
272 mod members {
273 #(
274 #vis_child_child enum #stateful_members_snake {}
275 )*
276 }
277 }
278 }
279}