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