1 use std::env;
2 
3 use crate::error::Result;
4 use crate::map::Map;
5 use crate::source::Source;
6 use crate::value::{Value, ValueKind};
7 
8 #[must_use]
9 #[derive(Clone, Debug, Default)]
10 pub struct Environment {
11     /// Optional prefix that will limit access to the environment to only keys that
12     /// begin with the defined prefix.
13     ///
14     /// A prefix with a separator of `_` is tested to be present on each key before its considered
15     /// to be part of the source environment.
16     ///
17     /// For example, the key `CONFIG_DEBUG` would become `DEBUG` with a prefix of `config`.
18     prefix: Option<String>,
19 
20     /// Optional character sequence that separates the prefix from the rest of the key
21     prefix_separator: Option<String>,
22 
23     /// Optional character sequence that separates each key segment in an environment key pattern.
24     /// Consider a nested configuration such as `redis.password`, a separator of `_` would allow
25     /// an environment key of `REDIS_PASSWORD` to match.
26     separator: Option<String>,
27 
28     /// Optional character sequence that separates each env value into a vector. only works when try_parsing is set to true
29     /// Once set, you cannot have type String on the same environment, unless you set list_parse_keys.
30     list_separator: Option<String>,
31     /// A list of keys which should always be parsed as a list. If not set you can have only Vec<String> or String (not both) in one environment.
32     list_parse_keys: Option<Vec<String>>,
33 
34     /// Ignore empty env values (treat as unset).
35     ignore_empty: bool,
36 
37     /// Parses booleans, integers and floats if they're detected (can be safely parsed).
38     try_parsing: bool,
39 
40     // Preserve the prefix while parsing
41     keep_prefix: bool,
42 
43     /// Alternate source for the environment. This can be used when you want to test your own code
44     /// using this source, without the need to change the actual system environment variables.
45     ///
46     /// ## Example
47     ///
48     /// ```rust
49     /// # use config::{Environment, Config};
50     /// # use serde::Deserialize;
51     /// # use std::collections::HashMap;
52     /// # use std::convert::TryInto;
53     /// #
54     /// #[test]
55     /// fn test_config() -> Result<(), config::ConfigError> {
56     ///   #[derive(Clone, Debug, Deserialize)]
57     ///   struct MyConfig {
58     ///     pub my_string: String,
59     ///   }
60     ///
61     ///   let source = Environment::default()
62     ///     .source(Some({
63     ///       let mut env = HashMap::new();
64     ///       env.insert("MY_STRING".into(), "my-value".into());
65     ///       env
66     ///   }));
67     ///
68     ///   let config: MyConfig = Config::builder()
69     ///     .add_source(source)
70     ///     .build()?
71     ///     .try_into()?;
72     ///   assert_eq!(config.my_string, "my-value");
73     ///
74     ///   Ok(())
75     /// }
76     /// ```
77     source: Option<Map<String, String>>,
78 }
79 
80 impl Environment {
81     #[deprecated(since = "0.12.0", note = "please use 'Environment::default' instead")]
new() -> Self82     pub fn new() -> Self {
83         Self::default()
84     }
85 
with_prefix(s: &str) -> Self86     pub fn with_prefix(s: &str) -> Self {
87         Self {
88             prefix: Some(s.into()),
89             ..Self::default()
90         }
91     }
92 
prefix(mut self, s: &str) -> Self93     pub fn prefix(mut self, s: &str) -> Self {
94         self.prefix = Some(s.into());
95         self
96     }
97 
prefix_separator(mut self, s: &str) -> Self98     pub fn prefix_separator(mut self, s: &str) -> Self {
99         self.prefix_separator = Some(s.into());
100         self
101     }
102 
separator(mut self, s: &str) -> Self103     pub fn separator(mut self, s: &str) -> Self {
104         self.separator = Some(s.into());
105         self
106     }
107 
108     /// When set and try_parsing is true, then all environment variables will be parsed as [`Vec<String>`] instead of [`String`].
109     /// See [`with_list_parse_key`] when you want to use [`Vec<String>`] in combination with [`String`].
list_separator(mut self, s: &str) -> Self110     pub fn list_separator(mut self, s: &str) -> Self {
111         self.list_separator = Some(s.into());
112         self
113     }
114 
115     /// Add a key which should be parsed as a list when collecting [`Value`]s from the environment.
116     /// Once list_separator is set, the type for string is [`Vec<String>`].
117     /// To switch the default type back to type Strings you need to provide the keys which should be [`Vec<String>`] using this function.
with_list_parse_key(mut self, key: &str) -> Self118     pub fn with_list_parse_key(mut self, key: &str) -> Self {
119         if self.list_parse_keys == None {
120             self.list_parse_keys = Some(vec![key.into()])
121         } else {
122             self.list_parse_keys = self.list_parse_keys.map(|mut keys| {
123                 keys.push(key.into());
124                 keys
125             });
126         }
127         self
128     }
129 
ignore_empty(mut self, ignore: bool) -> Self130     pub fn ignore_empty(mut self, ignore: bool) -> Self {
131         self.ignore_empty = ignore;
132         self
133     }
134 
135     /// Note: enabling `try_parsing` can reduce performance it will try and parse
136     /// each environment variable 3 times (bool, i64, f64)
try_parsing(mut self, try_parsing: bool) -> Self137     pub fn try_parsing(mut self, try_parsing: bool) -> Self {
138         self.try_parsing = try_parsing;
139         self
140     }
141 
keep_prefix(mut self, keep: bool) -> Self142     pub fn keep_prefix(mut self, keep: bool) -> Self {
143         self.keep_prefix = keep;
144         self
145     }
146 
source(mut self, source: Option<Map<String, String>>) -> Self147     pub fn source(mut self, source: Option<Map<String, String>>) -> Self {
148         self.source = source;
149         self
150     }
151 }
152 
153 impl Source for Environment {
clone_into_box(&self) -> Box<dyn Source + Send + Sync>154     fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
155         Box::new((*self).clone())
156     }
157 
collect(&self) -> Result<Map<String, Value>>158     fn collect(&self) -> Result<Map<String, Value>> {
159         let mut m = Map::new();
160         let uri: String = "the environment".into();
161 
162         let separator = self.separator.as_deref().unwrap_or("");
163         let prefix_separator = match (self.prefix_separator.as_deref(), self.separator.as_deref()) {
164             (Some(pre), _) => pre,
165             (None, Some(sep)) => sep,
166             (None, None) => "_",
167         };
168 
169         // Define a prefix pattern to test and exclude from keys
170         let prefix_pattern = self
171             .prefix
172             .as_ref()
173             .map(|prefix| format!("{}{}", prefix, prefix_separator).to_lowercase());
174 
175         let collector = |(key, value): (String, String)| {
176             // Treat empty environment variables as unset
177             if self.ignore_empty && value.is_empty() {
178                 return;
179             }
180 
181             let mut key = key.to_lowercase();
182 
183             // Check for prefix
184             if let Some(ref prefix_pattern) = prefix_pattern {
185                 if key.starts_with(prefix_pattern) {
186                     if !self.keep_prefix {
187                         // Remove this prefix from the key
188                         key = key[prefix_pattern.len()..].to_string();
189                     }
190                 } else {
191                     // Skip this key
192                     return;
193                 }
194             }
195 
196             // If separator is given replace with `.`
197             if !separator.is_empty() {
198                 key = key.replace(separator, ".");
199             }
200 
201             let value = if self.try_parsing {
202                 // convert to lowercase because bool parsing expects all lowercase
203                 if let Ok(parsed) = value.to_lowercase().parse::<bool>() {
204                     ValueKind::Boolean(parsed)
205                 } else if let Ok(parsed) = value.parse::<i64>() {
206                     ValueKind::I64(parsed)
207                 } else if let Ok(parsed) = value.parse::<f64>() {
208                     ValueKind::Float(parsed)
209                 } else if let Some(separator) = &self.list_separator {
210                     if let Some(keys) = &self.list_parse_keys {
211                         if keys.contains(&key) {
212                             let v: Vec<Value> = value
213                                 .split(separator)
214                                 .map(|s| Value::new(Some(&uri), ValueKind::String(s.to_string())))
215                                 .collect();
216                             ValueKind::Array(v)
217                         } else {
218                             ValueKind::String(value)
219                         }
220                     } else {
221                         let v: Vec<Value> = value
222                             .split(separator)
223                             .map(|s| Value::new(Some(&uri), ValueKind::String(s.to_string())))
224                             .collect();
225                         ValueKind::Array(v)
226                     }
227                 } else {
228                     ValueKind::String(value)
229                 }
230             } else {
231                 ValueKind::String(value)
232             };
233 
234             m.insert(key, Value::new(Some(&uri), value));
235         };
236 
237         match &self.source {
238             Some(source) => source.clone().into_iter().for_each(collector),
239             None => env::vars().for_each(collector),
240         }
241 
242         Ok(m)
243     }
244 }
245