diff options
author | Ryan Leckey <ryan@launchbadge.com> | 2017-06-03 01:21:10 -0700 |
---|---|---|
committer | Ryan Leckey <ryan@launchbadge.com> | 2017-06-03 01:21:10 -0700 |
commit | c26907b3ecf2b139fe61bf1403c952b85285ae02 (patch) | |
tree | 1cb1f275e9942c08c3d6f085de63bba43c652130 | |
parent | 028aaf5247dd3cd184b250afdba235d683525f97 (diff) |
Add set and set_default (and deep merging)
-rw-r--r-- | src/config.rs | 75 | ||||
-rw-r--r-- | src/path/mod.rs | 120 |
2 files changed, 188 insertions, 7 deletions
diff --git a/src/config.rs b/src/config.rs index 4d24a1d..ad5421b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,14 +3,15 @@ use serde::de::Deserialize; use error::*; use source::Source; -use value::Value; + +use value::{Value, ValueWithKey}; use path; enum ConfigKind { // A mutable configuration. This is the default. Mutable { - defaults: HashMap<String, Value>, - overrides: HashMap<String, Value>, + defaults: HashMap<path::Expression, Value>, + overrides: HashMap<path::Expression, Value>, sources: Vec<Box<Source + Send + Sync>>, }, @@ -71,7 +72,29 @@ impl Config { ref overrides, ref sources, ref defaults, - } => sources[0].collect()?, + } => { + let mut cache: Value = HashMap::<String, Value>::new().into(); + + // Add defaults + for (key, val) in defaults { + key.set(&mut cache, val.clone()); + } + + // Add sources + for source in sources { + let props = source.collect()?; + for (key, val) in &props { + path::Expression::Identifier(key.clone()).set(&mut cache, val.clone()); + } + } + + // Add overrides + for (key, val) in overrides { + key.set(&mut cache, val.clone()); + } + + cache + }, ConfigKind::Frozen => { return Err(ConfigError::Frozen); @@ -81,11 +104,11 @@ impl Config { Ok(()) } - pub fn deserialize<T: Deserialize>(&self) -> Result<T> { + pub fn deserialize<'de, T: Deserialize<'de>>(&self) -> Result<T> { return T::deserialize(self.cache.clone()); } - pub fn get<T: Deserialize>(&self, key: &str) -> Result<T> { + pub fn get<'de, T: Deserialize<'de>>(&self, key: &'de str) -> Result<T> { // Parse the key into a path expression let expr: path::Expression = key.to_lowercase().parse()?; @@ -95,10 +118,48 @@ impl Config { match value { Some(value) => { // Deserialize the received value into the requested type - T::deserialize(value) + T::deserialize(ValueWithKey::new(value, key)) } None => Err(ConfigError::NotFound(key.into())), } } + + pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<()> + where T: Into<Value> + { + match self.kind { + ConfigKind::Mutable { + ref mut defaults, + .. + } => { + defaults.insert(key.parse()?, value.into()); + } + + ConfigKind::Frozen => { + return Err(ConfigError::Frozen) + } + }; + + self.refresh() + } + + pub fn set<T>(&mut self, key: &str, value: T) -> Result<()> + where T: Into<Value> + { + match self.kind { + ConfigKind::Mutable { + ref mut overrides, + .. + } => { + overrides.insert(key.parse()?, value.into()); + } + + ConfigKind::Frozen => { + return Err(ConfigError::Frozen) + } + }; + + self.refresh() + } } diff --git a/src/path/mod.rs b/src/path/mod.rs index f889283..d2df442 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -1,4 +1,5 @@ use std::str::FromStr; +use std::collections::HashMap; use nom::ErrorKind; use error::*; use value::{Value, ValueKind}; @@ -33,6 +34,125 @@ impl Expression { } } + Expression::Child(expr, key) => { + match expr.get(root) { + Some(value) => { + match value.kind { + // Access on a table is identical to Identifier, it just forwards + ValueKind::Table(ref map) => map.get(&key), + + // all other variants return None + _ => None, + } + } + + _ => None, + } + } + + _ => { + unimplemented!(); + } + } + } + + pub fn get_mut<'a>(&self, root: &'a mut Value) -> Option<&'a mut Value> { + match *self { + Expression::Identifier(ref id) => { + match root.kind { + ValueKind::Table(ref mut map) => { + Some(map.entry(id.clone()).or_insert(Value::new(None, ValueKind::Nil))) + } + + _ => None, + } + } + + Expression::Child(ref expr, ref key) => { + match expr.get_mut(root) { + Some(value) => { + match value.kind { + ValueKind::Table(ref mut map) => { + Some(map.entry(key.clone()).or_insert(Value::new(None, ValueKind::Nil))) + } + + _ => { + *value = HashMap::<String, Value>::new().into(); + + if let ValueKind::Table(ref mut map) = value.kind { + Some(map.entry(key.clone()).or_insert(Value::new(None, ValueKind::Nil))) + } else { + println!("WHAT THE FUCK?"); + + unreachable!(); + } + } + } + } + + _ => None, + } + } + + _ => { + unimplemented!(); + } + } + } + + pub fn set<'a>(&self, root: &'a mut Value, value: Value) { + match *self { + Expression::Identifier(ref id) => { + // Ensure that root is a table + match root.kind { + ValueKind::Table(_) => { } + + _ => { + *root = HashMap::<String, Value>::new().into(); + } + } + + match value.kind { + ValueKind::Table(ref incoming_map) => { + // Pull out another table + let mut target = if let ValueKind::Table(ref mut map) = root.kind { + map.entry(id.clone()).or_insert(HashMap::<String, Value>::new().into()) + } else { + unreachable!(); + }; + + // Continue the deep merge + for (key, val) in incoming_map { + Expression::Identifier(key.clone()).set(&mut target, val.clone()); + } + } + + _ => { + if let ValueKind::Table(ref mut map) = root.kind { + // Just do a simple set + map.insert(id.clone(), value); + } + } + } + } + + Expression::Child(ref expr, ref key) => { + if let Some(parent) = expr.get_mut(root) { + match parent.kind { + ValueKind::Table(_) => { + Expression::Identifier(key.clone()).set(parent, value); + } + + _ => { + // Didn't find a table. Oh well. Make a table and do this anyway + *parent = HashMap::<String, Value>::new().into(); + + Expression::Identifier(key.clone()).set(parent, value); + } + } + } + } + _ => { unimplemented!(); } |