A derivable macro for declaring a builder pattern. This crate is highly inspired by derive_builder.
```rust use builder_pattern::Builder;
struct Person { #[into] name: String, age: i32, #[default(Gender::Nonbinary)] #[setter(value, async)] gender: Gender, }
let p1 = Person::new() // PersonBuilder<(), (), ()>
.name(String::from("Joe")) // PersonBuilder // Order does not matter.
let p2 = Person::new() // PersonBuilder<(), (), ()>
.age(32) // PersonBuilder<(), i32, ()>
// let p3 = Person::new() // PersonBuilder<(), (), (), ...>
.age(32) // PersonBuilder<(), i32, (), ...>
// // Add ```toml [dependencies]
builder-pattern = "0.4"
``` The crate feature ```toml [dependencies]
builder-pattern = { version = "0.4", default-features = false }
``` A field having this attribute will be considered as optional, and the ```rust struct Test {
#[default]
pub a: i32,
pub b: &'static str,
} let t1 = Test::new().b("Hello").build(); // The structure can be built without A field having this attribute will be considered as optional, and the ```rust struct Test {
#[defaultlazy(|| someheavytask() + 3)]
pub a: i32,
#[defaultlazy(someheavytask)]
pub b: i32,
} let t1 = Test::new().build(); // The structure can be built without If this attribute is present, the builder function would not be generated for the field. This field requires Example: ```rust struct Test {
#[default(Uuid::new_v4())]
#[hidden]
id: Uuid,
name: String,
} let test1 = Test::new() // TestBuilder<(), ()>
.name(String::from("Joe")) // TestBuilder let test2 = Test::new() // TestBuilder<(), ()>
.name(String::from("Jack")) // TestBuilder If this attribute presents, it provides specified setters. If it doesn't, only the value setter is provided. ```rust struct Person {
// All kinds of setters are provided.
#[setter(value, lazy, async)]
name: String,
// Only value setter is provided.
age: u8,
// Only lazy setter is provided.
#[setter(lazy)]
address: &'static str,
} let p1 = Person::new()
.nameasync(|| async { String::from("Joe") })
.age(15)
.addresslazy(|| "123 Main St")
.build() // A setter function for a field having this attribute will accept Example: ```rust struct Test {
#[into]
pub name: String,
} let test = Test::new() // TestBuilder<()>
// Implement a validator for a field. ```rust struct Test {
#[validator(isnotempty)]
#[into]
pub name: String,
} fn isnotempty(name: String) -> Result let test1 = Test::new() // TestBuilder<()>
.name("Hello") // Ok(TestBuilder let test2 = Test::new() // TestBuilder<()>
.name("") // Err(String{ "Validation failed: Name cannot be empty." })
.unwrap() // panic!
.build();
``` If the validator is used with lazy or async setters, it will also validated lazily or asynchronously. So, the setter doesn't return ```rust struct Test {
#[validator(isnotempty)]
#[setter(value, lazy, async)]
pub name: &'static str,
} let test1 = Test::new() // TestBuilder<()>
.name_lazy("Hello") // TestBuilder let test2 = Test::new() // TestBuilder<()>
.nameasync(|| async {
"Hello".tostring()
}) // TestBuilder This crate generates documentation for the builder functions. If you document fields,
the builder functions for them also copy the documentation. Example code: ```rust struct Test {
/// A positive integer.
pub positive: i32, }
``` Generated code: `` /// A builder for impl TestBuilder impl impl The following code ```rust struct Person {
#[into]
#[validator(isnotempty)]
name: String,
age: i32,
#[default(Gender::Nonbinary)]
gender: Gender,
} ``` will generates: `` {
_phantom: PhantomData<(
T1, T2, T3,
AsyncFieldMarker,
ValidatorOption,
)>,
// Fields are wrapped in {
// Setter for {
// Setter for {
// Setter for &str
is implicitly converted into String
// because of into
attribute!
.name("Jack") // PersonBuilder&str
is implicitly converted into String
// because of into
attribute!
.name("Jack") // PersonBuildername
field required - Compilation error.
let p4 = Person::new() // PersonBuilder<(), (), ()>
.age(15) // PersonBuilder<(), i32, ()>
.build();
```Get Started
builder-pattern
to Cargo.toml
.Cargo.toml
future
is enabled by default. If you don't need asynchronous features, you can disable it.Cargo.toml
Features
build
function is allowed only the all of required fields are provided. No Result, No Unwrap. Just use it.Attributes
#[default(expr)]
expr
will be evaluated as a default value of the field. build
function can be called without providing this field.[derive(Builder)]
a
.
let t2 = Test::new().b("Hi").a(3).build();
```#[default_lazy(expr)]
expr
will be lazily evaluated as a default value of the field. expr
should be a function or a closure having no arguments.[derive(Builder)]
a
and b
.
let t2 = Test::new().a(3).build();
```#[hidden]
default
or default_lazy
attribute.[derive(Builder)]
#[setter(value | lazy | async)]
[derive(Builder, Debug)]
address
is validated here
.await; // name
is validated here
```#[into]
Into
trait as a parameter. You can use this setter with implicit conversion. Currently, it cannot be used with async or lazy setters.[derive(Builder)]
&str
is implicitly converted into String
.
.name("Hello") // TestBuilder#[validator(expr)]
expr
could be a validating function that takes the field's type and returns Result
.[derive(Builder)]
Result
but it is returned when it is built.[derive(Builder)]
Auto-Generated Documentation
Example
[derive(Builder)]
/// An integer having zero as a default value.
#[default(0)]
pub zero: i32,
rust
impl Test {
/// Creating a builder.
/// ## Required fields
/// ###
positive
/// - Type:
i32
///
/// A positive integer.
///
/// ## Optional fields
/// ###
zero
/// - Type:
i32
/// - Default:
0`
///
/// An integer having zero as a default value.
fn new() -> TestBuilder<(), ()> {
TestBuilder {
_phantom: PhantomData,
positive: None,
zero: Some(0),
}
}
}Test
.
struct TestBuilderi32
///
/// A positive integer.
pub fn positive(self, value: i32) -> TestBuilder0
///
/// An integer having zero as a default value.
pub fn zero(self, value: i32) -> TestBuilderHow it works
[derive(Builder)]
rust
impl Person {
// Create an empty builder
fn new<'a>() -> PersonBuilder<'a, (), (), (), (), ()> {
PersonBuilder {
_phantom: PhantomData,
age: None,
name: None,
gender: Some(Setter::Value(Gender::Nonbinary)),
}
}
}
// A builder structure for
Person`.
struct PersonBuilder<
'a, T1, T2, T3,
AsyncFieldMarker, // A generic for checking async fields
ValidatorOption, // A generic for checking lazy validators
Option
s.
age: Optionbuild
function
impl<'a, T3>
// It can be called regardless of whether T3
is ()
or Gender
.
PersonBuilder<'a, i32, String, T3, (), ()>
{
fn build(self) -> Person {
let age = match self.age.unwrap() {
Setter::Value(v) => v,
Setter::Lazy(f) => f(),
_ => unimplemented!(),
};
let name = match self.name.unwrap() {
Setter::Value(v) => v,
Setter::Lazy(f) => f(),
_ => unimplemented!(),
};
let gender = match self.gender.unwrap() {
Setter::Value(v) => v,
Setter::Lazy(f) => f(),
_ => unimplemented!(),
};
Person { age, name, gender }
}
}
impl<'a, T2, T3, AsyncFieldMarker, ValidatorOption>
PersonBuilder<
'a, (), T2, T3,
AsyncFieldMarker,
ValidatorOption,age
fn age(
self,
value: i32,
) -> PersonBuilder<
'a, i32, T2, T3,
AsyncFieldMarker,
ValidatorOption,
{
PersonBuilder {
_phantom: PhantomData,
age: Some(Setter::Value(value.into())),
name: self.name,
gender: self.gender,
}
}
}
impl<'a, T1, T3, AsyncFieldMarker, ValidatorOption>
PersonBuilder<
'a, T1, (), T3,
AsyncFieldMarker,
ValidatorOption,name
fn namegender
fn gender(
self,
value: Gender,
) -> PersonBuilder<
'a, T1, T2, Gender,
AsyncFieldMarker,
ValidatorOption,
{
PersonBuilder {
_phantom: PhantomData,
age: self.age,
name: self.name,
gender: Some(Setter::Value(value.into())),
}
}
}
```License