#[macro_export]
macro_rules! builder_helper {
(@ { $self:ty, $($builder:ty),+ }) => {
trait Builder {
#[inline]
fn when_some<T>(self: $self, value: Option<T>, func: impl FnOnce($self, T) -> $self) -> $self
where
Self: Sized,
{
match value {
Some(v) => func(self, v),
None => self,
}
}
}
$(impl Builder for $builder {})+
};
(Self, $($builder:ty),+ $(,)?) => {
$crate::builder_helper!(@ {Self, $($builder),+});
};
(&mut Self, $($builder:ty),+ $(,)?) => {
$crate::builder_helper!(@ {&mut Self, $($builder),+});
};
}
#[cfg(test)]
mod tests {
#[test]
fn test_builder_helper_self() {
struct TestBuilder {
value: Option<String>,
count: Option<u32>,
}
impl TestBuilder {
fn new() -> Self {
Self {
value: None,
count: None,
}
}
fn set_value(mut self, value: String) -> Self {
self.value = Some(value);
self
}
fn set_count(mut self, count: u32) -> Self {
self.count = Some(count);
self
}
fn build(self) -> (Option<String>, Option<u32>) {
(self.value, self.count)
}
}
builder_helper!(Self, TestBuilder);
let builder = TestBuilder::new()
.when_some(Some("test".to_string()), |b, v| b.set_value(v))
.when_some(None::<u32>, |b, _| b.set_count(0))
.when_some(Some(42), |b, c| b.set_count(c));
let (value, count) = builder.build();
assert_eq!(value, Some("test".to_string()));
assert_eq!(count, Some(42));
}
#[test]
fn test_builder_helper_mut_self() {
struct MutTestBuilder {
value: Option<String>,
count: Option<u32>,
}
impl MutTestBuilder {
fn new() -> Self {
Self {
value: None,
count: None,
}
}
fn set_value(&mut self, value: String) -> &mut Self {
self.value = Some(value);
self
}
fn set_count(&mut self, count: u32) -> &mut Self {
self.count = Some(count);
self
}
fn build(&self) -> (Option<String>, Option<u32>) {
(self.value.clone(), self.count)
}
}
builder_helper!(&mut Self, MutTestBuilder);
let mut builder = MutTestBuilder::new();
builder
.when_some(Some("mut_test".to_string()), |b, v| b.set_value(v))
.when_some(None::<u32>, |b, _| b.set_count(0))
.when_some(Some(99), |b, c| b.set_count(c));
let (value, count) = builder.build();
assert_eq!(value, Some("mut_test".to_string()));
assert_eq!(count, Some(99));
}
#[test]
fn test_builder_helper_multiple_types() {
struct Builder1 {
value: Option<String>,
}
struct Builder2 {
value: Option<String>,
}
impl Builder1 {
fn new() -> Self {
Self { value: None }
}
fn set_value(mut self, value: String) -> Self {
self.value = Some(value);
self
}
fn build(self) -> Option<String> {
self.value
}
}
impl Builder2 {
fn new() -> Self {
Self { value: None }
}
fn set_value(mut self, value: String) -> Self {
self.value = Some(value);
self
}
fn build(self) -> Option<String> {
self.value
}
}
builder_helper!(Self, Builder1, Builder2);
let builder1 =
Builder1::new().when_some(Some("builder1".to_string()), |b, v| b.set_value(v));
assert_eq!(builder1.build(), Some("builder1".to_string()));
let builder2 =
Builder2::new().when_some(Some("builder2".to_string()), |b, v| b.set_value(v));
assert_eq!(builder2.build(), Some("builder2".to_string()));
}
#[test]
fn test_when_some_with_none() {
struct SimpleBuilder {
value: Option<String>,
}
impl SimpleBuilder {
fn new() -> Self {
Self { value: None }
}
fn set_value(mut self, value: String) -> Self {
self.value = Some(value);
self
}
fn build(self) -> Option<String> {
self.value
}
}
builder_helper!(Self, SimpleBuilder);
let builder = SimpleBuilder::new().when_some(None::<String>, |b, _| {
b.set_value("should not be set".to_string())
});
assert_eq!(builder.build(), None);
}
#[test]
fn test_when_some_with_some() {
struct SimpleBuilder {
value: Option<String>,
}
impl SimpleBuilder {
fn new() -> Self {
Self { value: None }
}
fn set_value(mut self, value: String) -> Self {
self.value = Some(value);
self
}
fn build(self) -> Option<String> {
self.value
}
}
builder_helper!(Self, SimpleBuilder);
let builder =
SimpleBuilder::new().when_some(Some("test_value".to_string()), |b, v| b.set_value(v));
assert_eq!(builder.build(), Some("test_value".to_string()));
}
#[test]
fn test_chaining_with_when_some() {
struct ChainBuilder {
value1: Option<String>,
value2: Option<String>,
count: Option<u32>,
}
impl ChainBuilder {
fn new() -> Self {
Self {
value1: None,
value2: None,
count: None,
}
}
fn set_value1(mut self, value: String) -> Self {
self.value1 = Some(value);
self
}
fn set_value2(mut self, value: String) -> Self {
self.value2 = Some(value);
self
}
fn set_count(mut self, count: u32) -> Self {
self.count = Some(count);
self
}
fn build(self) -> (Option<String>, Option<String>, Option<u32>) {
(self.value1, self.value2, self.count)
}
}
builder_helper!(Self, ChainBuilder);
let builder = ChainBuilder::new()
.set_value1("value1".to_string())
.when_some(Some("value2".to_string()), |b, v| b.set_value2(v))
.when_some(Some(100), |b, c| b.set_count(c))
.when_some(None::<String>, |b, _| b.set_value1("ignored".to_string()));
let (value1, value2, count) = builder.build();
assert_eq!(value1, Some("value1".to_string()));
assert_eq!(value2, Some("value2".to_string()));
assert_eq!(count, Some(100));
}
}