From a12d8e5992289bbb9c50bc2d734132e1cfc2798b Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Sat, 3 Jun 2017 01:21:43 -0700 Subject: Make Value itself be deserializable --- src/value.rs | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 1 deletion(-) diff --git a/src/value.rs b/src/value.rs index 3f659bc..25d9709 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use std::fmt::Display; +use std::fmt; use error::*; +use serde::de::{Deserialize, Deserializer, Visitor}; /// Underlying kind of the configuration value. #[derive(Debug, Clone)] @@ -99,7 +101,7 @@ pub struct Value { /// /// A Value originating from a File might contain: /// ``` - /// Settings.toml at line 1 column 2 + /// Settings.toml /// ``` /// /// A Value originating from the environment would contain: @@ -127,6 +129,10 @@ impl Value { } } + pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result { + return T::deserialize(self); + } + /// Returns `self` as a bool, if possible. pub fn into_bool(self) -> Result { match self.kind { @@ -316,6 +322,134 @@ impl Value { } } +impl<'de> Deserialize<'de> for Value { + #[inline] + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: Deserializer<'de>, + { + struct ValueVisitor; + + impl<'de> Visitor<'de> for ValueVisitor { + type Value = Value; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("any valid configuration value") + } + + #[inline] + fn visit_bool(self, value: bool) -> ::std::result::Result { + Ok(value.into()) + } + + #[inline] + fn visit_i8(self, value: i8) -> ::std::result::Result { + Ok((value as i64).into()) + } + + #[inline] + fn visit_i16(self, value: i16) -> ::std::result::Result { + Ok((value as i64).into()) + } + + #[inline] + fn visit_i32(self, value: i32) -> ::std::result::Result { + Ok((value as i64).into()) + } + + #[inline] + fn visit_i64(self, value: i64) -> ::std::result::Result { + Ok(value.into()) + } + + #[inline] + fn visit_u8(self, value: u8) -> ::std::result::Result { + Ok((value as i64).into()) + } + + #[inline] + fn visit_u16(self, value: u16) -> ::std::result::Result { + Ok((value as i64).into()) + } + + #[inline] + fn visit_u32(self, value: u32) -> ::std::result::Result { + Ok((value as i64).into()) + } + + #[inline] + fn visit_u64(self, value: u64) -> ::std::result::Result { + // FIXME: This is bad + Ok((value as i64).into()) + } + + #[inline] + fn visit_f64(self, value: f64) -> ::std::result::Result { + Ok(value.into()) + } + + #[inline] + fn visit_str(self, value: &str) -> ::std::result::Result + where + E: ::serde::de::Error, + { + self.visit_string(String::from(value)) + } + + #[inline] + fn visit_string(self, value: String) -> ::std::result::Result { + Ok(value.into()) + } + + #[inline] + fn visit_none(self) -> ::std::result::Result { + Ok(Value::new(None, ValueKind::Nil)) + } + + #[inline] + fn visit_some(self, deserializer: D) -> ::std::result::Result + where D: Deserializer<'de> + { + Deserialize::deserialize(deserializer) + } + + #[inline] + fn visit_unit(self) -> ::std::result::Result { + Ok(Value::new(None, ValueKind::Nil)) + } + + #[inline] + fn visit_seq(self, mut visitor: V) -> ::std::result::Result + where + V: ::serde::de::SeqAccess<'de>, + { + let mut vec = Array::new(); + + while let Some(elem) = try!(visitor.next_element()) { + vec.push(elem); + } + + Ok(vec.into()) + } + + fn visit_map(self, mut visitor: V) -> ::std::result::Result + where + V: ::serde::de::MapAccess<'de>, + { + let mut values = Table::new(); + + while let Some((key, value)) = try!(visitor.next_entry()) { + values.insert(key, value); + } + + Ok(values.into()) + } + } + + deserializer.deserialize_any(ValueVisitor) + } +} + impl From for Value where T: Into { @@ -326,3 +460,59 @@ impl From for Value } } } + +pub struct ValueWithKey<'a>(pub Value, &'a str); + +impl<'a> ValueWithKey<'a> { + pub fn new(value: Value, key: &'a str) -> Self + { + ValueWithKey(value, key) + } + + pub fn into_bool(self) -> Result { + match self.0.into_bool() { + Ok(value) => Ok(value), + Err(error) => Err(error.extend_with_key(self.1)) + } + } + + /// Returns `self` into an i64, if possible. + pub fn into_int(self) -> Result { + match self.0.into_int() { + Ok(value) => Ok(value), + Err(error) => Err(error.extend_with_key(self.1)) + } + } + + /// Returns `self` into a f64, if possible. + pub fn into_float(self) -> Result { + match self.0.into_float() { + Ok(value) => Ok(value), + Err(error) => Err(error.extend_with_key(self.1)) + } + } + + /// Returns `self` into a str, if possible. + pub fn into_str(self) -> Result { + match self.0.into_str() { + Ok(value) => Ok(value), + Err(error) => Err(error.extend_with_key(self.1)) + } + } + + /// Returns `self` into an array, if possible + pub fn into_array(self) -> Result> { + match self.0.into_array() { + Ok(value) => Ok(value), + Err(error) => Err(error.extend_with_key(self.1)) + } + } + + /// If the `Value` is a Table, returns the associated Map. + pub fn into_table(self) -> Result> { + match self.0.into_table() { + Ok(value) => Ok(value), + Err(error) => Err(error.extend_with_key(self.1)) + } + } +} -- cgit v1.2.3