derive_builder_core_fork_arti/
initializer.rs

1use proc_macro2::{Span, TokenStream};
2use quote::{ToTokens, TokenStreamExt};
3use syn;
4use BuilderPattern;
5use DEFAULT_STRUCT_NAME;
6
7use crate::{BlockContents, DefaultExpression};
8
9/// Initializer for the target struct fields, implementing `quote::ToTokens`.
10///
11/// Lives in the body of `BuildMethod`.
12///
13/// # Examples
14///
15/// Will expand to something like the following (depending on settings):
16///
17/// ```rust,ignore
18/// # extern crate proc_macro2;
19/// # #[macro_use]
20/// # extern crate quote;
21/// # extern crate syn;
22/// # #[macro_use]
23/// # extern crate derive_builder_core;
24/// # use derive_builder_core::{DeprecationNotes, Initializer, BuilderPattern};
25/// # fn main() {
26/// #    let mut initializer = default_initializer!();
27/// #    initializer.default_value = Some("42".parse().unwrap());
28/// #    initializer.builder_pattern = BuilderPattern::Owned;
29/// #
30/// #    assert_eq!(quote!(#initializer).to_string(), quote!(
31/// foo: match self.foo {
32///     Some(value) => value,
33///     None => { 42 },
34/// },
35/// #    ).to_string());
36/// # }
37/// ```
38#[derive(Debug, Clone)]
39pub struct Initializer<'a> {
40    /// Name of the target field.
41    pub field_ident: &'a syn::Ident,
42    /// Whether the builder implements a setter for this field.
43    pub field_enabled: bool,
44    /// How the build method takes and returns `self` (e.g. mutably).
45    pub builder_pattern: BuilderPattern,
46    /// Default value for the target field.
47    ///
48    /// This takes precedence over a default struct identifier.
49    pub default_value: Option<&'a DefaultExpression>,
50    /// Whether the build_method defines a default struct.
51    pub use_default_struct: bool,
52    /// Span where the macro was told to use a preexisting error type, instead of creating one,
53    /// to represent failures of the `build` method.
54    ///
55    /// An initializer can force early-return if a field has no set value and no default is
56    /// defined. In these cases, it will convert from `derive_builder::UninitializedFieldError`
57    /// into the return type of its enclosing `build` method. That conversion is guaranteed to
58    /// work for generated error types, but if the caller specified an error type to use instead
59    /// they may have forgotten the conversion from `UninitializedFieldError` into their specified
60    /// error type.
61    pub custom_error_type_span: Option<Span>,
62    /// Method to use to to convert the builder's field to the target field
63    ///
64    /// For sub-builder fields, this will be `build` (or similar)
65    pub conversion: FieldConversion<'a>,
66}
67
68impl<'a> ToTokens for Initializer<'a> {
69    fn to_tokens(&self, tokens: &mut TokenStream) {
70        let struct_field = &self.field_ident;
71        let builder_field = struct_field;
72
73        // This structure prevents accidental failure to add the trailing `,` due to incautious `return`
74        let append_rhs = |tokens: &mut TokenStream| {
75            if !self.field_enabled {
76                let default = self.default();
77                tokens.append_all(quote!(
78                    #default
79                ));
80            } else {
81                match &self.conversion {
82                    FieldConversion::Block(conv) => {
83                        conv.to_tokens(tokens);
84                    }
85                    FieldConversion::Method(meth) => {
86                        let span = self
87                            .custom_error_type_span
88                            .unwrap_or_else(|| self.field_ident.span());
89                        let field_name = self.field_ident.to_string();
90                        tokens.append_all(quote_spanned!(span=>
91                            ::derive_builder::export::core::result::Result::map_err(
92                                self.#builder_field.#meth(),
93                                |e| ::derive_builder::SubfieldBuildError::new(#field_name, e),
94                            )?
95                        ));
96                    }
97                    FieldConversion::Move => tokens.append_all(quote!( self.#builder_field )),
98                    FieldConversion::OptionOrDefault => {
99                        let match_some = self.match_some();
100                        let match_none = self.match_none();
101                        tokens.append_all(quote!(
102                            match self.#builder_field {
103                                #match_some,
104                                #match_none,
105                            }
106                        ));
107                    }
108                }
109            }
110        };
111
112        tokens.append_all(quote!(#struct_field:));
113        append_rhs(tokens);
114        tokens.append_all(quote!(,));
115    }
116}
117
118impl<'a> Initializer<'a> {
119    /// To be used inside of `#struct_field: match self.#builder_field { ... }`
120    fn match_some(&'a self) -> MatchSome {
121        match self.builder_pattern {
122            BuilderPattern::Owned => MatchSome::Move,
123            BuilderPattern::Mutable | BuilderPattern::Immutable => MatchSome::Clone,
124        }
125    }
126
127    /// To be used inside of `#struct_field: match self.#builder_field { ... }`
128    fn match_none(&'a self) -> MatchNone<'a> {
129        match self.default_value {
130            Some(expr) => MatchNone::DefaultTo(expr),
131            None => {
132                if self.use_default_struct {
133                    MatchNone::UseDefaultStructField(self.field_ident)
134                } else {
135                    MatchNone::ReturnError(
136                        self.field_ident.to_string(),
137                        self.custom_error_type_span,
138                    )
139                }
140            }
141        }
142    }
143
144    fn default(&'a self) -> TokenStream {
145        match self.default_value {
146            Some(ref expr) => quote!(#expr),
147            None if self.use_default_struct => {
148                let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
149                let field_ident = self.field_ident;
150                quote!(#struct_ident.#field_ident)
151            }
152            None => quote!(::derive_builder::export::core::default::Default::default()),
153        }
154    }
155}
156
157#[derive(Debug, Clone)]
158pub enum FieldConversion<'a> {
159    /// Usual conversion: unwrap the Option from the builder, or (hope to) use a default value
160    OptionOrDefault,
161    /// Custom conversion is a block contents expression
162    Block(&'a BlockContents),
163    /// Custom conversion is a method to be called on the corresponding builder field
164    Method(&'a syn::Ident),
165    /// Custom conversion is just to move the field from the builder
166    Move,
167}
168
169/// To be used inside of `#struct_field: match self.#builder_field { ... }`
170enum MatchNone<'a> {
171    /// Inner value must be a valid Rust expression
172    DefaultTo(&'a DefaultExpression),
173    /// Inner value must be the field identifier
174    ///
175    /// The default struct must be in scope in the build_method.
176    UseDefaultStructField(&'a syn::Ident),
177    /// Inner value must be the field name
178    ReturnError(String, Option<Span>),
179}
180
181impl<'a> ToTokens for MatchNone<'a> {
182    fn to_tokens(&self, tokens: &mut TokenStream) {
183        match *self {
184            MatchNone::DefaultTo(expr) => tokens.append_all(quote!(
185                None => #expr
186            )),
187            MatchNone::UseDefaultStructField(field_ident) => {
188                let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
189                tokens.append_all(quote!(
190                    None => #struct_ident.#field_ident
191                ))
192            }
193            MatchNone::ReturnError(ref field_name, ref span) => {
194                let conv_span = span.unwrap_or_else(Span::call_site);
195                let err_conv = quote_spanned!(conv_span => ::derive_builder::export::core::convert::Into::into(
196                    ::derive_builder::UninitializedFieldError::from(#field_name)
197                ));
198                tokens.append_all(quote!(
199                    None => return ::derive_builder::export::core::result::Result::Err(#err_conv)
200                ));
201            }
202        }
203    }
204}
205
206/// To be used inside of `#struct_field: match self.#builder_field { ... }`
207enum MatchSome {
208    Move,
209    Clone,
210}
211
212impl<'a> ToTokens for MatchSome {
213    fn to_tokens(&self, tokens: &mut TokenStream) {
214        match *self {
215            Self::Move => tokens.append_all(quote!(
216                Some(value) => value
217            )),
218            Self::Clone => tokens.append_all(quote!(
219                Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value)
220            )),
221        }
222    }
223}
224
225/// Helper macro for unit tests. This is _only_ public in order to be accessible
226/// from doc-tests too.
227#[doc(hidden)]
228#[macro_export]
229macro_rules! default_initializer {
230    () => {
231        Initializer {
232            field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
233            field_enabled: true,
234            builder_pattern: BuilderPattern::Mutable,
235            default_value: None,
236            use_default_struct: false,
237            conversion: FieldConversion::OptionOrDefault,
238            custom_error_type_span: None,
239        }
240    };
241}
242
243#[cfg(test)]
244mod tests {
245    #[allow(unused_imports)]
246    use super::*;
247
248    #[test]
249    fn immutable() {
250        let mut initializer = default_initializer!();
251        initializer.builder_pattern = BuilderPattern::Immutable;
252
253        assert_eq!(
254            quote!(#initializer).to_string(),
255            quote!(
256                foo: match self.foo {
257                    Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value),
258                    None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(
259                        ::derive_builder::UninitializedFieldError::from("foo")
260                    )),
261                },
262            )
263            .to_string()
264        );
265    }
266
267    #[test]
268    fn mutable() {
269        let mut initializer = default_initializer!();
270        initializer.builder_pattern = BuilderPattern::Mutable;
271
272        assert_eq!(
273            quote!(#initializer).to_string(),
274            quote!(
275                foo: match self.foo {
276                    Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value),
277                    None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(
278                        ::derive_builder::UninitializedFieldError::from("foo")
279                    )),
280                },
281            )
282            .to_string()
283        );
284    }
285
286    #[test]
287    fn owned() {
288        let mut initializer = default_initializer!();
289        initializer.builder_pattern = BuilderPattern::Owned;
290
291        assert_eq!(
292            quote!(#initializer).to_string(),
293            quote!(
294                foo: match self.foo {
295                    Some(value) => value,
296                    None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(
297                        ::derive_builder::UninitializedFieldError::from("foo")
298                    )),
299                },
300            )
301            .to_string()
302        );
303    }
304
305    #[test]
306    fn default_value() {
307        let mut initializer = default_initializer!();
308        let default_value = DefaultExpression::explicit::<syn::Expr>(parse_quote!(42));
309        initializer.default_value = Some(&default_value);
310
311        assert_eq!(
312            quote!(#initializer).to_string(),
313            quote!(
314                foo: match self.foo {
315                    Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value),
316                    None => { 42 },
317                },
318            )
319            .to_string()
320        );
321    }
322
323    #[test]
324    fn default_struct() {
325        let mut initializer = default_initializer!();
326        initializer.use_default_struct = true;
327
328        assert_eq!(
329            quote!(#initializer).to_string(),
330            quote!(
331                foo: match self.foo {
332                    Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value),
333                    None => __default.foo,
334                },
335            )
336            .to_string()
337        );
338    }
339
340    #[test]
341    fn setter_disabled() {
342        let mut initializer = default_initializer!();
343        initializer.field_enabled = false;
344
345        assert_eq!(
346            quote!(#initializer).to_string(),
347            quote!(foo: ::derive_builder::export::core::default::Default::default(),).to_string()
348        );
349    }
350
351    #[test]
352    fn no_std() {
353        let initializer = default_initializer!();
354
355        assert_eq!(
356            quote!(#initializer).to_string(),
357            quote!(
358                foo: match self.foo {
359                    Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value),
360                    None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(
361                        ::derive_builder::UninitializedFieldError::from("foo")
362                    )),
363                },
364            )
365            .to_string()
366        );
367    }
368}