use std::convert::From; use std::collections::HashMap; /// A configuration value. /// /// Has an underlying or native type that comes from the configuration source /// but will be coerced into the requested type. #[derive(Debug, Clone, PartialEq)] pub enum Value { Nil, String(String), Integer(i64), Float(f64), Boolean(bool), Table(HashMap), Array(Vec), } impl Value { /// Converts `self` into a string, if possible. /// Returns None otherwise. pub fn into_str(self) -> Option { match self { Value::String(value) => Some(value), Value::Integer(value) => Some(value.to_string()), Value::Float(value) => Some(value.to_string()), Value::Boolean(value) => Some(value.to_string()), _ => None, } } /// Converts `self` into a bool, if possible. /// Returns None otherwise. pub fn into_bool(self) -> Option { match self { Value::Boolean(value) => Some(value), Value::Integer(value) => Some(value != 0), Value::Float(value) => Some(value != 0.0), Value::String(ref value) => { match value.to_lowercase().as_ref() { "1" | "true" | "on" | "yes" => Some(true), "0" | "false" | "off" | "no" => Some(false), _ => None, } } _ => None, } } /// Converts `self` into an i64, if possible. /// Returns None otherwise. pub fn into_int(self) -> Option { match self { Value::Integer(value) => Some(value), Value::String(ref value) => value.parse().ok(), Value::Boolean(value) => Some(if value { 1 } else { 0 }), Value::Float(value) => Some(value.round() as i64), _ => None, } } /// Converts `self` into a f64, if possible. /// Returns None otherwise. pub fn into_float(self) -> Option { match self { Value::Float(value) => Some(value), Value::String(ref value) => value.parse().ok(), Value::Integer(value) => Some(value as f64), Value::Boolean(value) => Some(if value { 1.0 } else { 0.0 }), _ => None, } } /// If the `Value` is a Table, returns the associated Map. /// Returns None otherwise. pub fn into_table(self) -> Option> { match self { Value::Table(value) => Some(value), _ => None, } } /// If the `Value` is an Array, returns the associated Vector. /// Returns None otherwise. pub fn into_array(self) -> Option> { match self { Value::Array(value) => Some(value), _ => 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.into()) } } impl<'a> From<&'a str> for Value { fn from(value: &'a str) -> Value { Value::String(value.into()) } } impl From for Value { fn from(value: i64) -> Value { Value::Integer(value) } } impl From for Value { fn from(value: f64) -> Value { Value::Float(value) } } impl From for Value { fn from(value: bool) -> Value { Value::Boolean(value) } } impl From> for Value where T: Into { fn from(values: HashMap) -> Value { let mut r = HashMap::new(); for (k, v) in values { r.insert(k.clone(), v.into()); } Value::Table(r) } } impl From> for Value where T: Into { fn from(values: Vec) -> Value { let mut l = Vec::new(); for v in values { l.push(v.into()); } Value::Array(l) } }