diff --git a/examples/hierarchical-env/config/default.toml b/examples/hierarchical-env/config/default.toml index dbb2f302..fab3b7ad 100644 --- a/examples/hierarchical-env/config/default.toml +++ b/examples/hierarchical-env/config/default.toml @@ -10,6 +10,7 @@ version = 1 [twitter] consumer_token = "twitter-dev-consumer-key" consumer_secret = "twitter-dev-consumer-secret" +avatar = "avatar-default" [braintree] merchant_id = "braintree-merchant-id" diff --git a/examples/hierarchical-env/config/production.toml b/examples/hierarchical-env/config/production.toml index cd0c4cf4..c1f4ab58 100644 --- a/examples/hierarchical-env/config/production.toml +++ b/examples/hierarchical-env/config/production.toml @@ -7,6 +7,7 @@ token = "sparkpost-prod-token" [twitter] consumer_token = "twitter-prod-consumer-key" consumer_secret = "twitter-prod-consumer-secret" +avatar = "avatar-prod" [braintree] public_key = "braintree-prod-public-key" diff --git a/examples/hierarchical-env/settings.rs b/examples/hierarchical-env/settings.rs index a1cf8a8b..93539733 100644 --- a/examples/hierarchical-env/settings.rs +++ b/examples/hierarchical-env/settings.rs @@ -23,6 +23,7 @@ struct Sparkpost { struct Twitter { consumer_token: String, consumer_secret: String, + avatar: Option, } #[derive(Debug, Deserialize)] @@ -62,7 +63,12 @@ impl Settings { .add_source(File::with_name("examples/hierarchical-env/config/local").required(false)) // Add in settings from the environment (with a prefix of APP) // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key - .add_source(Environment::with_prefix("app")) + .add_source( + Environment::with_prefix("app") + .separator("__") + // APP__TWITTER__AVATAR=null will override the default value with null + .with_null_as("null"), + ) // You may also programmatically change settings .set_override("database.url", "postgres://")? .build()?; diff --git a/src/env.rs b/src/env.rs index 7da8c937..38823e60 100644 --- a/src/env.rs +++ b/src/env.rs @@ -52,6 +52,9 @@ pub struct Environment { /// Ignore empty env values (treat as unset). ignore_empty: bool, + /// Treat the given value as null + null_as: Option, + /// Parses booleans, integers and floats if they're detected (can be safely parsed). try_parsing: bool, @@ -168,6 +171,11 @@ impl Environment { self } + pub fn with_null_as(mut self, null_as: &str) -> Self { + self.null_as = Some(null_as.into()); + self + } + /// Note: enabling `try_parsing` can reduce performance it will try and parse /// each environment variable 3 times (bool, i64, f64) pub fn try_parsing(mut self, try_parsing: bool) -> Self { @@ -316,11 +324,17 @@ impl Source for Environment { .collect(); ValueKind::Array(v) } + } else if Some(value.to_lowercase()) == self.null_as { + ValueKind::Nil } else { ValueKind::String(value) } } else { - ValueKind::String(value) + if Some(value.to_lowercase()) == self.null_as { + ValueKind::Nil + } else { + ValueKind::String(value) + } }; m.insert(key, Value::new(Some(&uri), value)); diff --git a/tests/testsuite/env.rs b/tests/testsuite/env.rs index f8ef2b23..1748eb56 100644 --- a/tests/testsuite/env.rs +++ b/tests/testsuite/env.rs @@ -63,6 +63,14 @@ fn test_empty_value_is_ignored() { }); } +#[test] +fn test_null_is_parsed() { + temp_env::with_var("C_A_B", Some(""), || { + let environment = Environment::default().with_null_as(""); + assert!(environment.collect().unwrap().contains_key("c_a_b")); + }); +} + #[test] fn test_keep_prefix() { temp_env::with_var("C_A_B", Some(""), || {