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/value.rs | 189 ++++++++++++++++++++++++----------------------------------- 1 file changed, 78 insertions(+), 111 deletions(-) (limited to 'src/value.rs') 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