7个版本 (2个稳定版)
1.0.1 | 2022年4月15日 |
---|---|
1.0.0 | 2021年2月21日 |
0.9.2 | 2021年1月3日 |
0.9.1 | 2020年8月22日 |
0.8.1 | 2020年4月17日 |
#164 in 配置
每月下载量56
用于 serenade
140KB
1.5K SLoC
Rust配置库
Just-config是一个Rust配置库。它力求遵循旧Unix的格言“只做一件事,做好这件事”。它只是构建用来从不同的来源读取配置值,并将它们融合成一个易于处理的配置源。其主要目的是供配置类使用,并填充不同类型的配置值。
开箱即用,它具有以下配置来源
- 静态(回退、命令行)
- 环境变量
- 配置文件
它内置了验证支持,并可以接受每个键的多个配置值。它甚至可以限制给定配置键可以接受的配置值的数量。
编写自己的配置来源(例如,用于etcd)非常容易。您只需实现Source
trait的get
方法。
如果您只想使用这个库,打开文档,查看示例和描述,并通过在您的Cargo.toml
文件中添加以下内容开始使用它:
justconfig = "1.0"
基本示例
这里是一个小预告,基本示例是从文档中复制过来的。
use justconfig::Config;
use justconfig::ConfPath;
use justconfig::sources::text::ConfigText;
use justconfig::sources::env::Env;
use justconfig::sources::defaults::Defaults;
use justconfig::processors::Explode;
use justconfig::validators::Range;
use justconfig::item::ValueExtractor;
use std::ffi::OsStr;
use std::fs::File;
let mut conf = Config::default();
// Allow some environment variables to override configuration values read
// from the configuration file.
let config_env = Env::new(&[
(ConfPath::from(&["searchPath"]), OsStr::new("SEARCH_PATH")),
]);
// Open the configuration file
let config_file = File::open("myconfig.conf").expect("Could not open config file.");
conf.add_source(ConfigText::new(config_file, "myconfig.conf").expect("Loading configuration file failed."));
// Read the value `num_frobs` from the configuration file.
// Do not allow to use more than 10 frobs.
let num_frobs: i32 = conf.get(conf.root().push("num_frobs")).max(10).value()?;
// Read a list of tags from the configuration file.
let tag_list: Vec<String> = conf.get(conf.root().push("tags")).values(..)?;
// Read the paths from the config file and allow it to be overriden by
// the environment variable. We split everything at `:` to allow passing
// multiple paths using an environment variable. When read from the config
// file, multiple values can be set without using the `:` delimiter.
// Passing 1.. to values() makes sure at least one search path is set.
let search_paths: Vec<String> = conf.get(conf.root().push("searchPath")).explode(':').values(1..)?;
变更日志
-
版本 0.8.0
首次发布 -
版本 0.8.1
添加了一些示例 -
版本 0.9.0
重大变更:添加了配置值的范围语法和范围验证。所有values()
和between()
的出现都必须更新。同时,between-validator
的验证错误处理也进行了更改。 -
版本 0.9.1
向text
源模块添加了stack_config
函数。这个函数使合并来自多个源路径的配置文件变得更容易。 -
版本 0.9.2
更新文档以使用内部文档链接。 -
版本 1.0.0
修复了不存在的配置键满足1..
范围限制的问题。现在这被正确地检测为错误。
更新文档,在库页面提及stack_config
。 -
版本 1.0.1
进行了一些代码的美化改动以消除一些 clippy 警告。
设计理念
如果你对这个库的设计理念(以及一些有偏见的推理)感兴趣,可以继续阅读。
无数据类型
我认为配置文件中的数据类型不是一个好主意。我所说的“数据类型”指的是,例如 TOML 可以区分字符串、数字、日期等。它们在配置文件中有着不同的表示方式。假设你有一个配置文件,其内容为 cache_timeout=42
。这是可以的,但现在你程序的新版本应该允许用户禁用缓存。你想了一下,并决定(就像许多人之前所做的那样)使用值 0 作为禁用缓存的魔法值。目前这没问题。但经过几个版本后,你想添加无限缓存超时。你不能只用 "infinite"
或其他字符串,因为所有旧的配置文件中只有数字。你可以允许使用字符串和数字,但这会增加复杂性,而且它还做了另外一件事:它使得配置文件更难以理解。infinite
值不是一个字符串。它是一个特殊的常量(字面量),将其放在引号中传达了完全错误的信息。字符串是由用户任意选择的字符序列。不是一个单一的、恒定的值。通过在配置文件中使用不同的数据类型,你已经为自己作为开发者和/或为用户增加了维护负担。
我认为更好的解决方案是不要在配置格式中包含数据类型。cache_timeout=42
是一个有效的值,cache_timeout=infinite
也是一个有效的值。大胆添加 cache_timeout="twenty minutes"
。由应用程序来确定值部分的意义。如果你想在常量前加上感叹号:那就这么做吧。配置库不应对你施加任何限制。
行续接
许多配置文件格式在续接的行上使用行续接。例如,TOML 允许通过在行尾使用反斜杠来续接一行。这种方法有一些缺点:当编写多行值时,需要向前查看以确定当前行是否需要以续接字符结束。这使得自动写入多行值比应有的更复杂。即使对于人类用户来说,这也不是非常方便。你必须向上滚动一行,并将续接字符附加到上一行来继续它。LDIF 格式使用了一个更好的解决方案:在续接的行上标记续接。这还防止了一个安全问题。
想象以下配置文件
multiline=line1 \
line2 \
line3
critical_value=secure
现在你删除了 line3
,但遗漏了在 line2
上的续接字符。现在安全的 critical_value
只是 multiline
的一部分。如果该值是安全关键的但可选的,你刚刚创建了一个安全漏洞。
前导空白
首行空白应留给用户。将其视为有效(如在YAML中)会给用户带来两种烦恼。
- 用户应能以任何他们认为合理的方式缩进其配置文件。即使是不可理喻的方式也不应成为问题。
- 在非工作服务器上区分空白和制表符的数量,在匆忙中使用不超过基本
vi
的情况下很难,许多管理员至少会出错一次。
从安全角度考虑:缺少的空白,将你的关键配置值移至不起作用的部分,同样是一个问题。
无写入支持
配置文件是为用户准备的。只有他才能写入配置文件。不要干涉它。配置文件可能是自动化部署工作流程的一部分,如果决定更改配置文件的内容,它可能会变得不高兴。如果你想帮助用户创建第一个配置文件,提供示例。如果你的应用程序需要写入文件,它就不再是配置文件了。它是一个数据库。只需使用不同的库和格式(sqlite、yaml、xml)即可。
这里有一个例外。如果你正在编写配置管理或部署解决方案,你必须编写配置文件。但市面上有许多优秀的模板引擎可以完成这项工作。
无反序列化
配置文件是解析的,而不是反序列化的。序列化是将复杂的数据结构转换为一系列标记(主要是字节)的过程,并在以后从这些标记中重建数据结构。配置信息本身不是数据结构。试图将其变成一个往往最终会变成问题。最初,序列化库看起来像是一个不错的解决方案。但随着配置信息的增长和配置源数量的增加,它变得越来越复杂。
不同的来源在表达配置信息方面有不同的能力。如果信息来自环境变量,使用分隔符字符表示多个值可能是一个不错的选择。对于配置文件,简单地使用具有相同键的多个条目可能更直观。
大多数序列化库并不是为了让你深入控制你想解析的格式。很快,你就在选择的库之上开发了自己的解析器。
如果你正在寻找序列化库,我建议你看看 serde。
无不安全代码
根据定义,配置文件解析器正在解析不受信任的信息。你不应该通过使用 unsafe
代码来使事情变得更糟。不使用 unsafe
并不能保证代码安全,但使用它就放弃了许多 Rust 给你的保证。这应该只出于良好的理由。解析文本文件或环境变量并不是一个合理的理由。
无依赖
最后一点:无依赖。这是一个配置文件解析器,而不是应用程序框架。我认为将依赖项和子依赖项拖入一个只希望解析一些配置信息的项目中相当不礼貌,并增加了配置库消费者的维护负担。每个依赖项都可能存在安全问题,你必须跟踪并因安全问题而强制更新你的产品。当然,有些库是完全值得的。但我觉得配置库不应该这样做。它应该足够简单,可以在不使用任何依赖项的情况下工作。