4 个版本 (2 个破坏性更新)
0.3.0 | 2023 年 10 月 3 日 |
---|---|
0.2.0 | 2023 年 9 月 20 日 |
0.1.1 | 2023 年 9 月 13 日 |
0.1.0 | 2023 年 9 月 13 日 |
#1749 in Rust 模式
每月 39 次下载
用于 ansi_parser_extended
30KB
174 行
Builder
Derive 宏文档
Builder
derive 宏用于为 Rust 中的 struct 生成 builder 方法,这个 crate 的最大特点是它提供了对 struct 的编译时验证。用户可以采用一些配置来定义复杂 struct 的有效性,并在 struct 创建之前进行检查。
该库还检查了 struct 何时永远有效或始终无效的情况,但这仍在进行中。错误总是被发出,但警告仅在夜间通道上发出。这是由于 proc_macro_error 的限制。
先决条件
要使用 Builder
derive 宏,您应该在项目的 Cargo.toml
文件中将 const_typed_builder
crate 添加到项目的依赖项中
[dependencies]
const_typed_builder = "0.3"
同时,请确保您的代码中有以下使用声明
use const_typed_builder::Builder;
概述
该 crate 可用于在编译时检查 struct 的有效性。用户必须在 struct 有效之前调用 build。这是通过检查所有必需字段是否实例化以及所有用户定义的 "组" 是否有效来完成的。
示例
基本用法
use const_typed_builder::Builder;
#[derive(Builder)]
pub struct Foo {
bar: String,
}
let foo = Foo::builder()
.bar("Hello world!".to_string()) // <- The program would not compile without this call
.build(); // <- Because this function is only implemented for the
// .. version of FooBuilder where `bar` is initialized
launchd 的子集
use const_typed_builder::Builder;
use std::path::PathBuf;
#[derive(Builder)]
pub struct ResourceLimits {
core: Option<u64>,
// ...
}
#[derive(Builder)]
#[group(program = at_least(1))]
pub struct Launchd {
#[builder(mandatory)]
label: Option<String>,
disabled: Option<bool>,
user_name: Option<String>,
group_name: Option<String>,
// ...
#[builder(group = program)]
program: Option<PathBuf>,
bundle_program: Option<String>,
#[builder(group = program)]
program_arguments: Option<Vec<String>>,
// ...
#[builder(skip)]
on_demand: Option<bool>, // NB: deprecated (see KeepAlive), but still needed for reading old plists.
#[builder(skip)]
service_ipc: Option<bool>, // NB: "Please remove this key from your launchd.plist."
// ...
#[builder(propagate)]
soft_resource_limits: Option<ResourceLimits>,
#[builder(propagate)]
hard_resource_limits: Option<ResourceLimits>,
// ...
}
let launchd = Launchd::builder()
.label("my_label".to_string()) // <- 1: Mandatory
.program("./my_program".into()) // <- 2: Launchd expects that least one of these fields is set..
.program_arguments( // <- 2: .. We can remove either one, but never both
vec!["my_arg".to_string()]
)
// .on_demand(false) <- 3: This function doesn't exist
.soft_resource_limits(|builder|
Some(builder.core(Some(1)).build()) // <- 4: Propagating to `ResourceLimits::builder`
)
.build();
属性
这是对这个库中功能的快速概述。有关所有功能的更深入解释,包括示例,请参阅 const_typed_builder_derive::Builder
。 Struct
#[builder(assume_mandatory)]
:表示结构体中的所有字段都应被视为必填项。如果不提供等号(例如,#[builder(assume_mandatory)]
),则将字段的mandatory
标志设置为 true。如果提供等号(例如,#[builder(assume_mandatory = true)]
),则根据值设置字段的mandatory
标志。#[group(group_name = (exact(N)|at_least(N)|at_most(N)|single)]
:将结构体的字段与名为 "group_name" 的组关联,并指定组的操作行为。group_name
应该是一个字符串标识符。该组可以有以下几种操作行为exact(N)
:在构建器构建过程中,组中必须设置恰好 N 个字段。at_least(N)
:在构建器构建过程中,组中至少必须设置 N 个字段。at_most(N)
:在构建器构建过程中,组中最多可以设置 N 个字段。single
:在构建器构建过程中,组中只能设置一个字段。这是exact(1)
的简写。例如,#[group(foo = at_least(2))]
创建了一个组,其中至少需要初始化 2 个字段。
#[builder(solver = (brute_force|compiler))]
: 请谨慎使用,请参阅此文件底部注释!指定构建结构时使用的求解器类型。`solve_type
` 应为预定义的求解器类型之一,例如 `brute_force
` 或 `compiler
`。如果提供等于号(例如,`#[builder(solver = brute_force)]
`),则设置相应的“求解器类型”。此属性仍在测试中,`brute_force
` 是默认值,只有在编译时间有问题时,您可以尝试使用 `compiler
`。但 `compiler
` 提供的保证较少。
字段
#[builder(group = group_name)]
: 该库的核心。将字段与名为 `group_name
` 的组关联。同一组中的字段被视为一个单元,在构建器构建过程中至少必须设置其中一个。此属性允许将组名指定为标识符(例如,`group = my_group
`)或字符串(例如,`group = "my_group"
`)。#[builder(mandatory)]
: 标记字段为必填项,意味着在构建器构建过程中必须设置它。如果没有提供等于号(例如,`#[builder(mandatory)]
`),则将字段设置为必填项。如果提供等于号(例如,`#[builder(mandatory = true)]
`),则根据值设置必填标志。#[builder(optional)]
:将字段标记为可选,这与#[builder(mandatory)]
正好相反。如果不提供等号(例如,#[builder(optional)]
),则将字段设置为可选。如果提供等号(例如,#[builder(optional = true)]
),则根据值设置可选标志。#[builder(skip)]
:将字段标记为跳过,这意味着构建器将不包括它。这可以用于已弃用但必须反序列化的字段。这样,您可以确保新结构体永远不会使用此字段初始化,但旧结构体仍然可以使用。为了实现这一点,字段类型必须是Option<T>
。#[builder(propagate)]
:表示字段在构建器构建时应传播其值。如果存在此属性,则当使用构建器构建对象时,字段值将被复制或移动到构建的对象中。
字段可以是组的一部分、必填、可选或跳过。这些属性是互斥的。propagate
可以用于任何类型也派生自Builder
的字段。
[!注意]检查每个字段的合法性是与SAT问题直接相关的,SAT问题是一个NP完全问题。这对分组字段尤其有影响。当前默认的实现是
brute_force
,这个实现目前的复杂度是$O(2^g)
,其中$g
是分组变量的数量。这对几个字段来说不是问题,但可能会显著影响编译时间。这仍然可以进行显著的优化。未来的版本可能会提高这个复杂度。另一种实现是
compiler
。我还没有测试其速度提升,但可能存在一些问题。尽管我还没有能够重现该问题,但似乎const值并不保证在编译时被评估。这导致的问题是在编译时组验证不一定失败。用户可以通过添加以下代码到结构体上方来选择使用
compiler
解析器,#[builder(solver = compiler)]
来启用。我对其性能不提供任何保证。欢迎任何愿意帮助,并添加SAT解析器作为依赖(在特性标志之后)的人!
灵感
构建器宏以前已经做过,但并不完全符合我的使用场景。还可以看看derive_builder和typed-builder。这些项目目前更为成熟,但任何愿意测试这个crate的人都是天赐良机。
依赖项
~1.3–1.9MB
~36K SLoC