diff --git a/derive_builder/src/options/darling_opts.rs b/derive_builder/src/options/darling_opts.rs index d83aa7b3..54fc84f1 100644 --- a/derive_builder/src/options/darling_opts.rs +++ b/derive_builder/src/options/darling_opts.rs @@ -116,13 +116,25 @@ pub struct FieldLevelSetter { into: Option, strip_option: Option, skip: Option, + custom: Option, } impl FieldLevelSetter { - /// Get whether or not this field-level setter indicates a setter should - /// be emitted. The setter shorthand rules are that the presence of a - /// `setter` with _any_ properties set forces the setter to be emitted. - pub fn enabled(&self) -> Option { + /// Get whether the setter should be emitted. The rules are the same as + /// for `field_enabled`, except we only skip the setter if `setter(custom)` is present. + pub fn setter_enabled(&self) -> Option { + if self.custom.is_some() { + return self.custom.map(|x| !x); + } + + self.field_enabled() + } + + /// Get whether or not this field-level setter indicates a setter and + /// field should be emitted. The setter shorthand rules are that the + /// presence of a `setter` with _any_ properties set forces the setter + /// to be emitted. + pub fn field_enabled(&self) -> Option { if self.skip.is_some() { return self.skip.map(|x| !x); } @@ -404,10 +416,18 @@ pub struct FieldWithDefaults<'a> { /// parent struct's configuration. impl<'a> FieldWithDefaults<'a> { /// Check if this field should emit a setter. - pub fn enabled(&self) -> bool { + pub fn setter_enabled(&self) -> bool { + self.field + .setter + .setter_enabled() + .or(self.parent.setter.enabled()) + .unwrap_or(true) + } + + pub fn field_enabled(&self) -> bool { self.field .setter - .enabled() + .field_enabled() .or(self.parent.setter.enabled()) .unwrap_or(true) } @@ -510,7 +530,7 @@ impl<'a> FieldWithDefaults<'a> { /// Returns a `Setter` according to the options. pub fn as_setter(&'a self) -> Setter<'a> { Setter { - enabled: self.enabled(), + setter_enabled: self.setter_enabled(), try_setter: self.try_setter(), visibility: self.setter_vis(), pattern: self.pattern(), @@ -532,7 +552,7 @@ impl<'a> FieldWithDefaults<'a> { /// if `default_expression` can not be parsed as `Block`. pub fn as_initializer(&'a self) -> Initializer<'a> { Initializer { - setter_enabled: self.enabled(), + field_enabled: self.field_enabled(), field_ident: self.field_ident(), builder_pattern: self.pattern(), default_value: self @@ -549,7 +569,7 @@ impl<'a> FieldWithDefaults<'a> { BuilderField { field_ident: self.field_ident(), field_type: &self.field.ty, - setter_enabled: self.enabled(), + field_enabled: self.field_enabled(), field_visibility: self.field_vis(), attrs: &self.field.attrs, bindings: self.bindings(), diff --git a/derive_builder/tests/setter_custom.rs b/derive_builder/tests/setter_custom.rs new file mode 100644 index 00000000..08cc2a95 --- /dev/null +++ b/derive_builder/tests/setter_custom.rs @@ -0,0 +1,88 @@ +#[macro_use] +extern crate pretty_assertions; +#[macro_use] +extern crate derive_builder; + +#[derive(Debug, PartialEq, Default, Builder, Clone)] +#[builder(setter(skip = "false"), default)] +struct SetterCustom { + #[builder(setter(custom = "true"))] + setter_custom_by_explicit_opt_in: u32, + #[builder(setter(custom))] + setter_custom_shorthand: u32, + #[builder(setter(custom = "false"))] + setter_custom_by_explicit_opt_out: u32, + #[builder(setter(custom = "true"), default="4")] + setter_custom_with_explicit_default: u32, + #[builder(setter(custom = "true", strip_option))] + setter_custom_with_strip_option: Option, +} + +// compile test +#[allow(dead_code)] +impl SetterCustomBuilder { + // only possible if setter was skipped + fn setter_custom_by_explicit_opt_in(&mut self) -> &mut Self { + self.setter_custom_by_explicit_opt_in = Some(1); + self + } + + // only possible if setter was skipped + fn setter_custom_shorthand(&mut self) -> &mut Self { + self.setter_custom_shorthand = Some(2); + self + } + + // only possible if setter was skipped + fn setter_custom_with_explicit_default(&mut self) -> &mut Self { + self.setter_custom_with_explicit_default = Some(43); + self + } + + // only possible if setter was skipped + fn setter_custom_with_strip_option(&mut self) -> &mut Self { + self.setter_custom_with_strip_option = Some(Some(6)); + self + } +} + +#[test] +fn setter_custom_defaults() { + let x: SetterCustom = SetterCustomBuilder::default() + .build() + .unwrap(); + + assert_eq!( + x, + SetterCustom { + setter_custom_by_explicit_opt_in: 0, + setter_custom_shorthand: 0, + setter_custom_by_explicit_opt_out: 0, + setter_custom_with_explicit_default: 4, + setter_custom_with_strip_option: None, + } + ); +} + +#[test] +fn setter_custom_setters_called() { + let x: SetterCustom = SetterCustomBuilder::default() + .setter_custom_by_explicit_opt_in() // set to 1 + .setter_custom_shorthand() // set to 2 + .setter_custom_by_explicit_opt_out(42) + .setter_custom_with_explicit_default() // set to 43 + .setter_custom_with_strip_option() // set to 6 + .build() + .unwrap(); + + assert_eq!( + x, + SetterCustom { + setter_custom_by_explicit_opt_in: 1, + setter_custom_shorthand: 2, + setter_custom_by_explicit_opt_out: 42, + setter_custom_with_explicit_default: 43, + setter_custom_with_strip_option: Some(6) + } + ); +} diff --git a/derive_builder_core/src/builder_field.rs b/derive_builder_core/src/builder_field.rs index 14a997ad..c512150a 100644 --- a/derive_builder_core/src/builder_field.rs +++ b/derive_builder_core/src/builder_field.rs @@ -41,7 +41,7 @@ pub struct BuilderField<'a> { /// Note: We will fallback to `PhantomData` if the setter is disabled /// to hack around issues with unused generic type parameters - at /// least for now. - pub setter_enabled: bool, + pub field_enabled: bool, /// Visibility of this builder field, e.g. `syn::Visibility::Public`. pub field_visibility: syn::Visibility, /// Attributes which will be attached to this builder field. @@ -52,7 +52,7 @@ pub struct BuilderField<'a> { impl<'a> ToTokens for BuilderField<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - if self.setter_enabled { + if self.field_enabled { trace!("Deriving builder field for `{}`.", self.field_ident); let vis = &self.field_visibility; let ident = self.field_ident; @@ -89,7 +89,7 @@ macro_rules! default_builder_field { BuilderField { field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()), field_type: &syn::parse_str("String").unwrap(), - setter_enabled: true, + field_enabled: true, field_visibility: syn::parse_str("pub").unwrap(), attrs: &[parse_quote!(#[some_attr])], bindings: Default::default(), @@ -118,7 +118,7 @@ mod tests { #[test] fn setter_disabled() { let mut field = default_builder_field!(); - field.setter_enabled = false; + field.field_enabled = false; assert_eq!( quote!(#field).to_string(), @@ -148,7 +148,7 @@ mod tests { fn no_std_setter_disabled() { let mut field = default_builder_field!(); field.bindings.no_std = true; - field.setter_enabled = false; + field.field_enabled = false; assert_eq!( quote!(#field).to_string(), diff --git a/derive_builder_core/src/initializer.rs b/derive_builder_core/src/initializer.rs index df256eb1..329c89ee 100644 --- a/derive_builder_core/src/initializer.rs +++ b/derive_builder_core/src/initializer.rs @@ -40,7 +40,7 @@ pub struct Initializer<'a> { /// Name of the target field. pub field_ident: &'a syn::Ident, /// Whether the builder implements a setter for this field. - pub setter_enabled: bool, + pub field_enabled: bool, /// How the build method takes and returns `self` (e.g. mutably). pub builder_pattern: BuilderPattern, /// Default value for the target field. @@ -59,7 +59,7 @@ impl<'a> ToTokens for Initializer<'a> { let struct_field = &self.field_ident; - if self.setter_enabled { + if self.field_enabled { let match_some = self.match_some(); let match_none = self.match_none(); let builder_field = &*struct_field; @@ -196,7 +196,7 @@ macro_rules! default_initializer { () => { Initializer { field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()), - setter_enabled: true, + field_enabled: true, builder_pattern: BuilderPattern::Mutable, default_value: None, use_default_struct: false, @@ -304,7 +304,7 @@ mod tests { #[test] fn setter_disabled() { let mut initializer = default_initializer!(); - initializer.setter_enabled = false; + initializer.field_enabled = false; assert_eq!( quote!(#initializer).to_string(), @@ -335,7 +335,7 @@ mod tests { fn no_std_setter_disabled() { let mut initializer = default_initializer!(); initializer.bindings.no_std = true; - initializer.setter_enabled = false; + initializer.field_enabled = false; assert_eq!( quote!(#initializer).to_string(), diff --git a/derive_builder_core/src/setter.rs b/derive_builder_core/src/setter.rs index f9e926bc..371481e4 100644 --- a/derive_builder_core/src/setter.rs +++ b/derive_builder_core/src/setter.rs @@ -39,7 +39,7 @@ use DeprecationNotes; #[derive(Debug, Clone)] pub struct Setter<'a> { /// Enables code generation for this setter fn. - pub enabled: bool, + pub setter_enabled: bool, /// Enables code generation for the `try_` variant of this setter fn. pub try_setter: bool, /// Visibility of the setter, e.g. `syn::Visibility::Public`. @@ -69,7 +69,7 @@ pub struct Setter<'a> { impl<'a> ToTokens for Setter<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - if self.enabled { + if self.setter_enabled { trace!("Deriving setter for `{}`.", self.field_ident); let field_type = self.field_type; let pattern = self.pattern; @@ -221,7 +221,7 @@ fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> { macro_rules! default_setter { () => { Setter { - enabled: true, + setter_enabled: true, try_setter: false, visibility: syn::parse_str("pub").unwrap(), pattern: BuilderPattern::Mutable, @@ -461,7 +461,7 @@ mod tests { #[test] fn setter_disabled() { let mut setter = default_setter!(); - setter.enabled = false; + setter.setter_enabled = false; assert_eq!(quote!(#setter).to_string(), quote!().to_string()); }