Skip to content

Commit

Permalink
Add setter(each = "...") option to extend collection-like fields (#199)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrii Kuteiko <kuteiko@amazon.com>
  • Loading branch information
andy128k and Andrii Kuteiko committed Mar 23, 2021
1 parent c72cd24 commit 7cde486
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 2 deletions.
94 changes: 94 additions & 0 deletions derive_builder/tests/setter_extend.rs
@@ -0,0 +1,94 @@
#[macro_use]
extern crate pretty_assertions;
#[macro_use]
extern crate derive_builder;

use std::collections::HashMap;

#[derive(Debug, PartialEq, Default, Builder, Clone)]
struct Lorem {
#[builder(setter(each = "foo_append"))]
foo: String,
#[builder(setter(each = "bar"))]
bars: Vec<String>,
#[builder(setter(each = "baz"))]
bazes: HashMap<String, i32>,
}

#[derive(Debug, PartialEq, Default, Builder, Clone)]
#[builder(pattern = "mutable")]
struct Ipsum {
#[builder(setter(each = "foo_append"))]
foo: String,
#[builder(setter(each = "bar"))]
bars: Vec<String>,
#[builder(setter(each = "baz"))]
bazes: HashMap<String, i32>,
}

#[test]
fn extend_field() {
let x = LoremBuilder::default()
.foo("foo".into())
.bar("bar".into())
.bar("bar bar".into())
.bar("bar bar bar".into())
.foo_append('-')
.baz(("baz".into(), 1))
.baz(("bazz".into(), 2))
.baz(("bazzz".into(), 3))
.foo_append("foo")
.build()
.unwrap();

assert_eq!(
x,
Lorem {
foo: "foo-foo".into(),
bars: vec!["bar".into(), "bar bar".into(), "bar bar bar".into()],
bazes: vec![("baz".into(), 1), ("bazz".into(), 2), ("bazzz".into(), 3)]
.into_iter()
.collect(),
}
);
}

#[test]
fn extend_field_mutable() {
let x = IpsumBuilder::default()
.foo("foo".into())
.bar("bar".into())
.bar("bar bar".into())
.bar("bar bar bar".into())
.foo_append('-')
.baz(("baz".into(), 1))
.baz(("bazz".into(), 2))
.baz(("bazzz".into(), 3))
.foo_append("foo")
.build()
.unwrap();

assert_eq!(
x,
Ipsum {
foo: "foo-foo".into(),
bars: vec!["bar".into(), "bar bar".into(), "bar bar bar".into()],
bazes: vec![("baz".into(), 1), ("bazz".into(), 2), ("bazzz".into(), 3)]
.into_iter()
.collect(),
}
);
}

#[derive(Debug, PartialEq, Default, Builder, Clone)]
#[builder(setter(skip))]
struct Dolor {
#[builder(setter(each = "foo"))]
foos: Vec<i32>,
}

#[test]
fn extend_field_enabled() {
let x = DolorBuilder::default().foo(1).foo(2).build().unwrap();
assert_eq!(x, Dolor { foos: vec![1, 2] });
}
3 changes: 3 additions & 0 deletions derive_builder_core/src/macro_options/darling_opts.rs
Expand Up @@ -130,6 +130,7 @@ pub struct FieldLevelSetter {
strip_option: Option<bool>,
skip: Option<bool>,
custom: Option<bool>,
each: Option<Ident>,
}

impl FieldLevelSetter {
Expand All @@ -156,6 +157,7 @@ impl FieldLevelSetter {
|| self.name.is_some()
|| self.into.is_some()
|| self.strip_option.is_some()
|| self.each.is_some()
{
return Some(true);
}
Expand Down Expand Up @@ -555,6 +557,7 @@ impl<'a> FieldWithDefaults<'a> {
generic_into: self.setter_into(),
strip_option: self.setter_strip_option(),
deprecation_notes: self.deprecation_notes(),
each: self.field.setter.each.as_ref(),
}
}

Expand Down
27 changes: 25 additions & 2 deletions derive_builder_core/src/setter.rs
Expand Up @@ -62,6 +62,8 @@ pub struct Setter<'a> {
pub strip_option: bool,
/// Emit deprecation notes to the user.
pub deprecation_notes: &'a DeprecationNotes,
/// Emit extend method.
pub each: Option<&'a syn::Ident>,
}

impl<'a> ToTokens for Setter<'a> {
Expand Down Expand Up @@ -135,7 +137,8 @@ impl<'a> ToTokens for Setter<'a> {
let mut new = #self_into_return_ty;
new.#field_ident = ::derive_builder::export::core::option::Option::Some(#into_value);
new
}));
}
));

if self.try_setter {
let try_ty_params =
Expand All @@ -151,7 +154,26 @@ impl<'a> ToTokens for Setter<'a> {
let mut new = #self_into_return_ty;
new.#field_ident = ::derive_builder::export::core::option::Option::Some(converted);
Ok(new)
}));
}
));
}

if let Some(ref ident_each) = self.each {
tokens.append_all(quote!(
#(#attrs)*
#[allow(unused_mut)]
#vis fn #ident_each <VALUE>(#self_param, item: VALUE) -> #return_ty
where
#ty: ::derive_builder::export::core::default::Default + ::derive_builder::export::core::iter::Extend<VALUE>,
{
#deprecation_notes
let mut new = #self_into_return_ty;
new.#field_ident
.get_or_insert_with(::derive_builder::export::core::default::Default::default)
.extend(::derive_builder::export::core::option::Option::Some(item));
new
}
));
}
}
}
Expand Down Expand Up @@ -222,6 +244,7 @@ macro_rules! default_setter {
generic_into: false,
strip_option: false,
deprecation_notes: &Default::default(),
each: None,
};
};
}
Expand Down

0 comments on commit 7cde486

Please sign in to comment.