summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Leckey <ryan@launchbadge.com>2017-06-03 01:22:24 -0700
committerRyan Leckey <ryan@launchbadge.com>2017-06-03 01:22:24 -0700
commit5eef2539557b9d4eec72c7053457bf3025a3989e (patch)
tree049d7089dc154f39b5d834023703dd5143f68bb0
parentfb30d87cf096d0816abc3c40c1ff3f1b8440ee74 (diff)
More context for errors (value key if possible)
-rw-r--r--src/de.rs221
-rw-r--r--src/error.rs33
2 files changed, 209 insertions, 45 deletions
diff --git a/src/de.rs b/src/de.rs
index 9a9ef58..6487f8b 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -1,107 +1,216 @@
use serde::de;
-use value::{Value, ValueKind};
+use value::{Value, ValueWithKey, ValueKind};
use error::*;
use std::borrow::Cow;
use std::iter::Peekable;
use std::collections::HashMap;
use std::collections::hash_map::Drain;
-impl de::Deserializer for Value {
+// TODO: Use a macro or some other magic to reduce the code duplication here
+
+impl<'de> de::Deserializer<'de> for ValueWithKey<'de> {
type Error = ConfigError;
#[inline]
- fn deserialize<V>(self, visitor: V) -> Result<V::Value>
- where V: de::Visitor
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
+ where V: de::Visitor<'de>
+ {
+ // Deserialize based on the underlying type
+ match self.0.kind {
+ ValueKind::Nil => visitor.visit_unit(),
+ ValueKind::Integer(i) => visitor.visit_i64(i),
+ ValueKind::Boolean(b) => visitor.visit_bool(b),
+ ValueKind::Float(f) => visitor.visit_f64(f),
+ ValueKind::String(s) => visitor.visit_string(s),
+ ValueKind::Array(values) => visitor.visit_seq(SeqAccess::new(values)),
+ ValueKind::Table(map) => visitor.visit_map(MapAccess::new(map)),
+ }
+ }
+
+ #[inline]
+ fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_bool(self.into_bool()?)
+ }
+
+ #[inline]
+ fn deserialize_i8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ // FIXME: This should *fail* if the value does not fit in the requets integer type
+ visitor.visit_i8(self.into_int()? as i8)
+ }
+
+ #[inline]
+ fn deserialize_i16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ // FIXME: This should *fail* if the value does not fit in the requets integer type
+ visitor.visit_i16(self.into_int()? as i16)
+ }
+
+ #[inline]
+ fn deserialize_i32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ // FIXME: This should *fail* if the value does not fit in the requets integer type
+ visitor.visit_i32(self.into_int()? as i32)
+ }
+
+ #[inline]
+ fn deserialize_i64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_i64(self.into_int()?)
+ }
+
+ #[inline]
+ fn deserialize_u8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ // FIXME: This should *fail* if the value does not fit in the requets integer type
+ visitor.visit_u8(self.into_int()? as u8)
+ }
+
+ #[inline]
+ fn deserialize_u16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ // FIXME: This should *fail* if the value does not fit in the requets integer type
+ visitor.visit_u16(self.into_int()? as u16)
+ }
+
+ #[inline]
+ fn deserialize_u32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ // FIXME: This should *fail* if the value does not fit in the requets integer type
+ visitor.visit_u32(self.into_int()? as u32)
+ }
+
+ #[inline]
+ fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ // FIXME: This should *fail* if the value does not fit in the requets integer type
+ visitor.visit_u64(self.into_int()? as u64)
+ }
+
+ #[inline]
+ fn deserialize_f32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_f32(self.into_float()? as f32)
+ }
+
+ #[inline]
+ fn deserialize_f64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_f64(self.into_float()?)
+ }
+
+ #[inline]
+ fn deserialize_str<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_string(self.into_str()?)
+ }
+
+ #[inline]
+ fn deserialize_string<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_string(self.into_str()?)
+ }
+
+ #[inline]
+ fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
+ where V: de::Visitor<'de>
+ {
+ // Match an explicit nil as None and everything else as Some
+ match self.0.kind {
+ ValueKind::Nil => visitor.visit_none(),
+ _ => visitor.visit_some(self),
+ }
+ }
+
+ forward_to_deserialize_any! {
+ char seq
+ bytes byte_buf map struct unit enum newtype_struct
+ identifier ignored_any unit_struct tuple_struct tuple
+ }
+}
+
+impl<'de> de::Deserializer<'de> for Value {
+ type Error = ConfigError;
+
+ #[inline]
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
+ where V: de::Visitor<'de>
{
// Deserialize based on the underlying type
match self.kind {
+ ValueKind::Nil => visitor.visit_unit(),
ValueKind::Integer(i) => visitor.visit_i64(i),
ValueKind::Boolean(b) => visitor.visit_bool(b),
ValueKind::Float(f) => visitor.visit_f64(f),
ValueKind::String(s) => visitor.visit_string(s),
- ValueKind::Array(values) => unimplemented!(),
- ValueKind::Table(map) => visitor.visit_map(MapVisitor::new(map)),
- _ => {
- unimplemented!();
- }
+ ValueKind::Array(values) => visitor.visit_seq(SeqAccess::new(values)),
+ ValueKind::Table(map) => visitor.visit_map(MapAccess::new(map)),
}
}
#[inline]
- fn deserialize_bool<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
visitor.visit_bool(self.into_bool()?)
}
#[inline]
- fn deserialize_i8<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_i8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// FIXME: This should *fail* if the value does not fit in the requets integer type
visitor.visit_i8(self.into_int()? as i8)
}
#[inline]
- fn deserialize_i16<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_i16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// FIXME: This should *fail* if the value does not fit in the requets integer type
visitor.visit_i16(self.into_int()? as i16)
}
#[inline]
- fn deserialize_i32<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_i32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// FIXME: This should *fail* if the value does not fit in the requets integer type
visitor.visit_i32(self.into_int()? as i32)
}
#[inline]
- fn deserialize_i64<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_i64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
visitor.visit_i64(self.into_int()?)
}
#[inline]
- fn deserialize_u8<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_u8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// FIXME: This should *fail* if the value does not fit in the requets integer type
visitor.visit_u8(self.into_int()? as u8)
}
#[inline]
- fn deserialize_u16<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_u16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// FIXME: This should *fail* if the value does not fit in the requets integer type
visitor.visit_u16(self.into_int()? as u16)
}
#[inline]
- fn deserialize_u32<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_u32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// FIXME: This should *fail* if the value does not fit in the requets integer type
visitor.visit_u32(self.into_int()? as u32)
}
#[inline]
- fn deserialize_u64<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// FIXME: This should *fail* if the value does not fit in the requets integer type
visitor.visit_u64(self.into_int()? as u64)
}
#[inline]
- fn deserialize_f32<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_f32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
visitor.visit_f32(self.into_float()? as f32)
}
#[inline]
- fn deserialize_f64<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_f64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
visitor.visit_f64(self.into_float()?)
}
#[inline]
- fn deserialize_str<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_str<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
visitor.visit_string(self.into_str()?)
}
#[inline]
- fn deserialize_string<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_string<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
visitor.visit_string(self.into_str()?)
}
#[inline]
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
- where V: de::Visitor
+ where V: de::Visitor<'de>
{
// Match an explicit nil as None and everything else as Some
match self.kind {
@@ -110,10 +219,10 @@ impl de::Deserializer for Value {
}
}
- forward_to_deserialize! {
+ forward_to_deserialize_any! {
char seq
- seq_fixed_size bytes byte_buf map struct unit enum newtype_struct
- struct_field ignored_any unit_struct tuple_struct tuple
+ bytes byte_buf map struct unit enum newtype_struct
+ identifier ignored_any unit_struct tuple_struct tuple
}
}
@@ -125,54 +234,86 @@ impl<'a> StrDeserializer<'a> {
}
}
-impl<'a> de::Deserializer for StrDeserializer<'a> {
+impl<'de, 'a> de::Deserializer<'de> for StrDeserializer<'a> {
type Error = ConfigError;
#[inline]
- fn deserialize<V: de::Visitor>(self, visitor: V) -> Result<V::Value> {
+ fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
visitor.visit_str(self.0)
}
- forward_to_deserialize! {
+ forward_to_deserialize_any! {
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
- seq_fixed_size bytes byte_buf map struct unit enum newtype_struct
- struct_field ignored_any unit_struct tuple_struct tuple option
+ bytes byte_buf map struct unit enum newtype_struct
+ identifier ignored_any unit_struct tuple_struct tuple option
+ }
+}
+
+struct SeqAccess {
+ elements: ::std::vec::IntoIter<Value>,
+}
+
+impl SeqAccess {
+ fn new(elements: Vec<Value>) -> Self {
+ SeqAccess {
+ elements: elements.into_iter(),
+ }
+ }
+}
+
+impl<'de> de::SeqAccess<'de> for SeqAccess {
+ type Error = ConfigError;
+
+ fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
+ where T: de::DeserializeSeed<'de>
+ {
+ match self.elements.next() {
+ Some(value) => seed.deserialize(value).map(Some),
+ None => Ok(None),
+ }
+ }
+
+ fn size_hint(&self) -> Option<usize> {
+ match self.elements.size_hint() {
+ (lower, Some(upper)) if lower == upper => Some(upper),
+ _ => None,
+ }
}
}
-struct MapVisitor {
+struct MapAccess {
elements: Vec<(String, Value)>,
index: usize,
}
-impl MapVisitor {
+impl MapAccess {
fn new(mut table: HashMap<String, Value>) -> Self {
- MapVisitor {
+ MapAccess {
elements: table.drain().collect(),
index: 0,
}
}
}
-impl de::MapVisitor for MapVisitor {
+impl<'de> de::MapAccess<'de> for MapAccess {
type Error = ConfigError;
- fn visit_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
- where K: de::DeserializeSeed
+ fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
+ where K: de::DeserializeSeed<'de>
{
if self.index >= self.elements.len() {
return Ok(None);
}
- let key_s = &self.elements[0].0;
+ let key_s = &(self.elements[0].0);
let key_de = StrDeserializer(key_s);
let key = de::DeserializeSeed::deserialize(seed, key_de)?;
Ok(Some(key))
}
- fn visit_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
- where V: de::DeserializeSeed
+ fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
+ where V: de::DeserializeSeed<'de>
{
de::DeserializeSeed::deserialize(seed, self.elements.remove(0).1)
}
diff --git a/src/error.rs b/src/error.rs
index 8af5cee..92709c2 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -50,12 +50,13 @@ pub enum ConfigError {
origin: Option<String>,
unexpected: Unexpected,
expected: &'static str,
+ key: Option<String>,
},
/// Custom message
Message(String),
- /// Unadorned error from a foreign source.
+ /// Unadorned error from a foreign origin.
Foreign(Box<Error>),
}
@@ -66,9 +67,27 @@ impl ConfigError {
ConfigError::Type {
origin: origin,
unexpected: unexpected,
- expected: expected
+ expected: expected,
+ key: None,
}
}
+
+ // FIXME: pub(crate)
+ #[doc(hidden)]
+ pub fn extend_with_key(self, key: &str) -> Self {
+ match self {
+ ConfigError::Type { origin, unexpected, expected, .. } => {
+ ConfigError::Type {
+ origin: origin,
+ unexpected: unexpected,
+ expected: expected,
+ key: Some(key.into()),
+ }
+ }
+
+ _ => self,
+ }
+ }
}
/// Alias for a `Result` with the error type set to `ConfigError`.
@@ -100,12 +119,16 @@ impl fmt::Display for ConfigError {
write!(f, "configuration property {:?} not found", key)
}
- ConfigError::Type { ref origin, ref unexpected, expected } => {
+ ConfigError::Type { ref origin, ref unexpected, expected, ref key } => {
write!(f, "invalid type: {}, expected {}",
unexpected, expected)?;
+ if let Some(ref key) = *key {
+ write!(f, " for key `{}`", key)?;
+ }
+
if let Some(ref origin) = *origin {
- write!(f, " from {}", origin)?;
+ write!(f, " in {}", origin)?;
}
Ok(())
@@ -115,7 +138,7 @@ impl fmt::Display for ConfigError {
write!(f, "{}", cause)?;
if let Some(ref uri) = *uri {
- write!(f, " from {}", uri)?;
+ write!(f, " in {}", uri)?;
}
Ok(())