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 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(PosFnMember),
43
44 Field(CustomField),
46
47 FinishFn(PosFnMember),
49
50 Named(NamedMember),
52
53 Skip(SkipMember),
55}
56
57#[derive(Debug)]
58pub(crate) struct CustomField {
59 pub(crate) ident: syn::Ident,
60 pub(crate) norm_ty: Box<syn::Type>,
61
62 pub(crate) init: Option<syn::Expr>,
64}
65
66#[derive(Debug)]
67pub(crate) struct PosFnMember {
68 pub(crate) origin: MemberOrigin,
70
71 pub(crate) ident: syn::Ident,
73
74 pub(crate) ty: SyntaxVariant<Box<syn::Type>>,
76
77 pub(crate) config: MemberConfig,
79}
80
81#[derive(Debug)]
83pub(crate) struct SkipMember {
84 pub(crate) ident: syn::Ident,
85
86 pub(crate) norm_ty: Box<syn::Type>,
88
89 pub(crate) value: Option<syn::Expr>,
91}
92
93pub(crate) struct RawMember<'a> {
94 pub(crate) attrs: &'a [syn::Attribute],
95 pub(crate) ident: syn::Ident,
96 pub(crate) ty: SyntaxVariant<Box<syn::Type>>,
97}
98
99impl Member {
100 #[allow(single_use_lifetimes)]
105 pub(crate) fn from_raw<'a>(
106 on: &[OnConfig],
107 origin: MemberOrigin,
108 members: impl IntoIterator<Item = RawMember<'a>>,
109 ) -> Result<Vec<Self>> {
110 let mut members = members
111 .into_iter()
112 .map(|member| {
113 for attr in member.attrs {
114 if attr.meta.path().is_ident("builder") {
115 crate::parsing::require_non_empty_paren_meta_list_or_name_value(
116 &attr.meta,
117 )?;
118 }
119 }
120
121 let config = MemberConfig::from_attributes(member.attrs)?;
122 config.validate(origin)?;
123 Ok((member, config))
124 })
125 .collect::<Result<Vec<_>>>()?
126 .into_iter()
127 .peekable();
128
129 let mut output = vec![];
130
131 while let Some((member, config)) = members.next_if(|(_, cfg)| cfg.start_fn.is_present()) {
133 let member = PosFnMember::new(origin, member, on, config)?;
134 output.push(Self::StartFn(member));
135 }
136
137 while let Some((member, config)) = members.next_if(|(_, cfg)| cfg.field.is_some()) {
139 let init = config
140 .field
141 .expect("validated `field.is_some()` in `next_if`")
142 .value;
143
144 let member = CustomField::new(member, init)?;
145 output.push(Self::Field(member));
146 }
147
148 while let Some((member, cfg)) = members.next_if(|(_, cfg)| cfg.finish_fn.is_present()) {
150 let member = PosFnMember::new(origin, member, on, cfg)?;
151 output.push(Self::FinishFn(member));
152 }
153
154 let mut named_count = 0;
155
156 for (member, config) in members {
157 let RawMember { attrs, ident, ty } = member;
158
159 if let Some(value) = config.skip {
160 output.push(Self::Skip(SkipMember {
161 ident,
162 norm_ty: ty.norm,
163 value: value.value,
164 }));
165 continue;
166 }
167
168 let active_flag = |flag: darling::util::Flag| flag.is_present().then(|| flag.span());
169
170 let incorrect_order = None
171 .or_else(|| active_flag(config.start_fn))
172 .or_else(|| Some(config.field.as_ref()?.key.span()))
173 .or_else(|| active_flag(config.finish_fn));
174
175 if let Some(span) = incorrect_order {
176 bail!(
177 &span,
178 "incorrect members ordering; expected ordering:\n\
179 (1) members annotated with #[builder(start_fn)]\n\
180 (2) members annotated with #[builder(field)]\n\
181 (3) members annotated with #[builder(finish_fn)]\n\
182 (4) all other members in any order",
183 );
184 }
185
186 let docs = attrs
194 .iter()
195 .filter(|attr| attr.is_doc_expr())
196 .cloned()
197 .collect();
198
199 let mut member = NamedMember {
200 index: named_count.into(),
201 origin,
202 name: MemberName::new(ident, &config),
203 ty,
204 config,
205 docs,
206 };
207
208 member.merge_on_config(on)?;
209 member.validate()?;
210
211 output.push(Self::Named(member));
212 named_count += 1;
213 }
214
215 Ok(output)
216 }
217}
218
219impl Member {
220 pub(crate) fn norm_ty(&self) -> &syn::Type {
221 match self {
222 Self::Field(me) => &me.norm_ty,
223 Self::FinishFn(me) | Self::StartFn(me) => &me.ty.norm,
224 Self::Named(me) => &me.ty.norm,
225 Self::Skip(me) => &me.norm_ty,
226 }
227 }
228
229 pub(crate) fn orig_ident(&self) -> &syn::Ident {
230 match self {
231 Self::Field(me) => &me.ident,
232 Self::FinishFn(me) | Self::StartFn(me) => &me.ident,
233 Self::Named(me) => &me.name.orig,
234 Self::Skip(me) => &me.ident,
235 }
236 }
237
238 pub(crate) fn as_named(&self) -> Option<&NamedMember> {
239 match self {
240 Self::Named(me) => Some(me),
241 _ => None,
242 }
243 }
244
245 pub(crate) fn as_field(&self) -> Option<&CustomField> {
246 match self {
247 Self::Field(me) => Some(me),
248 _ => None,
249 }
250 }
251
252 pub(crate) fn as_start_fn(&self) -> Option<&PosFnMember> {
253 match self {
254 Self::StartFn(me) => Some(me),
255 _ => None,
256 }
257 }
258
259 pub(crate) fn as_finish_fn(&self) -> Option<&PosFnMember> {
260 match self {
261 Self::FinishFn(me) => Some(me),
262 _ => None,
263 }
264 }
265}
266
267impl PosFnMember {
268 fn new(
269 origin: MemberOrigin,
270 member: RawMember<'_>,
271 on: &[OnConfig],
272 config: MemberConfig,
273 ) -> Result<Self> {
274 let RawMember {
275 attrs: _,
276 ident,
277 ty,
278 } = member;
279
280 let mut me = Self {
281 origin,
282 ident,
283 ty,
284 config,
285 };
286
287 me.merge_config_into(on)?;
288
289 Ok(me)
290 }
291}
292
293impl CustomField {
294 fn new(member: RawMember<'_>, init: Option<syn::Expr>) -> Result<Self> {
295 if member.ident.to_string().starts_with("__") {
296 bail!(
297 &member.ident,
298 "field names starting with `__` are reserved for `bon`'s internal use; \
299 please, select a different name",
300 );
301 }
302
303 Ok(Self {
304 ident: member.ident,
305 norm_ty: member.ty.norm,
306 init,
307 })
308 }
309}