1 #![allow(clippy::useless_let_if_seq)]
2 use proc_macro2::{Span, TokenStream};
3 use quote::{ToTokens, TokenStreamExt};
4 use syn;
5 
6 use BuilderPattern;
7 use DeprecationNotes;
8 
9 /// Setter for the struct fields in the build method, implementing
10 /// `quote::ToTokens`.
11 ///
12 /// # Examples
13 ///
14 /// Will expand to something like the following (depending on settings):
15 ///
16 /// ```rust,ignore
17 /// # extern crate proc_macro2;
18 /// # #[macro_use]
19 /// # extern crate quote;
20 /// # extern crate syn;
21 /// # #[macro_use]
22 /// # extern crate derive_builder_core;
23 /// # use derive_builder_core::{Setter, BuilderPattern};
24 /// # fn main() {
25 /// #     let mut setter = default_setter!();
26 /// #     setter.pattern = BuilderPattern::Mutable;
27 /// #
28 /// #     assert_eq!(quote!(#setter).to_string(), quote!(
29 /// # #[allow(unused_mut)]
30 /// pub fn foo(&mut self, value: Foo) -> &mut Self {
31 ///     let mut new = self;
32 ///     new.foo = ::derive_builder::export::core::option::Option::Some(value);
33 ///     new
34 /// }
35 /// #     ).to_string());
36 /// # }
37 /// ```
38 #[derive(Debug, Clone)]
39 pub struct Setter<'a> {
40     /// Enables code generation for this setter fn.
41     pub setter_enabled: bool,
42     /// Enables code generation for the `try_` variant of this setter fn.
43     pub try_setter: bool,
44     /// Visibility of the setter, e.g. `syn::Visibility::Public`.
45     pub visibility: syn::Visibility,
46     /// How the setter method takes and returns `self` (e.g. mutably).
47     pub pattern: BuilderPattern,
48     /// Attributes which will be attached to this setter fn.
49     pub attrs: &'a [syn::Attribute],
50     /// Name of this setter fn.
51     pub ident: syn::Ident,
52     /// Name of the target field.
53     pub field_ident: &'a syn::Ident,
54     /// Type of the target field.
55     ///
56     /// The corresonding builder field will be `Option<field_type>`.
57     pub field_type: &'a syn::Type,
58     /// Make the setter generic over `Into<T>`, where `T` is the field type.
59     pub generic_into: bool,
60     /// Make the setter remove the Option wrapper from the setter, remove the need to call Some(...).
61     /// when combined with into, the into is used on the content Type of the Option.
62     pub strip_option: bool,
63     /// Emit deprecation notes to the user.
64     pub deprecation_notes: &'a DeprecationNotes,
65     /// Emit extend method.
66     pub each: Option<&'a syn::Ident>,
67 }
68 
69 impl<'a> ToTokens for Setter<'a> {
to_tokens(&self, tokens: &mut TokenStream)70     fn to_tokens(&self, tokens: &mut TokenStream) {
71         if self.setter_enabled {
72             let field_type = self.field_type;
73             let pattern = self.pattern;
74             let vis = &self.visibility;
75             let field_ident = self.field_ident;
76             let ident = &self.ident;
77             let attrs = self.attrs;
78             let deprecation_notes = self.deprecation_notes;
79             let (ty, stripped_option) = {
80                 if self.strip_option {
81                     match extract_type_from_option(field_type) {
82                         Some(ty) => (ty, true),
83                         None => (field_type, false),
84                     }
85                 } else {
86                     (field_type, false)
87                 }
88             };
89 
90             let self_param: TokenStream;
91             let return_ty: TokenStream;
92             let self_into_return_ty: TokenStream;
93 
94             match pattern {
95                 BuilderPattern::Owned => {
96                     self_param = quote!(self);
97                     return_ty = quote!(Self);
98                     self_into_return_ty = quote!(self);
99                 }
100                 BuilderPattern::Mutable => {
101                     self_param = quote!(&mut self);
102                     return_ty = quote!(&mut Self);
103                     self_into_return_ty = quote!(self);
104                 }
105                 BuilderPattern::Immutable => {
106                     self_param = quote!(&self);
107                     return_ty = quote!(Self);
108                     self_into_return_ty =
109                         quote!(::derive_builder::export::core::clone::Clone::clone(self));
110                 }
111             };
112 
113             let ty_params: TokenStream;
114             let param_ty: TokenStream;
115             let mut into_value: TokenStream;
116 
117             if self.generic_into {
118                 ty_params = quote!(<VALUE: ::derive_builder::export::core::convert::Into<#ty>>);
119                 param_ty = quote!(VALUE);
120                 into_value = quote!(value.into());
121             } else {
122                 ty_params = quote!();
123                 param_ty = quote!(#ty);
124                 into_value = quote!(value);
125             }
126             if stripped_option {
127                 into_value =
128                     quote!(::derive_builder::export::core::option::Option::Some(#into_value));
129             }
130             tokens.append_all(quote!(
131                 #(#attrs)*
132                 #[allow(unused_mut)]
133                 #vis fn #ident #ty_params (#self_param, value: #param_ty)
134                     -> #return_ty
135                 {
136                     #deprecation_notes
137                     let mut new = #self_into_return_ty;
138                     new.#field_ident = ::derive_builder::export::core::option::Option::Some(#into_value);
139                     new
140                 }
141             ));
142 
143             if self.try_setter {
144                 let try_ty_params =
145                     quote!(<VALUE: ::derive_builder::export::core::convert::TryInto<#ty>>);
146                 let try_ident = syn::Ident::new(&format!("try_{}", ident), Span::call_site());
147 
148                 tokens.append_all(quote!(
149                     #(#attrs)*
150                     #vis fn #try_ident #try_ty_params (#self_param, value: VALUE)
151                         -> ::derive_builder::export::core::result::Result<#return_ty, VALUE::Error>
152                     {
153                         let converted : #ty = value.try_into()?;
154                         let mut new = #self_into_return_ty;
155                         new.#field_ident = ::derive_builder::export::core::option::Option::Some(converted);
156                         Ok(new)
157                     }
158                 ));
159             }
160 
161             if let Some(ref ident_each) = self.each {
162                 tokens.append_all(quote!(
163                     #(#attrs)*
164                     #[allow(unused_mut)]
165                     #vis fn #ident_each <VALUE>(#self_param, item: VALUE) -> #return_ty
166                     where
167                         #ty: ::derive_builder::export::core::default::Default + ::derive_builder::export::core::iter::Extend<VALUE>,
168                     {
169                         #deprecation_notes
170                         let mut new = #self_into_return_ty;
171                         new.#field_ident
172                             .get_or_insert_with(::derive_builder::export::core::default::Default::default)
173                             .extend(::derive_builder::export::core::option::Option::Some(item));
174                         new
175                     }
176                 ));
177             }
178         }
179     }
180 }
181 
182 // adapted from https://stackoverflow.com/a/55277337/469066
183 // Note that since syn is a parser, it works with tokens.
184 // We cannot know for sure that this is an Option.
185 // The user could, for example, `type MaybeString = std::option::Option<String>`
186 // We cannot handle those arbitrary names.
extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type>187 fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> {
188     use syn::punctuated::Pair;
189     use syn::token::Colon2;
190     use syn::{GenericArgument, Path, PathArguments, PathSegment};
191 
192     fn extract_type_path(ty: &syn::Type) -> Option<&Path> {
193         match *ty {
194             syn::Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path),
195             _ => None,
196         }
197     }
198 
199     // TODO store (with lazy static) precomputed parsing of Option when support of rust 1.18 will be removed (incompatible with lazy_static)
200     // TODO maybe optimization, reverse the order of segments
201     fn extract_option_segment(path: &Path) -> Option<Pair<&PathSegment, &Colon2>> {
202         let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| {
203             acc.push_str(&v.ident.to_string());
204             acc.push('|');
205             acc
206         });
207         vec!["Option|", "std|option|Option|", "core|option|Option|"]
208             .into_iter()
209             .find(|s| idents_of_path == *s)
210             .and_then(|_| path.segments.last().map(Pair::End))
211     }
212 
213     extract_type_path(ty)
214         .and_then(|path| extract_option_segment(path))
215         .and_then(|pair_path_segment| {
216             let type_params = &pair_path_segment.into_value().arguments;
217             // It should have only on angle-bracketed param ("<String>"):
218             match *type_params {
219                 PathArguments::AngleBracketed(ref params) => params.args.first(),
220                 _ => None,
221             }
222         })
223         .and_then(|generic_arg| match *generic_arg {
224             GenericArgument::Type(ref ty) => Some(ty),
225             _ => None,
226         })
227 }
228 
229 /// Helper macro for unit tests. This is _only_ public in order to be accessible
230 /// from doc-tests too.
231 #[doc(hidden)]
232 #[macro_export]
233 macro_rules! default_setter {
234     () => {
235         Setter {
236             setter_enabled: true,
237             try_setter: false,
238             visibility: syn::parse_str("pub").unwrap(),
239             pattern: BuilderPattern::Mutable,
240             attrs: &vec![],
241             ident: syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
242             field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
243             field_type: &syn::parse_str("Foo").unwrap(),
244             generic_into: false,
245             strip_option: false,
246             deprecation_notes: &Default::default(),
247             each: None,
248         };
249     };
250 }
251 
252 #[cfg(test)]
253 mod tests {
254     #[allow(unused_imports)]
255     use super::*;
256 
257     #[test]
immutable()258     fn immutable() {
259         let mut setter = default_setter!();
260         setter.pattern = BuilderPattern::Immutable;
261 
262         assert_eq!(
263             quote!(#setter).to_string(),
264             quote!(
265                 #[allow(unused_mut)]
266                 pub fn foo(&self, value: Foo) -> Self {
267                     let mut new = ::derive_builder::export::core::clone::Clone::clone(self);
268                     new.foo = ::derive_builder::export::core::option::Option::Some(value);
269                     new
270                 }
271             )
272             .to_string()
273         );
274     }
275 
276     #[test]
mutable()277     fn mutable() {
278         let mut setter = default_setter!();
279         setter.pattern = BuilderPattern::Mutable;
280 
281         assert_eq!(
282             quote!(#setter).to_string(),
283             quote!(
284                 #[allow(unused_mut)]
285                 pub fn foo(&mut self, value: Foo) -> &mut Self {
286                     let mut new = self;
287                     new.foo = ::derive_builder::export::core::option::Option::Some(value);
288                     new
289                 }
290             )
291             .to_string()
292         );
293     }
294 
295     #[test]
owned()296     fn owned() {
297         let mut setter = default_setter!();
298         setter.pattern = BuilderPattern::Owned;
299 
300         assert_eq!(
301             quote!(#setter).to_string(),
302             quote!(
303                 #[allow(unused_mut)]
304                 pub fn foo(self, value: Foo) -> Self {
305                     let mut new = self;
306                     new.foo = ::derive_builder::export::core::option::Option::Some(value);
307                     new
308                 }
309             )
310             .to_string()
311         );
312     }
313 
314     #[test]
private()315     fn private() {
316         let vis = syn::Visibility::Inherited;
317 
318         let mut setter = default_setter!();
319         setter.visibility = vis;
320 
321         assert_eq!(
322             quote!(#setter).to_string(),
323             quote!(
324                 #[allow(unused_mut)]
325                 fn foo(&mut self, value: Foo) -> &mut Self {
326                     let mut new = self;
327                     new.foo = ::derive_builder::export::core::option::Option::Some(value);
328                     new
329                 }
330             )
331             .to_string()
332         );
333     }
334 
335     #[test]
generic()336     fn generic() {
337         let mut setter = default_setter!();
338         setter.generic_into = true;
339 
340         #[rustfmt::skip]
341         assert_eq!(
342             quote!(#setter).to_string(),
343             quote!(
344                 #[allow(unused_mut)]
345                 pub fn foo<VALUE: ::derive_builder::export::core::convert::Into<Foo>>(
346                     &mut self,
347                     value: VALUE
348                 ) -> &mut Self {
349                     let mut new = self;
350                     new.foo = ::derive_builder::export::core::option::Option::Some(value.into());
351                     new
352                 }
353             )
354             .to_string()
355         );
356     }
357 
358     #[test]
strip_option()359     fn strip_option() {
360         let ty = syn::parse_str("Option<Foo>").unwrap();
361         let mut setter = default_setter!();
362         setter.strip_option = true;
363         setter.field_type = &ty;
364 
365         #[rustfmt::skip]
366         assert_eq!(
367             quote!(#setter).to_string(),
368             quote!(
369                 #[allow(unused_mut)]
370                 pub fn foo(&mut self, value: Foo) -> &mut Self {
371                     let mut new = self;
372                     new.foo = ::derive_builder::export::core::option::Option::Some(
373                         ::derive_builder::export::core::option::Option::Some(value)
374                     );
375                     new
376                 }
377             )
378             .to_string()
379         );
380     }
381 
382     #[test]
strip_option_into()383     fn strip_option_into() {
384         let ty = syn::parse_str("Option<Foo>").unwrap();
385         let mut setter = default_setter!();
386         setter.strip_option = true;
387         setter.generic_into = true;
388         setter.field_type = &ty;
389 
390         #[rustfmt::skip]
391         assert_eq!(
392             quote!(#setter).to_string(),
393             quote!(
394                 #[allow(unused_mut)]
395                 pub fn foo<VALUE: ::derive_builder::export::core::convert::Into<Foo>>(
396                     &mut self,
397                     value: VALUE
398                 ) -> &mut Self {
399                     let mut new = self;
400                     new.foo = ::derive_builder::export::core::option::Option::Some(
401                         ::derive_builder::export::core::option::Option::Some(value.into())
402                     );
403                     new
404                 }
405             )
406             .to_string()
407         );
408     }
409 
410     // including try_setter
411     #[test]
full()412     fn full() {
413         //named!(outer_attrs -> Vec<syn::Attribute>, many0!(syn::Attribute::parse_outer));
414         //let attrs = outer_attrs.parse_str("#[some_attr]").unwrap();
415         let attrs: Vec<syn::Attribute> = vec![parse_quote!(#[some_attr])];
416 
417         let mut deprecated = DeprecationNotes::default();
418         deprecated.push("Some example.".to_string());
419 
420         let mut setter = default_setter!();
421         setter.attrs = attrs.as_slice();
422         setter.generic_into = true;
423         setter.deprecation_notes = &deprecated;
424         setter.try_setter = true;
425 
426         assert_eq!(
427             quote!(#setter).to_string(),
428             quote!(
429             #[some_attr]
430             #[allow(unused_mut)]
431             pub fn foo <VALUE: ::derive_builder::export::core::convert::Into<Foo>>(&mut self, value: VALUE) -> &mut Self {
432                 #deprecated
433                 let mut new = self;
434                 new.foo = ::derive_builder::export::core::option::Option::Some(value.into());
435                 new
436             }
437 
438             #[some_attr]
439             pub fn try_foo<VALUE: ::derive_builder::export::core::convert::TryInto<Foo>>(&mut self, value: VALUE)
440                 -> ::derive_builder::export::core::result::Result<&mut Self, VALUE::Error> {
441                 let converted : Foo = value.try_into()?;
442                 let mut new = self;
443                 new.foo = ::derive_builder::export::core::option::Option::Some(converted);
444                 Ok(new)
445             }
446         ).to_string()
447         );
448     }
449 
450     #[test]
no_std()451     fn no_std() {
452         let mut setter = default_setter!();
453         setter.pattern = BuilderPattern::Immutable;
454 
455         assert_eq!(
456             quote!(#setter).to_string(),
457             quote!(
458                 #[allow(unused_mut)]
459                 pub fn foo(&self, value: Foo) -> Self {
460                     let mut new = ::derive_builder::export::core::clone::Clone::clone(self);
461                     new.foo = ::derive_builder::export::core::option::Option::Some(value);
462                     new
463                 }
464             )
465             .to_string()
466         );
467     }
468 
469     #[test]
no_std_generic()470     fn no_std_generic() {
471         let mut setter = default_setter!();
472         setter.generic_into = true;
473 
474         #[rustfmt::skip]
475         assert_eq!(
476             quote!(#setter).to_string(),
477             quote!(
478                 #[allow(unused_mut)]
479                 pub fn foo<VALUE: ::derive_builder::export::core::convert::Into<Foo>>(
480                     &mut self,
481                     value: VALUE
482                 ) -> &mut Self {
483                     let mut new = self;
484                     new.foo = ::derive_builder::export::core::option::Option::Some(value.into());
485                     new
486                 }
487             )
488             .to_string()
489         );
490     }
491 
492     #[test]
setter_disabled()493     fn setter_disabled() {
494         let mut setter = default_setter!();
495         setter.setter_enabled = false;
496 
497         assert_eq!(quote!(#setter).to_string(), quote!().to_string());
498     }
499 
500     #[test]
try_setter()501     fn try_setter() {
502         let mut setter: Setter = default_setter!();
503         setter.pattern = BuilderPattern::Mutable;
504         setter.try_setter = true;
505 
506         #[rustfmt::skip]
507         assert_eq!(
508             quote!(#setter).to_string(),
509             quote!(
510                 #[allow(unused_mut)]
511                 pub fn foo(&mut self, value: Foo) -> &mut Self {
512                     let mut new = self;
513                     new.foo = ::derive_builder::export::core::option::Option::Some(value);
514                     new
515                 }
516 
517                 pub fn try_foo<VALUE: ::derive_builder::export::core::convert::TryInto<Foo>>(
518                     &mut self,
519                     value: VALUE
520                 ) -> ::derive_builder::export::core::result::Result<&mut Self, VALUE::Error> {
521                     let converted: Foo = value.try_into()?;
522                     let mut new = self;
523                     new.foo = ::derive_builder::export::core::option::Option::Some(converted);
524                     Ok(new)
525                 }
526             )
527             .to_string()
528         );
529     }
530 
531     #[test]
extract_type_from_option_on_simple_type()532     fn extract_type_from_option_on_simple_type() {
533         let ty_foo = syn::parse_str("Foo").unwrap();
534         assert_eq!(extract_type_from_option(&ty_foo), None);
535 
536         for s in vec![
537             "Option<Foo>",
538             "std::option::Option<Foo>",
539             "::std::option::Option<Foo>",
540             "core::option::Option<Foo>",
541             "::core::option::Option<Foo>",
542         ] {
543             let ty_foo_opt = syn::parse_str(s).unwrap();
544             assert_eq!(extract_type_from_option(&ty_foo_opt), Some(&ty_foo));
545         }
546     }
547 }
548