typed_builder_macro/
field_info.rs

1use proc_macro2::{Ident, Span, TokenStream};
2use quote::quote_spanned;
3use syn::{parse::Error, spanned::Spanned};
4
5use crate::mutator::Mutator;
6use crate::util::{expr_to_lit_string, ident_to_type, path_to_single_string, strip_raw_ident_prefix, ApplyMeta, AttrArg};
7
8#[derive(Debug)]
9pub struct FieldInfo<'a> {
10    pub ordinal: usize,
11    pub name: &'a syn::Ident,
12    pub generic_ident: syn::Ident,
13    pub ty: &'a syn::Type,
14    pub builder_attr: FieldBuilderAttr<'a>,
15}
16
17impl<'a> FieldInfo<'a> {
18    pub fn new(ordinal: usize, field: &'a syn::Field, field_defaults: FieldBuilderAttr<'a>) -> Result<FieldInfo<'a>, Error> {
19        if let Some(ref name) = field.ident {
20            FieldInfo {
21                ordinal,
22                name,
23                generic_ident: syn::Ident::new(&format!("__{}", strip_raw_ident_prefix(name.to_string())), Span::call_site()),
24                ty: &field.ty,
25                builder_attr: field_defaults.with(name, &field.attrs)?,
26            }
27            .post_process()
28        } else {
29            Err(Error::new(field.span(), "Nameless field in struct"))
30        }
31    }
32
33    pub fn generic_ty_param(&self) -> syn::GenericParam {
34        syn::GenericParam::Type(self.generic_ident.clone().into())
35    }
36
37    pub fn type_ident(&self) -> syn::Type {
38        ident_to_type(self.generic_ident.clone())
39    }
40
41    pub fn tuplized_type_ty_param(&self) -> syn::Type {
42        let mut types = syn::punctuated::Punctuated::default();
43        types.push(self.ty.clone());
44        types.push_punct(Default::default());
45        syn::TypeTuple {
46            paren_token: Default::default(),
47            elems: types,
48        }
49        .into()
50    }
51
52    pub fn type_from_inside_option(&self) -> Option<&syn::Type> {
53        let path = if let syn::Type::Path(type_path) = self.ty {
54            if type_path.qself.is_some() {
55                return None;
56            }
57            &type_path.path
58        } else {
59            return None;
60        };
61        let segment = path.segments.last()?;
62        if segment.ident != "Option" {
63            return None;
64        }
65        let generic_params = if let syn::PathArguments::AngleBracketed(generic_params) = &segment.arguments {
66            generic_params
67        } else {
68            return None;
69        };
70        if let syn::GenericArgument::Type(ty) = generic_params.args.first()? {
71            Some(ty)
72        } else {
73            None
74        }
75    }
76
77    pub fn setter_method_name(&self) -> Ident {
78        let name = strip_raw_ident_prefix(self.name.to_string());
79
80        if let (Some(prefix), Some(suffix)) = (&self.builder_attr.setter.prefix, &self.builder_attr.setter.suffix) {
81            Ident::new(&format!("{}{}{}", prefix, name, suffix), Span::call_site())
82        } else if let Some(prefix) = &self.builder_attr.setter.prefix {
83            Ident::new(&format!("{}{}", prefix, name), Span::call_site())
84        } else if let Some(suffix) = &self.builder_attr.setter.suffix {
85            Ident::new(&format!("{}{}", name, suffix), Span::call_site())
86        } else {
87            self.name.clone()
88        }
89    }
90
91    fn post_process(mut self) -> Result<Self, Error> {
92        if let Some(ref strip_bool) = self.builder_attr.setter.strip_bool {
93            if let Some(default_span) = self.builder_attr.default.as_ref().map(Spanned::span) {
94                let mut error = Error::new(
95                    strip_bool.span,
96                    "cannot set both strip_bool and default - default is assumed to be false",
97                );
98                error.combine(Error::new(default_span, "default set here"));
99                return Err(error);
100            }
101            self.builder_attr.default = Some(syn::Expr::Lit(syn::ExprLit {
102                attrs: Default::default(),
103                lit: syn::Lit::Bool(syn::LitBool {
104                    value: false,
105                    span: strip_bool.span,
106                }),
107            }));
108        }
109        Ok(self)
110    }
111}
112
113#[derive(Debug, Default, Clone)]
114pub struct FieldBuilderAttr<'a> {
115    pub default: Option<syn::Expr>,
116    pub via_mutators: Option<ViaMutators>,
117    pub deprecated: Option<&'a syn::Attribute>,
118    pub doc_comments: Vec<&'a syn::Expr>,
119    pub setter: SetterSettings,
120    /// Functions that are able to mutate fields in the builder that are already set
121    pub mutators: Vec<Mutator>,
122    pub mutable_during_default_resolution: Option<Span>,
123}
124
125#[derive(Debug, Default, Clone)]
126pub struct SetterSettings {
127    pub doc: Option<syn::Expr>,
128    pub skip: Option<Span>,
129    pub auto_into: Option<Span>,
130    pub strip_option: Option<Strip>,
131    pub strip_bool: Option<Strip>,
132    pub transform: Option<Transform>,
133    pub prefix: Option<String>,
134    pub suffix: Option<String>,
135}
136
137impl<'a> FieldBuilderAttr<'a> {
138    pub fn with(mut self, name: &Ident, attrs: &'a [syn::Attribute]) -> Result<Self, Error> {
139        for attr in attrs {
140            let list = match &attr.meta {
141                syn::Meta::List(list) => {
142                    let Some(path) = path_to_single_string(&list.path) else {
143                        continue;
144                    };
145
146                    if path == "deprecated" {
147                        self.deprecated = Some(attr);
148                        continue;
149                    }
150
151                    if path != "builder" {
152                        continue;
153                    }
154
155                    list
156                }
157                syn::Meta::NameValue(syn::MetaNameValue { path, value, .. }) => {
158                    match path_to_single_string(path).as_deref() {
159                        Some("deprecated") => self.deprecated = Some(attr),
160                        Some("doc") => self.doc_comments.push(value),
161                        _ => continue,
162                    }
163
164                    continue;
165                }
166                syn::Meta::Path(path) => {
167                    match path_to_single_string(path).as_deref() {
168                        Some("deprecated") => self.deprecated = Some(attr),
169                        _ => continue,
170                    }
171
172                    continue;
173                }
174            };
175
176            self.apply_subsections(list)?;
177        }
178
179        for mutator in self.mutators.iter_mut() {
180            mutator.required_fields.insert(name.clone());
181        }
182
183        self.inter_fields_conflicts()?;
184
185        Ok(self)
186    }
187
188    fn inter_fields_conflicts(&self) -> Result<(), Error> {
189        if let (Some(skip), None) = (&self.setter.skip, &self.default) {
190            return Err(Error::new(
191                *skip,
192                "#[builder(skip)] must be accompanied by default or default_code",
193            ));
194        }
195
196        let conflicting_transformations = [
197            ("transform", self.setter.transform.as_ref().map(|t| &t.span)),
198            ("strip_option", self.setter.strip_option.as_ref().map(|s| &s.span)),
199            ("strip_bool", self.setter.strip_bool.as_ref().map(|s| &s.span)),
200        ];
201        let mut conflicting_transformations = conflicting_transformations
202            .iter()
203            .filter_map(|(caption, span)| span.map(|span| (caption, span)))
204            .collect::<Vec<_>>();
205
206        if 1 < conflicting_transformations.len() {
207            let (first_caption, first_span) = conflicting_transformations.pop().unwrap();
208            let conflicting_captions = conflicting_transformations
209                .iter()
210                .map(|(caption, _)| **caption)
211                .collect::<Vec<_>>();
212            let mut error = Error::new(
213                *first_span,
214                format_args!("{} conflicts with {}", first_caption, conflicting_captions.join(", ")),
215            );
216            for (caption, span) in conflicting_transformations {
217                error.combine(Error::new(*span, format_args!("{} set here", caption)));
218            }
219            return Err(error);
220        }
221        Ok(())
222    }
223}
224
225impl ApplyMeta for FieldBuilderAttr<'_> {
226    fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> {
227        match expr.name().to_string().as_str() {
228            "default" => match expr {
229                AttrArg::Flag(ident) => {
230                    self.default =
231                        Some(syn::parse2(quote_spanned!(ident.span() => ::core::default::Default::default())).unwrap());
232                    Ok(())
233                }
234                AttrArg::KeyValue(key_value) => {
235                    self.default = Some(key_value.parse_value()?);
236                    Ok(())
237                }
238                AttrArg::Not { .. } => {
239                    self.default = None;
240                    Ok(())
241                }
242                AttrArg::Sub(_) => Err(expr.incorrect_type()),
243            },
244            "default_code" => {
245                use std::str::FromStr;
246
247                let code = expr.key_value()?.parse_value::<syn::LitStr>()?;
248                let tokenized_code = TokenStream::from_str(&code.value())?;
249                self.default = Some(syn::parse2(tokenized_code).map_err(|e| Error::new_spanned(code, format!("{}", e)))?);
250
251                Ok(())
252            }
253            "setter" => self.setter.apply_sub_attr(expr.sub_attr()?),
254            "mutable_during_default_resolution" => expr.apply_flag_to_field(
255                &mut self.mutable_during_default_resolution,
256                "made mutable during default resolution",
257            ),
258            "via_mutators" => {
259                match expr {
260                    AttrArg::Flag(ident) => {
261                        self.via_mutators = Some(ViaMutators {
262                            span: ident.span(),
263                            init: syn::parse2(quote_spanned!(ident.span() => ::core::default::Default::default())).unwrap(),
264                        });
265                    }
266                    AttrArg::KeyValue(key_value) => {
267                        self.via_mutators = Some(ViaMutators {
268                            span: key_value.span(),
269                            init: key_value.parse_value()?,
270                        });
271                    }
272                    AttrArg::Not { .. } => {
273                        self.via_mutators = None;
274                    }
275                    AttrArg::Sub(sub) => {
276                        if let Some(via_mutators) = self.via_mutators.as_mut() {
277                            if let Some(joined_span) = via_mutators.span.join(sub.span()) {
278                                via_mutators.span = joined_span;
279                            } else {
280                                // Shouldn't happen, but whatever
281                                via_mutators.span = sub.span();
282                            };
283                            via_mutators.apply_sub_attr(sub)?;
284                        } else {
285                            let mut via_mutators = ViaMutators::empty_spanned(sub.span());
286                            via_mutators.apply_sub_attr(sub)?;
287                            self.via_mutators = Some(via_mutators);
288                        }
289                    }
290                }
291                Ok(())
292            }
293            "mutators" => {
294                self.mutators.extend(expr.sub_attr()?.undelimited()?);
295                Ok(())
296            }
297            _ => Err(Error::new_spanned(
298                expr.name(),
299                format!("Unknown parameter {:?}", expr.name().to_string()),
300            )),
301        }
302    }
303}
304
305impl ApplyMeta for SetterSettings {
306    fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> {
307        match expr.name().to_string().as_str() {
308            "doc" => {
309                self.doc = expr.key_value_or_not()?.map(|kv| kv.parse_value()).transpose()?;
310                Ok(())
311            }
312            "transform" => {
313                self.transform = if let Some(key_value) = expr.key_value_or_not()? {
314                    Some(parse_transform_closure(key_value.name.span(), key_value.parse_value()?)?)
315                } else {
316                    None
317                };
318                Ok(())
319            }
320            "prefix" => {
321                self.prefix = if let Some(key_value) = expr.key_value_or_not()? {
322                    Some(expr_to_lit_string(&key_value.parse_value()?)?)
323                } else {
324                    None
325                };
326                Ok(())
327            }
328            "suffix" => {
329                self.suffix = if let Some(key_value) = expr.key_value_or_not()? {
330                    Some(expr_to_lit_string(&key_value.parse_value()?)?)
331                } else {
332                    None
333                };
334                Ok(())
335            }
336            "skip" => expr.apply_flag_to_field(&mut self.skip, "skipped"),
337            "into" => expr.apply_flag_to_field(&mut self.auto_into, "calling into() on the argument"),
338            "strip_option" => {
339                expr.apply_potentialy_empty_sub_to_field(&mut self.strip_option, "putting the argument in Some(...)", Strip::new)
340            }
341            "strip_bool" => expr.apply_potentialy_empty_sub_to_field(
342                &mut self.strip_bool,
343                "zero arguments setter, sets the field to true",
344                Strip::new,
345            ),
346            _ => Err(Error::new_spanned(
347                expr.name(),
348                format!("Unknown parameter {:?}", expr.name().to_string()),
349            )),
350        }
351    }
352}
353
354#[derive(Debug, Clone)]
355pub struct Strip {
356    pub fallback: Option<syn::Ident>,
357    pub fallback_prefix: Option<String>,
358    pub fallback_suffix: Option<String>,
359    pub ignore_invalid: bool,
360    span: Span,
361}
362
363impl Strip {
364    fn new(span: Span) -> Self {
365        Self {
366            fallback: None,
367            fallback_prefix: None,
368            fallback_suffix: None,
369            ignore_invalid: false,
370            span,
371        }
372    }
373}
374
375impl ApplyMeta for Strip {
376    fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> {
377        match expr.name().to_string().as_str() {
378            "fallback" => {
379                if self.fallback.is_some() {
380                    return Err(Error::new_spanned(
381                        expr.name(),
382                        format!("Duplicate fallback parameter {:?}", expr.name().to_string()),
383                    ));
384                }
385
386                let ident: syn::Ident = expr.key_value().map(|kv| kv.parse_value())??;
387                self.fallback = Some(ident);
388                Ok(())
389            }
390            "fallback_prefix" => {
391                if self.fallback_prefix.is_some() {
392                    return Err(Error::new_spanned(
393                        expr.name(),
394                        format!("Duplicate fallback_prefix parameter {:?}", expr.name().to_string()),
395                    ));
396                }
397
398                self.fallback_prefix = Some(expr.key_value()?.parse_value::<syn::LitStr>()?.value());
399                Ok(())
400            }
401            "fallback_suffix" => {
402                if self.fallback_suffix.is_some() {
403                    return Err(Error::new_spanned(
404                        expr.name(),
405                        format!("Duplicate fallback_suffix parameter {:?}", expr.name().to_string()),
406                    ));
407                }
408
409                self.fallback_suffix = Some(expr.key_value()?.parse_value::<syn::LitStr>()?.value());
410                Ok(())
411            }
412            "ignore_invalid" => {
413                if self.ignore_invalid {
414                    return Err(Error::new_spanned(
415                        expr.name(),
416                        format!("Duplicate ignore_invalid parameter {:?}", expr.name().to_string()),
417                    ));
418                }
419
420                expr.flag()?;
421                self.ignore_invalid = true;
422                Ok(())
423            }
424            _ => Err(Error::new_spanned(
425                expr.name(),
426                format!("Unknown parameter {:?}", expr.name().to_string()),
427            )),
428        }
429    }
430}
431
432#[derive(Debug, Clone)]
433pub struct Transform {
434    pub params: Vec<(syn::Pat, syn::Type)>,
435    pub body: syn::Expr,
436    span: Span,
437}
438
439fn parse_transform_closure(span: Span, expr: syn::Expr) -> Result<Transform, Error> {
440    let closure = match expr {
441        syn::Expr::Closure(closure) => closure,
442        _ => return Err(Error::new_spanned(expr, "Expected closure")),
443    };
444    if let Some(kw) = &closure.asyncness {
445        return Err(Error::new(kw.span, "Transform closure cannot be async"));
446    }
447    if let Some(kw) = &closure.capture {
448        return Err(Error::new(kw.span, "Transform closure cannot be move"));
449    }
450
451    let params = closure
452        .inputs
453        .into_iter()
454        .map(|input| match input {
455            syn::Pat::Type(pat_type) => Ok((*pat_type.pat, *pat_type.ty)),
456            _ => Err(Error::new_spanned(input, "Transform closure must explicitly declare types")),
457        })
458        .collect::<Result<Vec<_>, _>>()?;
459
460    Ok(Transform {
461        params,
462        body: *closure.body,
463        span,
464    })
465}
466
467#[derive(Debug, Clone)]
468pub struct ViaMutators {
469    pub span: Span,
470    pub init: syn::Expr,
471}
472
473impl ViaMutators {
474    fn empty_spanned(span: Span) -> Self {
475        Self {
476            span,
477            init: syn::parse2(quote_spanned!(span => ::core::default::Default::default())).unwrap(),
478        }
479    }
480}
481
482impl ApplyMeta for ViaMutators {
483    fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> {
484        match expr.name().to_string().as_str() {
485            "init" => {
486                self.init = expr.key_value()?.parse_value()?;
487                Ok(())
488            }
489            _ => Err(Error::new_spanned(
490                expr.name(),
491                format!("Unknown parameter {:?}", expr.name().to_string()),
492            )),
493        }
494    }
495}