#builder #consuming #macro #non-consuming

builder_macro

一个用于生成结构和相应构建器的宏

7个版本 (4个破坏性版本)

使用旧的Rust 2015

0.5.1 2016年11月27日
0.5.0 2016年11月25日
0.4.0 2016年10月24日
0.3.0 2016年10月19日
0.1.1 2016年10月9日

#2571 in Rust模式


entropy-rs使用

MIT许可证

74KB
1.5K SLoC

Rust 1.5K SLoC // 0.1% comments Shell 189 SLoC // 0.1% comments

Builder宏

Crates.io Build Status Coverage Status

此crate包含一个builder!宏,用于声明结构和相应的构建器。

该宏受到jadpole/builder-macro的启发,旨在消除字段声明的重复,并生成适当的setter方法。

文档

文档和使用示例可在azriel.im/builder_macro找到。

许可证

MIT许可证


lib.rs:

此crate包含两个宏,用于声明结构和相应的构建器。

  • data_struct!:构建器返回一个Result<StructName, &'static str>
  • object_struct!:构建器返回声明的StructName

该宏受到jadpole/builder-macro的启发,旨在消除字段声明的重复,并生成适当的setter方法。

背景

有关用法,请参阅用法部分。

此crate旨在支持两种结构类型

  • 数据结构:参数值仅在运行时知道,并且应该由应用程序处理构建失败。
  • 对象结构:参数值主要在编译时知道,构建失败意味着应用程序无法正常工作,应该引发恐慌。

对于数据结构,返回一个Result允许调用者优雅地处理失败。对于对象结构,任何panic!应该在发布之前由开发者捕获。通过删除中介的Result,开发者也不再需要调用unwrap(),这使得代码更加简洁。

用法

在您的crate的Cargo.toml中指定依赖项

[dependencies]
builder_macro = "0.5.0"

在您的crate的lib.rsmain.rs中包含宏。

#[macro_use]
extern crate builder_macro;
#

示例

免责声明:以下示例使用data_struct!宏。它们同样适用于object_struct!宏,区别在于返回类型是结构本身,而不是Result

非消耗性构建器

使用构建宏生成非消耗型构建器的最简单方法是

#
data_struct!(ItemBuilder -> Item {
    required_field: i32,
    defaulted_field: &'static str = "abc",
});

let item = ItemBuilder::new(123).build().unwrap();
let another = ItemBuilder::new(456).defaulted_field("def").build().unwrap();

assert_eq!(123, item.required_field);
assert_eq!("abc", item.defaulted_field);
assert_eq!(456, another.required_field);
assert_eq!("def", another.defaulted_field);

生成的代码功能如下

#
struct Item {
    required_field: i32,
    defaulted_field: &'static str,
}

/// Auto-generated builder
struct ItemBuilder {
    required_field: Option<i32>,
    defaulted_field: Option<&'static str>,
}

impl ItemBuilder {
    /// Construct the builder
    pub fn new(required_field: i32) -> ItemBuilder {
        ItemBuilder { required_field: Some(required_field), defaulted_field: Some("abc"), }
    }

    /// Build the struct
    pub fn build(&self) -> Result<Item, &'static str> {
        let required_field = self.required_field.clone().ok_or(
            concat!("Must pass argument for field: '", stringify!(required_field), "'"))?;
        let defaulted_field = self.defaulted_field.clone().ok_or(
            concat!("Must pass argument for field: '", stringify!(defaulted_field), "'"))?;

        Ok(Item { required_field: required_field, defaulted_field: defaulted_field })
    }

    #[allow(dead_code)]
    /// Auto-generated setter
    pub fn defaulted_field(&mut self, defaulted_field: &'static str) -> &mut Self {
        self.defaulted_field = Some(defaulted_field);
        self
    }
}

要生成公开的结构体和构建器,请参阅可见性

消耗型构建器

当生成的结构体应拥有特例对象时,它们无法被克隆,因此构建器必须将所有权转移到构造实例上。

要生成消耗型构建器,在构建器名称和目标结构体名称之间使用=>代替->

#
trait Magic {
    fn abracadabra(&mut self) -> i32;
}
struct Dust {
    value: i32,
}
impl Magic for Dust {
    fn abracadabra(&mut self) -> i32 {
        self.value
    }
}

// Note: we use => instead of -> for the consuming variant of the builder
data_struct!(MyStructBuilder => MyStruct {
    field_trait: Box<Magic> = Box::new(Dust { value: 1 }),
    field_vec: Vec<Box<Magic>> = vec![Box::new(Dust { value: 2 })],
});

let mut my_struct = MyStructBuilder::new().build().unwrap();

assert_eq!(my_struct.field_trait.abracadabra(), 1);
assert_eq!(my_struct.field_vec[0].abracadabra(), 2);

可见性

生成模块私有可见性的构建器和结构体

#
data_struct!(MyStructBuilder -> MyStruct {
    field_i32: i32 = 123,
    field_str: &'static str = "abc",
});

let my_struct = MyStructBuilder::new()
    .field_i32(456)
    .build()
    .unwrap();
assert_eq!(my_struct.field_i32, 456);
assert_eq!(my_struct.field_str, "abc"); // uses default

生成公开可见性的构建器和结构体

#
mod inner {
    data_struct!(pub MyStructBuilder -> MyStruct {
        pub field_i32: i32 = 123,
        field_str: &'static str = "abc",
    });
}

let my_struct = inner::MyStructBuilder::new()
    .field_i32(456)
    .build()
    .unwrap();
assert_eq!(my_struct.field_i32, 456);

// The next line will fail compilation if uncommented as field_str is private
// assert_eq!(my_struct.field_str, "abc");

断言

您可以在assertions: { ... {块内指定字段声明后的断言。

如果断言失败,则build()方法将返回一个Err(...)

#
data_struct! {
    pub BuilderName -> StructName {
        #[allow(dead_code)]
        a_private_field: &'static str,
        /// a_field is an i32 which must be between 0 and 100 inclusive
        pub a_field: i32 = 50,
    }, assertions: {
        assert!(a_field >= 0);
        assert!(a_field <= 100);
        // Yes you can assert on private fields
        assert!(!a_private_field.is_empty());
    }
}

let result_1 = BuilderName::new("non-empty string").build();
let result_2 = BuilderName::new("").build();

assert!(result_1.is_ok());
assert_eq!(result_2.err(),
           Some("assertion failed: 'assert!(! a_private_field . is_empty (  ))'"));

完整使用格式

完整宏使用格式为

#
// We declare the builder insider a module simply to demonstrate scope
mod inner {
    data_struct! {
        /// StructName is an example struct.
        /// These docs are copied over to the generated struct.
        pub BuilderName -> StructName {
            // meta attributes are copied over to the struct's fields
            #[allow(dead_code)]
            a_private_field: &'static str,

            /// a_field is an i32 which must be between 0 and 100 inclusive
            pub a_field: i32 = 50,
        }, assertions: {
            assert!(a_field >= 0);
            assert!(a_field <= 100);
            // Yes you can assert on private fields
            assert!(!a_private_field.is_empty());
        }
    }
}

let my_struct = inner::BuilderName::new("a_private_field must be non-empty")
    .build()
    .unwrap();

assert_eq!(50, my_struct.a_field);

无运行时依赖