#toml #toml-config #env #merge #env-var #variables #cli

cli_env_toml

用于从环境变量、命令行参数生成 toml,然后与已有的 toml 文件做配置合并。

3 个版本

0.0.5 2023年6月21日
0.0.3 2023年6月21日
0.0.1 2023年6月21日

#72 in #toml-config

Apache-2.0

16KB
367 代码行数(不含注释)

cli_env_toml

用于从环境变量、命令行参数生成 toml,然后与已有的 toml 文件做配置合并。

先设置环境变量 source ./env

export TEST_server__host=127.0.0.1
export TEST_site__title="xxAI.Art - 我们计算艺术"
export TEST_grpc_port=9999
export TEST_site__xxai_art__mail=xxai.art@gmail.com
export TEST_compress=true

用法见 ./src/lib.rs

use std::env::vars;

mod env;
pub use env::{env_with_prefix, kv_toml};
use serde::de::DeserializeOwned;
mod merge;
use std::path::Path;

pub use merge::merge;

#[test]
fn test() {
  let config = "grpc_port=1234
mysql_port=1235

[site]
title=\"a b c\"
password=\"xyz\"

[site.xxai_art]
hide=true
    ";
  println!("\n## toml config\n\n```toml\n{config}\n```");

  let env = env_with_prefix(vars(), "TEST_");

  let env_toml = kv_toml(env, "__");
  println!("## convert env into toml\n\n```toml\n{env_toml}\n```");

  let mut config = config.parse().unwrap();
  merge(&mut config, &env_toml.parse().unwrap());
  let config = toml::ser::to_string_pretty(&config).unwrap();
  println!("## merge config and env\n\n```toml\n{config}\n```");
}

/// 从 命令行、环境变量、配置文件 读取参数(前面的会覆盖后面的设置)
pub fn cli_env_toml_value(
  cli: Option<Vec<impl AsRef<str>>>,
  env_prefix: impl AsRef<str>,
  toml_path: Option<impl AsRef<Path>>,
) -> anyhow::Result<toml::Value> {
  let toml = if let Some(path) = toml_path {
    std::fs::read_to_string(path)?
  } else {
    String::new()
  };

  let mut config = toml.parse()?;

  // 从环境变量读取配置
  {
    let env_toml = kv_toml(env_with_prefix(vars(), env_prefix), "__");
    merge(&mut config, &env_toml.parse().unwrap());
  }

  // 从命令行参数读取配置
  if let Some(cli) = cli {
    let cli: Vec<(String, String)> = cli
      .iter()
      .filter_map(|s| {
        let s = s.as_ref();
        if let Some(index) = s.find('=') {
          let (left, right) = s.split_at(index);
          Some((left.to_string(), right[1..].to_string()))
        } else {
          None
        }
      })
      .collect();
    let cli_toml = kv_toml(cli, ".");
    merge(&mut config, &cli_toml.parse().unwrap());
  };
  Ok(config)
}

pub fn cli_env_toml_str(
  cli: Option<Vec<impl AsRef<str>>>,
  env_prefix: impl AsRef<str>,
  toml_path: Option<impl AsRef<Path>>,
) -> anyhow::Result<String> {
  let config = cli_env_toml_value(cli, env_prefix, toml_path)?;
  Ok(toml::ser::to_string_pretty(&config)?)
}

pub fn cli_env_toml<T: DeserializeOwned>(
  cli: Option<Vec<impl AsRef<str>>>,
  env_prefix: impl AsRef<str>,
  toml_path: Option<impl AsRef<Path>>,
) -> anyhow::Result<T> {
  let config = cli_env_toml_value(cli, env_prefix, toml_path)?;
  Ok(T::deserialize(config)?)
}

test 输出为

toml 配置

grpc_port=1234
mysql_port=1235

[site]
title="a b c"
password="xyz"

[site.xxai_art]
hide=true
    

将环境变量转换为 toml

grpc_port=9999
compress=true
[site.xxai_art]
mail="[email protected]"
[server]
host="127.0.0.1"
[site]
title="xxAI.Art - 我们计算艺术"

合并配置和环境变量

compress = true
grpc_port = 9999
mysql_port = 1235

[server]
host = "127.0.0.1"

[site]
password = "xyz"
title = "xxAI.Art - 我们计算艺术"

[site.xxai_art]
hide = true
mail = "[email protected]"

依赖

~390–650KB
~13K SLoC