12 个版本 (3 个稳定版)

1.2.0 2024 年 2 月 18 日
1.1.1 2023 年 9 月 25 日
0.3.5 2023 年 9 月 23 日
0.2.0 2023 年 9 月 23 日
0.1.1 2023 年 9 月 23 日

#56 in 配置

Download history 24/week @ 2024-04-02 70/week @ 2024-04-09 2/week @ 2024-04-23 8/week @ 2024-05-28 1/week @ 2024-06-04 22/week @ 2024-06-11 56/week @ 2024-06-18 92/week @ 2024-07-02

每月下载量 92 次

MIT 许可证

48KB
832

toml-env

crates.io docs.rs GitHub Workflow Status (with event)

一个使用 toml 的简单配置库。

此库旨在使用 initialize() 函数在启动时加载应用程序的配置。配置可以按以下顺序加载

  1. 从 dotenv 风格的文件 .env.toml(您选择的文件名)
  2. 从环境变量 CONFIG(或您选择的变量名)。
  3. 从映射的环境(例如 MY_VARIABLE => my_variable.child)。
  4. 从配置文件。

为什么还需要另一个配置库?

以下是这个库的一些可能替代品

  • config 您需要最大限度的灵活性。
  • figment 您需要最大限度的灵活性。
  • just-config 您需要最大限度的灵活性。
  • dotenvy 您只需要 .env 支持。
  • env_inventory 您只需要环境变量配置文件支持。

为什么选择这个?

  • 小型且具有意见的功能集。
  • 最小依赖。
  • .env 使用 TOML,这是一个更成熟的文件格式标准。
  • 使用自定义映射将环境变量加载到配置中(MY_VARIABLE => child.child.config),采用 json pointer 风格(不支持完整语法)。
  • 使用可配置的自动映射将环境变量加载到配置中(MY_APP__PARENT__CHILD => parent.child
  • 从存储在多行环境变量中的 TOML 加载配置。
    • 对于具有嵌套映射的大配置,这可能与以下代码相比更容易阅读:MY_VARIABLE__SOMETHING_ELSE__SOMETHING_SOMETHING_ELSE
    • 您也可以直接复制TOML文件中的文本以用作环境变量,而不是将其翻译成复杂的变量名称。

配置结构

首先,您需要定义一个实现 serde::de::DeserializeOwned + serde::Serialize + Default

#[derive(serde::Serialize, serde::Deserialize, Default)]
struct Config {
    config_value_1: String,
    config_value_2: String,
    config_child: ConfigChild
}

#[derive(serde::Serialize, serde::Deserialize, Default)]
struct ConfigChild {
    config_value_3: String,
}

.env.toml

默认情况下,配置将尝试从名为 .env.toml 的文件中加载。您可以自定义此文件的名称。该文件的格式如下:

SECRET_ENV_VAR_1="some value"
SECRET_ENV_VAR_2="some other value"

[CONFIG]
config_value_1="some value"
config_value_2="some other value"

[CONFIG.config_child]
config_value_3="some other other value"

您可以使用文件中的顶层键来设置应用程序的环境变量(例如,SECRET_ENV_VAR_1)。

配置可以从文件的一部分加载到 CONFIG 中。CONFIG 键将是 Args::config_variable_name 的名称,默认为 CONFIG

环境变量 CONFIG

您可以通过使用 Args::config_variable_name(默认为 CONFIG)指定的变量名称来指定配置。

# Store a multiline string into an environment variable in bash shell.
read -r -d '' CONFIG << EOM
config_value_1="some value"
config_value_2="some other value"

[config_child]
config_value_3="some other other value"
EOM

示例

CONFIG 变量

一个简单示例,使用默认设置从 CONFIG 加载配置。

use serde::{Deserialize, Serialize};
use toml_env::{initialize, Args};

#[derive(Serialize, Deserialize)]
struct Config {
    value_1: String,
    value_2: bool,
}

// Normally you may choose set this from a shell script or some
// other source in your environment (docker file or server config file).
std::env::set_var(
    "CONFIG",
    r#"
value_1="Something from CONFIG environment"
value_2=true
"#,
);

let config: Config = initialize(Args::default())
    .unwrap()
    .unwrap();

assert_eq!(config.value_1, "Something from CONFIG environment");
assert_eq!(config.value_2, true);

自定义变量映射

自定义环境变量映射的简单演示

use serde::{Deserialize, Serialize};
use toml_env::{Args, initialize, TomlKeyPath};
use std::str::FromStr;

#[derive(Serialize, Deserialize)]
struct Config {
    value_1: String,
    value_2: bool,
}

// Normally you may choose set this from a shell script or some
// other source in your environment (docker file or server config file).
std::env::set_var("VALUE_1", "Hello World");
std::env::set_var("VALUE_2", "true");

let config: Config = initialize(Args {
    map_env: [
        ("VALUE_1", "value_1"),
        ("VALUE_2", "value_2"),
    ]
    .into_iter()
    .map(|(key, value)| {
        (key, TomlKeyPath::from_str(value).unwrap())
    }).collect(),
    ..Args::default()
})
    .unwrap()
    .unwrap();

assert_eq!(config.value_1, "Hello World");
assert_eq!(config.value_2, true);

自动变量映射

自动环境变量映射的简单演示

use serde::{Deserialize, Serialize};
use toml_env::{Args, initialize, AutoMapEnvArgs};

// NOTE: the `deny_unknown_fields` can be used to reject
// mappings which don't conform to the current spec.
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct Config {
    value_1: String,
    value_2: bool,
}

// Normally you may choose set this from a shell script or some
// other source in your environment (docker file or server config file).
std::env::set_var("CONFIG__VALUE_1", "Hello World");
std::env::set_var("CONFIG__VALUE_2", "true");

let config: Config = initialize(Args {
    auto_map_env: Some(AutoMapEnvArgs::default()),
    // The default prefix is CONFIG.
    // In practice you would usually use a custom prefix:
    // prefix: Some("MY_APP"),
    ..Args::default()
})
    .unwrap()
    .unwrap();

assert_eq!(config.value_1, "Hello World");
assert_eq!(config.value_2, true);

.env.toml 文件

一个简单示例,使用默认设置从 .env.toml 加载配置和环境变量。

use serde::{Deserialize, Serialize};
use toml_env::{Args, initialize};

#[derive(Serialize, Deserialize)]
struct Config {
    value_1: String,
    value_2: bool,
}

let dir = tempfile::tempdir().unwrap();
std::env::set_current_dir(&dir).unwrap();
let dotenv_path = dir.path().join(".env.toml");

// Normally you would read this from .env.toml file
std::fs::write(
    &dotenv_path,
    r#"
OTHER_VARIABLE="hello-world"
[CONFIG]
value_1="Something from .env.toml"
value_2=true
"#,
)
.unwrap();

let config: Config = initialize(Args::default())
    .unwrap()
    .unwrap();

assert_eq!(config.value_1, "Something from .env.toml");
assert_eq!(config.value_2, true);

let secret = std::env::var("OTHER_VARIABLE").unwrap();
assert_eq!(secret, "hello-world");

所有功能

一个更复杂的示例,演示了所有功能。

use serde::{Deserialize, Serialize};
use tempfile::tempdir;
use toml_env::{Args, initialize, Logging, TomlKeyPath, AutoMapEnvArgs};
use std::str::FromStr;

#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct Config {
    value_1: String,
    value_2: bool,
    child: Child,
    array: Vec<String>,
}

#[derive(Serialize, Deserialize, Default)]
#[serde(deny_unknown_fields)]
struct Child {
    value_3: i32,
    value_4: u8,
    value_5: String,
    value_6: String,
}

let dir = tempdir().unwrap();
let dotenv_path = dir.path().join(".env.toml");
let config_path = dir.path().join("config.toml");

// Normally you would read this from .env.toml file
std::fs::write(
    &dotenv_path,
    r#"
SECRET="hello-world"
[MY_CONFIG]
value_1="Something from .env.toml"
[MY_CONFIG.child]
value_3=-5
value_4=16
"#,
)
.unwrap();

// Normally you may choose set this from a shell script or some
// other source in your environment (docker file or server config file).
std::env::set_var(
    "MY_CONFIG",
    r#"
value_1="Something from MY_CONFIG environment"
value_2=true
"#,
);

std::env::set_var(
    "VALUE_1",
    "Something from Environment"
);
std::env::set_var(
    "VALUE_5",
    "Something from Environment"
);
std::env::set_var(
    "MY_APP__CHILD__VALUE_6",
    "Something from Environment"
);
std::env::set_var(
    "MY_APP__ARRAY__1",
    "Hello"
);
std::env::set_var(
    "MY_APP__ARRAY__0",
    "Hello"
);

// Normally you would read this from config.toml
// (or whatever name you want) file.
std::fs::write(
    &config_path,
    r#"
value_1="Something from config.toml"
value_2=false
[child]
value_4=45
"#,
)
.unwrap();

let config: Config = initialize(Args {
    dotenv_path: &dotenv_path,
    config_path: Some(&config_path),
    config_variable_name: "MY_CONFIG",
    logging: Logging::StdOut,
    map_env: [
        ("VALUE_1", "value_1"),
        ("VALUE_5", "child.value_5"),
        ("VALUE_99", "does.not.exist"),
    ]
    .into_iter()
    .map(|(key, value)| {
        (key, TomlKeyPath::from_str(value).unwrap())
    }).collect(),
    auto_map_env: Some(AutoMapEnvArgs {
        divider: "__",
        prefix: Some("MY_APP"),
        transform: Box::new(|name| name.to_lowercase()),
    })
})
    .unwrap()
    .unwrap();

assert_eq!(config.value_1, "Something from .env.toml");
assert_eq!(config.value_2, true);
assert_eq!(config.array[0], "Hello");
assert_eq!(config.child.value_3, -5);
assert_eq!(config.child.value_4, 16);
assert_eq!(config.child.value_5, "Something from Environment");

let secret = std::env::var("SECRET").unwrap();
assert_eq!(secret, "hello-world");

变更日志

有关此库的更改,请参阅 CHANGELOG.md

依赖项

~0.6–1.2MB
~27K SLoC