builder-pattern
A derivable macro for declaring a builder pattern. This crate is highly inspired by derive_builder.
Usage
use Builder;
let p1 = new // PersonBuilder<(), (), (), ...>
.name // PersonBuilder<String, (), (), ...>
.age // PersonBuilder<String, i32, (), ...>
.build; // Person
// Order does not matter.
let p2 = new // PersonBuilder<(), (), (), ...>
.age // PersonBuilder<(), i32, (), ...>
// `&str` is implicitly converted into `String`
// because of `into` attribute!
.name // PersonBuilder<String, i32, (), ...>
.gender // PersonBuilder<String, i32, Gender, ...>
.build; // Person
let p3 = new // PersonBuilder<(), (), (), ...>
.age // PersonBuilder<(), i32, (), ...>
// `&str` is implicitly converted into `String`
// because of `into` attribute!
.name // PersonBuilder<String, i32, (), ...>
.gender_async // PersonBuilder<String, i32, Gender, ...>
.build // Future<Person>
.await; // Person
// `name` field required - Compilation error.
let p4 = new // PersonBuilder<(), (), (), ...>
.age // PersonBuilder<(), i32, (), ...>
.build;
Get Started
Add builder-pattern
to Cargo.toml
.
# Cargo.toml
[]
= "0.4"
The crate feature future
is enabled by default. If you don't need asynchronous features, you can disable it.
# Cargo.toml
[]
= { = "0.4", = 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.
let t1 = new.b.build; // The structure can be built without `a`.
let t2 = new.b.a.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.
let t1 = new.build; // The structure can be built without `a` and `b`.
let t2 = new.a.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:
let test1 = new // TestBuilder<(), (), ...>
.name // TestBuilder<String, (), ...>
.build; // Test
let test2 = new // TestBuilder<(), (), ...>
.name // TestBuilder<String, (), ...>
// Error: `id` function is not generated.
.id
.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:
use Test;
let test1 = new // TestBuilder<(), (), ...>
.id // TestBuilder<Uuid, (), ...>
.name // TestBuilder<Uuid, &'static str, ...>
.build; // Test
assert_eq!;
println!; // 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.
let p1 = new
.name_async
.age
.address_lazy
.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:
let test1 = new // TestBuilder<(), ...>
// `&str` is implicitly converted into `String`.
.name // TestBuilder<String, ...>
.build; //
let test2 = new // TestBuilder<(), ...>
// `&str` is implicitly converted into `String`.
.name_lazy // TestBuilder<String, ...>
.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
.
let test1 = new // TestBuilder<(), ...>
.name // Ok(TestBuilder<String, ...>)
.unwrap // TestBuilder<String, ...>
.build; // Test
let test2 = 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.
let test1 = new // TestBuilder<(), ...>
.name_lazy // TestBuilder<String, ...>
.build // Ok(Test)
.unwrap; // Test
let test2 = new // TestBuilder<(), ...>
.name_async // TestBuilder<String, ...>
.build // Future<Result<Test, Strin
.await // Ok(Test)
.unwrap; // Test
Auto-Generated Documentation
This crate generates documentation for the builder functions. If you document fields, the builder functions for them also copy the documentation.
Example
Example code:
Generated code:
/// A builder for `Test`.
How it works
The following code
will generates:
// A builder structure for `Person`.
// Implementation for `build` function