4个版本
0.1.2 | 2024年5月18日 |
---|---|
0.1.1 | 2024年5月17日 |
0.1.0 | 2024年5月17日 |
0.0.1 | 2024年5月17日 |
#111 in 构建工具
98 每月下载量
用于 poestat_static
27KB
426 行
uneval_static
将数据序列化到可以作为静态变量存储的Rust源代码中
如何实现?
在 build.rs
中生成数据
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
struct Unit(());
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
struct Input {
string: String,
vector: Vec<i32>,
reference: Option<Box<Input>>,
map: HashMap<String, Unit>,
}
fn main() {
let input = Input::deserialize(json!({
"string": "test",
"vector": [1,2,3],
"reference": {"string": "", "vector": [], "map": {}},
"map": {"key": null},
}))
.unwrap();
let path = Path::new(&env::var("OUT_DIR").unwrap()).join("generated.rs");
let mut uneval = uneval_static::ser::Uneval::new(File::create(path).unwrap());
// Mappings may be required depending on your data structure
// Serde provides type names and field names as plain identifiers;
// these can be mapped to any output text
uneval.add_mapping("Input".into(), "&Output".into());
uneval.add_mapping("vector".into(), "slice".into());
uneval.serialize(input).unwrap();
}
然后在 src/some_mod.rs
中包含生成的代码
#![allow(clippy::all)]
pub struct Unit(());
pub struct Output {
string: &'static str,
slice: &'static [i32],
reference: Option<&'static Output>,
map: phf::Map<&'static str, Unit>,
}
pub static VALUE: &Output = include!(concat!(env!("OUT_DIR"), "/generated.rs"));
为什么这么做?
从库中分叉出的这个crate,uneval,通过使用如.into()
之类的trait函数来将serde类型转换为Rust类型,从而提供类型灵活性。然而,这意味着输出代码必须承担一些运行时成本来初始化自身,这在情感层面上以及性能层面上都是次优的。这个crate输出的代码可以适应更窄的类型范围,但不需要lazy_static
初始化器。
它是如何工作的?
有关详细信息,请参阅crate文档。简而言之,我们使用Serde提供的信息来生成代码,当将其分配给正确类型的变量时,将使用Into
和迭代器提供所有必要的转换。
这真的那么简单吗?
嗯...并不简单。存在一些限制。
- 序列化结构体中使用的所有类型必须在包含位置的作用域内。Serde不提供序列化器的限定名称(即路径),只提供“最后一个”名称。可能最简单的方法是将序列化数据用作以下
let static VALUE: MainType = {
use ::path::to::Type1;
// ...and other types
include!("path/to/file.rs")
}
- 因此,序列化器使用的所有类型都必须具有不同的名称(否则它们将相互冲突)。
- 没有实现反序列化器。这是故意的,因为这个crate并不真正打算用于运行时使用。实际上,反序列化器已经实现了 - 它实际上是Rust编译器本身。
- 本序列化器旨在与派生实现一起使用。当与定制的
Serialize
一起使用时,可能会返回错误的结果。
如果您发现其他任何不工作的情况,请随时提交一个 pull request。
测试
此 crate 使用 trybuild
来运行其测试。每个测试案例都会输出到 test_fixtures/
目录下的单独目录,其中 {test_name}/main.rs
首先被编译和运行,从而创建 {test_name}/generated.rs
,用于编译 {test_name}/user.rs
,断言生成的值与输入匹配。
测试数据定义在 test_fixtures/data.toml 中,格式如下
- TOML 中的部分名称对应于测试案例的名称。请注意,只有一个测试由 cargo 运行,每个测试案例由 cargo test 在多个阶段生成、编译和运行。
- 字段
main_type
对应于正在测试的序列化类型。 - 字段
definition
是类型定义。它将原样包含在{test_name}/main.rs
中,并在将其转换为静态声明后包含在{test_name}/user.rs
中。由于这些特质在测试入口运行期间使用,因此需要在这些类型上实现Debug
、Serialize
和PartialEq
。 - 字段
value
实际上在两个地方复制:首先,在{test_name}/main.rs
中生成代码;其次,在{test_name}/user.rs
中,测试检查两个值是否相等。 - 字段
test_values
是可选的,对于在user.rs
断言中难以使用value
的情况。而不是在整个结构体上断言相等,test_values
映射中的每个值都会生成单个断言。
许可证
MIT
依赖项
~1-2MB
~42K SLoC