1 use proc_macro2::TokenStream;
2 use quote::{format_ident, ToTokens, TokenStreamExt};
3 use syn::punctuated::Punctuated;
4 use syn::{self, Path, TraitBound, TraitBoundModifier, TypeParamBound};
5 
6 use doc_comment_from;
7 use BuildMethod;
8 use BuilderField;
9 use BuilderPattern;
10 use DeprecationNotes;
11 use Setter;
12 
13 /// Builder, implementing `quote::ToTokens`.
14 ///
15 /// # Examples
16 ///
17 /// Will expand to something like the following (depending on settings):
18 ///
19 /// ```rust,ignore
20 /// # extern crate proc_macro2;
21 /// # #[macro_use]
22 /// # extern crate quote;
23 /// # extern crate syn;
24 /// # #[macro_use]
25 /// # extern crate derive_builder_core;
26 /// # use quote::TokenStreamExt;
27 /// # use derive_builder_core::{Builder, DeprecationNotes};
28 /// # fn main() {
29 /// #    let builder = default_builder!();
30 /// #
31 /// #    assert_eq!(
32 /// #       quote!(#builder).to_string(),
33 /// #       {
34 /// #           let mut result = quote!();
35 /// #           #[cfg(not(feature = "clippy"))]
36 /// #           result.append_all(quote!(#[allow(clippy::all)]));
37 /// #
38 /// #           result.append_all(quote!(
39 /// #[derive(Clone)]
40 /// pub struct FooBuilder {
41 ///     foo: u32,
42 /// }
43 ///
44 /// #[doc="Error type for FooBuilder"]
45 /// #[derive(Debug)]
46 /// #[non_exhaustive]
47 /// pub enum FooBuilderError {
48 ///     /// Uninitialized field
49 ///     UninitializedField(&'static str),
50 ///     /// Custom validation error
51 ///     ValidationError(::derive_builder::export::core::string::String),
52 /// }
53 ///
54 /// impl ::derive_builder::export::core::convert::From<&'static str> for FooBuilderError {
55 ///     fn from(s: &'static str) -> Self {
56 ///         Self::UninitializedField(s)
57 ///     }
58 /// }
59 ///
60 /// impl ::derive_builder::export::core::convert::From<::derive_builder::export::core::string::String> for FooBuilderError {
61 ///     fn from(s: ::derive_builder::export::core::string::String) -> Self {
62 ///         Self::ValidationError(s)
63 ///     }
64 /// }
65 ///
66 /// impl ::derive_builder::export::core::fmt::Display for FooBuilderError {
67 ///     fn fmt(&self, f: &mut ::derive_builder::export::core::fmt::Formatter) -> ::derive_builder::export::core::fmt::Result {
68 ///         match self {
69 ///             Self::UninitializedField(ref field) => write!(f, "`{}` must be initialized", field),
70 ///             Self::ValidationError(ref error) => write!(f, "{}", error),
71 ///         }
72 ///     }
73 /// }
74 ///
75 /// #[cfg(not(no_std))]
76 /// impl std::error::Error for FooBuilderError {}
77 /// #           ));
78 /// #           #[cfg(not(feature = "clippy"))]
79 /// #           result.append_all(quote!(#[allow(clippy::all)]));
80 /// #
81 /// #           result.append_all(quote!(
82 ///
83 /// #[allow(dead_code)]
84 /// impl FooBuilder {
85 ///     fn bar () -> {
86 ///         unimplemented!()
87 ///     }
88 /// }
89 ///
90 /// impl ::derive_builder::export::core::default::Default for FooBuilder {
91 ///     fn default() -> Self {
92 ///         Self {
93 ///            foo: ::derive_builder::export::core::default::Default::default(),
94 ///         }
95 ///     }
96 /// }
97 ///
98 /// #           ));
99 /// #           result
100 /// #       }.to_string()
101 /// #   );
102 /// # }
103 /// ```
104 #[derive(Debug)]
105 pub struct Builder<'a> {
106     /// Enables code generation for this builder struct.
107     pub enabled: bool,
108     /// Name of this builder struct.
109     pub ident: syn::Ident,
110     /// Pattern of this builder struct.
111     pub pattern: BuilderPattern,
112     /// Traits to automatically derive on the builder type.
113     pub derives: &'a [Path],
114     /// Type parameters and lifetimes attached to this builder's struct
115     /// definition.
116     pub generics: Option<&'a syn::Generics>,
117     /// Visibility of the builder struct, e.g. `syn::Visibility::Public`.
118     pub visibility: syn::Visibility,
119     /// Fields of the builder struct, e.g. `foo: u32,`
120     ///
121     /// Expects each entry to be terminated by a comma.
122     pub fields: Vec<TokenStream>,
123     /// Builder field initializers, e.g. `foo: Default::default(),`
124     ///
125     /// Expects each entry to be terminated by a comma.
126     pub field_initializers: Vec<TokenStream>,
127     /// Functions of the builder struct, e.g. `fn bar() -> { unimplemented!() }`
128     pub functions: Vec<TokenStream>,
129     /// Whether or not a generated error type is required.
130     ///
131     /// This would be `false` in the case where an already-existing error is to be used.
132     pub generate_error: bool,
133     /// Whether this builder must derive `Clone`.
134     ///
135     /// This is true even for a builder using the `owned` pattern if there is a field whose setter
136     /// uses a different pattern.
137     pub must_derive_clone: bool,
138     /// Doc-comment of the builder struct.
139     pub doc_comment: Option<syn::Attribute>,
140     /// Emit deprecation notes to the user.
141     pub deprecation_notes: DeprecationNotes,
142     /// Whether or not a libstd is used.
143     pub std: bool,
144 }
145 
146 impl<'a> ToTokens for Builder<'a> {
to_tokens(&self, tokens: &mut TokenStream)147     fn to_tokens(&self, tokens: &mut TokenStream) {
148         if self.enabled {
149             let builder_vis = &self.visibility;
150             let builder_ident = &self.ident;
151             let bounded_generics = self.compute_impl_bounds();
152             let (impl_generics, _, _) = bounded_generics.split_for_impl();
153             let (struct_generics, ty_generics, where_clause) = self
154                 .generics
155                 .map(syn::Generics::split_for_impl)
156                 .map(|(i, t, w)| (Some(i), Some(t), Some(w)))
157                 .unwrap_or((None, None, None));
158             let builder_fields = &self.fields;
159             let builder_field_initializers = &self.field_initializers;
160             let functions = &self.functions;
161 
162             // Create the comma-separated set of derived traits for the builder
163             let derive_attr = {
164                 let clone_trait: Path = parse_quote!(Clone);
165 
166                 let mut traits: Punctuated<&Path, Token![,]> = Default::default();
167                 if self.must_derive_clone {
168                     traits.push(&clone_trait);
169                 }
170                 traits.extend(self.derives);
171 
172                 if traits.is_empty() {
173                     quote!()
174                 } else {
175                     quote!(#[derive(#traits)])
176                 }
177             };
178 
179             let builder_doc_comment = &self.doc_comment;
180             let deprecation_notes = &self.deprecation_notes.as_item();
181 
182             #[cfg(not(feature = "clippy"))]
183             tokens.append_all(quote!(#[allow(clippy::all)]));
184 
185             tokens.append_all(quote!(
186                 #derive_attr
187                 #builder_doc_comment
188                 #builder_vis struct #builder_ident #struct_generics #where_clause {
189                     #(#builder_fields)*
190                 }
191             ));
192 
193             #[cfg(not(feature = "clippy"))]
194             tokens.append_all(quote!(#[allow(clippy::all)]));
195 
196             tokens.append_all(quote!(
197                 #[allow(dead_code)]
198                 impl #impl_generics #builder_ident #ty_generics #where_clause {
199                     #(#functions)*
200                     #deprecation_notes
201                 }
202 
203                 impl #impl_generics ::derive_builder::export::core::default::Default for #builder_ident #ty_generics #where_clause {
204                     fn default() -> Self {
205                         Self {
206                             #(#builder_field_initializers)*
207                         }
208                     }
209                 }
210             ));
211 
212             if self.generate_error {
213                 let builder_error_ident = format_ident!("{}Error", builder_ident);
214                 let builder_error_doc = format!("Error type for {}", builder_ident);
215 
216                 tokens.append_all(quote!(
217                     #[doc=#builder_error_doc]
218                     #[derive(Debug)]
219                     #[non_exhaustive]
220                     #builder_vis enum #builder_error_ident {
221                         /// Uninitialized field
222                         UninitializedField(&'static str),
223                         /// Custom validation error
224                         ValidationError(::derive_builder::export::core::string::String),
225                     }
226 
227                     impl ::derive_builder::export::core::convert::From<::derive_builder::UninitializedFieldError> for #builder_error_ident {
228                         fn from(s: ::derive_builder::UninitializedFieldError) -> Self {
229                             Self::UninitializedField(s.field_name())
230                         }
231                     }
232 
233                     impl ::derive_builder::export::core::convert::From<::derive_builder::export::core::string::String> for #builder_error_ident {
234                         fn from(s: ::derive_builder::export::core::string::String) -> Self {
235                             Self::ValidationError(s)
236                         }
237                     }
238 
239                     impl ::derive_builder::export::core::fmt::Display for #builder_error_ident {
240                         fn fmt(&self, f: &mut ::derive_builder::export::core::fmt::Formatter) -> ::derive_builder::export::core::fmt::Result {
241                             match self {
242                                 Self::UninitializedField(ref field) => write!(f, "`{}` must be initialized", field),
243                                 Self::ValidationError(ref error) => write!(f, "{}", error),
244                             }
245                         }
246                     }
247                 ));
248 
249                 if self.std {
250                     tokens.append_all(quote!(
251                         impl std::error::Error for #builder_error_ident {}
252                     ));
253                 }
254             }
255         }
256     }
257 }
258 
259 impl<'a> Builder<'a> {
260     /// Set a doc-comment for this item.
doc_comment(&mut self, s: String) -> &mut Self261     pub fn doc_comment(&mut self, s: String) -> &mut Self {
262         self.doc_comment = Some(doc_comment_from(s));
263         self
264     }
265 
266     /// Add a field to the builder
push_field(&mut self, f: BuilderField) -> &mut Self267     pub fn push_field(&mut self, f: BuilderField) -> &mut Self {
268         self.fields.push(quote!(#f));
269         self.field_initializers.push(f.default_initializer_tokens());
270         self
271     }
272 
273     /// Add a setter function to the builder
push_setter_fn(&mut self, f: Setter) -> &mut Self274     pub fn push_setter_fn(&mut self, f: Setter) -> &mut Self {
275         self.functions.push(quote!(#f));
276         self
277     }
278 
279     /// Add final build function to the builder
push_build_fn(&mut self, f: BuildMethod) -> &mut Self280     pub fn push_build_fn(&mut self, f: BuildMethod) -> &mut Self {
281         self.functions.push(quote!(#f));
282         self
283     }
284 
285     /// Add `Clone` trait bound to generic types for non-owned builders.
286     /// This enables target types to declare generics without requiring a
287     /// `Clone` impl. This is the same as how the built-in derives for
288     /// `Clone`, `Default`, `PartialEq`, and other traits work.
compute_impl_bounds(&self) -> syn::Generics289     fn compute_impl_bounds(&self) -> syn::Generics {
290         if let Some(type_gen) = self.generics {
291             let mut generics = type_gen.clone();
292 
293             if !self.pattern.requires_clone() || type_gen.type_params().next().is_none() {
294                 return generics;
295             }
296 
297             let clone_bound = TypeParamBound::Trait(TraitBound {
298                 paren_token: None,
299                 modifier: TraitBoundModifier::None,
300                 lifetimes: None,
301                 path: syn::parse_str("::derive_builder::export::core::clone::Clone").unwrap(),
302             });
303 
304             for typ in generics.type_params_mut() {
305                 typ.bounds.push(clone_bound.clone());
306             }
307 
308             generics
309         } else {
310             Default::default()
311         }
312     }
313 }
314 
315 /// Helper macro for unit tests. This is _only_ public in order to be accessible
316 /// from doc-tests too.
317 #[doc(hidden)]
318 #[macro_export]
319 macro_rules! default_builder {
320     () => {
321         Builder {
322             enabled: true,
323             ident: syn::Ident::new("FooBuilder", ::proc_macro2::Span::call_site()),
324             pattern: Default::default(),
325             derives: &vec![],
326             generics: None,
327             visibility: syn::parse_str("pub").unwrap(),
328             fields: vec![quote!(foo: u32,)],
329             field_initializers: vec![quote!(foo: ::derive_builder::export::core::default::Default::default(), )],
330             functions: vec![quote!(fn bar() -> { unimplemented!() })],
331             generate_error: true,
332             must_derive_clone: true,
333             doc_comment: None,
334             deprecation_notes: DeprecationNotes::default(),
335             std: true,
336         }
337     };
338 }
339 
340 #[cfg(test)]
341 mod tests {
342     #[allow(unused_imports)]
343     use super::*;
344     use proc_macro2::TokenStream;
345 
add_generated_error(result: &mut TokenStream)346     fn add_generated_error(result: &mut TokenStream) {
347         result.append_all(quote!(
348             #[doc="Error type for FooBuilder"]
349             #[derive(Debug)]
350             #[non_exhaustive]
351             pub enum FooBuilderError {
352                 /// Uninitialized field
353                 UninitializedField(&'static str),
354                 /// Custom validation error
355                 ValidationError(::derive_builder::export::core::string::String),
356             }
357 
358             impl ::derive_builder::export::core::convert::From<::derive_builder::UninitializedFieldError> for FooBuilderError {
359                 fn from(s: ::derive_builder::UninitializedFieldError) -> Self {
360                     Self::UninitializedField(s.field_name())
361                 }
362             }
363 
364             impl ::derive_builder::export::core::convert::From<::derive_builder::export::core::string::String> for FooBuilderError {
365                 fn from(s: ::derive_builder::export::core::string::String) -> Self {
366                     Self::ValidationError(s)
367                 }
368             }
369 
370             impl ::derive_builder::export::core::fmt::Display for FooBuilderError {
371                 fn fmt(&self, f: &mut ::derive_builder::export::core::fmt::Formatter) -> ::derive_builder::export::core::fmt::Result {
372                     match self {
373                         Self::UninitializedField(ref field) => write!(f, "`{}` must be initialized", field),
374                         Self::ValidationError(ref error) => write!(f, "{}", error),
375                     }
376                 }
377             }
378 
379             impl std::error::Error for FooBuilderError {}
380         ));
381     }
382 
383     #[test]
simple()384     fn simple() {
385         let builder = default_builder!();
386 
387         assert_eq!(
388             quote!(#builder).to_string(),
389             {
390                 let mut result = quote!();
391 
392                 #[cfg(not(feature = "clippy"))]
393                 result.append_all(quote!(#[allow(clippy::all)]));
394 
395                 result.append_all(quote!(
396                     #[derive(Clone)]
397                     pub struct FooBuilder {
398                         foo: u32,
399                     }
400                 ));
401 
402                 #[cfg(not(feature = "clippy"))]
403                 result.append_all(quote!(#[allow(clippy::all)]));
404 
405                 result.append_all(quote!(
406                     #[allow(dead_code)]
407                     impl FooBuilder {
408                         fn bar () -> {
409                             unimplemented!()
410                         }
411                     }
412 
413                     impl ::derive_builder::export::core::default::Default for FooBuilder {
414                         fn default() -> Self {
415                             Self {
416                                 foo: ::derive_builder::export::core::default::Default::default(),
417                             }
418                         }
419                     }
420                 ));
421 
422                 add_generated_error(&mut result);
423 
424                 result
425             }
426             .to_string()
427         );
428     }
429 
430     // This test depends on the exact formatting of the `stringify`'d code,
431     // so we don't automatically format the test
432     #[rustfmt::skip]
433     #[test]
generic()434     fn generic() {
435         let ast: syn::DeriveInput = syn::parse_str(stringify!(
436             struct Lorem<'a, T: Debug> where T: PartialEq { }
437         )).expect("Couldn't parse item");
438         let generics = ast.generics;
439         let mut builder = default_builder!();
440         builder.generics = Some(&generics);
441 
442         assert_eq!(
443             quote!(#builder).to_string(),
444             {
445                 let mut result = quote!();
446 
447                 #[cfg(not(feature = "clippy"))]
448                 result.append_all(quote!(#[allow(clippy::all)]));
449 
450                 result.append_all(quote!(
451                     #[derive(Clone)]
452                     pub struct FooBuilder<'a, T: Debug> where T: PartialEq {
453                         foo: u32,
454                     }
455                 ));
456 
457                 #[cfg(not(feature = "clippy"))]
458                 result.append_all(quote!(#[allow(clippy::all)]));
459 
460                 result.append_all(quote!(
461                     #[allow(dead_code)]
462                     impl<'a, T: Debug + ::derive_builder::export::core::clone::Clone> FooBuilder<'a, T> where T: PartialEq {
463                         fn bar() -> {
464                             unimplemented!()
465                         }
466                     }
467 
468                     impl<'a, T: Debug + ::derive_builder::export::core::clone::Clone> ::derive_builder::export::core::default::Default for FooBuilder<'a, T> where T: PartialEq {
469                         fn default() -> Self {
470                             Self {
471                                 foo: ::derive_builder::export::core::default::Default::default(),
472                             }
473                         }
474                     }
475                 ));
476 
477                 add_generated_error(&mut result);
478 
479                 result
480             }.to_string()
481         );
482     }
483 
484     // This test depends on the exact formatting of the `stringify`'d code,
485     // so we don't automatically format the test
486     #[rustfmt::skip]
487     #[test]
generic_reference()488     fn generic_reference() {
489         let ast: syn::DeriveInput = syn::parse_str(stringify!(
490             struct Lorem<'a, T: 'a + Default> where T: PartialEq{ }
491         )).expect("Couldn't parse item");
492 
493         let generics = ast.generics;
494         let mut builder = default_builder!();
495         builder.generics = Some(&generics);
496 
497         assert_eq!(
498             quote!(#builder).to_string(),
499             {
500                 let mut result = quote!();
501 
502                 #[cfg(not(feature = "clippy"))]
503                 result.append_all(quote!(#[allow(clippy::all)]));
504 
505                 result.append_all(quote!(
506                     #[derive(Clone)]
507                     pub struct FooBuilder<'a, T: 'a + Default> where T: PartialEq {
508                         foo: u32,
509                     }
510                 ));
511 
512                 #[cfg(not(feature = "clippy"))]
513                 result.append_all(quote!(#[allow(clippy::all)]));
514 
515                 result.append_all(quote!(
516                     #[allow(dead_code)]
517                     impl<'a, T: 'a + Default + ::derive_builder::export::core::clone::Clone> FooBuilder<'a, T>
518                     where
519                         T: PartialEq
520                     {
521                         fn bar() -> {
522                             unimplemented!()
523                         }
524                     }
525 
526                     impl<'a, T: 'a + Default + ::derive_builder::export::core::clone::Clone> ::derive_builder::export::core::default::Default for FooBuilder<'a, T> where T: PartialEq {
527                         fn default() -> Self {
528                             Self {
529                                 foo: ::derive_builder::export::core::default::Default::default(),
530                             }
531                         }
532                     }
533                 ));
534 
535                 add_generated_error(&mut result);
536 
537                 result
538             }.to_string()
539         );
540     }
541 
542     // This test depends on the exact formatting of the `stringify`'d code,
543     // so we don't automatically format the test
544     #[rustfmt::skip]
545     #[test]
owned_generic()546     fn owned_generic() {
547         let ast: syn::DeriveInput = syn::parse_str(stringify!(
548             struct Lorem<'a, T: Debug> where T: PartialEq { }
549         )).expect("Couldn't parse item");
550         let generics = ast.generics;
551         let mut builder = default_builder!();
552         builder.generics = Some(&generics);
553         builder.pattern = BuilderPattern::Owned;
554         builder.must_derive_clone = false;
555 
556         assert_eq!(
557             quote!(#builder).to_string(),
558             {
559                 let mut result = quote!();
560 
561                 #[cfg(not(feature = "clippy"))]
562                 result.append_all(quote!(#[allow(clippy::all)]));
563 
564                 result.append_all(quote!(
565                     pub struct FooBuilder<'a, T: Debug> where T: PartialEq {
566                         foo: u32,
567                     }
568                 ));
569 
570                 #[cfg(not(feature = "clippy"))]
571                 result.append_all(quote!(#[allow(clippy::all)]));
572 
573                 result.append_all(quote!(
574                     #[allow(dead_code)]
575                     impl<'a, T: Debug> FooBuilder<'a, T> where T: PartialEq {
576                         fn bar() -> {
577                             unimplemented!()
578                         }
579                     }
580 
581                     impl<'a, T: Debug> ::derive_builder::export::core::default::Default for FooBuilder<'a, T>
582                     where T: PartialEq {
583                         fn default() -> Self {
584                             Self {
585                                 foo: ::derive_builder::export::core::default::Default::default(),
586                             }
587                         }
588                     }
589                 ));
590 
591                 add_generated_error(&mut result);
592 
593                 result
594             }.to_string()
595         );
596     }
597 
598     #[test]
disabled()599     fn disabled() {
600         let mut builder = default_builder!();
601         builder.enabled = false;
602 
603         assert_eq!(quote!(#builder).to_string(), quote!().to_string());
604     }
605 
606     #[test]
add_derives()607     fn add_derives() {
608         let derives = vec![syn::parse_str("Serialize").unwrap()];
609         let mut builder = default_builder!();
610         builder.derives = &derives;
611 
612         assert_eq!(
613             quote!(#builder).to_string(),
614             {
615                 let mut result = quote!();
616 
617                 #[cfg(not(feature = "clippy"))]
618                 result.append_all(quote!(#[allow(clippy::all)]));
619 
620                 result.append_all(quote!(
621                     #[derive(Clone, Serialize)]
622                     pub struct FooBuilder {
623                         foo: u32,
624                     }
625                 ));
626 
627                 #[cfg(not(feature = "clippy"))]
628                 result.append_all(quote!(#[allow(clippy::all)]));
629 
630                 result.append_all(quote!(
631                     #[allow(dead_code)]
632                     impl FooBuilder {
633                         fn bar () -> {
634                             unimplemented!()
635                         }
636                     }
637 
638                     impl ::derive_builder::export::core::default::Default for FooBuilder {
639                         fn default() -> Self {
640                             Self {
641                                 foo: ::derive_builder::export::core::default::Default::default(),
642                             }
643                         }
644                     }
645                 ));
646 
647                 add_generated_error(&mut result);
648 
649                 result
650             }
651             .to_string()
652         );
653     }
654 }
655