derive_builder_core_fork_arti/
build_method.rs

1use std::borrow::Cow;
2
3use doc_comment_from;
4use proc_macro2::{Span, TokenStream};
5use quote::{ToTokens, TokenStreamExt};
6use syn;
7use syn::spanned::Spanned;
8use BuilderPattern;
9use Initializer;
10use DEFAULT_STRUCT_NAME;
11
12use crate::DefaultExpression;
13
14/// Initializer for the struct fields in the build method, implementing
15/// `quote::ToTokens`.
16///
17/// # Examples
18///
19/// Will expand to something like the following (depending on settings):
20///
21/// ```rust,ignore
22/// # extern crate proc_macro2;
23/// # #[macro_use]
24/// # extern crate quote;
25/// # extern crate syn;
26/// # #[macro_use(default_build_method)]
27/// # extern crate derive_builder_core;
28/// # use derive_builder_core::{BuildMethod, BuilderPattern};
29/// # fn main() {
30/// #    let build_method = default_build_method!();
31/// #
32/// #    assert_eq!(quote!(#build_method).to_string(), quote!(
33/// pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
34///     Ok(Foo {
35///         foo: self.foo,
36///     })
37/// }
38/// #    ).to_string());
39/// # }
40/// ```
41#[derive(Debug)]
42pub struct BuildMethod<'a> {
43    /// Enables code generation for this build method.
44    pub enabled: bool,
45    /// Name of this build fn.
46    pub ident: &'a syn::Ident,
47    /// Visibility of the build method, e.g. `syn::Visibility::Public`.
48    pub visibility: Cow<'a, syn::Visibility>,
49    /// How the build method takes and returns `self` (e.g. mutably).
50    pub pattern: BuilderPattern,
51    /// Type of the target field.
52    ///
53    /// The corresonding builder field will be `Option<field_type>`.
54    pub target_ty: &'a syn::Ident,
55    /// Type parameters and lifetimes attached to this builder struct.
56    pub target_ty_generics: Option<syn::TypeGenerics<'a>>,
57    /// Type of error.
58    pub error_ty: syn::Path,
59    /// Field initializers for the target type.
60    pub initializers: Vec<TokenStream>,
61    /// Doc-comment of the builder struct.
62    pub doc_comment: Option<syn::Attribute>,
63    /// Default value for the whole struct.
64    ///
65    /// This will be in scope for all initializers as `__default`.
66    pub default_struct: Option<&'a DefaultExpression>,
67    /// Validation function with signature `&FooBuilder -> Result<(), String>`
68    /// to call before the macro-provided struct buildout.
69    pub validate_fn: Option<&'a syn::Path>,
70}
71
72impl<'a> ToTokens for BuildMethod<'a> {
73    fn to_tokens(&self, tokens: &mut TokenStream) {
74        let ident = &self.ident;
75        let vis = &self.visibility;
76        let target_ty = &self.target_ty;
77        let target_ty_generics = &self.target_ty_generics;
78        let initializers = &self.initializers;
79        let self_param = match self.pattern {
80            BuilderPattern::Owned => quote!(self),
81            BuilderPattern::Mutable | BuilderPattern::Immutable => quote!(&self),
82        };
83        let doc_comment = &self.doc_comment;
84        let default_struct = self.default_struct.as_ref().map(|default_expr| {
85            let ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
86            quote!(let #ident: #target_ty #target_ty_generics = #default_expr;)
87        });
88        let validate_fn = self
89            .validate_fn
90            .as_ref()
91            .map(|vfn| quote_spanned!(vfn.span() => #vfn(&self)?;));
92        let error_ty = &self.error_ty;
93
94        if self.enabled {
95            tokens.append_all(quote!(
96                #doc_comment
97                #vis fn #ident(#self_param)
98                    -> ::derive_builder::export::core::result::Result<#target_ty #target_ty_generics, #error_ty>
99                {
100                    #validate_fn
101                    #default_struct
102                    Ok(#target_ty {
103                        #(#initializers)*
104                    })
105                }
106            ))
107        }
108    }
109}
110
111impl<'a> BuildMethod<'a> {
112    /// Set a doc-comment for this item.
113    pub fn doc_comment(&mut self, s: String) -> &mut Self {
114        self.doc_comment = Some(doc_comment_from(s));
115        self
116    }
117
118    /// Populate the `BuildMethod` with appropriate initializers of the
119    /// underlying struct.
120    ///
121    /// For each struct field this must be called with the appropriate
122    /// initializer.
123    pub fn push_initializer(&mut self, init: Initializer) -> &mut Self {
124        self.initializers.push(quote!(#init));
125        self
126    }
127}
128
129// pub struct BuildMethodError {
130//     is_generated: bool,
131//     ident: syn::Ident,
132// }
133
134/// Helper macro for unit tests. This is _only_ public in order to be accessible
135/// from doc-tests too.
136#[doc(hidden)]
137#[macro_export]
138macro_rules! default_build_method {
139    () => {
140        BuildMethod {
141            enabled: true,
142            ident: &syn::Ident::new("build", ::proc_macro2::Span::call_site()),
143            visibility: ::std::borrow::Cow::Owned(syn::parse_quote!(pub)),
144            pattern: BuilderPattern::Mutable,
145            target_ty: &syn::Ident::new("Foo", ::proc_macro2::Span::call_site()),
146            target_ty_generics: None,
147            error_ty: syn::parse_quote!(FooBuilderError),
148            initializers: vec![quote!(foo: self.foo,)],
149            doc_comment: None,
150            default_struct: None,
151            validate_fn: None,
152        }
153    };
154}
155
156#[cfg(test)]
157mod tests {
158    #[allow(unused_imports)]
159    use super::*;
160
161    #[test]
162    fn std() {
163        let build_method = default_build_method!();
164
165        #[rustfmt::skip]
166        assert_eq!(
167            quote!(#build_method).to_string(),
168            quote!(
169                pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
170                    Ok(Foo {
171                        foo: self.foo,
172                    })
173                }
174            )
175            .to_string()
176        );
177    }
178
179    #[test]
180    fn default_struct() {
181        let mut build_method = default_build_method!();
182        let alt_default =
183            DefaultExpression::explicit::<syn::Expr>(parse_quote!(Default::default()));
184        build_method.default_struct = Some(&alt_default);
185
186        #[rustfmt::skip]
187        assert_eq!(
188            quote!(#build_method).to_string(),
189            quote!(
190                pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
191                    let __default: Foo = { Default::default() };
192                    Ok(Foo {
193                        foo: self.foo,
194                    })
195                }
196            )
197            .to_string()
198        );
199    }
200
201    #[test]
202    fn skip() {
203        let mut build_method = default_build_method!();
204        build_method.enabled = false;
205        build_method.enabled = false;
206
207        assert_eq!(quote!(#build_method).to_string(), quote!().to_string());
208    }
209
210    #[test]
211    fn rename() {
212        let ident = syn::Ident::new("finish", Span::call_site());
213        let mut build_method: BuildMethod = default_build_method!();
214        build_method.ident = &ident;
215
216        #[rustfmt::skip]
217        assert_eq!(
218            quote!(#build_method).to_string(),
219            quote!(
220                pub fn finish(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
221                    Ok(Foo {
222                        foo: self.foo,
223                    })
224                }
225            )
226            .to_string()
227        );
228    }
229
230    #[test]
231    fn validation() {
232        let validate_path: syn::Path = parse_quote!(IpsumBuilder::validate);
233
234        let mut build_method: BuildMethod = default_build_method!();
235        build_method.validate_fn = Some(&validate_path);
236
237        #[rustfmt::skip]
238        assert_eq!(
239            quote!(#build_method).to_string(),
240            quote!(
241                pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
242                    IpsumBuilder::validate(&self)?;
243
244                    Ok(Foo {
245                        foo: self.foo,
246                    })
247                }
248            )
249            .to_string()
250        );
251    }
252}