Skip to content

Commit

Permalink
Move default calculation for disabled fields into field_default_value
Browse files Browse the repository at this point in the history
  • Loading branch information
Wasabi375 committed Mar 24, 2024
1 parent 33b1a45 commit c2510cf
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 84 deletions.
50 changes: 50 additions & 0 deletions derive_builder/tests/custom_default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,53 @@ mod struct_level {
assert_eq!(ipsum.not_type_default, None);
}
}

mod owned_field {

#[derive(Debug, Clone, PartialEq, Eq, Builder)]
#[builder(pattern = "owned")]
struct Lorem {
#[builder(default = "self.ipsum_default()")]
ipsum: String,

#[builder(setter(skip), default = "self.dolor_default()")]
dolor: String,
}

impl LoremBuilder {
fn ipsum_default(&self) -> String {
"ipsum".to_string()
}
fn dolor_default(&self) -> String {
"dolor".to_string()
}
}

#[test]
fn builder_test() {
let x = LoremBuilder::create_empty()
.ipsum("Ipsum".to_string())
.build()
.unwrap();

assert_eq!(
x,
Lorem {
ipsum: "Ipsum".to_string(),
dolor: "dolor".to_string(),
}
);
}
#[test]
fn defaults_test() {
let x = LoremBuilder::create_empty().build().unwrap();

assert_eq!(
x,
Lorem {
ipsum: "ipsum".to_string(),
dolor: "dolor".to_string(),
}
);
}
}
59 changes: 40 additions & 19 deletions derive_builder_core/src/field_default_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,9 @@ pub struct FieldDefaultValue<'a> {

impl<'a> ToTokens for FieldDefaultValue<'a> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
if !self.field_enabled || !self.enabled {
// disabled fields have their value calculated in the initializer
if !self.enabled {
return;
}
// should be:
// ```
// let $prefix$fieldname = match self.$field.as_ref() {
// Some(_) => None,
// None => Some($default_value)
// }
// ```

let struct_field = &self.field_ident;
let builder_field = struct_field;
Expand All @@ -89,19 +81,40 @@ impl<'a> ToTokens for FieldDefaultValue<'a> {
Span::call_site(),
);

let default_calculation = self.default_value();

tokens.append_all(quote!(
let #default_value: Option<#field_type> = match self.#builder_field.as_ref() {
Some(_) => None,
None => #default_calculation,
};
));
if self.field_enabled {
let default_calculation = self.default_value_calculation();
tokens.append_all(quote!(
let #default_value: Option<#field_type> = match self.#builder_field.as_ref() {
Some(_) => None,
None => #default_calculation,
};
));
} else {
let default_calculation = self.default_value_for_disabled();
tokens.append_all(quote!(
let #default_value: #field_type = #default_calculation;
));
}
}
}

impl<'a> FieldDefaultValue<'a> {
fn default_value(&'a self) -> DefaultValue<'a> {
fn default_value_for_disabled(&'a self) -> TokenStream {
let crate_root = self.crate_root;
match self.default_value {
Some(expr) => expr.with_crate_root(crate_root).into_token_stream(),
None if self.use_default_struct => {
let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
let field_ident = self.field_ident;
quote!(#struct_ident.#field_ident)
}
None => {
quote!(#crate_root::export::core::default::Default::default())
}
}
}

fn default_value_calculation(&'a self) -> DefaultValue<'a> {
match self.default_value {
Some(expr) => DefaultValue::DefaultTo {
expr,
Expand Down Expand Up @@ -216,8 +229,16 @@ mod tests {
fn disabled_field() {
let mut default = default_field_default_value!();
default.field_enabled = false;
let default_value = DefaultExpression::explicit::<syn::Expr>(parse_quote!(42));
default.default_value = Some(&default_value);

assert_eq!(quote!(#default).to_string(), quote!().to_string());
assert_eq!(
quote!(#default).to_string(),
quote!(
let __default_foo: usize = { 42 };
)
.to_string()
);
}

#[test]
Expand Down
71 changes: 8 additions & 63 deletions derive_builder_core/src/initializer.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};

use crate::{
BlockContents, BuilderPattern, DefaultExpression, DEFAULT_FIELD_NAME_PREFIX,
DEFAULT_STRUCT_NAME,
};
use crate::{BlockContents, BuilderPattern, DEFAULT_FIELD_NAME_PREFIX};

/// Initializer for the target struct fields, implementing `quote::ToTokens`.
///
Expand Down Expand Up @@ -42,12 +39,6 @@ pub struct Initializer<'a> {
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.
///
/// This takes precedence over a default struct identifier.
pub default_value: Option<&'a DefaultExpression>,
/// Whether the build_method defines a default struct.
pub use_default_struct: bool,
/// Method to use to to convert the builder's field to the target field
///
/// For sub-builder fields, this will be `build` (or similar)
Expand All @@ -62,20 +53,19 @@ impl<'a> ToTokens for Initializer<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let struct_field = &self.field_ident;
let builder_field = struct_field;
let default_value = Ident::new(
&format!("{}{}", DEFAULT_FIELD_NAME_PREFIX, struct_field),
Span::call_site(),
);

// This structure prevents accidental failure to add the trailing `,` due to incautious `return`
let append_rhs = |tokens: &mut TokenStream| {
if !self.field_enabled {
tokens.append_all(self.default());
tokens.append(default_value);
} else {
match &self.conversion {
FieldConversion::Move => tokens.append_all(quote!(self.#builder_field)),
FieldConversion::OptionOrDefault => {
let default_value = Ident::new(
&format!("{}{}", DEFAULT_FIELD_NAME_PREFIX, struct_field),
Span::call_site(),
);

let moved_or_cloned =
self.move_or_clone_option(quote!(self.#builder_field));
tokens.append_all(quote!( #moved_or_cloned.or(#default_value).unwrap()))
Expand Down Expand Up @@ -105,21 +95,6 @@ impl<'a> Initializer<'a> {
}
}
}

fn default(&'a self) -> TokenStream {
let crate_root = self.crate_root;
match self.default_value {
Some(expr) => expr.with_crate_root(crate_root).into_token_stream(),
None if self.use_default_struct => {
let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
let field_ident = self.field_ident;
quote!(#struct_ident.#field_ident)
}
None => {
quote!(#crate_root::export::core::default::Default::default())
}
}
}
}

#[derive(Debug, Clone)]
Expand All @@ -145,8 +120,6 @@ macro_rules! default_initializer {
field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
field_enabled: true,
builder_pattern: BuilderPattern::Mutable,
default_value: None,
use_default_struct: false,
conversion: FieldConversion::OptionOrDefault,
}
};
Expand Down Expand Up @@ -204,47 +177,19 @@ mod tests {
}

#[test]
fn default_value() {
let mut initializer = default_initializer!();
initializer.field_enabled = false;
let default_value = DefaultExpression::explicit::<syn::Expr>(parse_quote!(42));
initializer.default_value = Some(&default_value);

assert_eq!(
quote!(#initializer).to_string(),
quote!(
foo: { 42 },
)
.to_string()
);
}

#[test]
fn default_struct() {
fn setter_disabled() {
let mut initializer = default_initializer!();
initializer.field_enabled = false;
initializer.use_default_struct = true;

assert_eq!(
quote!(#initializer).to_string(),
quote!(
foo: __default.foo,
foo: __default_foo,
)
.to_string()
);
}

#[test]
fn setter_disabled() {
let mut initializer = default_initializer!();
initializer.field_enabled = false;

assert_eq!(
quote!(#initializer).to_string(),
quote!(foo: ::db::export::core::default::Default::default(),).to_string()
);
}

#[test]
fn no_std() {
let initializer = default_initializer!();
Expand Down
2 changes: 0 additions & 2 deletions derive_builder_core/src/macro_options/darling_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -894,8 +894,6 @@ impl<'a> FieldWithDefaults<'a> {
field_enabled: self.field_enabled(),
field_ident: self.field_ident(),
builder_pattern: self.pattern(),
default_value: self.field.default.as_ref(),
use_default_struct: self.use_parent_default(),
conversion: self.conversion(),
}
}
Expand Down

0 comments on commit c2510cf

Please sign in to comment.