From 7d870758cbd9ad4181471ad40184d1bac1204e1e Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Sat, 28 Jan 2017 20:46:29 -0800 Subject: Use 'Cow' to remove unnecessary allocations --- src/config.rs | 263 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 190 insertions(+), 73 deletions(-) (limited to 'src/config.rs') 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, - overrides: HashMap, - sources: Vec>, +#[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(&mut self, source: T) -> Result<(), Box> - where T: SourceBuilder - { - self.sources.push(source.build()?); +// Underlying storage for the configuration +enum ConfigStore<'a> { + Mutable { + defaults: HashMap>, + overrides: HashMap>, + sources: Vec>, + }, + + // 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(&mut self, key: &str, value: T) - where T: Into +impl<'a> ConfigStore<'a> { + fn merge(&mut self, source: T) -> Result<(), Box> + 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(&mut self, key: &str, value: T) - where T: Into + fn set_default(&mut self, key: &str, value: T) -> Result<(), Box> + where T: Into> { - 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 { - // Check explicit override + fn set(&mut self, key: &str, value: T) -> Result<(), Box> + where T: Into> + { + 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 { + 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> { + 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(&mut self, source: T) -> Result<(), Box> + where T: SourceBuilder + { + self.store.merge(source) + } - pub fn get_str(&self, key: &str) -> Option { - 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(&mut self, key: &str, value: T) -> Result<(), Box> + where T: Into> + { + self.store.set_default(key, value) + } + + /// Sets an override for this key. + pub fn set(&mut self, key: &str, value: T) -> Result<(), Box> + where T: Into> + { + self.store.set(key, value) + } + + pub fn get(&self, key: &str) -> Option> { + self.store.get(key).map(Cow::Owned) + } + + pub fn get_str(&'a self, key: &str) -> Option> { + // 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 { - 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 { - 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 { - 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)); -- cgit v1.2.3