8个版本

0.4.2 2022年12月10日
0.4.1 2020年2月23日
0.3.2 2018年10月16日
0.3.0 2018年9月11日
0.1.0 2018年8月6日

#1349 in 编码

每月44次下载
用于 libdeadmock

MIT/Apache

43KB
792

tomlenv

Crates.io Crates.io

Linux: 构建状态

从TOML文件生成特定运行时配置。


lib.rs:

tomlenv 允许您使用TOML来驱动环境配置。 Environments 结构体持有一个从您的环境层次结构到特定环境相关配置的引用,即。

  • prod_key -> prod_config
  • stage_key -> stage_config
  • dev_key -> dev_config

在键的方面,您可以使用由该库定义的 Environment 层次结构(Prod -> Stage -> Test -> Dev -> Local),或者您可以定义自己的自定义层次结构以与 Environments 结构体一起使用。如果您定义了自定义层次结构,您必须实现 DeserializeSerializeOrdPartialOrdTryFrom<String> 特性。更多信息请参见下面的 自定义环境层次结构 部分。

用法

首先,定义一个表示您的环境配置的结构体。对于在某个环境中出现但在另一个环境中不出现的项,使用 Option。请参见下面的示例中的键字段。

其次,从您的TOML生成一个 Reader。通常,TOML 被定义为 Path,并使用 from_path 方法。您还可以向 from_reader 方法提供一个泛型 Read 实现如下。

第三,将TOML反序列化为你的Environments结构体。在这个阶段,你可以使用current方法来访问由环境变量env指定的环境的配置。

#
/// Define your environment specific configuration.
/// *NOTE*: This must implement `Deserialize` and `Serialize`
#[derive(Debug, Deserialize, Getters, Serialize)]
struct MyAppEnv {
  /// The display name of this environment.
  #[get]
  name: String,
  /// The secret key only used in the Prod environment.
  #[get]
  key: Option<String>,
}

/// Grab your environment TOML.  This would usually be in a file and can
/// be read to a string such as below.
let toml = r#"[envs.prod]
name = "Production"
key = "abcd-123-efg-45"

[envs.stage]
name = "Stage"

[envs.test]
name = "Test"

[envs.dev]
name = "Development"

[envs.local]
name = "Local"
"#;

// Deserialize the TOML config into your environment structs.  This example
// is using the `Enrivorment` hierarchy supplied by the library.
let mut cursor = Cursor::new(toml);
let envs: Environments<Environment, MyAppEnv> = Environments::from_reader(&mut cursor)?;

// Check the `Production` environment.
env::set_var("env", "prod");
let mut current = envs.current()?;
assert_eq!(current.name(), "Production");
assert_eq!(current.key(), &Some("abcd-123-efg-45".to_string()));

// Switch to the `Development` environment.
env::set_var("env", "dev");
current = envs.current()?;
assert_eq!(current.name(), "Development");
assert_eq!(current.key(), &None);

自定义环境层次结构

如果你不想使用此库提供的Environment层次结构,请实现一个自定义的层次结构。为了与Environments一起工作,你必须实现几个特性。

必需

  • DeserializeSerialize:这些特性用于将数据从TOML中转换出来或转换到TOML。
  • OrdPartialOrd:这些特性用于维护层次结构的正确排序,并确保序列化的TOML始终以相同的顺序。
  • TryFrom<String>:此特性用于将环境变量env转换为你的层次结构类型。

可选

  • Display:用于将你的层次结构类型转换为格式化的字符串。如果你需要显示正在使用哪个环境,这很有用。
  • TryFrom<&'a str>:在这种情况下,由自定义反序列化器使用。

下面是一个自定义层次结构的示例。此示例包含自定义序列化器/反序列化器,但这并不是所有情况都必要的。

#
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum MyHierarchy {
    /// Production
    Prod,
    /// Certification
    Cert,
    /// Sandbox
    Sandbox,
    /// Local
    Local,
}

impl fmt::Display for MyHierarchy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let env = match *self {
            MyHierarchy::Prod => "prod",
            MyHierarchy::Cert => "ce",
            MyHierarchy::Sandbox => "sb",
            MyHierarchy::Local => "local",
        };
        write!(f, "{}", env)
    }
}

impl<'a> TryFrom<&'a str> for MyHierarchy {
    type Error = Error;

    fn try_from(env: &str) -> Result<Self> {
        match env {
            "prod" => Ok(MyHierarchy::Prod),
            "ce" => Ok(MyHierarchy::Cert),
            "sb" => Ok(MyHierarchy::Sandbox),
            "local" => Ok(MyHierarchy::Local),
            _ => Err(Error::invalid_runtime_environment(env)),
        }
    }
}

impl TryFrom<String> for MyHierarchy {
    type Error = Error;

    fn try_from(env: String) -> Result<Self> {
        match &env[..] {
            "prod" => Ok(MyHierarchy::Prod),
            "ce" => Ok(MyHierarchy::Cert),
            "sb" => Ok(MyHierarchy::Sandbox),
            "local" => Ok(MyHierarchy::Local),
            _ => Err(Error::invalid_runtime_environment(&env)),
        }
    }
}

impl Serialize for MyHierarchy {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.to_string())
    }
}

impl<'de> Deserialize<'de> for MyHierarchy {
    fn deserialize<D>(deserializer: D) -> std::result::Result<MyHierarchy, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct MyHierarchyVisitor;

        impl<'de> de::Visitor<'de> for MyHierarchyVisitor {
            type Value = MyHierarchy;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("any valid environment")
            }

            fn visit_str<E>(self, value: &str) -> std::result::Result<MyHierarchy, E>
            where
                E: de::Error,
            {
                TryFrom::try_from(value).map_err(de::Error::custom)
            }
        }

        deserializer.deserialize_string(MyHierarchyVisitor)
    }
}

#[derive(Debug, De, Getters, Ser)]
struct MyAppEnv {
  /// The display name of this environment.
  #[get]
  name: String,
  /// The secret key only used in the Prod environment.
  #[get]
  key: Option<String>,
}
#

/// Grab your environment TOML.  This would usually be in a file and can
/// be read to a string such as below.
let toml = r#"[envs.prod]
name = "Production"
key = "abcd-123-efg-45"

[envs.ce]
name = "Certification"

[envs.sb]
name = "Sandbox"

[envs.local]
name = "Local"
"#;

// Deserialize the TOML config into your environment structs.  This example
// is using the custom `MyHierarchy` hierarchy.
let mut cursor = Cursor::new(toml);
let envs: Environments<MyHierarchy, MyAppEnv> = Environments::from_reader(&mut cursor)?;

// Check the `Production` environment.
env::set_var("env", "prod");
let mut current = envs.current()?;
assert_eq!(current.name(), "Production");
assert_eq!(current.key(), &Some("abcd-123-efg-45".to_string()));

// Switch to the `Sandbox` environment.
env::set_var("env", "sb");
current = envs.current()?;
assert_eq!(current.name(), "Sandbox");
assert_eq!(current.key(), &None);

fn main() {
    foo().unwrap()
}

依赖项

~2.5MB
~52K SLoC