Type Safe Builder Pattern
Infallible compile-time checked builders for your structs.
No more worrying whether the `build` call on your builder will return `Ok` or not. Maybe you forgot to set a field? `typesafe-builders` solves this by using the Rust type-system to ensure correct usage.
# Example
```rust
use typesafe_builders::prelude::*;
fn main() {
#[derive(Builder)]
struct Point {
#[builder(constructor)]
x: u8,
y: u8,
#[builder(optional)]
z: Option,
}
// `builder` requires `x` since it is marked as `constructor`.
let builder = Point::builder(1);
// These do not compile:
// partial.x(6); // `x` is already set
// partial.build(); // `y` is not set
// `build` is only available once all required fields are set:
let result = builder.y(2).build();
assert_eq!(result.x, 1);
assert_eq!(result.y, 2);
assert_eq!(result.z, None);
}
```
# Known Downside
I can recommend this only for *internal use*. It is best to not expose these builder types as an API of your crate, since they look extremely ugly and verbose. For example:
```rust
use typesafe_builders::prelude::*;
#[derive(Builder)]
struct Point {
x: u8,
y: u8,
z: u8,
}
// Ugly type name here... and it only gets worse for const-generics etc.
fn preset() -> GenericPointBuilder {
Point::builder().z(0)
}
fn main() {
let partial = preset();
let point = partial.x(1).y(2).build();
}
```
Please open an MR/Issue if you know how to improve this.
## Field Attributes
Attributes can be combined. Ones that do not work together will throw an explicit error at compile time. Duplicates always error.
### Optional
A field can be set, but does not have to be. Requires the field type to be `Default`.
```rust
use typesafe_builders::prelude::*;
#[derive(Builder)]
pub struct Struct {
#[builder(optional)]
x: u8,
}
fn main() {
// without x
Struct::builder().build();
// with x
Struct::builder().x(4).build();
}
```
### Constructor
Require a field to be set upon builder construction.
```rust
use typesafe_builders::prelude::*;
#[derive(Builder)]
pub struct Struct {
#[builder(constructor)]
x: u8,
}
fn main() {
Struct::builder(4).build();
// does not work:
// Struct::builder(4).x(5).build();
}
```
### Decay
Decay the type to its first generic. Eases use for `Option`, `Box` etc. Requires that the decayed type can be `into`ed its original. Works on all types with one generic arg.
```rust
use typesafe_builders::prelude::*;
#[derive(Builder)]
pub struct Struct {
#[builder(decay)]
x: Option,
}
fn main() {
// Use `4` of `Some(4)`
Struct::builder().x(4).build();
}
```
# How does it work?
Const generic one-hot bitfields. What you get is similar to this:
```rust
pub struct Builder {
x: Option,
y: Option,
}
impl Builder {
fn set_x(self, x: u8) -> Builder {
unimplemented!()
}
}
impl Builder {
fn set_y(self, y: u8) -> Builder {
unimplemented!()
}
}
// The build function is only available once all fields are set:
impl Builder {
fn build() {
}
}
```
# More Examples
### Lifetimes
They work as expected
```rust
use typesafe_builders::prelude::*;
#[derive(Builder)]
pub struct Struct<'a, 'b, 'c> {
x: &'a Box<&'b Option<&'c str>>, // yikes
}
fn main() {
Struct::builder().x(&Box::new(&Some("hi"))).build();
}
```
### Generics
Works as expected, but does not yet support defaults.
```rust
mod other {
use typesafe_builders::prelude::*;
#[derive(Builder)]
pub struct Struct {
y: Option,
}
}
fn main() {
other::Struct::::builder().y(Some(4)).build();
}
```
### Const Generics
Works as expected, but does not yet support defaults.
```rust
mod other {
use typesafe_builders::prelude::*;
#[derive(Builder)]
pub struct Struct {
x: [u8; LEN],
}
}
fn main() {
other::Struct::<1>::builder().x([1]).build();
}
```
# TODOs
- [x] Lifetimes
- [x] Generics
- [x] Bounds
- [ ] With default
- [x] Const generics
- [ ] With default
- [x] Add `optional` fields.
- [ ] Add `rename` field attribute.
- [x] Add `constructor` or something like this to have mandatory args directly in the `builder` function.
- [ ] Add `Into` or whatever to cast types.
- [ ] Add way to pass options as `Some` automatically.
- [ ] Cleanup