summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Leckey <ryan@launchbadge.com>2017-06-03 01:21:10 -0700
committerRyan Leckey <ryan@launchbadge.com>2017-06-03 01:21:10 -0700
commitc26907b3ecf2b139fe61bf1403c952b85285ae02 (patch)
tree1cb1f275e9942c08c3d6f085de63bba43c652130
parent028aaf5247dd3cd184b250afdba235d683525f97 (diff)
Add set and set_default (and deep merging)
-rw-r--r--src/config.rs75
-rw-r--r--src/path/mod.rs120
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!();
}