config_maint/
config.rs

1use serde::de::{Deserialize, Deserializer};
2use serde::ser::{Serialize, Serializer};
3use std::collections::HashMap;
4use std::fmt::Debug;
5use std::ops::Deref;
6use std::str::FromStr;
7
8use error::*;
9use ser::ConfigSerializer;
10use source::Source;
11
12use path;
13use value::{Table, Value, ValueKind};
14
15#[derive(Clone, Debug)]
16enum ConfigKind {
17    // A mutable configuration. This is the default.
18    Mutable {
19        defaults: HashMap<path::Expression, Value>,
20        overrides: HashMap<path::Expression, Value>,
21        sources: Vec<Box<dyn Source + Send + Sync>>,
22    },
23
24    // A frozen configuration.
25    // Configuration can no longer be mutated.
26    Frozen,
27}
28
29impl Default for ConfigKind {
30    fn default() -> Self {
31        ConfigKind::Mutable {
32            defaults: HashMap::new(),
33            overrides: HashMap::new(),
34            sources: Vec::new(),
35        }
36    }
37}
38
39/// A prioritized configuration repository. It maintains a set of
40/// configuration sources, fetches values to populate those, and provides
41/// them according to the source's priority.
42#[derive(Default, Clone, Debug)]
43pub struct Config {
44    kind: ConfigKind,
45
46    /// Root of the cached configuration.
47    pub cache: Value,
48}
49
50impl Config {
51    pub fn new() -> Self {
52        Self {
53            kind: ConfigKind::default(),
54            // Config root should be instantiated as an empty table
55            // to avoid deserialization errors.
56            cache: Value::new(None, Table::new()),
57        }
58    }
59
60    /// Merge in a configuration property source.
61    pub fn merge<T>(&mut self, source: T) -> Result<&mut Config>
62    where
63        T: 'static,
64        T: Source + Send + Sync,
65    {
66        match self.kind {
67            ConfigKind::Mutable {
68                ref mut sources, ..
69            } => {
70                sources.push(Box::new(source));
71            }
72
73            ConfigKind::Frozen => {
74                return Err(ConfigError::Frozen);
75            }
76        }
77
78        self.refresh()
79    }
80
81    /// Refresh the configuration cache with fresh
82    /// data from added sources.
83    ///
84    /// Configuration is automatically refreshed after a mutation
85    /// operation (`set`, `merge`, `set_default`, etc.).
86    pub fn refresh(&mut self) -> Result<&mut Config> {
87        self.cache = match self.kind {
88            // TODO: We need to actually merge in all the stuff
89            ConfigKind::Mutable {
90                ref overrides,
91                ref sources,
92                ref defaults,
93            } => {
94                let mut cache: Value = HashMap::<String, Value>::new().into();
95
96                // Add defaults
97                for (key, val) in defaults {
98                    key.set(&mut cache, val.clone());
99                }
100
101                // Add sources
102                sources.collect_to(&mut cache)?;
103
104                // Add overrides
105                for (key, val) in overrides {
106                    key.set(&mut cache, val.clone());
107                }
108
109                cache
110            }
111
112            ConfigKind::Frozen => {
113                return Err(ConfigError::Frozen);
114            }
115        };
116
117        Ok(self)
118    }
119
120    pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
121    where
122        T: Into<Value>,
123    {
124        match self.kind {
125            ConfigKind::Mutable {
126                ref mut defaults, ..
127            } => {
128                defaults.insert(key.parse()?, value.into());
129            }
130
131            ConfigKind::Frozen => return Err(ConfigError::Frozen),
132        };
133
134        self.refresh()
135    }
136
137    pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
138    where
139        T: Into<Value>,
140    {
141        match self.kind {
142            ConfigKind::Mutable {
143                ref mut overrides, ..
144            } => {
145                overrides.insert(key.parse()?, value.into());
146            }
147
148            ConfigKind::Frozen => return Err(ConfigError::Frozen),
149        };
150
151        self.refresh()
152    }
153
154    pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Result<T> {
155        // Parse the key into a path expression
156        let expr: path::Expression = key.parse()?;
157
158        // Traverse the cache using the path to (possibly) retrieve a value
159        let value = expr.get(&self.cache).cloned();
160
161        match value {
162            Some(value) => {
163                // Deserialize the received value into the requested type
164                T::deserialize(value).map_err(|e| e.extend_with_key(key))
165            }
166
167            None => Err(ConfigError::NotFound(key.into())),
168        }
169    }
170
171    pub fn get_str(&self, key: &str) -> Result<String> {
172        self.get(key).and_then(Value::into_str)
173    }
174
175    pub fn get_int(&self, key: &str) -> Result<i64> {
176        self.get(key).and_then(Value::into_int)
177    }
178
179    pub fn get_float(&self, key: &str) -> Result<f64> {
180        self.get(key).and_then(Value::into_float)
181    }
182
183    pub fn get_bool(&self, key: &str) -> Result<bool> {
184        self.get(key).and_then(Value::into_bool)
185    }
186
187    pub fn get_table(&self, key: &str) -> Result<HashMap<String, Value>> {
188        self.get(key).and_then(Value::into_table)
189    }
190
191    pub fn get_array(&self, key: &str) -> Result<Vec<Value>> {
192        self.get(key).and_then(Value::into_array)
193    }
194
195    /// Attempt to deserialize the entire configuration into the requested type.
196    pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> {
197        T::deserialize(self)
198    }
199
200    /// Attempt to serialize the entire configuration from the given type.
201    pub fn try_from<T: Serialize>(from: &T) -> Result<Self> {
202        let mut serializer = ConfigSerializer::default();
203        from.serialize(&mut serializer)?;
204        Ok(serializer.output)
205    }
206
207    #[deprecated(since = "0.7.0", note = "please use 'try_into' instead")]
208    pub fn deserialize<'de, T: Deserialize<'de>>(self) -> Result<T> {
209        self.try_into()
210    }
211}
212
213impl Source for Config {
214    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
215        Box::new((*self).clone())
216    }
217
218    fn collect(&self) -> Result<HashMap<String, Value>> {
219        self.cache.clone().into_table()
220    }
221}