从各种格式的配置文件、环境变量中读取配置,然后转换为 struct 值:
struct Config 对象:
- Config 的 merge/refresh/set 等方法已经被 Deprecated, 建议使用 ConfigBuiler 相关的方法。
pub struct Config {
pub cache: Value,
/* private fields */
}
// 提供的方法
pub fn builder() -> ConfigBuilder<DefaultState>
pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Result<T, ConfigError>
pub fn get_string(&self, key: &str) -> Result<String, ConfigError>
pub fn get_int(&self, key: &str) -> Result<i64, ConfigError>
pub fn get_float(&self, key: &str) -> Result<f64, ConfigError>
pub fn get_bool(&self, key: &str) -> Result<bool, ConfigError>
pub fn get_table(&self, key: &str) -> Result<Map<String, Value>, ConfigError>
pub fn get_array(&self, key: &str) -> Result<Vec<Value>, ConfigError>
// 将配置参数反序列化到 T 类型值上
pub fn try_deserialize<'de, T: Deserialize<'de>>(self) -> Result<T, ConfigError>
// 从可以序列化的对象值创建配置
pub fn try_from<T: Serialize>(from: &T) -> Result<Self, ConfigError>
Struct config::builder::ConfigBuilder
pub struct ConfigBuilder<St: BuilderState> { /* private fields */ }
// 实现的方法
// 设置缺省值:优先级最低
pub fn set_default<S, T>(self, key: S, value: T) -> Result<Self, ConfigError>
where
S: AsRef<str>,
T: Into<Value>
// 设置覆盖值:优先级最高
pub fn set_override<S, T>(self, key: S, value: T) -> Result<Self, ConfigError>
where
S: AsRef<str>,
T: Into<Value>
pub fn set_override_option<S, T>( self, key: S, value: Option<T>) -> Result<Self, ConfigError>
where
S: AsRef<str>,
T: Into<Value>
// 添加数据源,可以多次链式调用添加多个,后面的配置覆盖前面的配置(类似于配置 merge 功能)
pub fn add_source<T>(self, source: T) -> Self where T: Source + Send + Sync + 'static
pub fn add_async_source<T>(self, source: T) -> ConfigBuilder<AsyncState>
where T: AsyncSource + Send + Sync + 'static
// 构建配置
pub fn build(self) -> Result<Config, ConfigError>
pub fn build_cloned(&self) -> Result<Config, ConfigError>
Trait config::Source:
pub trait Source: Debug {
// Required methods
fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>;
fn collect(&self) -> Result<Map<String, Value>, ConfigError>;
// Provided method
fn collect_to(&self, cache: &mut Value) -> Result<(), ConfigError> { ... }
}
impl Source for Vec<Box<dyn Source + Send + Sync>>
impl Source for [Box<dyn Source + Send + Sync>]
impl<T> Source for Vec<T> where T: Source + Sync + Send + Clone + 'static
impl Source for Config
impl Source for Environment
impl<T, F> Source for File<T, F>
where
F: FileStoredFormat + Debug + Clone + Send + Sync + 'static,
T: Sync + Send + FileSource<F> + 'static
Struct config::Environment
pub struct Environment { /* private fields */ }
pub fn new() -> Self
pub fn with_prefix(s: &str) -> Self
// 环境变量前缀(自动大写和加下划线),如 prefix 为 config 时,debug 对应 CONFIG_DEBUG
pub fn prefix(self, s: &str) -> Self
// 指定环境变量的大小写风格
pub fn with_convert_case(tt: Case) -> Self
pub fn convert_case(self, tt: Case) -> Self
// 指定嵌套配置参数的各级间的风格符,例如:使用 _ 作为分隔符时 redis.password 对应 REDIS_PASSWORD
pub fn prefix_separator(self, s: &str) -> Self
pub fn separator(self, s: &str) -> Self
// 尝试按照三种类型 bool、i64、f64(默认是 String)来解析参数,会影响性能。
// 一般和 list_separator() 连用。
pub fn try_parsing(self, try_parsing: bool) -> Self
// 调用 list_separator("x") 后,所有环境变量的值都按照指定的分隔符来进行风格,然后结果为
// Vec<String> (而非默认的 String 类型);示例:
// https://github.com/mehcode/config-rs/blob/master/examples/env-list/main.rs
pub fn list_separator(self, s: &str) -> Self
// 指定在调用 list_separator("x") 后,不解析为 Vec<String> ,而解析为默认的 String 值的 key。
pub fn with_list_parse_key(self, key: &str) -> Self
pub fn ignore_empty(self, ignore: bool) -> Self
pub fn keep_prefix(self, keep: bool) -> Self
// 指定要解析的环境变量来源 Map
pub fn source(self, source: Option<Map<String, String>>) -> Self
// 示例
#[test]
fn test_config() -> Result<(), config::ConfigError> {
#[derive(Clone, Debug, Deserialize)]
struct MyConfig {
pub my_string: String,
}
let source = Environment::default()
.source(Some({
let mut env = HashMap::new();
env.insert("MY_STRING".into(), "my-value".into());
env
}));
let config: MyConfig = Config::builder()
.add_source(source)
.build()?
.try_into()?;
assert_eq!(config.my_string, "my-value");
Ok(())
}
Struct config::File,支持自动文件格式发现(默认支持 json/yaml/toml/ini 格式):
pub struct File<T, F> { /* private fields */ }
// 实现的方法
// 将字符串 s 作为文件内容, 示例:
// https://github.com/mehcode/config-rs/blob/master/examples/custom_str_format/main.rs
impl<F> File<FileSourceString, F> where F: FileStoredFormat + 'static
pub fn from_str(s: &str, format: F) -> Self
// 从 name 对应的文件路径读取文件内容, 示例:
// https://github.com/mehcode/config-rs/blob/master/examples/custom_file_format/main.rs
impl<F> File<FileSourceFile, F> where F: FileStoredFormat + 'static
pub fn new(name: &str, format: F) -> Self
// name 如果不加后缀,则自动尝试默认注册支持的文件格式。name 如果带后缀,则使用后缀对应的文件格式。
// 示例:https://github.com/mehcode/config-rs/blob/master/examples/glob/main.rs
impl File<FileSourceFile, FileFormat>
pub fn with_name(name: &str) -> Self
impl<T, F> File<T, F> where F: FileStoredFormat + 'static, T: FileSource<F>
pub fn format(self, format: F) -> Self
pub fn required(self, required: bool) -> Self
// 其中的 FileStoredFormat trait 定义如下
pub trait FileStoredFormat: Format {
// Required method
fn file_extensions(&self) -> &'static [&'static str];
}
// FileFormat 实现了 FileStoredFormat
impl FileStoredFormat for FileFormat
// Enum config::FileFormat
pub enum FileFormat {
Toml,
Json,
Yaml,
Ini,
Ron,
Json5,
}
示例:
// 示例:简单的 set/get
#![allow(deprecated)]
use config::Config;
use lazy_static::lazy_static;
use std::error::Error;
use std::sync::RwLock;
lazy_static! {
static ref SETTINGS: RwLock<Config> = RwLock::new(Config::default());
}
fn try_main() -> Result<(), Box<dyn Error>> {
// Set property
SETTINGS.write()?.set("property", 42)?;
// Get property
println!("property: {}", SETTINGS.read()?.get::<i32>("property")?);
Ok(())
}
// 示例:从配置文件和环境变量中读取配置参数
fn main() {
let settings = Config::builder()
// Add in `./Settings.toml`
.add_source(config::File::with_name("examples/simple/Settings")) // 可选的指定文件名
// Add in settings from the environment (with a prefix of APP) Eg.. `APP_DEBUG=1
// ./target/app` would set the `debug` key
.add_source(config::Environment::with_prefix("APP"))
.build()
.unwrap();
// Print out our settings (as a HashMap)
println!(
"{:?}",
settings
.try_deserialize::<HashMap<String, String>>()
.unwrap()
);
}
// 示例
use config::Config;
#[derive(Debug, Default, serde_derive::Deserialize, PartialEq, Eq)]
struct AppConfig {
list: Vec<String>, // 环境变量值被风格解析为 Vec<String>
}
fn main() {
std::env::set_var("APP_LIST", "Hello World");
let config = Config::builder()
.add_source(
config::Environment::with_prefix("APP")
.try_parsing(true)
.separator("_")
.list_separator(" "), // 将所有环境变量的值,使用该分隔符风格,结果为 Vec<String>
)
.build()
.unwrap();
// 将配置反序列化到 AppConfig 类型对象
let app: AppConfig = config.try_deserialize().unwrap();
assert_eq!(app.list, vec![String::from("Hello"), String::from("World")]);
std::env::remove_var("APP_LIST");
}
// buidler 示例
let mut builder = Config::builder()
.set_default("default", "1")?
.add_source(File::new("config/settings", FileFormat::Json))
// .add_async_source(...)
.set_override("override", "1")?;
match builder.build() {
Ok(config) => {
// use your config
},
Err(e) => {
// something went wrong
}
}
let mut builder = Config::builder();
builder = builder.set_default("default", "1")?; // 优先级最低
builder = builder.add_source(File::new("config/settings", FileFormat::Json));
// 后续的 source 覆盖前面 source 的配置参数
builder = builder.add_source(File::new("config/settings.prod", FileFormat::Json));
builder = builder.set_override("override", "1")?; // 优先级最高
自定义文件格式:
- 创建 File 类型的 Source:<:new>(name: &str, format: FileStoredFormat + ‘static): 指定文件名路径和它的格式;
- format 需要实现 Format 和 FileStoredFormat trait;
- Format trait 返回一个 Map<String, config::Value> , key 为配置参数 key 名称,value 为该 key 的配置参数值(来源于文件);
- FileStoredFormat trait 返回支持的文件格式名称后缀;
- 创建 Config Builder,然后添加上面创建的 File Source;
use config::{Config, File, FileStoredFormat, Format, Map, Value, ValueKind};
use std::io::{Error, ErrorKind};
/// The private and public key sources will be read into their associated variable:
#[derive(serde::Deserialize, Clone, Debug)]
pub struct Settings {
// 各 field 对应 PemFile 实现的 Format trait 返回的 map 的 key。
pub private_key: Option<String>,
pub public_key: Option<String>,
}
fn main() {
// Sourcing from two separate files for the `Settings` struct,:
let file_public_key = File::new("examples/custom_file_format/files/public.pem", PemFile);
let file_private_key = File::new("examples/custom_file_format/files/private.pem", PemFile);
// Provide the sources and build the config object:
// Both are marked as optional to avoid failure if the file doesn't exist.
let settings = Config::builder()
.add_source(file_public_key.required(false))
.add_source(file_private_key.required(false))
.build()
.unwrap();
// Deserialize the config object into your Settings struct:
let settings: Settings = settings.try_deserialize().unwrap();
println!("{:#?}", settings);
}
#[derive(Debug, Clone)]
pub struct PemFile;
impl Format for PemFile {
fn parse(&self, uri: Option<&String>, text: &str, ) -> Result<Map<String, config::Value>, Box<dyn std::error::Error + Send + Sync>> {
// Store any valid keys into this map, they'll be merged with other sources into the final
// config map:
let mut result = Map::new();
// Identify the PEM encoded data type by the first occurrence found: NOTE: This example is
// kept simple, multiple or other encoded types are not handled.
let key_type = vec!["PUBLIC", "PRIVATE"]
.into_iter()
.find(|s| text.contains(s));
let key = match key_type {
Some("PRIVATE") => "private_key",
Some("PUBLIC") => "public_key",
// Otherwise fail with an error message (the filename is implicitly appended):
_ => {
return Err(Box::new(Error::new(
ErrorKind::InvalidData,
"PEM file did not contain a Private or Public key",
)))
}
};
result.insert(
key.to_owned(),
Value::new(uri, ValueKind::String(text.into())), // String 类型值
);
Ok(result)
}
}
// A slice of extensions associated to this format, when an extension is omitted from a file
// source, these will be tried implicitly:
impl FileStoredFormat for PemFile {
fn file_extensions(&self) -> &'static [&'static str] {
&["pem"]
}
}