# builder-pattern [![creates.io](https://img.shields.io/crates/v/builder-pattern?logo=rust)](https://crates.io/crates/builder-pattern) [![API Docs](https://docs.rs/builder-pattern/badge.svg?logo=docs-rs)](https://docs.rs/builder-pattern/) [![Build Status (Windows)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_windows.yml/badge.svg)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_windows.yml) [![Build Status (MacOS)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_macos.yml/badge.svg)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_macos.yml) [![Build Status (Ubuntu)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_ubuntu.yml/badge.svg)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_ubuntu.yml) [![Build Status (WebAssembly)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_wasm.yml/badge.svg)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_wasm.yml) A derivable macro for declaring a builder pattern. This crate is highly inspired by [derive_builder](https://github.com/colin-kiegel/rust-derive-builder). ## Usage ```rust use builder_pattern::Builder; #[derive(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 .age(27) // PersonBuilder .build(); // Person // Order does not matter. let p2 = Person::new() // PersonBuilder<(), (), (), ...> .age(32) // PersonBuilder<(), i32, (), ...> // `&str` is implicitly converted into `String` // because of `into` attribute! .name("Jack") // PersonBuilder .gender(Gender::Male) // PersonBuilder .build(); // Person let p3 = Person::new() // PersonBuilder<(), (), (), ...> .age(32) // PersonBuilder<(), i32, (), ...> // `&str` is implicitly converted into `String` // because of `into` attribute! .name("Jack") // PersonBuilder .gender_async(|| async { Gender::Male }) // PersonBuilder .build() // Future .await; // Person // `name` field required - Compilation error. let p4 = Person::new() // PersonBuilder<(), (), (), ...> .age(15) // PersonBuilder<(), i32, (), ...> .build(); ``` ## Get Started Add `builder-pattern` to `Cargo.toml`. ```toml # Cargo.toml [dependencies] builder-pattern = "0.4" ``` The crate feature `future` is enabled by default. If you don't need asynchronous features, you can disable it. ```toml # Cargo.toml [dependencies] builder-pattern = { version = "0.4", default-features = false } ``` ## Features - **Chaining**: Can make structure with chained setters. - **Complex types are supported**: Lifetime, trait bounds, and where clauses are well supported. - **Type safety**: Autocompletion tools can suggest correct setters to build the struct. Also, `build` function is allowed only the all of required fields are provided. **No Result**, **No Unwrap**. Just use it. - **Lazy evaluation and asynchronous**: Lazy evaluation and asynchronous are supported. The values will be evaluated when the structure is built. - **No additional tasks**: There's no additional constraints to use the macro. Any structures and fields are allowed. - **Auto-generated documentation**: Documentation for the builder functions are automatically generated. ## 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. ```rust #[derive(Builder)] struct Test { #[default] pub a: i32, pub b: &'static str, } let t1 = Test::new().b("Hello").build(); // The structure can be built without `a`. let t2 = Test::new().b("Hi").a(3).build(); ``` ### `#[default_lazy(expr)]` A field having this attribute will be considered as optional, and the `expr` will be lazily evaluated as a default value of the field. `expr` should be a function or a closure having no arguments. ```rust #[derive(Builder)] struct Test { #[default_lazy(|| some_heavy_task() + 3)] pub a: i32, #[default_lazy(some_heavy_task)] pub b: i32, } let t1 = Test::new().build(); // The structure can be built without `a` and `b`. let t2 = Test::new().a(3).build(); ``` ### `#[hidden]` If this attribute is present, the builder function would not be generated for the field. This field requires `default` or `default_lazy` attribute. Example: ```rust #[derive(Builder)] struct Test { #[default(Uuid::new_v4())] #[hidden] id: Uuid, name: String, } let test1 = Test::new() // TestBuilder<(), (), ...> .name(String::from("Joe")) // TestBuilder .build(); // Test let test2 = Test::new() // TestBuilder<(), (), ...> .name(String::from("Jack")) // TestBuilder // Error: `id` function is not generated. .id(Uuid::parse_str("46ebd0ee-0e6d-43c9-b90d-ccc35a913f3e").unwrap()) .build(); ``` ### `#[public]` If this attribute is present, a field would be exposed with setter functions even though the field is private. It provides a way to access private fields during the building. Example: ```rust mod test { #[derive(Builder)] pub struct Test { #[public] id: Uuid, pub name: &'static str, } } use test::Test; let test1 = Test::new() // TestBuilder<(), (), ...> .id(Uuid::new_v4()) // TestBuilder .name("Joe") // TestBuilder .build(); // Test assert_eq!(test1.name, "Joe"); println!("{}", test1.id); // Error: `id` is a private field. ``` ### `#[setter(value | lazy | async)]` If this attribute presents, it provides specified setters. If it doesn't, only the value setter is provided. ```rust #[derive(Builder, Debug)] 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() .name_async(|| async { String::from("Joe") }) .age(15) .address_lazy(|| "123 Main St") .build() // `address` is validated here .await; // `name` is validated here ``` ### `#[into]` A setter function for a field having this attribute will accept `Into` trait as a parameter. You can use this setter with implicit conversion. Example: ```rust #[derive(Builder)] struct Test { #[into] #[setter(value, lazy)] pub name: String, } let test1 = Test::new() // TestBuilder<(), ...> // `&str` is implicitly converted into `String`. .name("Hello") // TestBuilder .build(); // let test2 = Test::new() // TestBuilder<(), ...> // `&str` is implicitly converted into `String`. .name_lazy(|| "Hello") // TestBuilder .build(); // Test ``` ### `#[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(is_not_empty)] #[into] pub name: String, } fn is_not_empty(name: String) -> Result { if name.is_empty() { Err("Name cannot be empty.") } else { Ok(name) } } let test1 = Test::new() // TestBuilder<(), ...> .name("Hello") // Ok(TestBuilder) .unwrap() // TestBuilder .build(); // Test 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 `Result` but it is returned when it is built. ```rust #[derive(Builder)] struct Test { #[validator(is_not_empty)] #[setter(value, lazy, async)] pub name: &'static str, } let test1 = Test::new() // TestBuilder<(), ...> .name_lazy("Hello") // TestBuilder .build() // Ok(Test) .unwrap(); // Test let test2 = Test::new() // TestBuilder<(), ...> .name_async(|| async { "Hello".to_string() }) // TestBuilder .build() // Future TestBuilder<(), ()> { TestBuilder { _phantom: PhantomData, positive: None, zero: Some(0), } } } /// A builder for `Test`. struct TestBuilder { _phantom: PhantomData<(T1, T2)>, positive: Option, zero: Option, } impl TestBuilder { fn build(self) -> Test { Test { positive: self.positive.unwrap(), zero: self.zero.unwrap(), } } } impl TestBuilder<(), T2> { /// # positive /// - Type: `i32` /// /// A positive integer. pub fn positive(self, value: i32) -> TestBuilder { TestBuilder { _phantom: PhantomData, positive: Some(Setter::Value(value)), zero: self.zero, } } } impl TestBuilder { /// # zero /// - Type: `i32` /// - Default: `0` /// /// An integer having zero as a default value. pub fn zero(self, value: i32) -> TestBuilder { TestBuilder { _phantom: PhantomData, positive: self.positive, zero: Some(Setter::Value(value)), } } } ``` ## How it works The following code ```rust #[derive(Builder)] struct Person { #[into] #[validator(is_not_empty)] name: String, age: i32, #[default(Gender::Nonbinary)] gender: Gender, } ``` will generates: ```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 > { _phantom: PhantomData<( T1, T2, T3, AsyncFieldMarker, ValidatorOption, )>, // Fields are wrapped in `Option`s. age: Option>, name: Option>, gender: Option>, } // Implementation for `build` 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, > { // Setter for `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, > { // Setter for `name` fn name>( self, value: IntoType, ) -> Result< PersonBuilder< 'a, T1, String, T3, AsyncFieldMarker, ValidatorOption, >, String, > { // Validate the value match is_not_empty(value.into()) { Ok(value) => Ok(PersonBuilder { _phantom: PhantomData, age: self.age, name: Some(Setter::Value(value)), gender: self.gender, }), Err(e) => Err(format!("Validation failed: {:?}", e)), } } } impl<'a, T1, T2, AsyncFieldMarker, ValidatorOption> PersonBuilder< 'a, T1, T2, (), AsyncFieldMarker, ValidatorOption, > { // Setter for `gender` 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 [MIT](../LICENSE)