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