16个版本 (破坏性更新)
0.13.0 | 2024年7月10日 |
---|---|
0.11.0 | 2024年5月2日 |
0.9.0 | 2023年11月1日 |
0.7.1 | 2023年6月23日 |
0.2.0 | 2021年10月28日 |
#111 in 解析器实现
每月567次 下载
用于 6 crates
95KB
1.5K SLoC
miniconf:对树进行序列化/反序列化/访问反射
miniconf
通过键允许对异构类型树进行轻量级(no_std
/无分配)序列化、反序列化和访问。
示例
下面是一个示例,展示了 Tree*
特性的某些功能。有关详细信息,请参阅 TreeKey
特性的文档和doctests。
请注意,下面的示例侧重于JSON和斜杠分隔的路径,但实际上支持任何 serde
后端(或 dyn Any
特性对象)以及许多不同的 Keys/
Transcode
提供者。
use serde::{Deserialize, Serialize};
use miniconf::{Error, JsonCoreSlash, JsonPath, Traversal, Tree, TreeKey, Path, Packed, Node};
#[derive(Deserialize, Serialize, Default)]
enum Either {
#[default]
Bad,
Good,
}
#[derive(Deserialize, Serialize, Default, Tree)]
struct Inner {
a: i32,
b: i32,
}
#[derive(Tree, Default)]
struct Settings {
foo: bool,
enum_: Either,
struct_: Inner,
array: [i32; 2],
option: Option<i32>,
// Skipping (`()` is not Deserialize/Serialize)
#[tree(skip)]
skipped: (),
#[tree(depth=1)]
struct_tree: Inner,
#[tree(depth=1)]
array_tree: [i32; 2],
#[tree(depth=2)]
array_tree2: [Inner; 2],
#[tree(depth=1)]
option_tree: Option<i32>,
#[tree(depth=2)]
option_tree2: Option<Inner>,
#[tree(depth=3)]
array_option_tree: [Option<Inner>; 2],
}
let mut settings = Settings::default();
// Atomic updates by field name
settings.set_json("/foo", b"true")?;
assert_eq!(settings.foo, true);
settings.set_json("/enum_", br#""Good""#)?;
settings.set_json("/struct_", br#"{"a": 3, "b": 3}"#)?;
settings.set_json("/array", b"[6, 6]")?;
settings.set_json("/option", b"12")?;
settings.set_json("/option", b"null")?;
// Exposing nodes of containers
// ... by field name in a struct
settings.set_json("/struct_tree/a", b"4")?;
// ... or by index in an array
settings.set_json("/array_tree/0", b"7")?;
// ... or by index and then struct field name
settings.set_json("/array_tree2/0/a", b"11")?;
// ... or by hierarchical index
settings.set_json_by_key([7, 0, 1], b"8")?;
// ... or by packed index
let (packed, node) = Settings::transcode::<Packed, _>([7, 1, 0]).unwrap();
assert_eq!(packed.into_lsb().get(), 0b1_0111_1_0);
assert_eq!(node, Node::leaf(3));
settings.set_json_by_key(packed, b"9")?;
// ... or by JSON path
settings.set_json_by_key(&JsonPath(".array_tree2[1].b"), b"10")?;
// Hiding paths by setting an Option to `None` at runtime
assert_eq!(settings.set_json("/option_tree", b"13"), Err(Traversal::Absent(1).into()));
settings.option_tree = Some(0);
settings.set_json("/option_tree", b"13")?;
// Hiding a path and descending into the inner `Tree`
settings.option_tree2 = Some(Inner::default());
settings.set_json("/option_tree2/a", b"14")?;
// Hiding items of an array of `Tree`s
settings.array_option_tree[1] = Some(Inner::default());
settings.set_json("/array_option_tree/1/a", b"15")?;
let mut buf = [0; 16];
// Serializing nodes by path
let len = settings.get_json("/struct_", &mut buf).unwrap();
assert_eq!(&buf[..len], br#"{"a":3,"b":3}"#);
// Iterating over all paths
for path in Settings::nodes::<Path<String, '/'>>() {
let (path, node) = path.unwrap();
assert!(node.is_leaf());
// Serialize each
match settings.get_json(&path, &mut buf) {
// Full round-trip: deserialize and set again
Ok(len) => { settings.set_json(&path, &buf[..len])?; }
// Some settings are still `None` and thus their paths are expected to be absent
Err(Error::Traversal(Traversal::Absent(_))) => {}
e => { e.unwrap(); }
}
}
# Ok::<(), Error<serde_json_core::de::Error>>(())
设置管理
miniconf
的一个可能用途是嵌入式设备运行时设置管理的后端。
它最初设计用于与JSON(serde_json_core
)有效载荷通过MQTT(minimq
)一起工作,并在 miniconf_mqtt
包中提供了一个MQTT设置管理客户端,以及一个用于与其交互的Python参考实现。Miniconf对 serde
后端/格式、层次分隔符以及传输/协议是无关的。
格式
miniconf
可以与任何 serde::Serializer
/serde::Deserializer
后端和键格式一起使用。
支持使用 /
作为路径分层分隔符,并通过 JsonCoreSlash
子特质实现了 JSON (serde_json_core
)。
Postcard
子特质支持任何 postcard
味道和任何 Keys
类型的 postcard
线路格式。结合 Packed
键表示法,这是一个非常节省空间的 serde-by-key API。
为所有格式提供了所有 TreeSerialize
+TreeDeserialize
类型的泛型实现。
传输
miniconf
也是协议无关的。任何可以接收或发射序列化键值数据的手段都可以用来通过路径访问节点。
miniconf_mqtt
包中的 MqttClient
实现了通过 MQTT 协议(带有 JSON 负载)进行设置管理。提供了一个与之接口的 Python 参考库。此示例发现监听主题 quartiq/application/12345
的应用程序的唯一前缀,并将其 /foo
设置设置为 true
。
python -m miniconf -d quartiq/application/+ /foo=true
派生宏
对于结构体,miniconf
提供了 [macro@TreeKey
], [macro@TreeSerialize
], 和 [macro@TreeDeserialize
] 的派生宏。这些宏实现了 TreeKey
、TreeSerialize
和 TreeDeserialize
特质。构成内部节点(非叶节点)的字段/项需要实现相应的 Tree{Key,Serialize,Deserialize}
特质。叶字段/项需要支持相应的 serde
特质(和所需的 serde::Serializer
/serde::Deserializer
后端)。
然后可以将结构体、数组和选项级联以构建更复杂的树。在使用派生宏时,可以使用 #[tree(depth(Y))]
属性为每个结构体字段配置行为和树递归深度。
有关详细信息,请参阅 TreeKey
特质文档。
键和路径
树查找是通过使用Keys
实现来完成的。提供了一个通用的实现,通过IntoKeys
为IntoIterator
处理Key
项。为usize
索引和&str
名称实现了Key
查找功能。
支持使用任意分隔符进行路径迭代。
可以从Packed
结构中获取非常紧凑的分层索引编码。它实现了Keys
。
限制
某些类型内部字段的访问尚未支持,例如除了Option
之外的其他枚举。然而,它们仍然可以作为原子serde
形式作为叶子节点使用。
许多std
智能指针不受支持或没有特殊处理:Box
、Rc
、Arc
。
特性
json-core
:启用从和到json切片的序列化JsonCoreSlash
实现(使用serde_json_core
存储库)。postcard
:启用从和到postcard紧凑二进制格式的序列化Postcard
实现(使用postcard
存储库)。derive
:启用miniconf_derive
中的derive宏。默认启用。
反射
miniconf
允许对异构树进行某些类型的反射访问。让我们将其与bevy_reflect
进行比较,这是一个全面且成熟的反射存储库
bevy_reflect
是彻底的std
,而miniconf
旨在no_std
。 bevy_reflect
使用其Reflect
特质来操作和传递节点作为特质对象。 miniconf
使用序列化数据或Any
来访问叶子节点,并使用纯“代码”来遍历内部节点。因此,如Reflect
之类的Tree*
特质提供了对节点的访问,但与Reflect
不同,它们都绝对不是对象安全的,不能用作特质对象。这允许miniconf
支持非'static
借用数据(只有对于TreeAny
叶子节点需要是'static
),而bevy_reflect
要求'static
用于Reflect
类型。
miniconf
支持至少在bevy_reflect
的README中提到的以下反射特性
- ➕ 继承特质:
miniconf
有Tree*
derive宏,为数组和Options提供了泛型实现。叶子节点只需实现一些Serialize/Deserialize/Any
即可。 - ➕ 使用它们的名称与字段交互
- ➖ "修补"你的类型以使用新值:
miniconf
只支持在运行时对树结构进行有限的更改(Option
和自定义访问器),而bevy_reflect
具有强大的动态类型工具。 - ➕ 使用“路径字符串”查找嵌套字段:除了JSON路径风格的“路径字符串”的超集之外,
miniconf
还支持层次索引和位打包有序键。 - ➕ 遍历结构体字段:
miniconf
支持递归遍历节点键。 - ➕ 通过Serde自动序列化和反序列化,无需显式实现serde:
miniconf
支持在没有显式容器serde实现的情况下自动将数据序列化和反序列化为键值对。 - ➕ 特性“反射”:与
crosstrait
一起,支持构建类型注册表,并使得可以从TreeAny
返回的dyn Any
转换为其他所需的特性对象。与erased-serde
一起,它可以用来实现使用miniconf
的TreeAny
进行节点序列化和反序列化,而无需使用TreeSerialize
/TreeDeserialize
,类似于bevy_reflect
。
一些相关crate
serde-reflection
:从serde实现中提取模式typetag
:“为特性对象推导serde”(本地特性和实现)deflect
:使用相邻的DWARF调试信息作为类型注册表的特性对象反射intertrait
:crosstrait
的灵感和想法来源
函数式编程,多态
miniconf
的类型异构性也接近函数式编程功能。以下crate可能也相关
依赖
~115–590KB
~13K SLoC