#configuration #hash-map #map #toml #condition #values #wrapper

cfgmap

专门针对配置而制作的特殊哈希表

8 个不稳定版本 (3 个破坏性更新)

0.4.0 2020年6月1日
0.3.1 2020年3月3日
0.2.3 2020年3月2日
0.1.0 2020年2月29日

#877 in 配置

每月23次下载
用于 glitchup

MPL-2.0 许可证

69KB
842

cfgmap

一个专门针对配置而制作的特殊哈希表。


这个crate包含一个新的数据结构,它作为一个包装器围绕着一个HashMap。它提供了一个自己的数据枚举(CfgValue),并包含多个辅助函数,可以轻松地在哈希表中导航。

其主要目的是用于配置,同时也支持验证。本质上,一个CfgMap将代表一个应用程序的配置。到目前为止,配置的替代方案是直接使用数据格式库,或者使用一个结构体,例如JSON或TOML,将其序列化。

这可以非常令人满意,特别是对于基本的配置,但在某些情况下可能会显得有些繁琐。例如,如果你计划在某些选项未设置时使用默认选项,需要验证多个嵌套对象等。

如果你想使用这个crate提供的最常见功能,你可以这样做

use cfgmap::prelude::*;

这将包括CfgMap,所有CfgValue,所有公共宏,所有Conditions,以及Checkable特性。

特性

这个crate是可定制的,可以根据你的需求提供多个特性

  • from_toml:允许从TOML值创建哈希表,还包含额外的Datetime CfgValue
  • from_json:允许从JSON值创建哈希表,还包含额外的Null CfgValue
  • generator:为CfgValue提供了额外的生成数字(整数或浮点数)的方法。

教程(类似)

创建一个新的CfgMap非常简单,有多种方法

use cfgmap::CfgMap;

let map1 = CfgMap::new();
let mut map2 = CfgMap::new();
map2.default = "default".into();

CfgMap 允许对默认值进行一些功能设置。对于上面的 map1default 从未被设置,因此值将从根目录检索。然而,对于 map2,假设所有默认值都位于 default 中。

路径语法

CfgMap 还支持其键的特定 path 语法。

cfgmap.get("hello/there/pal");

这有助于轻松访问嵌套项。上面的行本质上等于

map.get("hello")
    .and_then(|a| a.as_map())
    .and_then(|a| a.get("there"))
    .and_then(|a| a.as_map())
    .and_then(|a| a.get("pal"));

注意,如果 hellothere 不是 CfgMap,整个表达式的求值结果将为 None。此键还可以包含数组索引。例如,对于 a/0/c,它将检查 a 是一个 Map 还是 List。如果是前者,它将尝试找到具有值 0 的键。如果是后者,它将尝试索引列表。

条件

现在,如果你想检查某个值求值的结果是什么?如果你想要使用任何值,你很快就会遇到这个问题。这个库提供了对 Conditions 的广泛支持!

use cfgmap::{Condition::*, Checkable};
let is_number = cfgmap.get("hello/there/pal").check_that(IsInt | IsFloat);

上面的行将检查 hello/there/pal 位置的值是否为 CfgValue::IntCfgValue::Float。更多条件列在这里。如果你希望添加更多条件,请随时提出问题或提交 PR!所有这些都有助于帮助验证特定值。

默认值

默认值也可以非常容易地使用:

map.get_option("http_settings", "ip_address");

假设 map 在其默认值 default 下初始化。上面的行将与以下行等效

map.get("http_settings/ip_address").or(map.get("default/ip_address"));

您还可以使用 update_option 更新选项,就像使用 add 一样。它的工作方式相似,但在找不到选项时不会添加新的选项,只会更新现有的选项。

HashMap 方法

由于 CfgMapHashMap<String, CfgValue> 实现了 DerefDerefMut,因此所有 HashMap 方法都可用。例如,您可以对其调用 .iter(),尽管这没有直接实现。

完整示例

use cfgmap::{CfgMap, CfgValue::*, Condition::*, Checkable};

let toml = toml::toml! {
   [package]
   name = "cfgmap"
   version = "0.1.0"
   authors = ["Andrea Jenkins <[email protected]>"]

   [lib]
   name = "cfgmap"
   path = "src/cfgmap.rs"

   [dependencies]
   serde_json = { version = "1.0.48", optional = true }
   toml = { version = "0.5.6", optional = true }

   [other]
   date = 2020-02-29
   float = 1.2
   int = 3
   internal.more = "hello"

   [[person]]
   name = "a"

   [[person]]
   name = "b"
};

let cmap = CfgMap::from_toml(toml);

assert!(cmap.get("package/name").check_that(IsExactlyStr("cfgmap".into())));
assert!(cmap.get("package/version").check_that(IsExactlyStr("0.1.0".into())));
assert!(cmap.get("package/authors").check_that(IsExactlyList(vec![Str("Andrea Jenkins <[email protected]>".into())])));

assert!(cmap.get("lib/name").check_that(IsExactlyStr("cfgmap".into())));
assert!(cmap.get("lib/path").check_that(IsExactlyStr("src/cfgmap.rs".into())));

assert!(cmap.get("dependencies/serde_json/version").check_that(IsExactlyStr("1.0.48".into())));
assert!(cmap.get("dependencies/serde_json/optional").check_that(IsTrue));
assert!(cmap.get("dependencies/toml/version").check_that(IsExactlyStr("0.5.6".into())));
assert!(cmap.get("dependencies/toml/optional").check_that(IsTrue));

assert!(cmap.get("other/date").check_that(IsDatetime));
assert!(cmap.get("other/float").check_that(IsExactlyFloat(1.2)));
assert!(cmap.get("other/int").check_that(IsExactlyInt(3)));
assert!(cmap.get("other/internal/more").check_that(IsExactlyStr("hello".into())));

assert!(cmap.get("person").check_that(IsListWith(Box::new(IsMap))));
assert!(cmap.get("person/0/name").check_that(IsExactlyStr("a".into())));
assert!(cmap.get("person/1/name").check_that(IsExactlyStr("b".into())));

依赖关系

~0–430KB