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 解析器实现

Download history 163/week @ 2024-04-25 375/week @ 2024-05-02 147/week @ 2024-05-09 167/week @ 2024-05-16 153/week @ 2024-05-23 131/week @ 2024-05-30 104/week @ 2024-06-06 207/week @ 2024-06-13 171/week @ 2024-06-20 140/week @ 2024-06-27 498/week @ 2024-07-04 261/week @ 2024-07-11 100/week @ 2024-07-18 149/week @ 2024-07-25 165/week @ 2024-08-01 137/week @ 2024-08-08

每月567次 下载
用于 6 crates

MIT 许可证

95KB
1.5K SLoC

miniconf:对树进行序列化/反序列化/访问反射

crates.io docs QUARTIQ Matrix Chat Continuous Integration

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] 的派生宏。这些宏实现了 TreeKeyTreeSerializeTreeDeserialize 特质。构成内部节点(非叶节点)的字段/项需要实现相应的 Tree{Key,Serialize,Deserialize} 特质。叶字段/项需要支持相应的 serde 特质(和所需的 serde::Serializer/serde::Deserializer 后端)。

然后可以将结构体、数组和选项级联以构建更复杂的树。在使用派生宏时,可以使用 #[tree(depth(Y))] 属性为每个结构体字段配置行为和树递归深度。

有关详细信息,请参阅 TreeKey 特质文档。

键和路径

树查找是通过使用Keys实现来完成的。提供了一个通用的实现,通过IntoKeysIntoIterator处理Key项。为usize索引和&str名称实现了Key查找功能。

支持使用任意分隔符进行路径迭代。

可以从Packed结构中获取非常紧凑的分层索引编码。它实现了Keys

限制

某些类型内部字段的访问尚未支持,例如除了Option之外的其他枚举。然而,它们仍然可以作为原子serde形式作为叶子节点使用。

许多std智能指针不受支持或没有特殊处理:BoxRcArc

特性

  • json-core:启用从和到json切片的序列化JsonCoreSlash实现(使用serde_json_core存储库)。
  • postcard:启用从和到postcard紧凑二进制格式的序列化Postcard实现(使用postcard存储库)。
  • derive:启用miniconf_derive中的derive宏。默认启用。

反射

miniconf允许对异构树进行某些类型的反射访问。让我们将其与bevy_reflect进行比较,这是一个全面且成熟的反射存储库

bevy_reflect是彻底的std,而miniconf旨在no_stdbevy_reflect使用其Reflect特质来操作和传递节点作为特质对象。 miniconf使用序列化数据或Any来访问叶子节点,并使用纯“代码”来遍历内部节点。因此,如Reflect之类的Tree*特质提供了对节点的访问,但与Reflect不同,它们都绝对不是对象安全的,不能用作特质对象。这允许miniconf支持非'static借用数据(只有对于TreeAny叶子节点需要是'static),而bevy_reflect要求'static用于Reflect类型。

miniconf支持至少在bevy_reflect的README中提到的以下反射特性

  • ➕ 继承特质:miniconfTree*derive宏,为数组和Options提供了泛型实现。叶子节点只需实现一些Serialize/Deserialize/Any即可。
  • ➕ 使用它们的名称与字段交互
  • ➖ "修补"你的类型以使用新值:miniconf只支持在运行时对树结构进行有限的更改(Option和自定义访问器),而bevy_reflect具有强大的动态类型工具。
  • ➕ 使用“路径字符串”查找嵌套字段:除了JSON路径风格的“路径字符串”的超集之外,miniconf还支持层次索引和位打包有序键。
  • ➕ 遍历结构体字段:miniconf支持递归遍历节点键。
  • ➕ 通过Serde自动序列化和反序列化,无需显式实现serde:miniconf支持在没有显式容器serde实现的情况下自动将数据序列化和反序列化为键值对。
  • ➕ 特性“反射”:与crosstrait一起,支持构建类型注册表,并使得可以从TreeAny返回的dyn Any转换为其他所需的特性对象。与erased-serde一起,它可以用来实现使用miniconfTreeAny进行节点序列化和反序列化,而无需使用TreeSerialize/TreeDeserialize,类似于bevy_reflect

一些相关crate

  • serde-reflection:从serde实现中提取模式
  • typetag:“为特性对象推导serde”(本地特性和实现)
  • deflect:使用相邻的DWARF调试信息作为类型注册表的特性对象反射
  • intertraitcrosstrait的灵感和想法来源

函数式编程,多态

miniconf的类型异构性也接近函数式编程功能。以下crate可能也相关

依赖

~115–590KB
~13K SLoC