Skip to content

Commit

Permalink
setter(custom) which allows a custom setter implementation (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
Janosch Gräf authored and TedDriggs committed Nov 7, 2019
1 parent 62976a6 commit b4d8e69
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 23 deletions.
38 changes: 29 additions & 9 deletions derive_builder/src/options/darling_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,25 @@ pub struct FieldLevelSetter {
into: Option<bool>,
strip_option: Option<bool>,
skip: Option<bool>,
custom: Option<bool>,
}

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<bool> {
/// 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<bool> {
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<bool> {
if self.skip.is_some() {
return self.skip.map(|x| !x);
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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(),
Expand All @@ -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
Expand All @@ -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(),
Expand Down
88 changes: 88 additions & 0 deletions derive_builder/tests/setter_custom.rs
Original file line number Diff line number Diff line change
@@ -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<u32>,
}

// 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)
}
);
}
10 changes: 5 additions & 5 deletions derive_builder_core/src/builder_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down
10 changes: 5 additions & 5 deletions derive_builder_core/src/initializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down
8 changes: 4 additions & 4 deletions derive_builder_core/src/setter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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());
}
Expand Down

0 comments on commit b4d8e69

Please sign in to comment.