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使用
74KB
1.5K SLoC
Builder宏
此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.rs
或main.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);