| 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::{Path, TraitBound, TraitBoundModifier, TypeParamBound}; |
| 7 | |
| 8 | use crate::{doc_comment_from, BuildMethod, BuilderField, BuilderPattern, Setter}; |
| 9 | |
| 10 | const ALLOC_NOT_ENABLED_ERROR: &str = r#"`alloc` is disabled within 'derive_builder', consider one of the following: |
| 11 | * enable feature `alloc` on 'dervie_builder' if a `global_allocator` is present |
| 12 | * use a custom error `#[builder(build_fn(error = "path::to::Error"))] |
| 13 | * disable the validation error `#[builder(build_fn(error(validation_error = false)))]"# ; |
| 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 to include `ValidationError` in the generated enum. Necessary to avoid dependency |
| 133 | /// on `alloc::string`. |
| 134 | /// |
| 135 | /// This would be `false` when `build_fn.error.as_validation_error() == Some((false, _))`. This |
| 136 | /// has no effect when `generate_error` is `false`. |
| 137 | pub generate_validation_error: bool, |
| 138 | /// Indicator of `cfg!(not(any(feature = "alloc", feature = "std")))`, as a field for tests |
| 139 | pub no_alloc: bool, |
| 140 | /// Whether this builder must derive `Clone`. |
| 141 | /// |
| 142 | /// This is true even for a builder using the `owned` pattern if there is a field whose setter |
| 143 | /// uses a different pattern. |
| 144 | pub must_derive_clone: bool, |
| 145 | /// Doc-comment of the builder struct. |
| 146 | pub doc_comment: Option<syn::Attribute>, |
| 147 | /// Whether or not a libstd is used. |
| 148 | pub std: bool, |
| 149 | } |
| 150 | |
| 151 | impl<'a> ToTokens for Builder<'a> { |
| 152 | fn to_tokens(&self, tokens: &mut TokenStream) { |
| 153 | if self.enabled { |
| 154 | let crate_root = self.crate_root; |
| 155 | let builder_vis = &self.visibility; |
| 156 | let builder_ident = &self.ident; |
| 157 | // Splitting because Generics doesn't output WhereClause, see dtolnay/syn#782 |
| 158 | let (struct_generics, struct_where_clause) = ( |
| 159 | self.generics, |
| 160 | self.generics.and_then(|g| g.where_clause.as_ref()), |
| 161 | ); |
| 162 | let bounded_generics = self.compute_impl_bounds(); |
| 163 | let (impl_generics, impl_ty_generics, impl_where_clause) = |
| 164 | bounded_generics.split_for_impl(); |
| 165 | let builder_fields = &self.fields; |
| 166 | let builder_field_initializers = &self.field_initializers; |
| 167 | let create_empty = &self.create_empty; |
| 168 | let functions = &self.functions; |
| 169 | |
| 170 | // Create the comma-separated set of derived traits for the builder |
| 171 | let derive_attr = { |
| 172 | let clone_trait: Path = parse_quote!(Clone); |
| 173 | |
| 174 | let mut traits: Punctuated<&Path, Token![,]> = Default::default(); |
| 175 | if self.must_derive_clone { |
| 176 | traits.push(&clone_trait); |
| 177 | } |
| 178 | traits.extend(self.derives); |
| 179 | |
| 180 | if traits.is_empty() { |
| 181 | quote!() |
| 182 | } else { |
| 183 | quote!(#[derive(#traits)]) |
| 184 | } |
| 185 | }; |
| 186 | |
| 187 | let struct_attrs = self.struct_attrs; |
| 188 | let impl_attrs = self.impl_attrs; |
| 189 | |
| 190 | let builder_doc_comment = &self.doc_comment; |
| 191 | |
| 192 | #[cfg (not(feature = "clippy" ))] |
| 193 | tokens.append_all(quote!(#[allow(clippy::all)])); |
| 194 | |
| 195 | // struct_attrs MUST come after derive_attr, otherwise attributes for a derived |
| 196 | // trait will appear before its derivation. As of rustc 1.59.0 this is a compiler |
| 197 | // warning; see https://github.com/rust-lang/rust/issues/79202 |
| 198 | tokens.append_all(quote!( |
| 199 | #derive_attr |
| 200 | #(#struct_attrs)* |
| 201 | #builder_doc_comment |
| 202 | #builder_vis struct #builder_ident #struct_generics #struct_where_clause { |
| 203 | #(#builder_fields)* |
| 204 | } |
| 205 | )); |
| 206 | |
| 207 | #[cfg (not(feature = "clippy" ))] |
| 208 | tokens.append_all(quote!(#[allow(clippy::all)])); |
| 209 | |
| 210 | tokens.append_all(quote!( |
| 211 | #(#impl_attrs)* |
| 212 | #[allow(dead_code)] |
| 213 | impl #impl_generics #builder_ident #impl_ty_generics #impl_where_clause { |
| 214 | #(#functions)* |
| 215 | |
| 216 | /// Create an empty builder, with all fields set to `None` or `PhantomData`. |
| 217 | fn #create_empty() -> Self { |
| 218 | Self { |
| 219 | #(#builder_field_initializers)* |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | )); |
| 224 | |
| 225 | if self.impl_default { |
| 226 | tokens.append_all(quote!( |
| 227 | impl #impl_generics #crate_root::export::core::default::Default for #builder_ident #impl_ty_generics #impl_where_clause { |
| 228 | fn default() -> Self { |
| 229 | Self::#create_empty() |
| 230 | } |
| 231 | } |
| 232 | )); |
| 233 | } |
| 234 | |
| 235 | if self.no_alloc && self.generate_error && self.generate_validation_error { |
| 236 | let err = syn::Error::new_spanned(&self.ident, ALLOC_NOT_ENABLED_ERROR); |
| 237 | tokens.append_all(err.to_compile_error()); |
| 238 | } else if self.generate_error { |
| 239 | let builder_error_ident = format_ident!(" {}Error" , builder_ident); |
| 240 | let builder_error_doc = format!("Error type for {}" , builder_ident); |
| 241 | |
| 242 | let validation_error = if self.generate_validation_error { |
| 243 | quote!( |
| 244 | /// Custom validation error |
| 245 | ValidationError(#crate_root::export::core::string::String), |
| 246 | ) |
| 247 | } else { |
| 248 | TokenStream::new() |
| 249 | }; |
| 250 | let validation_from = if self.generate_validation_error { |
| 251 | quote!( |
| 252 | impl #crate_root::export::core::convert::From<#crate_root::export::core::string::String> for #builder_error_ident { |
| 253 | fn from(s: #crate_root::export::core::string::String) -> Self { |
| 254 | Self::ValidationError(s) |
| 255 | } |
| 256 | } |
| 257 | ) |
| 258 | } else { |
| 259 | TokenStream::new() |
| 260 | }; |
| 261 | let validation_display = if self.generate_validation_error { |
| 262 | quote!( |
| 263 | Self::ValidationError(ref error) => write!(f, "{}" , error), |
| 264 | ) |
| 265 | } else { |
| 266 | TokenStream::new() |
| 267 | }; |
| 268 | |
| 269 | tokens.append_all(quote!( |
| 270 | #[doc=#builder_error_doc] |
| 271 | #[derive(Debug)] |
| 272 | #[non_exhaustive] |
| 273 | #builder_vis enum #builder_error_ident { |
| 274 | /// Uninitialized field |
| 275 | UninitializedField(&'static str), |
| 276 | #validation_error |
| 277 | } |
| 278 | |
| 279 | impl #crate_root::export::core::convert::From<#crate_root::UninitializedFieldError> for #builder_error_ident { |
| 280 | fn from(s: #crate_root::UninitializedFieldError) -> Self { |
| 281 | Self::UninitializedField(s.field_name()) |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | #validation_from |
| 286 | |
| 287 | impl #crate_root::export::core::fmt::Display for #builder_error_ident { |
| 288 | fn fmt(&self, f: &mut #crate_root::export::core::fmt::Formatter) -> #crate_root::export::core::fmt::Result { |
| 289 | match self { |
| 290 | Self::UninitializedField(ref field) => write!(f, "`{}` must be initialized" , field), |
| 291 | #validation_display |
| 292 | } |
| 293 | } |
| 294 | } |
| 295 | )); |
| 296 | |
| 297 | if self.std { |
| 298 | tokens.append_all(quote!( |
| 299 | impl std::error::Error for #builder_error_ident {} |
| 300 | )); |
| 301 | } |
| 302 | } |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | impl<'a> Builder<'a> { |
| 308 | /// Set a doc-comment for this item. |
| 309 | pub fn doc_comment(&mut self, s: String) -> &mut Self { |
| 310 | self.doc_comment = Some(doc_comment_from(s)); |
| 311 | self |
| 312 | } |
| 313 | |
| 314 | /// Add a field to the builder |
| 315 | pub fn push_field(&mut self, f: BuilderField) -> &mut Self { |
| 316 | self.fields.push(quote!(#f)); |
| 317 | self.field_initializers.push(f.default_initializer_tokens()); |
| 318 | self |
| 319 | } |
| 320 | |
| 321 | /// Add a setter function to the builder |
| 322 | pub fn push_setter_fn(&mut self, f: Setter) -> &mut Self { |
| 323 | self.functions.push(quote!(#f)); |
| 324 | self |
| 325 | } |
| 326 | |
| 327 | /// Add final build function to the builder |
| 328 | pub fn push_build_fn(&mut self, f: BuildMethod) -> &mut Self { |
| 329 | self.functions.push(quote!(#f)); |
| 330 | self |
| 331 | } |
| 332 | |
| 333 | /// Add `Clone` trait bound to generic types for non-owned builders. |
| 334 | /// This enables target types to declare generics without requiring a |
| 335 | /// `Clone` impl. This is the same as how the built-in derives for |
| 336 | /// `Clone`, `Default`, `PartialEq`, and other traits work. |
| 337 | fn compute_impl_bounds(&self) -> syn::Generics { |
| 338 | if let Some(type_gen) = self.generics { |
| 339 | let mut generics = type_gen.clone(); |
| 340 | |
| 341 | if !self.pattern.requires_clone() || type_gen.type_params().next().is_none() { |
| 342 | return generics; |
| 343 | } |
| 344 | |
| 345 | let crate_root = self.crate_root; |
| 346 | |
| 347 | let clone_bound = TypeParamBound::Trait(TraitBound { |
| 348 | paren_token: None, |
| 349 | modifier: TraitBoundModifier::None, |
| 350 | lifetimes: None, |
| 351 | path: syn::parse_quote!(#crate_root::export::core::clone::Clone), |
| 352 | }); |
| 353 | |
| 354 | for typ in generics.type_params_mut() { |
| 355 | typ.bounds.push(clone_bound.clone()); |
| 356 | } |
| 357 | |
| 358 | generics |
| 359 | } else { |
| 360 | Default::default() |
| 361 | } |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | /// Helper macro for unit tests. This is _only_ public in order to be accessible |
| 366 | /// from doc-tests too. |
| 367 | #[doc (hidden)] |
| 368 | #[macro_export ] |
| 369 | macro_rules! default_builder { |
| 370 | () => { |
| 371 | Builder { |
| 372 | // Deliberately don't use the default value here - make sure |
| 373 | // that all test cases are passing crate_root through properly. |
| 374 | crate_root: &parse_quote!(::db), |
| 375 | enabled: true, |
| 376 | ident: syn::Ident::new("FooBuilder" , ::proc_macro2::Span::call_site()), |
| 377 | pattern: Default::default(), |
| 378 | derives: &vec![], |
| 379 | struct_attrs: &vec![], |
| 380 | impl_attrs: &vec![], |
| 381 | impl_default: true, |
| 382 | create_empty: syn::Ident::new("create_empty" , ::proc_macro2::Span::call_site()), |
| 383 | generics: None, |
| 384 | visibility: ::std::borrow::Cow::Owned(parse_quote!(pub)), |
| 385 | fields: vec![quote!(foo: u32,)], |
| 386 | field_initializers: vec![quote!(foo: ::db::export::core::default::Default::default(), )], |
| 387 | functions: vec![quote!(fn bar() -> { unimplemented!() })], |
| 388 | generate_error: true, |
| 389 | generate_validation_error: true, |
| 390 | no_alloc: false, |
| 391 | must_derive_clone: true, |
| 392 | doc_comment: None, |
| 393 | std: true, |
| 394 | } |
| 395 | }; |
| 396 | } |
| 397 | |
| 398 | #[cfg (test)] |
| 399 | mod tests { |
| 400 | #[allow (unused_imports)] |
| 401 | use super::*; |
| 402 | use syn::Ident; |
| 403 | |
| 404 | fn add_simple_foo_builder(result: &mut TokenStream) { |
| 405 | #[cfg (not(feature = "clippy" ))] |
| 406 | result.append_all(quote!(#[allow(clippy::all)])); |
| 407 | |
| 408 | result.append_all(quote!( |
| 409 | #[derive(Clone)] |
| 410 | pub struct FooBuilder { |
| 411 | foo: u32, |
| 412 | } |
| 413 | )); |
| 414 | |
| 415 | #[cfg (not(feature = "clippy" ))] |
| 416 | result.append_all(quote!(#[allow(clippy::all)])); |
| 417 | |
| 418 | result.append_all(quote!( |
| 419 | #[allow(dead_code)] |
| 420 | impl FooBuilder { |
| 421 | fn bar () -> { |
| 422 | unimplemented!() |
| 423 | } |
| 424 | |
| 425 | /// Create an empty builder, with all fields set to `None` or `PhantomData`. |
| 426 | fn create_empty() -> Self { |
| 427 | Self { |
| 428 | foo: ::db::export::core::default::Default::default(), |
| 429 | } |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | impl ::db::export::core::default::Default for FooBuilder { |
| 434 | fn default() -> Self { |
| 435 | Self::create_empty() |
| 436 | } |
| 437 | } |
| 438 | )); |
| 439 | } |
| 440 | |
| 441 | fn add_generated_error(result: &mut TokenStream) { |
| 442 | result.append_all(quote!( |
| 443 | #[doc="Error type for FooBuilder" ] |
| 444 | #[derive(Debug)] |
| 445 | #[non_exhaustive] |
| 446 | pub enum FooBuilderError { |
| 447 | /// Uninitialized field |
| 448 | UninitializedField(&'static str), |
| 449 | /// Custom validation error |
| 450 | ValidationError(::db::export::core::string::String), |
| 451 | } |
| 452 | |
| 453 | impl ::db::export::core::convert::From<::db::UninitializedFieldError> for FooBuilderError { |
| 454 | fn from(s: ::db::UninitializedFieldError) -> Self { |
| 455 | Self::UninitializedField(s.field_name()) |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | impl ::db::export::core::convert::From<::db::export::core::string::String> for FooBuilderError { |
| 460 | fn from(s: ::db::export::core::string::String) -> Self { |
| 461 | Self::ValidationError(s) |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | impl ::db::export::core::fmt::Display for FooBuilderError { |
| 466 | fn fmt(&self, f: &mut ::db::export::core::fmt::Formatter) -> ::db::export::core::fmt::Result { |
| 467 | match self { |
| 468 | Self::UninitializedField(ref field) => write!(f, "`{}` must be initialized" , field), |
| 469 | Self::ValidationError(ref error) => write!(f, "{}" , error), |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | impl std::error::Error for FooBuilderError {} |
| 475 | )); |
| 476 | } |
| 477 | |
| 478 | #[test ] |
| 479 | fn simple() { |
| 480 | let builder = default_builder!(); |
| 481 | |
| 482 | assert_eq!( |
| 483 | quote!(#builder).to_string(), |
| 484 | { |
| 485 | let mut result = quote!(); |
| 486 | |
| 487 | add_simple_foo_builder(&mut result); |
| 488 | |
| 489 | add_generated_error(&mut result); |
| 490 | |
| 491 | result |
| 492 | } |
| 493 | .to_string() |
| 494 | ); |
| 495 | } |
| 496 | |
| 497 | #[test ] |
| 498 | fn rename_create_empty() { |
| 499 | let mut builder = default_builder!(); |
| 500 | builder.create_empty = Ident::new("empty" , proc_macro2::Span::call_site()); |
| 501 | |
| 502 | assert_eq!( |
| 503 | quote!(#builder).to_string(), |
| 504 | { |
| 505 | let mut result = quote!(); |
| 506 | |
| 507 | #[cfg(not(feature = "clippy" ))] |
| 508 | result.append_all(quote!(#[allow(clippy::all)])); |
| 509 | |
| 510 | result.append_all(quote!( |
| 511 | #[derive(Clone)] |
| 512 | pub struct FooBuilder { |
| 513 | foo: u32, |
| 514 | } |
| 515 | )); |
| 516 | |
| 517 | #[cfg(not(feature = "clippy" ))] |
| 518 | result.append_all(quote!(#[allow(clippy::all)])); |
| 519 | |
| 520 | result.append_all(quote!( |
| 521 | #[allow(dead_code)] |
| 522 | impl FooBuilder { |
| 523 | fn bar () -> { |
| 524 | unimplemented!() |
| 525 | } |
| 526 | |
| 527 | /// Create an empty builder, with all fields set to `None` or `PhantomData`. |
| 528 | fn empty() -> Self { |
| 529 | Self { |
| 530 | foo: ::db::export::core::default::Default::default(), |
| 531 | } |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | impl ::db::export::core::default::Default for FooBuilder { |
| 536 | fn default() -> Self { |
| 537 | Self::empty() |
| 538 | } |
| 539 | } |
| 540 | )); |
| 541 | |
| 542 | add_generated_error(&mut result); |
| 543 | |
| 544 | result |
| 545 | } |
| 546 | .to_string() |
| 547 | ); |
| 548 | } |
| 549 | |
| 550 | // This test depends on the exact formatting of the `stringify`'d code, |
| 551 | // so we don't automatically format the test |
| 552 | #[rustfmt::skip] |
| 553 | #[test ] |
| 554 | fn generic() { |
| 555 | let ast: syn::DeriveInput = parse_quote! { |
| 556 | struct Lorem<'a, T: Debug> where T: PartialEq { } |
| 557 | }; |
| 558 | let generics = ast.generics; |
| 559 | let mut builder = default_builder!(); |
| 560 | builder.generics = Some(&generics); |
| 561 | |
| 562 | assert_eq!( |
| 563 | quote!(#builder).to_string(), |
| 564 | { |
| 565 | let mut result = quote!(); |
| 566 | |
| 567 | #[cfg(not(feature = "clippy" ))] |
| 568 | result.append_all(quote!(#[allow(clippy::all)])); |
| 569 | |
| 570 | result.append_all(quote!( |
| 571 | #[derive(Clone)] |
| 572 | pub struct FooBuilder<'a, T: Debug> where T: PartialEq { |
| 573 | foo: u32, |
| 574 | } |
| 575 | )); |
| 576 | |
| 577 | #[cfg(not(feature = "clippy" ))] |
| 578 | result.append_all(quote!(#[allow(clippy::all)])); |
| 579 | |
| 580 | result.append_all(quote!( |
| 581 | #[allow(dead_code)] |
| 582 | impl<'a, T: Debug + ::db::export::core::clone::Clone> FooBuilder<'a, T> where T: PartialEq { |
| 583 | fn bar() -> { |
| 584 | unimplemented!() |
| 585 | } |
| 586 | |
| 587 | /// Create an empty builder, with all fields set to `None` or `PhantomData`. |
| 588 | fn create_empty() -> Self { |
| 589 | Self { |
| 590 | foo: ::db::export::core::default::Default::default(), |
| 591 | } |
| 592 | } |
| 593 | } |
| 594 | |
| 595 | impl<'a, T: Debug + ::db::export::core::clone::Clone> ::db::export::core::default::Default for FooBuilder<'a, T> where T: PartialEq { |
| 596 | fn default() -> Self { |
| 597 | Self::create_empty() |
| 598 | } |
| 599 | } |
| 600 | )); |
| 601 | |
| 602 | add_generated_error(&mut result); |
| 603 | |
| 604 | result |
| 605 | }.to_string() |
| 606 | ); |
| 607 | } |
| 608 | |
| 609 | // This test depends on the exact formatting of the `stringify`'d code, |
| 610 | // so we don't automatically format the test |
| 611 | #[rustfmt::skip] |
| 612 | #[test ] |
| 613 | fn generic_reference() { |
| 614 | let ast: syn::DeriveInput = parse_quote! { |
| 615 | struct Lorem<'a, T: 'a + Default> where T: PartialEq{ } |
| 616 | }; |
| 617 | |
| 618 | let generics = ast.generics; |
| 619 | let mut builder = default_builder!(); |
| 620 | builder.generics = Some(&generics); |
| 621 | |
| 622 | assert_eq!( |
| 623 | quote!(#builder).to_string(), |
| 624 | { |
| 625 | let mut result = quote!(); |
| 626 | |
| 627 | #[cfg(not(feature = "clippy" ))] |
| 628 | result.append_all(quote!(#[allow(clippy::all)])); |
| 629 | |
| 630 | result.append_all(quote!( |
| 631 | #[derive(Clone)] |
| 632 | pub struct FooBuilder<'a, T: 'a + Default> where T: PartialEq { |
| 633 | foo: u32, |
| 634 | } |
| 635 | )); |
| 636 | |
| 637 | #[cfg(not(feature = "clippy" ))] |
| 638 | result.append_all(quote!(#[allow(clippy::all)])); |
| 639 | |
| 640 | result.append_all(quote!( |
| 641 | #[allow(dead_code)] |
| 642 | impl<'a, T: 'a + Default + ::db::export::core::clone::Clone> FooBuilder<'a, T> |
| 643 | where |
| 644 | T: PartialEq |
| 645 | { |
| 646 | fn bar() -> { |
| 647 | unimplemented!() |
| 648 | } |
| 649 | |
| 650 | /// Create an empty builder, with all fields set to `None` or `PhantomData`. |
| 651 | fn create_empty() -> Self { |
| 652 | Self { |
| 653 | foo: ::db::export::core::default::Default::default(), |
| 654 | } |
| 655 | } |
| 656 | } |
| 657 | |
| 658 | impl<'a, T: 'a + Default + ::db::export::core::clone::Clone> ::db::export::core::default::Default for FooBuilder<'a, T> where T: PartialEq { |
| 659 | fn default() -> Self { |
| 660 | Self::create_empty() |
| 661 | } |
| 662 | } |
| 663 | )); |
| 664 | |
| 665 | add_generated_error(&mut result); |
| 666 | |
| 667 | result |
| 668 | }.to_string() |
| 669 | ); |
| 670 | } |
| 671 | |
| 672 | // This test depends on the exact formatting of the `stringify`'d code, |
| 673 | // so we don't automatically format the test |
| 674 | #[rustfmt::skip] |
| 675 | #[test ] |
| 676 | fn generic_with_default_type() { |
| 677 | let ast: syn::DeriveInput = parse_quote! { |
| 678 | struct Lorem<T = ()> { } |
| 679 | }; |
| 680 | |
| 681 | let generics = ast.generics; |
| 682 | let mut builder = default_builder!(); |
| 683 | builder.generics = Some(&generics); |
| 684 | |
| 685 | assert_eq!( |
| 686 | quote!(#builder).to_string(), |
| 687 | { |
| 688 | let mut result = quote!(); |
| 689 | |
| 690 | #[cfg(not(feature = "clippy" ))] |
| 691 | result.append_all(quote!(#[allow(clippy::all)])); |
| 692 | |
| 693 | result.append_all(quote!( |
| 694 | #[derive(Clone)] |
| 695 | pub struct FooBuilder<T = ()> { |
| 696 | foo: u32, |
| 697 | } |
| 698 | )); |
| 699 | |
| 700 | #[cfg(not(feature = "clippy" ))] |
| 701 | result.append_all(quote!(#[allow(clippy::all)])); |
| 702 | |
| 703 | result.append_all(quote!( |
| 704 | #[allow(dead_code)] |
| 705 | impl<T: ::db::export::core::clone::Clone> FooBuilder<T> |
| 706 | { |
| 707 | fn bar() -> { |
| 708 | unimplemented!() |
| 709 | } |
| 710 | |
| 711 | /// Create an empty builder, with all fields set to `None` or `PhantomData`. |
| 712 | fn create_empty() -> Self { |
| 713 | Self { |
| 714 | foo: ::db::export::core::default::Default::default(), |
| 715 | } |
| 716 | } |
| 717 | } |
| 718 | |
| 719 | impl<T: ::db::export::core::clone::Clone> ::db::export::core::default::Default for FooBuilder<T> { |
| 720 | fn default() -> Self { |
| 721 | Self::create_empty() |
| 722 | } |
| 723 | } |
| 724 | )); |
| 725 | |
| 726 | add_generated_error(&mut result); |
| 727 | |
| 728 | result |
| 729 | }.to_string() |
| 730 | ); |
| 731 | } |
| 732 | |
| 733 | // This test depends on the exact formatting of the `stringify`'d code, |
| 734 | // so we don't automatically format the test |
| 735 | #[rustfmt::skip] |
| 736 | #[test ] |
| 737 | fn owned_generic() { |
| 738 | let ast: syn::DeriveInput = parse_quote! { |
| 739 | struct Lorem<'a, T: Debug> where T: PartialEq { } |
| 740 | }; |
| 741 | let generics = ast.generics; |
| 742 | let mut builder = default_builder!(); |
| 743 | builder.generics = Some(&generics); |
| 744 | builder.pattern = BuilderPattern::Owned; |
| 745 | builder.must_derive_clone = false; |
| 746 | |
| 747 | assert_eq!( |
| 748 | quote!(#builder).to_string(), |
| 749 | { |
| 750 | let mut result = quote!(); |
| 751 | |
| 752 | #[cfg(not(feature = "clippy" ))] |
| 753 | result.append_all(quote!(#[allow(clippy::all)])); |
| 754 | |
| 755 | result.append_all(quote!( |
| 756 | pub struct FooBuilder<'a, T: Debug> where T: PartialEq { |
| 757 | foo: u32, |
| 758 | } |
| 759 | )); |
| 760 | |
| 761 | #[cfg(not(feature = "clippy" ))] |
| 762 | result.append_all(quote!(#[allow(clippy::all)])); |
| 763 | |
| 764 | result.append_all(quote!( |
| 765 | #[allow(dead_code)] |
| 766 | impl<'a, T: Debug> FooBuilder<'a, T> where T: PartialEq { |
| 767 | fn bar() -> { |
| 768 | unimplemented!() |
| 769 | } |
| 770 | |
| 771 | /// Create an empty builder, with all fields set to `None` or `PhantomData`. |
| 772 | fn create_empty() -> Self { |
| 773 | Self { |
| 774 | foo: ::db::export::core::default::Default::default(), |
| 775 | } |
| 776 | } |
| 777 | } |
| 778 | |
| 779 | impl<'a, T: Debug> ::db::export::core::default::Default for FooBuilder<'a, T> |
| 780 | where T: PartialEq { |
| 781 | fn default() -> Self { |
| 782 | Self::create_empty() |
| 783 | } |
| 784 | } |
| 785 | )); |
| 786 | |
| 787 | add_generated_error(&mut result); |
| 788 | |
| 789 | result |
| 790 | }.to_string() |
| 791 | ); |
| 792 | } |
| 793 | |
| 794 | #[test ] |
| 795 | fn disabled() { |
| 796 | let mut builder = default_builder!(); |
| 797 | builder.enabled = false; |
| 798 | |
| 799 | assert_eq!(quote!(#builder).to_string(), quote!().to_string()); |
| 800 | } |
| 801 | |
| 802 | #[test ] |
| 803 | fn add_derives() { |
| 804 | let derives = vec![parse_quote!(Serialize)]; |
| 805 | let mut builder = default_builder!(); |
| 806 | builder.derives = &derives; |
| 807 | |
| 808 | assert_eq!( |
| 809 | quote!(#builder).to_string(), |
| 810 | { |
| 811 | let mut result = quote!(); |
| 812 | |
| 813 | #[cfg(not(feature = "clippy" ))] |
| 814 | result.append_all(quote!(#[allow(clippy::all)])); |
| 815 | |
| 816 | result.append_all(quote!( |
| 817 | #[derive(Clone, Serialize)] |
| 818 | pub struct FooBuilder { |
| 819 | foo: u32, |
| 820 | } |
| 821 | )); |
| 822 | |
| 823 | #[cfg(not(feature = "clippy" ))] |
| 824 | result.append_all(quote!(#[allow(clippy::all)])); |
| 825 | |
| 826 | result.append_all(quote!( |
| 827 | #[allow(dead_code)] |
| 828 | impl FooBuilder { |
| 829 | fn bar () -> { |
| 830 | unimplemented!() |
| 831 | } |
| 832 | |
| 833 | /// Create an empty builder, with all fields set to `None` or `PhantomData`. |
| 834 | fn create_empty() -> Self { |
| 835 | Self { |
| 836 | foo: ::db::export::core::default::Default::default(), |
| 837 | } |
| 838 | } |
| 839 | } |
| 840 | |
| 841 | impl ::db::export::core::default::Default for FooBuilder { |
| 842 | fn default() -> Self { |
| 843 | Self::create_empty() |
| 844 | } |
| 845 | } |
| 846 | )); |
| 847 | |
| 848 | add_generated_error(&mut result); |
| 849 | |
| 850 | result |
| 851 | } |
| 852 | .to_string() |
| 853 | ); |
| 854 | } |
| 855 | |
| 856 | #[test ] |
| 857 | fn no_validation_error() { |
| 858 | let mut builder = default_builder!(); |
| 859 | builder.generate_validation_error = false; |
| 860 | |
| 861 | assert_eq!( |
| 862 | quote!(#builder).to_string(), |
| 863 | { |
| 864 | let mut result = quote!(); |
| 865 | |
| 866 | add_simple_foo_builder(&mut result); |
| 867 | |
| 868 | result.append_all(quote!( |
| 869 | #[doc="Error type for FooBuilder" ] |
| 870 | #[derive(Debug)] |
| 871 | #[non_exhaustive] |
| 872 | pub enum FooBuilderError { |
| 873 | /// Uninitialized field |
| 874 | UninitializedField(&'static str), |
| 875 | } |
| 876 | |
| 877 | impl ::db::export::core::convert::From<::db::UninitializedFieldError> for FooBuilderError { |
| 878 | fn from(s: ::db::UninitializedFieldError) -> Self { |
| 879 | Self::UninitializedField(s.field_name()) |
| 880 | } |
| 881 | } |
| 882 | |
| 883 | impl ::db::export::core::fmt::Display for FooBuilderError { |
| 884 | fn fmt(&self, f: &mut ::db::export::core::fmt::Formatter) -> ::db::export::core::fmt::Result { |
| 885 | match self { |
| 886 | Self::UninitializedField(ref field) => write!(f, "`{}` must be initialized" , field), |
| 887 | } |
| 888 | } |
| 889 | } |
| 890 | |
| 891 | impl std::error::Error for FooBuilderError {} |
| 892 | )); |
| 893 | |
| 894 | result |
| 895 | } |
| 896 | .to_string() |
| 897 | ); |
| 898 | } |
| 899 | |
| 900 | #[test ] |
| 901 | fn no_alloc_bug_using_string() { |
| 902 | let mut builder = default_builder!(); |
| 903 | builder.no_alloc = true; |
| 904 | |
| 905 | assert_eq!( |
| 906 | quote!(#builder).to_string(), |
| 907 | { |
| 908 | let mut result = quote!(); |
| 909 | |
| 910 | add_simple_foo_builder(&mut result); |
| 911 | |
| 912 | result.append_all(quote!(::core::compile_error! { #ALLOC_NOT_ENABLED_ERROR })); |
| 913 | |
| 914 | result |
| 915 | } |
| 916 | .to_string() |
| 917 | ); |
| 918 | } |
| 919 | } |
| 920 | |