22 个不稳定版本 (3 个重大更改)
0.4.2 | 2021 年 10 月 3 日 |
---|---|
0.4.1 | 2021 年 10 月 3 日 |
0.3.11 | 2021 年 9 月 26 日 |
0.3.10 | 2021 年 8 月 25 日 |
0.1.3 | 2021 年 8 月 16 日 |
在 Rust 模式 中排名 #1060
每月下载量 1,155
在 10 个crate中使用(直接使用 8 个)
38KB
builder-pattern
用于声明构建器模式的可衍化宏。这个 crate 极大地受到了 derive_builder 的启发。
用法
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<String, (), (), ...>
.age(27) // PersonBuilder<String, i32, (), ...>
.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<String, i32, (), ...>
.gender(Gender::Male) // PersonBuilder<String, i32, Gender, ...>
.build(); // Person
let p3 = Person::new() // PersonBuilder<(), (), (), ...>
.age(32) // PersonBuilder<(), i32, (), ...>
// `&str` is implicitly converted into `String`
// because of `into` attribute!
.name("Jack") // PersonBuilder<String, i32, (), ...>
.gender_async(|| async {
Gender::Male
}) // PersonBuilder<String, i32, Gender, ...>
.build() // Future<Person>
.await; // Person
// `name` field required - Compilation error.
let p4 = Person::new() // PersonBuilder<(), (), (), ...>
.age(15) // PersonBuilder<(), i32, (), ...>
.build();
入门
将 builder-pattern
添加到 Cargo.toml
。
# Cargo.toml
[dependencies]
builder-pattern = "0.4"
默认启用了 crate 功能 future
。如果您不需要异步功能,可以禁用它。
# Cargo.toml
[dependencies]
builder-pattern = { version = "0.4", default-features = false }
功能
- 链式设置:可以创建带有链式设置的结构。
- 支持复杂类型:生命周期、特性和 where 子句得到良好支持。
- 类型安全:自动补全工具可以建议正确的设置器来构建结构。此外,只有当所有必需字段都提供时,才允许使用
build
函数。无 Result,无 Unwrap。只需使用它。 - 惰性评估和异步:支持惰性评估和异步。值将在结构构建时进行评估。
- 无额外任务:没有使用宏的额外约束。任何结构和字段都是允许的。
- 自动生成文档:自动生成构建器函数的文档。
属性
#[default(expr)]
具有此属性的字段将被视为可选的,并将 expr
评估为字段的默认值。可以不提供此字段而调用 build
函数。
#[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)]
具有此属性的字段将被视为可选的,并将 expr
惰性评估为字段的默认值。应将 expr
设计为无参数的函数或闭包。
#[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]
如果存在此属性,则不会为该字段生成构建器函数。该字段需要 default
或 default_lazy
属性。
示例
#[derive(Builder)]
struct Test {
#[default(Uuid::new_v4())]
#[hidden]
id: Uuid,
name: String,
}
let test1 = Test::new() // TestBuilder<(), (), ...>
.name(String::from("Joe")) // TestBuilder<String, (), ...>
.build(); // Test
let test2 = Test::new() // TestBuilder<(), (), ...>
.name(String::from("Jack")) // TestBuilder<String, (), ...>
// Error: `id` function is not generated.
.id(Uuid::parse_str("46ebd0ee-0e6d-43c9-b90d-ccc35a913f3e").unwrap())
.build();
#[public]
如果存在此属性,即使字段是私有的,也会暴露具有设置器函数的字段。这提供了在构建过程中访问私有字段的方法。
示例
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<Uuid, (), ...>
.name("Joe") // TestBuilder<Uuid, &'static str, ...>
.build(); // Test
assert_eq!(test1.name, "Joe");
println!("{}", test1.id); // Error: `id` is a private field.
#[setter(value | lazy | async)]
如果存在此属性,则提供指定的设置器。如果没有,则只提供值设置器。
#[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
特征作为参数。您可以使用此设置器进行隐式转换。
示例
#[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<String, ...>
.build(); //
let test2 = Test::new() // TestBuilder<(), ...>
// `&str` is implicitly converted into `String`.
.name_lazy(|| "Hello") // TestBuilder<String, ...>
.build(); // Test
#[验证器(expr)]
为字段实现一个验证器。 expr
可以是一个验证函数,该函数接受字段的类型并返回 Result
。
#[derive(Builder)]
struct Test {
#[validator(is_not_empty)]
#[into]
pub name: String,
}
fn is_not_empty(name: String) -> Result<String, &'static str> {
if name.is_empty() {
Err("Name cannot be empty.")
} else {
Ok(name)
}
}
let test1 = Test::new() // TestBuilder<(), ...>
.name("Hello") // Ok(TestBuilder<String, ...>)
.unwrap() // TestBuilder<String, ...>
.build(); // Test
let test2 = Test::new() // TestBuilder<(), ...>
.name("") // Err(String{ "Validation failed: Name cannot be empty." })
.unwrap() // panic!
.build();
如果验证器与懒或异步设置器一起使用,它也将进行懒或异步验证。因此,设置器不会返回 Result
,而是在构建时返回。
#[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<String, ...>
.build() // Ok(Test)
.unwrap(); // Test
let test2 = Test::new() // TestBuilder<(), ...>
.name_async(|| async {
"Hello".to_string()
}) // TestBuilder<String, ...>
.build() // Future<Result<Test, Strin
.await // Ok(Test)
.unwrap(); // Test
自动生成的文档
此crate为构建函数生成文档。如果您为字段编写了文档,则相应的构建函数也会复制文档。
示例
示例代码
#[derive(Builder)]
struct Test {
/// A positive integer.
pub positive: i32,
/// An integer having zero as a default value.
#[default(0)]
pub zero: i32,
}
生成的代码
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),
}
}
}
/// A builder for `Test`.
struct TestBuilder<T1, T2> {
_phantom: PhantomData<(T1, T2)>,
positive: Option<i32>,
zero: Option<i32>,
}
impl TestBuilder<i32, i32> {
fn build(self) -> Test {
Test {
positive: self.positive.unwrap(),
zero: self.zero.unwrap(),
}
}
}
impl<T2> TestBuilder<(), T2> {
/// # positive
/// - Type: `i32`
///
/// A positive integer.
pub fn positive(self, value: i32) -> TestBuilder<i32, T2> {
TestBuilder {
_phantom: PhantomData,
positive: Some(Setter::Value(value)),
zero: self.zero,
}
}
}
impl<T1> TestBuilder<T1, ()> {
/// # zero
/// - Type: `i32`
/// - Default: `0`
///
/// An integer having zero as a default value.
pub fn zero(self, value: i32) -> TestBuilder<T1, i32> {
TestBuilder {
_phantom: PhantomData,
positive: self.positive,
zero: Some(Setter::Value(value)),
}
}
}
工作原理
以下代码
#[derive(Builder)]
struct Person {
#[into]
#[validator(is_not_empty)]
name: String,
age: i32,
#[default(Gender::Nonbinary)]
gender: Gender,
}
将生成
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<Setter<'a, i32>>,
name: Option<Setter<'a, String>>,
gender: Option<Setter<'a, Gender>>,
}
// 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<IntoType: Into<String>>(
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())),
}
}
}
许可证
依赖项
~1.5MB
~41K SLoC