bon_macros/builder/builder_gen/member/
mod.rs1mod config;
2mod into_conversion;
3mod named;
4
5pub(crate) use config::*;
6pub(crate) use named::*;
7
8use super::top_level_config::OnConfig;
9use super::TopLevelConfig;
10use crate::normalization::SyntaxVariant;
11use crate::util::prelude::*;
12use config::MemberConfig;
13use darling::FromAttributes;
14use std::fmt;
15
16#[derive(Debug, Clone, Copy)]
17pub(crate) enum MemberOrigin {
18 FnArg,
19 StructField,
20}
21
22impl fmt::Display for MemberOrigin {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 Self::FnArg => write!(f, "function argument"),
26 Self::StructField => write!(f, "struct field"),
27 }
28 }
29}
30
31impl MemberOrigin {
32 fn parent_construct(self) -> &'static str {
33 match self {
34 Self::FnArg => "function",
35 Self::StructField => "struct",
36 }
37 }
38}
39
40#[derive(Debug)]
41pub(crate) enum Member {
42 StartFn(PosFnMember),
44
45 Field(CustomField),
47
48 FinishFn(PosFnMember),
50
51 Named(NamedMember),
53
54 Skip(SkipMember),
56}
57
58#[derive(Debug)]
59pub(crate) struct CustomField {
60 pub(crate) ident: syn::Ident,
61 pub(crate) norm_ty: Box<syn::Type>,
62
63 pub(crate) init: Option<syn::Expr>,
65}
66
67#[derive(Debug)]
68pub(crate) struct PosFnMember {
69 pub(crate) origin: MemberOrigin,
71
72 pub(crate) ident: syn::Ident,
74
75 pub(crate) ty: SyntaxVariant<Box<syn::Type>>,
77
78 pub(crate) config: MemberConfig,
80}
81
82#[derive(Debug)]
84pub(crate) struct SkipMember {
85 pub(crate) ident: syn::Ident,
86
87 pub(crate) norm_ty: Box<syn::Type>,
89
90 pub(crate) value: Option<syn::Expr>,
92}
93
94pub(crate) struct RawMember<'a> {
95 pub(crate) attrs: &'a [syn::Attribute],
96 pub(crate) ident: syn::Ident,
97 pub(crate) ty: SyntaxVariant<Box<syn::Type>>,
98 pub(crate) span: Span,
99}
100
101impl Member {
102 #[allow(single_use_lifetimes)]
107 pub(crate) fn from_raw<'a>(
108 top_config: &TopLevelConfig,
109 origin: MemberOrigin,
110 members: impl IntoIterator<Item = RawMember<'a>>,
111 ) -> Result<Vec<Self>> {
112 let on = &top_config.on;
113
114 let mut members = members
115 .into_iter()
116 .map(|member| {
117 for attr in member.attrs {
118 if attr.meta.path().is_ident("builder") {
119 crate::parsing::require_non_empty_paren_meta_list_or_name_value(
120 &attr.meta,
121 )?;
122 }
123 }
124
125 let config = MemberConfig::from_attributes(member.attrs)?;
126 config.validate(top_config, origin)?;
127 Ok((member, config))
128 })
129 .collect::<Result<Vec<_>>>()?
130 .into_iter()
131 .peekable();
132
133 let mut output = vec![];
134
135 while let Some((member, config)) = members.next_if(|(_, cfg)| cfg.start_fn.is_present()) {
137 let member = PosFnMember::new(origin, member, on, config)?;
138 output.push(Self::StartFn(member));
139 }
140
141 while let Some((member, config)) = members.next_if(|(_, cfg)| cfg.field.is_some()) {
143 let init = config
144 .field
145 .expect("validated `field.is_some()` in `next_if`")
146 .value;
147
148 let member = CustomField::new(member, init)?;
149 output.push(Self::Field(member));
150 }
151
152 while let Some((member, cfg)) = members.next_if(|(_, cfg)| cfg.finish_fn.is_present()) {
154 let member = PosFnMember::new(origin, member, on, cfg)?;
155 output.push(Self::FinishFn(member));
156 }
157
158 let mut named_count = 0;
159
160 for (member, config) in members {
161 let RawMember {
162 attrs,
163 ident,
164 ty,
165 span: _,
166 } = member;
167
168 if let Some(value) = config.skip {
169 output.push(Self::Skip(SkipMember {
170 ident,
171 norm_ty: ty.norm,
172 value: value.value,
173 }));
174 continue;
175 }
176
177 let active_flag = |flag: darling::util::Flag| flag.is_present().then(|| flag.span());
178
179 let incorrect_order = None
180 .or_else(|| active_flag(config.start_fn))
181 .or_else(|| Some(config.field.as_ref()?.key.span()))
182 .or_else(|| active_flag(config.finish_fn));
183
184 if let Some(span) = incorrect_order {
185 bail!(
186 &span,
187 "incorrect members ordering; expected ordering:\n\
188 (1) members annotated with #[builder(start_fn)]\n\
189 (2) members annotated with #[builder(field)]\n\
190 (3) members annotated with #[builder(finish_fn)]\n\
191 (4) all other members in any order",
192 );
193 }
194
195 let docs = attrs
203 .iter()
204 .filter(|attr| attr.is_doc_expr())
205 .cloned()
206 .collect();
207
208 let mut member = NamedMember {
209 index: named_count.into(),
210 origin,
211 name: MemberName::new(ident, &config),
212 ty,
213 config,
214 docs,
215 span: member.span,
216 };
217
218 member.merge_on_config(on)?;
219 member.validate()?;
220
221 output.push(Self::Named(member));
222 named_count += 1;
223 }
224
225 Ok(output)
226 }
227}
228
229impl Member {
230 pub(crate) fn norm_ty(&self) -> &syn::Type {
231 match self {
232 Self::Field(me) => &me.norm_ty,
233 Self::FinishFn(me) | Self::StartFn(me) => &me.ty.norm,
234 Self::Named(me) => &me.ty.norm,
235 Self::Skip(me) => &me.norm_ty,
236 }
237 }
238
239 pub(crate) fn orig_ident(&self) -> &syn::Ident {
240 match self {
241 Self::Field(me) => &me.ident,
242 Self::FinishFn(me) | Self::StartFn(me) => &me.ident,
243 Self::Named(me) => &me.name.orig,
244 Self::Skip(me) => &me.ident,
245 }
246 }
247
248 pub(crate) fn as_named(&self) -> Option<&NamedMember> {
249 match self {
250 Self::Named(me) => Some(me),
251 _ => None,
252 }
253 }
254
255 pub(crate) fn as_field(&self) -> Option<&CustomField> {
256 match self {
257 Self::Field(me) => Some(me),
258 _ => None,
259 }
260 }
261
262 pub(crate) fn as_start_fn(&self) -> Option<&PosFnMember> {
263 match self {
264 Self::StartFn(me) => Some(me),
265 _ => None,
266 }
267 }
268
269 pub(crate) fn as_finish_fn(&self) -> Option<&PosFnMember> {
270 match self {
271 Self::FinishFn(me) => Some(me),
272 _ => None,
273 }
274 }
275}
276
277impl PosFnMember {
278 fn new(
279 origin: MemberOrigin,
280 member: RawMember<'_>,
281 on: &[OnConfig],
282 config: MemberConfig,
283 ) -> Result<Self> {
284 let RawMember {
285 attrs: _,
286 ident,
287 ty,
288 span: _,
289 } = member;
290
291 let mut me = Self {
292 origin,
293 ident,
294 ty,
295 config,
296 };
297
298 me.merge_config_into(on)?;
299
300 Ok(me)
301 }
302}
303
304impl CustomField {
305 fn new(member: RawMember<'_>, init: Option<syn::Expr>) -> Result<Self> {
306 if member.ident.to_string().starts_with("__") {
307 bail!(
308 &member.ident,
309 "field names starting with `__` are reserved for `bon`'s internal use; \
310 please, select a different name",
311 );
312 }
313
314 Ok(Self {
315 ident: member.ident,
316 norm_ty: member.ty.norm,
317 init,
318 })
319 }
320}