1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
use std::env;
use crate::error::*;
use crate::map::Map;
use crate::source::Source;
use crate::value::{Value, ValueKind};
#[derive(Clone, Debug, Default)]
pub struct Environment {
/// Optional prefix that will limit access to the environment to only keys that
/// begin with the defined prefix.
///
/// A prefix with a separator of `_` is tested to be present on each key before its considered
/// to be part of the source environment.
///
/// For example, the key `CONFIG_DEBUG` would become `DEBUG` with a prefix of `config`.
prefix: Option<String>,
/// Optional character sequence that separates each key segment in an environment key pattern.
/// Consider a nested configuration such as `redis.password`, a separator of `_` would allow
/// an environment key of `REDIS_PASSWORD` to match.
separator: Option<String>,
/// Ignore empty env values (treat as unset).
ignore_empty: bool,
/// Parses booleans, integers and floats if they're detected (can be safely parsed).
try_parsing: bool,
}
impl Environment {
#[deprecated(since = "0.12.0", note = "please use 'Environment::default' instead")]
pub fn new() -> Self {
Environment::default()
}
pub fn with_prefix(s: &str) -> Self {
Environment {
prefix: Some(s.into()),
..Environment::default()
}
}
pub fn prefix(mut self, s: &str) -> Self {
self.prefix = Some(s.into());
self
}
pub fn separator(mut self, s: &str) -> Self {
self.separator = Some(s.into());
self
}
pub fn ignore_empty(mut self, ignore: bool) -> Self {
self.ignore_empty = ignore;
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 {
self.try_parsing = try_parsing;
self
}
}
impl Source for Environment {
fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
Box::new((*self).clone())
}
fn collect(&self) -> Result<Map<String, Value>> {
let mut m = Map::new();
let uri: String = "the environment".into();
let separator = self.separator.as_deref().unwrap_or("");
let group_separator = self.separator.as_deref().unwrap_or("_");
// Define a prefix pattern to test and exclude from keys
let prefix_pattern = self
.prefix
.as_ref()
.map(|prefix| format!("{}{}", prefix, group_separator).to_lowercase());
for (key, value) in env::vars() {
// Treat empty environment variables as unset
if self.ignore_empty && value.is_empty() {
continue;
}
let mut key = key.to_lowercase();
// Check for prefix
if let Some(ref prefix_pattern) = prefix_pattern {
if key.starts_with(prefix_pattern) {
// Remove this prefix from the key
key = key[prefix_pattern.len()..].to_string();
} else {
// Skip this key
continue;
}
}
// If separator is given replace with `.`
if !separator.is_empty() {
key = key.replace(separator, ".");
}
let value = if self.try_parsing {
// convert to lowercase because bool parsing expects all lowercase
if let Ok(parsed) = value.to_lowercase().parse::<bool>() {
ValueKind::Boolean(parsed)
} else if let Ok(parsed) = value.parse::<i64>() {
ValueKind::Integer(parsed)
} else if let Ok(parsed) = value.parse::<f64>() {
ValueKind::Float(parsed)
} else {
ValueKind::String(value)
}
} else {
ValueKind::String(value)
};
m.insert(key, Value::new(Some(&uri), value));
}
Ok(m)
}
}
|