1 use quote::{ToTokens, TokenStreamExt};
2 use proc_macro2::{Span, TokenStream};
3 use syn;
4 use Bindings;
5 use Block;
6 use BuilderPattern;
7 use DEFAULT_STRUCT_NAME;
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
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)]
39 pub 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 setter_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<Block>,
50     /// Whether the build_method defines a default struct.
51     pub use_default_struct: bool,
52     /// Bindings to libstd or libcore.
53     pub bindings: Bindings,
54 }
55 
56 impl<'a> ToTokens for Initializer<'a> {
to_tokens(&self, tokens: &mut TokenStream)57     fn to_tokens(&self, tokens: &mut TokenStream) {
58         trace!("Deriving initializer for `{}`.", self.field_ident);
59 
60         let struct_field = &self.field_ident;
61 
62         if self.setter_enabled {
63             let match_some = self.match_some();
64             let match_none = self.match_none();
65             let builder_field = &*struct_field;
66             tokens.append_all(quote!(
67                 #struct_field: match self.#builder_field {
68                     #match_some,
69                     #match_none,
70                 },
71             ));
72         } else {
73             let default = self.default();
74             tokens.append_all(quote!(
75                 #struct_field: #default,
76             ));
77         }
78     }
79 }
80 
81 impl<'a> Initializer<'a> {
82     /// To be used inside of `#struct_field: match self.#builder_field { ... }`
match_some(&'a self) -> MatchSome83     fn match_some(&'a self) -> MatchSome {
84         match self.builder_pattern {
85             BuilderPattern::Owned => MatchSome::Move,
86             BuilderPattern::Mutable | BuilderPattern::Immutable => {
87                 if self.bindings.no_std {
88                     MatchSome::CloneNoStd
89                 } else {
90                     MatchSome::Clone
91                 }
92             }
93         }
94     }
95 
96     /// To be used inside of `#struct_field: match self.#builder_field { ... }`
match_none(&'a self) -> MatchNone<'a>97     fn match_none(&'a self) -> MatchNone<'a> {
98         match self.default_value {
99             Some(ref expr) => MatchNone::DefaultTo(expr),
100             None => {
101                 if self.use_default_struct {
102                     MatchNone::UseDefaultStructField(self.field_ident)
103                 } else if self.bindings.no_std {
104                     MatchNone::ReturnErrorNoStd(format!(
105                         "`{}` must be initialized",
106                         self.field_ident
107                     ))
108                 } else {
109                     MatchNone::ReturnError(format!("`{}` must be initialized", self.field_ident))
110                 }
111             }
112         }
113     }
114 
default(&'a self) -> TokenStream115     fn default(&'a self) -> TokenStream {
116         match self.default_value {
117             Some(ref expr) => quote!(#expr),
118             None if self.use_default_struct => {
119                 let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
120                 let field_ident = self.field_ident;
121                 quote!(#struct_ident.#field_ident)
122             }
123             None => {
124                 let default = self.bindings.default_trait();
125                 quote!(#default::default())
126             }
127         }
128     }
129 }
130 
131 /// To be used inside of `#struct_field: match self.#builder_field { ... }`
132 enum MatchNone<'a> {
133     /// Inner value must be a valid Rust expression
134     DefaultTo(&'a Block),
135     /// Inner value must be the field identifier
136     ///
137     /// The default struct must be in scope in the build_method.
138     UseDefaultStructField(&'a syn::Ident),
139     /// Inner value must be the field name
140     ReturnError(String),
141     /// Inner value must be the field name
142     ReturnErrorNoStd(String),
143 }
144 
145 impl<'a> ToTokens for MatchNone<'a> {
to_tokens(&self, tokens: &mut TokenStream)146     fn to_tokens(&self, tokens: &mut TokenStream) {
147         match *self {
148             MatchNone::DefaultTo(expr) => tokens.append_all(quote!(
149                 None => #expr
150             )),
151             MatchNone::UseDefaultStructField(field_ident) => {
152                 let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
153                 tokens.append_all(quote!(
154                     None => #struct_ident.#field_ident
155                 ))
156             }
157             MatchNone::ReturnError(ref err) => tokens.append_all(quote!(
158                 None => return ::std::result::Result::Err(::std::string::String::from(#err))
159             )),
160             MatchNone::ReturnErrorNoStd(ref err) => tokens.append_all(quote!(
161                 None => return ::core::result::Result::Err(
162                     ::alloc::string::String::from(#err))
163             )),
164         }
165     }
166 }
167 
168 /// To be used inside of `#struct_field: match self.#builder_field { ... }`
169 enum MatchSome {
170     Move,
171     Clone,
172     CloneNoStd,
173 }
174 
175 impl<'a> ToTokens for MatchSome {
to_tokens(&self, tokens: &mut TokenStream)176     fn to_tokens(&self, tokens: &mut TokenStream) {
177         match *self {
178             MatchSome::Move => tokens.append_all(quote!(
179                 Some(value) => value
180             )),
181             MatchSome::Clone => tokens.append_all(quote!(
182                 Some(ref value) => ::std::clone::Clone::clone(value)
183             )),
184             MatchSome::CloneNoStd => tokens.append_all(quote!(
185                 Some(ref value) => ::core::clone::Clone::clone(value)
186             )),
187         }
188     }
189 }
190 
191 /// Helper macro for unit tests. This is _only_ public in order to be accessible
192 /// from doc-tests too.
193 #[doc(hidden)]
194 #[macro_export]
195 macro_rules! default_initializer {
196     () => {
197         Initializer {
198             field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
199             setter_enabled: true,
200             builder_pattern: BuilderPattern::Mutable,
201             default_value: None,
202             use_default_struct: false,
203             bindings: Default::default(),
204         }
205     };
206 }
207 
208 #[cfg(test)]
209 mod tests {
210     #[allow(unused_imports)]
211     use super::*;
212 
213     #[test]
immutable()214     fn immutable() {
215         let mut initializer = default_initializer!();
216         initializer.builder_pattern = BuilderPattern::Immutable;
217 
218         assert_eq!(
219             quote!(#initializer).to_string(),
220             quote!(
221             foo: match self.foo {
222                 Some(ref value) => ::std::clone::Clone::clone(value),
223                 None => return ::std::result::Result::Err(::std::string::String::from(
224                     "`foo` must be initialized"
225                 )),
226             },
227         ).to_string()
228         );
229     }
230 
231     #[test]
mutable()232     fn mutable() {
233         let mut initializer = default_initializer!();
234         initializer.builder_pattern = BuilderPattern::Mutable;
235 
236         assert_eq!(
237             quote!(#initializer).to_string(),
238             quote!(
239             foo: match self.foo {
240                 Some(ref value) => ::std::clone::Clone::clone(value),
241                 None => return ::std::result::Result::Err(::std::string::String::from(
242                     "`foo` must be initialized"
243                 )),
244             },
245         ).to_string()
246         );
247     }
248 
249     #[test]
owned()250     fn owned() {
251         let mut initializer = default_initializer!();
252         initializer.builder_pattern = BuilderPattern::Owned;
253 
254         assert_eq!(
255             quote!(#initializer).to_string(),
256             quote!(
257             foo: match self.foo {
258                 Some(value) => value,
259                 None => return ::std::result::Result::Err(::std::string::String::from(
260                     "`foo` must be initialized"
261                 )),
262             },
263         ).to_string()
264         );
265     }
266 
267     #[test]
default_value()268     fn default_value() {
269         let mut initializer = default_initializer!();
270         initializer.default_value = Some("42".parse().unwrap());
271 
272         assert_eq!(
273             quote!(#initializer).to_string(),
274             quote!(
275             foo: match self.foo {
276                 Some(ref value) => ::std::clone::Clone::clone(value),
277                 None => { 42 },
278             },
279         ).to_string()
280         );
281     }
282 
283     #[test]
default_struct()284     fn default_struct() {
285         let mut initializer = default_initializer!();
286         initializer.use_default_struct = true;
287 
288         assert_eq!(
289             quote!(#initializer).to_string(),
290             quote!(
291             foo: match self.foo {
292                 Some(ref value) => ::std::clone::Clone::clone(value),
293                 None => __default.foo,
294             },
295         ).to_string()
296         );
297     }
298 
299     #[test]
setter_disabled()300     fn setter_disabled() {
301         let mut initializer = default_initializer!();
302         initializer.setter_enabled = false;
303 
304         assert_eq!(
305             quote!(#initializer).to_string(),
306             quote!(foo: ::std::default::Default::default(),).to_string()
307         );
308     }
309 
310     #[test]
no_std()311     fn no_std() {
312         let mut initializer = default_initializer!();
313         initializer.bindings.no_std = true;
314 
315         assert_eq!(
316             quote!(#initializer).to_string(),
317             quote!(
318             foo: match self.foo {
319                 Some(ref value) => ::core::clone::Clone::clone(value),
320                 None => return ::core::result::Result::Err(::alloc::string::String::from(
321                     "`foo` must be initialized"
322                 )),
323             },
324         ).to_string()
325         );
326     }
327 
328     #[test]
no_std_setter_disabled()329     fn no_std_setter_disabled() {
330         let mut initializer = default_initializer!();
331         initializer.bindings.no_std = true;
332         initializer.setter_enabled = false;
333 
334         assert_eq!(
335             quote!(#initializer).to_string(),
336             quote!(foo: ::core::default::Default::default(),).to_string()
337         );
338     }
339 }
340