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