summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Leckey <leckey.ryan@gmail.com>2019-01-03 11:05:38 -0800
committerGitHub <noreply@github.com>2019-01-03 11:05:38 -0800
commitc7448652727de308a3830f78408fc20e759b782a (patch)
tree29c4d09169f08df2597f985efcfaf71267cc16e6
parent05bc8dee889dd95663a3441bd58fb5abdf322d9e (diff)
parent85f9735978433c9d49a24f51fa76b28f45beeb7c (diff)
Merge pull request #89 from vorner/err-path
Error path
-rw-r--r--src/de.rs43
-rw-r--r--src/error.rs37
-rw-r--r--tests/errors.rs25
3 files changed, 84 insertions, 21 deletions
diff --git a/src/de.rs b/src/de.rs
index e465d60..9267338 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -1,10 +1,8 @@
use config::Config;
use error::*;
use serde::de;
-use std::borrow::Cow;
-use std::collections::hash_map::Drain;
-use std::collections::HashMap;
-use std::iter::Peekable;
+use std::collections::{HashMap, VecDeque};
+use std::iter::Enumerate;
use value::{Value, ValueKind, ValueWithKey, Table};
// TODO: Use a macro or some other magic to reduce the code duplication here
@@ -287,13 +285,13 @@ impl<'de, 'a> de::Deserializer<'de> for StrDeserializer<'a> {
}
struct SeqAccess {
- elements: ::std::vec::IntoIter<Value>,
+ elements: Enumerate<::std::vec::IntoIter<Value>>,
}
impl SeqAccess {
fn new(elements: Vec<Value>) -> Self {
SeqAccess {
- elements: elements.into_iter(),
+ elements: elements.into_iter().enumerate(),
}
}
}
@@ -306,7 +304,11 @@ impl<'de> de::SeqAccess<'de> for SeqAccess {
T: de::DeserializeSeed<'de>,
{
match self.elements.next() {
- Some(value) => seed.deserialize(value).map(Some),
+ Some((idx, value)) => {
+ seed.deserialize(value)
+ .map(Some)
+ .map_err(|e| e.prepend_index(idx))
+ }
None => Ok(None),
}
}
@@ -320,15 +322,13 @@ impl<'de> de::SeqAccess<'de> for SeqAccess {
}
struct MapAccess {
- elements: Vec<(String, Value)>,
- index: usize,
+ elements: VecDeque<(String, Value)>,
}
impl MapAccess {
- fn new(mut table: HashMap<String, Value>) -> Self {
+ fn new(table: HashMap<String, Value>) -> Self {
MapAccess {
- elements: table.drain().collect(),
- index: 0,
+ elements: table.into_iter().collect(),
}
}
}
@@ -340,22 +340,23 @@ impl<'de> de::MapAccess<'de> for MapAccess {
where
K: de::DeserializeSeed<'de>,
{
- if self.index >= self.elements.len() {
- return Ok(None);
- }
-
- let key_s = &(self.elements[0].0);
- let key_de = StrDeserializer(key_s);
- let key = de::DeserializeSeed::deserialize(seed, key_de)?;
+ if let Some(&(ref key_s, _)) = self.elements.front() {
+ let key_de = StrDeserializer(key_s);
+ let key = de::DeserializeSeed::deserialize(seed, key_de)?;
- Ok(Some(key))
+ Ok(Some(key))
+ } else {
+ Ok(None)
+ }
}
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)
+ let (key, value) = self.elements.pop_front().unwrap();
+ de::DeserializeSeed::deserialize(seed, value)
+ .map_err(|e| e.prepend_key(key))
}
}
diff --git a/src/error.rs b/src/error.rs
index 1d348b6..e305750 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -114,6 +114,43 @@ impl ConfigError {
_ => self,
}
}
+
+ fn prepend(self, segment: String, add_dot: bool) -> Self {
+ let concat = |key: Option<String>| {
+ let key = key.unwrap_or_else(String::new);
+ let dot = if add_dot && key.as_bytes().get(0).unwrap_or(&b'[') != &b'[' {
+ "."
+ } else {
+ ""
+ };
+ format!("{}{}{}", segment, dot, key)
+ };
+ match self {
+ ConfigError::Type {
+ origin,
+ unexpected,
+ expected,
+ key,
+ } => {
+ ConfigError::Type {
+ origin,
+ unexpected,
+ expected,
+ key: Some(concat(key)),
+ }
+ }
+ ConfigError::NotFound(key) => ConfigError::NotFound(concat(Some(key))),
+ _ => self,
+ }
+ }
+
+ pub(crate) fn prepend_key(self, key: String) -> Self {
+ self.prepend(key, true)
+ }
+
+ pub(crate) fn prepend_index(self, idx: usize) -> Self {
+ self.prepend(format!("[{}]", idx), false)
+ }
}
/// Alias for a `Result` with the error type set to `ConfigError`.
diff --git a/tests/errors.rs b/tests/errors.rs
index fa2816e..030ae74 100644
--- a/tests/errors.rs
+++ b/tests/errors.rs
@@ -92,3 +92,28 @@ fn test_error_enum_de() {
);
}
+#[test]
+fn error_with_path() {
+ #[derive(Debug, Deserialize)]
+ struct Inner {
+ test: i32,
+ }
+
+ #[derive(Debug, Deserialize)]
+ struct Outer {
+ inner: Inner,
+ }
+ const CFG: &str = r#"
+inner:
+ test: ABC
+"#;
+
+ let mut cfg = Config::new();
+ cfg.merge(File::from_str(CFG, FileFormat::Yaml)).unwrap();
+ let e = cfg.try_into::<Outer>().unwrap_err();
+ if let ConfigError::Type { key: Some(path), .. } = e {
+ assert_eq!(path, "inner.test");
+ } else {
+ panic!("Wrong error {:?}", e);
+ }
+}