builder-pattern

A derivable macro for declaring a builder pattern.

Usage

```rust use builder_pattern::Builder;

enum Gender { Male, Female, Nonbinary }

[derive(Builder)]

struct Person { #[into] name: String, age: i32, #[default(Gender::Nonbinary)] gender: Gender, }

let p1 = Person::new() .name(String::from("Joe")) .age(27) .build();

// Orders does not matter. let p2 = Person::new() .age(32) // &str is implicitly converted into String // because of into attribute! .name("Jack") .gender(Gender::Male).build();

// name field required - Compilation error. let p3 = Person::new() .age(15) .build(); ```

Get Started

Add builder-pattern to Cargo.toml.

toml // Cargo.toml [dependencies] builder-pattern = "0.3"

Features

Attributes

#[default(expr)]

A field having this attribute will be considered as optional and the expr will be evaluated as a default value of the field. build function can be called without providing this field.

#[into]

A setter function for a field having this attribute will accept an Into trait as a parameter. You can use this setter with implicit conversion.

Example:

```rust

[derive(Builder)]

struct Test { #[into] pub name: String, }

let test = Test::new() // &str is implicitly converted into String. .name("Hello") .build(); ```

#[validator(expr)]

Implement a validator for a field. expr could be a validating function that takes the field's type and returns Result.

```rust

[derive(Builder)]

struct Test { #[validator(isnotempty)] #[into] pub name: String, }

fn isnotempty(name: String) -> Result

let test1 = Test::new().name(""); // Err(()) let test2 = Test::new().name("Hello").unwrap().build(); ```

How it works

The following code

```rust

[derive(Builder)]

struct Person { #[into] #[validator(isnotempty)] name: String, age: i32, #[default(Gender::Nonbinary)] gender: Gender, }

```

will generates:

```rust struct PersonBuilder { name: Option, age: Option, gender: Option, _phantom: PhantomData<(T1, T2, T3)> }

impl Person { // Create an empty builder fn new() -> PersonBuilder<(), (), ()> { PersonBuilder { name: None, age: None, // Default value gender: Some(Gender::Nonbinary), _phantom: PhantomData } } }

// Builder for name. impl PersonBuilder<(), T2, T3> { // Receives Into traits. fn name>(self, value: IntoType) -> Result, ()> { // Validation check. match isnotempty(value.into()) { Ok(value) => Ok(PersonBuilder { // Converts IntoType into String. name: Some(value.into()), age: self.age, gender: self.gender, phantom: PhantomData, }), Err() => Err(()) } } }

// Builder for age. impl PersonBuilder { fn age(self, value: i32) -> PersonBuilder { PersonBuilder { name: self.name, age: Some(value), gender: self.gender, _phantom: PhantomData, } } }

// Builder for gender. impl PersonBuilder PersonBuilder { PersonBuilder { name: self.name, age: self.age, gender: Some(value), _phantom: PhantomData, } } }

// build function // It can be called regardless of whether T3 is () or Gender. impl PersonBuilder { fn build(self) -> Person { Person { name: self.name.unwrap(), age: self.age.unwrap(), gender: self.gender.unwrap(), } } } ```