summaryrefslogtreecommitdiffstats
path: root/src/config.rs
diff options
context:
space:
mode:
authorRyan Leckey <leckey.ryan@gmail.com>2017-01-28 20:46:29 -0800
committerRyan Leckey <leckey.ryan@gmail.com>2017-01-28 20:47:26 -0800
commit7d870758cbd9ad4181471ad40184d1bac1204e1e (patch)
tree5f84b003c3dfe4c65b5ed97cbce8de6b8f984c49 /src/config.rs
parent0b3087a8cbe6c0b9428d33c3a2c92155a3bbc558 (diff)
Use 'Cow' to remove unnecessary allocations
Diffstat (limited to 'src/config.rs')
-rw-r--r--src/config.rs263
1 files changed, 190 insertions, 73 deletions
diff --git a/src/config.rs b/src/config.rs
index e5b25f6..a1a36cb 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -2,82 +2,199 @@ use value::Value;
use source::{Source, SourceBuilder};
use std::error::Error;
+use std::fmt;
+use std::borrow::Cow;
use std::collections::HashMap;
-#[derive(Default)]
-pub struct Config {
- defaults: HashMap<String, Value>,
- overrides: HashMap<String, Value>,
- sources: Vec<Box<Source>>,
+#[derive(Default, Debug)]
+pub struct FrozenError { }
+
+impl fmt::Display for FrozenError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "FrozenError")
+ }
}
-impl Config {
- pub fn new() -> Config {
- Default::default()
+impl Error for FrozenError {
+ fn description(&self) -> &'static str {
+ "configuration is frozen"
}
+}
- /// Merge in configuration values from the given source.
- pub fn merge<T>(&mut self, source: T) -> Result<(), Box<Error>>
- where T: SourceBuilder
- {
- self.sources.push(source.build()?);
+// Underlying storage for the configuration
+enum ConfigStore<'a> {
+ Mutable {
+ defaults: HashMap<String, Value<'a>>,
+ overrides: HashMap<String, Value<'a>>,
+ sources: Vec<Box<Source>>,
+ },
+
+ // TODO: Will be used for frozen configuratino soon
+ #[allow(dead_code)]
+ Frozen,
+}
- Ok(())
+impl<'a> Default for ConfigStore<'a> {
+ fn default() -> Self {
+ ConfigStore::Mutable {
+ defaults: HashMap::new(),
+ overrides: HashMap::new(),
+ sources: Vec::new(),
+ }
}
+}
- /// Sets the default value for this key. The default value is only used
- /// when no other value is provided.
- pub fn set_default<T>(&mut self, key: &str, value: T)
- where T: Into<Value>
+impl<'a> ConfigStore<'a> {
+ fn merge<T>(&mut self, source: T) -> Result<(), Box<Error>>
+ where T: SourceBuilder
{
- self.defaults.insert(key.to_lowercase(), value.into());
+ if let ConfigStore::Mutable { ref mut sources, .. } = *self {
+ sources.push(source.build()?);
+
+ Ok(())
+ } else {
+ Err(FrozenError::default().into())
+ }
}
- /// Sets an override for this key.
- pub fn set<T>(&mut self, key: &str, value: T)
- where T: Into<Value>
+ fn set_default<T>(&mut self, key: &str, value: T) -> Result<(), Box<Error>>
+ where T: Into<Value<'a>>
{
- self.overrides.insert(key.to_lowercase(), value.into());
+ if let ConfigStore::Mutable { ref mut defaults, .. } = *self {
+ defaults.insert(key.to_lowercase(), value.into());
+
+ Ok(())
+ } else {
+ Err(FrozenError::default().into())
+ }
}
- pub fn get(&self, key: &str) -> Option<Value> {
- // Check explicit override
+ fn set<T>(&mut self, key: &str, value: T) -> Result<(), Box<Error>>
+ where T: Into<Value<'a>>
+ {
+ if let ConfigStore::Mutable { ref mut overrides, .. } = *self {
+ overrides.insert(key.to_lowercase(), value.into());
- if let Some(value) = self.overrides.get(key) {
- return Some(value.clone());
+ Ok(())
+ } else {
+ Err(FrozenError::default().into())
}
+ }
+
+ fn get(&self, key: &str) -> Option<Value> {
+ if let ConfigStore::Mutable { ref overrides, ref sources, ref defaults } = *self {
+ // Check explicit override
+ if let Some(value) = overrides.get(key) {
+ return Some(value.clone());
+ }
- // Check sources
+ // Check sources
+ for source in &mut sources.iter().rev() {
+ if let Some(value) = source.get(key) {
+ return Some(value);
+ }
+ }
- for source in &mut self.sources.iter().rev() {
- if let Some(value) = source.get(key) {
- return Some(value);
+ // Check explicit defaults
+ if let Some(value) = defaults.get(key) {
+ return Some(value.clone());
}
}
- // Check explicit defaults
+ None
+ }
+}
- if let Some(value) = self.defaults.get(key) {
- return Some(value.clone());
- }
+#[derive(Default)]
+pub struct Config<'a> {
+ store: ConfigStore<'a>,
+}
+// TODO(@rust): There must be a way to remove this function and use Value::as_str
+#[allow(needless_lifetimes)]
+fn value_into_str<'a>(value: Value<'a>) -> Option<Cow<'a, str>> {
+ if let Value::String(value) = value {
+ Some(value)
+ } else if let Value::Integer(value) = value {
+ Some(Cow::Owned(value.to_string()))
+ } else if let Value::Float(value) = value {
+ Some(Cow::Owned(value.to_string()))
+ } else if let Value::Boolean(value) = value {
+ Some(Cow::Owned(value.to_string()))
+ } else {
None
}
+}
+
+impl<'a> Config<'a> {
+ pub fn new() -> Config<'a> {
+ Default::default()
+ }
+
+ /// Merge in configuration values from the given source.
+ pub fn merge<T>(&mut self, source: T) -> Result<(), Box<Error>>
+ where T: SourceBuilder
+ {
+ self.store.merge(source)
+ }
- pub fn get_str(&self, key: &str) -> Option<String> {
- self.get(key).and_then(Value::as_str)
+ /// Sets the default value for this key. The default value is only used
+ /// when no other value is provided.
+ pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<(), Box<Error>>
+ where T: Into<Value<'a>>
+ {
+ self.store.set_default(key, value)
+ }
+
+ /// Sets an override for this key.
+ pub fn set<T>(&mut self, key: &str, value: T) -> Result<(), Box<Error>>
+ where T: Into<Value<'a>>
+ {
+ self.store.set(key, value)
+ }
+
+ pub fn get(&self, key: &str) -> Option<Cow<'a, Value>> {
+ self.store.get(key).map(Cow::Owned)
+ }
+
+ pub fn get_str(&'a self, key: &str) -> Option<Cow<'a, str>> {
+ // TODO(@rust): This is a bit nasty looking; 3x match and requires this
+ // odd into_str method
+ if let Some(value) = self.get(key) {
+ match value {
+ Cow::Borrowed(value) => {
+ match value.as_str() {
+ Some(value) => {
+ match value {
+ Cow::Borrowed(value) => Some(Cow::Borrowed(value)),
+ Cow::Owned(value) => Some(Cow::Owned(value)),
+ }
+ }
+
+ _ => None,
+ }
+ }
+
+ Cow::Owned(value) => value_into_str(value),
+ }
+ } else {
+ None
+ }
}
pub fn get_int(&self, key: &str) -> Option<i64> {
- self.get(key).and_then(Value::as_int)
+ // TODO(@rust): Why doesn't .and_then(Value::as_int) work?
+ self.get(key).and_then(|v| v.as_int())
}
pub fn get_float(&self, key: &str) -> Option<f64> {
- self.get(key).and_then(Value::as_float)
+ // TODO(@rust): Why doesn't .and_then(Value::as_float) work?
+ self.get(key).and_then(|v| v.as_float())
}
pub fn get_bool(&self, key: &str) -> Option<bool> {
- self.get(key).and_then(Value::as_bool)
+ // TODO(@rust): Why doesn't .and_then(Value::as_bool) work?
+ self.get(key).and_then(|v| v.as_bool())
}
}
@@ -99,13 +216,13 @@ mod test {
fn test_default_override() {
let mut c = Config::new();
- c.set_default("key_1", false);
- c.set_default("key_2", false);
+ c.set_default("key_1", false).unwrap();
+ c.set_default("key_2", false).unwrap();
assert!(!c.get_bool("key_1").unwrap());
assert!(!c.get_bool("key_2").unwrap());
- c.set("key_2", true);
+ c.set("key_2", true).unwrap();
assert!(!c.get_bool("key_1").unwrap());
assert!(c.get_bool("key_2").unwrap());
@@ -116,7 +233,7 @@ mod test {
fn test_str() {
let mut c = Config::new();
- c.set("key", "value");
+ c.set("key", "value").unwrap();
assert_eq!(c.get_str("key").unwrap(), "value");
}
@@ -126,7 +243,7 @@ mod test {
fn test_bool() {
let mut c = Config::new();
- c.set("key", true);
+ c.set("key", true).unwrap();
assert_eq!(c.get_bool("key").unwrap(), true);
}
@@ -136,7 +253,7 @@ mod test {
fn test_float() {
let mut c = Config::new();
- c.set("key", 3.14);
+ c.set("key", 3.14).unwrap();
assert_eq!(c.get_float("key").unwrap(), 3.14);
}
@@ -146,7 +263,7 @@ mod test {
fn test_int() {
let mut c = Config::new();
- c.set("key", 42);
+ c.set("key", 42).unwrap();
assert_eq!(c.get_int("key").unwrap(), 42);
}
@@ -156,9 +273,9 @@ mod test {
fn test_retrieve_str() {
let mut c = Config::new();
- c.set("key_1", 115);
- c.set("key_2", 1.23);
- c.set("key_3", false);
+ c.set("key_1", 115).unwrap();
+ c.set("key_2", 1.23).unwrap();
+ c.set("key_3", false).unwrap();
assert_eq!(c.get_str("key_1").unwrap(), "115");
assert_eq!(c.get_str("key_2").unwrap(), "1.23");
@@ -170,12 +287,12 @@ mod test {
fn test_retrieve_int() {
let mut c = Config::new();
- c.set("key_1", "121");
- c.set("key_2", 5.12);
- c.set("key_3", 5.72);
- c.set("key_4", false);
- c.set("key_5", true);
- c.set("key_6", "asga");
+ c.set("key_1", "121").unwrap();
+ c.set("key_2", 5.12).unwrap();
+ c.set("key_3", 5.72).unwrap();
+ c.set("key_4", false).unwrap();
+ c.set("key_5", true).unwrap();
+ c.set("key_6", "asga").unwrap();
assert_eq!(c.get_int("key_1"), Some(121));
assert_eq!(c.get_int("key_2"), Some(5));
@@ -190,12 +307,12 @@ mod test {
fn test_retrieve_float() {
let mut c = Config::new();
- c.set("key_1", "121");
- c.set("key_2", "121.512");
- c.set("key_3", 5);
- c.set("key_4", false);
- c.set("key_5", true);
- c.set("key_6", "asga");
+ c.set("key_1", "121").unwrap();
+ c.set("key_2", "121.512").unwrap();
+ c.set("key_3", 5).unwrap();
+ c.set("key_4", false).unwrap();
+ c.set("key_5", true).unwrap();
+ c.set("key_6", "asga").unwrap();
assert_eq!(c.get_float("key_1"), Some(121.0));
assert_eq!(c.get_float("key_2"), Some(121.512));
@@ -210,17 +327,17 @@ mod test {
fn test_retrieve_bool() {
let mut c = Config::new();
- c.set("key_1", "121");
- c.set("key_2", "1");
- c.set("key_3", "0");
- c.set("key_4", "true");
- c.set("key_5", "");
- c.set("key_6", 51);
- c.set("key_7", 0);
- c.set("key_8", 12.12);
- c.set("key_9", 1.0);
- c.set("key_10", 0.0);
- c.set("key_11", "asga");
+ c.set("key_1", "121").unwrap();
+ c.set("key_2", "1").unwrap();
+ c.set("key_3", "0").unwrap();
+ c.set("key_4", "true").unwrap();
+ c.set("key_5", "").unwrap();
+ c.set("key_6", 51).unwrap();
+ c.set("key_7", 0).unwrap();
+ c.set("key_8", 12.12).unwrap();
+ c.set("key_9", 1.0).unwrap();
+ c.set("key_10", 0.0).unwrap();
+ c.set("key_11", "asga").unwrap();
assert_eq!(c.get_bool("key_1"), None);
assert_eq!(c.get_bool("key_2"), Some(true));