config_maint/
env.rs

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