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

Download history 378/week @ 2024-04-23 622/week @ 2024-04-30 402/week @ 2024-05-07 310/week @ 2024-05-14 510/week @ 2024-05-21 432/week @ 2024-05-28 264/week @ 2024-06-04 170/week @ 2024-06-11 184/week @ 2024-06-18 217/week @ 2024-06-25 263/week @ 2024-07-02 250/week @ 2024-07-09 196/week @ 2024-07-16 165/week @ 2024-07-23 382/week @ 2024-07-30 325/week @ 2024-08-06

每月下载量 1,155
10crate中使用(直接使用 8 个)

MIT 许可证

38KB

builder-pattern

creates.io API Docs

Build Status (Windows) Build Status (MacOS) Build Status (Ubuntu) Build Status (WebAssembly)

用于声明构建器模式的可衍化宏。这个 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]

如果存在此属性,则不会为该字段生成构建器函数。该字段需要 defaultdefault_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())),
        }
    }
}

许可证

MIT

依赖项

~1.5MB
~41K SLoC