Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/hierarchical-env/config/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions examples/hierarchical-env/config/production.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 7 additions & 1 deletion examples/hierarchical-env/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct Sparkpost {
struct Twitter {
consumer_token: String,
consumer_secret: String,
avatar: Option<String>,
}

#[derive(Debug, Deserialize)]
Expand Down Expand Up @@ -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()?;
Expand Down
16 changes: 15 additions & 1 deletion src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,

/// Parses booleans, integers and floats if they're detected (can be safely parsed).
try_parsing: bool,

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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));
Expand Down
8 changes: 8 additions & 0 deletions tests/testsuite/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ fn test_empty_value_is_ignored() {
});
}

#[test]
fn test_null_is_parsed() {
temp_env::with_var("C_A_B", Some("<null>"), || {
let environment = Environment::default().with_null_as("<null>");
assert!(environment.collect().unwrap().contains_key("c_a_b"));
});
}

#[test]
fn test_keep_prefix() {
temp_env::with_var("C_A_B", Some(""), || {
Expand Down