From 91f72c5c2a20c5a9b6b17b5d9ee626645783e5db Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Wed, 25 Jan 2017 18:07:12 -0800 Subject: Big cleanup of the Value API --- src/config.rs | 85 +++++++++++++++++--------- src/lib.rs | 13 ++-- src/value.rs | 189 ++++++++++++++++++++++++---------------------------------- 3 files changed, 139 insertions(+), 148 deletions(-) (limited to 'src') diff --git a/src/config.rs b/src/config.rs index 8590a98..fcdc2e1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,8 +3,8 @@ use source::Source; use std::env; use std::error::Error; -use std::convert::TryFrom; use std::collections::HashMap; +use std::borrow::Cow; #[derive(Default)] pub struct Config { @@ -16,6 +16,26 @@ pub struct Config { sources: Vec>, } +trait ConfigGetResult { + type Type; +} + +impl<'a> ConfigGetResult for &'a str { + type Type = Cow<'a, str>; +} + +impl ConfigGetResult for i64 { + type Type = i64; +} + +impl ConfigGetResult for f64 { + type Type = f64; +} + +impl ConfigGetResult for bool { + type Type = bool; +} + impl Config { pub fn new() -> Config { Default::default() @@ -54,14 +74,11 @@ impl Config { self.overrides.insert(key.to_lowercase(), value.into()); } - pub fn get<'a, T>(&'a mut self, key: &str) -> Option - where T: TryFrom<&'a mut Value>, - T: Default - { + pub fn get<'a>(&'a mut self, key: &str) -> Option<&'a Value> { // Check explicit override - if let Some(value) = self.overrides.get_mut(key) { - return T::try_from(value).ok(); + if let Some(value) = self.overrides.get(key) { + return Some(value); } // Check environment @@ -78,44 +95,58 @@ impl Config { env_key.push_str(&key.to_uppercase()); if let Ok(value) = env::var(env_key.clone()) { - // Store the environment variable into an environ - // hash map; we want to return references - self.environ.insert(key.to_lowercase().into(), value.into()); - - return T::try_from(self.environ.get_mut(key).unwrap()).ok(); + // TODO: Find a better way to do this? + self.environ.insert(key.into(), value.into()); + return self.environ.get(key); } // Check sources - for source in &mut self.sources.iter_mut().rev() { - if let Some(value) = source.get_mut(key) { - return T::try_from(value).ok(); + 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) = self.defaults.get_mut(key) { - return T::try_from(value).ok(); + if let Some(value) = self.defaults.get(key) { + return Some(value); } None } - pub fn get_str<'a>(&'a mut self, key: &str) -> Option<&'a str> { - self.get(key) + pub fn get_str<'a>(&'a mut self, key: &str) -> Option> { + if let Some(value) = self.get(key) { + value.as_str() + } else { + None + } } pub fn get_int(&mut self, key: &str) -> Option { - self.get(key) + if let Some(value) = self.get(key) { + value.as_int() + } else { + None + } } pub fn get_float(&mut self, key: &str) -> Option { - self.get(key) + if let Some(value) = self.get(key) { + value.as_float() + } else { + None + } } pub fn get_bool(&mut self, key: &str) -> Option { - self.get(key) + if let Some(value) = self.get(key) { + value.as_bool() + } else { + None + } } } @@ -190,7 +221,6 @@ mod test { c.set("key", "value"); assert_eq!(c.get_str("key").unwrap(), "value"); - assert!("value" == c.get::<&str>("key").unwrap()); } // Storage and retrieval of Boolean values @@ -201,7 +231,6 @@ mod test { c.set("key", true); assert_eq!(c.get_bool("key").unwrap(), true); - assert!(false != c.get("key").unwrap()); } // Storage and retrieval of Float values @@ -212,7 +241,6 @@ mod test { c.set("key", 3.14); assert_eq!(c.get_float("key").unwrap(), 3.14); - assert!(3.14 >= c.get("key").unwrap()); } // Storage and retrieval of Integer values @@ -223,7 +251,6 @@ mod test { c.set("key", 42); assert_eq!(c.get_int("key").unwrap(), 42); - assert!(42 == c.get::("key").unwrap()); } // Storage of various values and retrieval as String @@ -235,9 +262,9 @@ mod test { c.set("key_2", 1.23); c.set("key_3", false); - assert_eq!(c.get_str("key_1"), Some("115")); - assert_eq!(c.get_str("key_2"), Some("1.23")); - assert_eq!(c.get_str("key_3"), Some("false")); + assert_eq!(c.get_str("key_1").unwrap(), "115"); + assert_eq!(c.get_str("key_2").unwrap(), "1.23"); + assert_eq!(c.get_str("key_3").unwrap(), "false"); } // Storage of various values and retrieval as Integer diff --git a/src/lib.rs b/src/lib.rs index 55266f4..0ae525b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ -#![feature(try_from)] #![feature(drop_types_in_const)] +#![allow(unknown_lints)] extern crate toml; @@ -8,13 +8,13 @@ mod source; mod config; use std::error::Error; -use std::convert::TryFrom; +use std::borrow::Cow; use std::sync::{Once, ONCE_INIT}; pub use source::Source; pub use source::File; -use value::Value; +pub use value::Value; pub use config::Config; @@ -55,14 +55,11 @@ pub fn set(key: &str, value: T) global().set(key, value) } -pub fn get<'a, T>(key: &str) -> Option - where T: TryFrom<&'a mut Value>, - T: Default -{ +pub fn get<'a>(key: &str) -> Option<&'a Value> { global().get(key) } -pub fn get_str<'a>(key: &str) -> Option<&'a str> { +pub fn get_str<'a>(key: &str) -> Option> { global().get_str(key) } diff --git a/src/value.rs b/src/value.rs index 295c7c0..a2b037c 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,16 +1,84 @@ -use std::convert::{From, TryFrom}; +use std::convert::From; +use std::borrow::Cow; // Variant for a configuration Value -// The additional Option is used for the textual representation of the -// underlying type (to cache the string generation) but only if requested. +#[derive(Clone)] pub enum Value { String(String), - Integer(i64, Option), - Float(f64, Option), - Boolean(bool, Option), + Integer(i64), + Float(f64), + Boolean(bool), } -// Conversion from type into variant +impl Value { + /// Gets the underyling value as a string, performing a conversion only if neccessary. + #[allow(needless_lifetimes)] + pub fn as_str<'a>(&'a self) -> Option> { + if let Value::String(ref value) = *self { + Some(Cow::Borrowed(value)) + } else if let Value::Integer(value) = *self { + Some(Cow::Owned(value.to_string())) + } else if let Value::Float(value) = *self { + Some(Cow::Owned(value.to_string())) + } else if let Value::Boolean(value) = *self { + Some(Cow::Owned(value.to_string())) + } else { + None + } + } + + /// Gets the underlying type as a boolean, performing a conversion only if neccessary. + pub fn as_bool(&self) -> Option { + if let Value::Boolean(value) = *self { + Some(value) + } else if let Value::String(ref value) = *self { + Some(match value.to_lowercase().as_ref() { + "1" | "true" | "on" | "yes" => true, + _ => false, + }) + } else if let Value::Integer(value) = *self { + Some(value != 0) + } else if let Value::Float(value) = *self { + Some(value != 0.0) + } else { + None + } + } + + /// Gets the underlying type as an integer, performing a conversion only if neccessary. + pub fn as_int(&self) -> Option { + if let Value::Integer(value) = *self { + Some(value) + } else if let Value::String(ref value) = *self { + value.parse().ok() + } else if let Value::Boolean(value) = *self { + Some(if value { 1 } else { 0 }) + } else if let Value::Float(value) = *self { + Some(value.round() as i64) + } else { + None + } + } + + /// Gets the underlying type as a floating-point, performing a conversion only if neccessary. + pub fn as_float(&self) -> Option { + if let Value::Float(value) = *self { + Some(value) + } else if let Value::String(ref value) = *self { + value.parse().ok() + } else if let Value::Integer(value) = *self { + Some(value as f64) + } else if let Value::Boolean(value) = *self { + Some(if value { 1.0 } else { 0.0 }) + } else { + None + } + } +} + +// Generalized construction from type into variant is needed +// for setting configuration values + impl From for Value { fn from(value: String) -> Value { Value::String(value) @@ -25,119 +93,18 @@ impl<'a> From<&'a str> for Value { impl From for Value { fn from(value: i64) -> Value { - Value::Integer(value, None) + Value::Integer(value) } } impl From for Value { fn from(value: f64) -> Value { - Value::Float(value, None) + Value::Float(value) } } impl From for Value { fn from(value: bool) -> Value { - Value::Boolean(value, None) - } -} - -// Conversion from variant into type -impl<'a> TryFrom<&'a mut Value> for &'a str { - type Err = (); - - fn try_from(value: &mut Value) -> Result<&str, ()> { - // When converting a non-string value into a string; - // cache the conversion and return a reference - - if let Value::String(ref value) = *value { - Ok(value) - } else if let Value::Integer(value, ref mut text) = *value { - if let Some(ref text) = *text { - Ok(text) - } else { - *text = Some(value.to_string()); - - Ok(text.as_ref().unwrap()) - } - } else if let Value::Float(value, ref mut text) = *value { - if let Some(ref text) = *text { - Ok(text) - } else { - *text = Some(value.to_string()); - - Ok(text.as_ref().unwrap()) - } - } else if let Value::Boolean(value, ref mut text) = *value { - if let Some(ref text) = *text { - Ok(text) - } else { - *text = Some(value.to_string()); - - Ok(text.as_ref().unwrap()) - } - } else { - Err(()) - } - } -} - -impl<'a> TryFrom<&'a mut Value> for i64 { - type Err = (); - - fn try_from(value: &mut Value) -> Result { - if let Value::Integer(value, ..) = *value { - Ok(value) - } else if let Value::String(ref value) = *value { - value.parse().map_err(|_| { - // Drop specific error - }) - } else if let Value::Boolean(value, ..) = *value { - Ok(if value { 1 } else { 0 }) - } else if let Value::Float(value, ..) = *value { - Ok(value.round() as i64) - } else { - Err(()) - } - } -} - -impl<'a> TryFrom<&'a mut Value> for f64 { - type Err = (); - - fn try_from(value: &mut Value) -> Result { - if let Value::Float(value, ..) = *value { - Ok(value) - } else if let Value::String(ref value) = *value { - value.parse().map_err(|_| { - // Drop specific error - }) - } else if let Value::Integer(value, ..) = *value { - Ok(value as f64) - } else if let Value::Boolean(value, ..) = *value { - Ok(if value { 1.0 } else { 0.0 }) - } else { - Err(()) - } - } -} - -impl<'a> TryFrom<&'a mut Value> for bool { - type Err = (); - - fn try_from(value: &mut Value) -> Result { - if let Value::Boolean(value, ..) = *value { - Ok(value) - } else if let Value::String(ref value) = *value { - Ok(match value.to_lowercase().as_ref() { - "1" | "true" | "on" | "yes" => true, - _ => false, - }) - } else if let Value::Integer(value, ..) = *value { - Ok(value != 0) - } else if let Value::Float(value, ..) = *value { - Ok(value != 0.0) - } else { - Err(()) - } + Value::Boolean(value) } } -- cgit v1.2.3