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