typed_builder_macro/
builder_attr.rs

1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::parse::Error;
4
5use crate::field_info::FieldBuilderAttr;
6use crate::mutator::Mutator;
7use crate::util::{path_to_single_string, ApplyMeta, AttrArg};
8
9#[derive(Debug, Default, Clone)]
10pub struct CommonDeclarationSettings {
11    pub vis: Option<syn::Visibility>,
12    pub name: Option<syn::Expr>,
13    pub doc: Option<syn::Expr>,
14}
15
16impl ApplyMeta for CommonDeclarationSettings {
17    fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> {
18        match expr.name().to_string().as_str() {
19            "vis" => {
20                let expr_str = expr.key_value()?.parse_value::<syn::LitStr>()?.value();
21                self.vis = Some(syn::parse_str(&expr_str)?);
22                Ok(())
23            }
24            "name" => {
25                self.name = Some(expr.key_value()?.parse_value()?);
26                Ok(())
27            }
28            "doc" => {
29                self.doc = Some(expr.key_value()?.parse_value()?);
30                Ok(())
31            }
32            _ => Err(Error::new_spanned(
33                expr.name(),
34                format!("Unknown parameter {:?}", expr.name().to_string()),
35            )),
36        }
37    }
38}
39
40impl CommonDeclarationSettings {
41    pub fn get_name(&self) -> Option<TokenStream> {
42        self.name.as_ref().map(|name| name.to_token_stream())
43    }
44
45    pub fn get_doc_or(&self, gen_doc: impl FnOnce() -> String) -> TokenStream {
46        if let Some(ref doc) = self.doc {
47            quote!(#[doc = #doc])
48        } else {
49            let doc = gen_doc();
50            quote!(#[doc = #doc])
51        }
52    }
53}
54
55/// Setting of the `into` argument.
56#[derive(Debug, Clone)]
57pub enum IntoSetting {
58    /// Do not run any conversion on the built value.
59    NoConversion,
60    /// Convert the build value into the generic parameter passed to the `build` method.
61    GenericConversion,
62    /// Convert the build value into a specific type specified in the attribute.
63    TypeConversionToSpecificType(syn::TypePath),
64}
65
66impl Default for IntoSetting {
67    fn default() -> Self {
68        Self::NoConversion
69    }
70}
71
72#[derive(Debug, Default, Clone)]
73pub struct BuildMethodSettings {
74    pub common: CommonDeclarationSettings,
75
76    /// Whether to convert the built type into another while finishing the build.
77    pub into: IntoSetting,
78}
79
80impl ApplyMeta for BuildMethodSettings {
81    fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> {
82        match expr.name().to_string().as_str() {
83            "into" => match expr {
84                AttrArg::Flag(_) => {
85                    self.into = IntoSetting::GenericConversion;
86                    Ok(())
87                }
88                AttrArg::KeyValue(key_value) => {
89                    let type_path = key_value.parse_value::<syn::TypePath>()?;
90                    self.into = IntoSetting::TypeConversionToSpecificType(type_path);
91                    Ok(())
92                }
93                _ => Err(expr.incorrect_type()),
94            },
95            _ => self.common.apply_meta(expr),
96        }
97    }
98}
99
100#[derive(Debug)]
101pub struct TypeBuilderAttr<'a> {
102    /// Whether to show docs for the `TypeBuilder` type (rather than hiding them).
103    pub doc: bool,
104
105    /// Customize builder method, ex. visibility, name
106    pub builder_method: CommonDeclarationSettings,
107
108    /// Customize builder type, ex. visibility, name
109    pub builder_type: CommonDeclarationSettings,
110
111    /// Customize build method, ex. visibility, name
112    pub build_method: BuildMethodSettings,
113
114    pub field_defaults: FieldBuilderAttr<'a>,
115
116    pub crate_module_path: syn::Path,
117
118    /// Functions that are able to mutate fields in the builder that are already set
119    pub mutators: Vec<Mutator>,
120}
121
122impl Default for TypeBuilderAttr<'_> {
123    fn default() -> Self {
124        Self {
125            doc: Default::default(),
126            builder_method: Default::default(),
127            builder_type: Default::default(),
128            build_method: Default::default(),
129            field_defaults: Default::default(),
130            crate_module_path: syn::parse_quote!(::typed_builder),
131            mutators: Default::default(),
132        }
133    }
134}
135
136impl<'a> TypeBuilderAttr<'a> {
137    pub fn new(attrs: &[syn::Attribute]) -> Result<Self, Error> {
138        let mut result = Self::default();
139
140        for attr in attrs {
141            let list = match &attr.meta {
142                syn::Meta::List(list) => {
143                    if path_to_single_string(&list.path).as_deref() != Some("builder") {
144                        continue;
145                    }
146
147                    list
148                }
149                _ => continue,
150            };
151
152            result.apply_subsections(list)?;
153        }
154
155        if result.builder_type.doc.is_some() || result.build_method.common.doc.is_some() {
156            result.doc = true;
157        }
158
159        Ok(result)
160    }
161}
162
163impl ApplyMeta for TypeBuilderAttr<'_> {
164    fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> {
165        match expr.name().to_string().as_str() {
166            "crate_module_path" => {
167                let crate_module_path = expr.key_value()?.parse_value::<syn::ExprPath>()?;
168                self.crate_module_path = crate_module_path.path;
169                Ok(())
170            }
171            "builder_method_doc" => Err(Error::new_spanned(
172                expr.name(),
173                "`builder_method_doc` is deprecated - use `builder_method(doc = \"...\")`",
174            )),
175            "builder_type_doc" => Err(Error::new_spanned(
176                expr.name(),
177                "`builder_typemethod_doc` is deprecated - use `builder_type(doc = \"...\")`",
178            )),
179            "build_method_doc" => Err(Error::new_spanned(
180                expr.name(),
181                "`build_method_doc` is deprecated - use `build_method(doc = \"...\")`",
182            )),
183            "doc" => {
184                expr.flag()?;
185                self.doc = true;
186                Ok(())
187            }
188            "mutators" => {
189                self.mutators.extend(expr.sub_attr()?.undelimited()?);
190                Ok(())
191            }
192            "field_defaults" => self.field_defaults.apply_sub_attr(expr.sub_attr()?),
193            "builder_method" => self.builder_method.apply_sub_attr(expr.sub_attr()?),
194            "builder_type" => self.builder_type.apply_sub_attr(expr.sub_attr()?),
195            "build_method" => self.build_method.apply_sub_attr(expr.sub_attr()?),
196            _ => Err(Error::new_spanned(
197                expr.name(),
198                format!("Unknown parameter {:?}", expr.name().to_string()),
199            )),
200        }
201    }
202}