diff options
author | Ryan Leckey <leckey.ryan@gmail.com> | 2017-01-23 18:58:19 -0800 |
---|---|---|
committer | Ryan Leckey <leckey.ryan@gmail.com> | 2017-01-23 18:58:19 -0800 |
commit | 40a44a459fde4c4042d5188faedea718a34e2b96 (patch) | |
tree | 41dce965b779c29e60504c9f1121286388f0e932 /src/value.rs |
Initial commit.
Diffstat (limited to 'src/value.rs')
-rw-r--r-- | src/value.rs | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..295c7c0 --- /dev/null +++ b/src/value.rs @@ -0,0 +1,143 @@ +use std::convert::{From, TryFrom}; + +// Variant for a configuration Value +// The additional Option<String> is used for the textual representation of the +// underlying type (to cache the string generation) but only if requested. +pub enum Value { + String(String), + Integer(i64, Option<String>), + Float(f64, Option<String>), + Boolean(bool, Option<String>), +} + +// Conversion from type into variant +impl From<String> for Value { + fn from(value: String) -> Value { + Value::String(value) + } +} + +impl<'a> From<&'a str> for Value { + fn from(value: &'a str) -> Value { + Value::String(value.into()) + } +} + +impl From<i64> for Value { + fn from(value: i64) -> Value { + Value::Integer(value, None) + } +} + +impl From<f64> for Value { + fn from(value: f64) -> Value { + Value::Float(value, None) + } +} + +impl From<bool> 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<i64, ()> { + 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<f64, ()> { + 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<bool, ()> { + 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(()) + } + } +} |