Expose kinds of errors
This commit is contained in:
parent
21e91fe753
commit
ed88ef0b81
|
@ -1,5 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use serialize;
|
use serialize;
|
||||||
use {Value, Table, Array, String, Integer, Float, Boolean, Parser};
|
use {Value, Table, Array, String, Integer, Float, Boolean, Parser};
|
||||||
|
@ -48,7 +49,7 @@ pub struct Encoder {
|
||||||
/// arbitrary TOML value.
|
/// arbitrary TOML value.
|
||||||
pub struct Decoder {
|
pub struct Decoder {
|
||||||
toml: Option<Value>,
|
toml: Option<Value>,
|
||||||
cur_field: String,
|
cur_field: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumeration of errors which can occur while encoding a rust value into a
|
/// Enumeration of errors which can occur while encoding a rust value into a
|
||||||
|
@ -69,10 +70,27 @@ pub enum Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Description for errors which can occur while decoding a type.
|
/// Description for errors which can occur while decoding a type.
|
||||||
#[deriving(Show)]
|
|
||||||
pub struct DecodeError {
|
pub struct DecodeError {
|
||||||
/// Human-readable description of this error.
|
/// Field that this error applies to.
|
||||||
pub desc: String,
|
pub field: Option<String>,
|
||||||
|
/// The type of error which occurred while decoding,
|
||||||
|
pub kind: DecodeErrorKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumeration of possible errors which can occur while decoding a structure.
|
||||||
|
pub enum DecodeErrorKind {
|
||||||
|
/// A field was expected, but none was found.
|
||||||
|
ExpectedField(/* type */ &'static str),
|
||||||
|
/// A field was found, but it had the wrong type.
|
||||||
|
ExpectedType(/* expected */ &'static str, /* found */ &'static str),
|
||||||
|
/// The nth map key was expected, but none was found.
|
||||||
|
ExpectedMapKey(uint),
|
||||||
|
/// The nth map element was expected, but none was found.
|
||||||
|
ExpectedMapElement(uint),
|
||||||
|
/// An enum decoding was requested, but no variants were supplied
|
||||||
|
NoEnumVariants,
|
||||||
|
/// The unit type was being decoded, but a non-zero length string was found
|
||||||
|
NilTooLong
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(PartialEq, Show)]
|
#[deriving(PartialEq, Show)]
|
||||||
|
@ -349,41 +367,35 @@ impl Decoder {
|
||||||
/// This decoder can be passed to the `Decodable` methods or driven
|
/// This decoder can be passed to the `Decodable` methods or driven
|
||||||
/// manually.
|
/// manually.
|
||||||
pub fn new(toml: Value) -> Decoder {
|
pub fn new(toml: Value) -> Decoder {
|
||||||
Decoder { toml: Some(toml), cur_field: "".to_string() }
|
Decoder { toml: Some(toml), cur_field: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sub_decoder(&self, toml: Option<Value>, field: &str) -> Decoder {
|
fn sub_decoder(&self, toml: Option<Value>, field: &str) -> Decoder {
|
||||||
Decoder {
|
Decoder {
|
||||||
toml: toml,
|
toml: toml,
|
||||||
cur_field: if self.cur_field.len() == 0 {
|
cur_field: if field.len() == 0 {
|
||||||
field.to_string()
|
self.cur_field.clone()
|
||||||
} else if field.len() == 0 {
|
|
||||||
self.cur_field.to_string()
|
|
||||||
} else {
|
} else {
|
||||||
format!("{}.{}", self.cur_field, field)
|
match self.cur_field {
|
||||||
|
None => Some(field.to_string()),
|
||||||
|
Some(ref s) => Some(format!("{}.{}", s, field))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field_error(&self, desc: &str) -> DecodeError {
|
fn err(&self, kind: DecodeErrorKind) -> DecodeError {
|
||||||
DecodeError {
|
DecodeError {
|
||||||
desc: format!("{}", desc),
|
field: self.cur_field.clone(),
|
||||||
|
kind: kind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_error(&self, expected: &str, found: &Option<Value>) -> DecodeError {
|
fn mismatch(&self, expected: &'static str,
|
||||||
DecodeError {
|
found: &Option<Value>) -> DecodeError{
|
||||||
desc: format!("{}expected type `{}`, but found {}",
|
match *found {
|
||||||
if self.cur_field.len() > 0 {
|
Some(ref val) => self.err(ExpectedType(expected, val.type_str())),
|
||||||
format!("for field `{}` ", self.cur_field)
|
None => self.err(ExpectedField(expected)),
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
},
|
|
||||||
expected,
|
|
||||||
match *found {
|
|
||||||
Some(ref val) => format!("`{}`", val.type_str()),
|
|
||||||
None => "no value".to_string(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,8 +404,8 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
fn read_nil(&mut self) -> Result<(), DecodeError> {
|
fn read_nil(&mut self) -> Result<(), DecodeError> {
|
||||||
match self.toml {
|
match self.toml {
|
||||||
Some(String(ref s)) if s.len() == 0 => Ok(()),
|
Some(String(ref s)) if s.len() == 0 => Ok(()),
|
||||||
Some(String(..)) => Err(self.field_error("expected 0-length string")),
|
Some(String(..)) => Err(self.err(NilTooLong)),
|
||||||
ref found => Err(self.type_error("string", found)),
|
ref found => Err(self.mismatch("string", found)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_uint(&mut self) -> Result<uint, DecodeError> {
|
fn read_uint(&mut self) -> Result<uint, DecodeError> {
|
||||||
|
@ -417,7 +429,7 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
fn read_i64(&mut self) -> Result<i64, DecodeError> {
|
fn read_i64(&mut self) -> Result<i64, DecodeError> {
|
||||||
match self.toml {
|
match self.toml {
|
||||||
Some(Integer(i)) => Ok(i),
|
Some(Integer(i)) => Ok(i),
|
||||||
ref found => Err(self.type_error("integer", found)),
|
ref found => Err(self.mismatch("integer", found)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_i32(&mut self) -> Result<i32, DecodeError> {
|
fn read_i32(&mut self) -> Result<i32, DecodeError> {
|
||||||
|
@ -432,13 +444,13 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
fn read_bool(&mut self) -> Result<bool, DecodeError> {
|
fn read_bool(&mut self) -> Result<bool, DecodeError> {
|
||||||
match self.toml {
|
match self.toml {
|
||||||
Some(Boolean(b)) => Ok(b),
|
Some(Boolean(b)) => Ok(b),
|
||||||
ref found => Err(self.type_error("bool", found)),
|
ref found => Err(self.mismatch("bool", found)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_f64(&mut self) -> Result<f64, DecodeError> {
|
fn read_f64(&mut self) -> Result<f64, DecodeError> {
|
||||||
match self.toml {
|
match self.toml {
|
||||||
Some(Float(f)) => Ok(f),
|
Some(Float(f)) => Ok(f),
|
||||||
ref found => Err(self.type_error("float", found)),
|
ref found => Err(self.mismatch("float", found)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_f32(&mut self) -> Result<f32, DecodeError> {
|
fn read_f32(&mut self) -> Result<f32, DecodeError> {
|
||||||
|
@ -448,13 +460,13 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
match self.toml {
|
match self.toml {
|
||||||
Some(String(ref s)) if s.as_slice().char_len() == 1 =>
|
Some(String(ref s)) if s.as_slice().char_len() == 1 =>
|
||||||
Ok(s.as_slice().char_at(0)),
|
Ok(s.as_slice().char_at(0)),
|
||||||
ref found => Err(self.type_error("string", found)),
|
ref found => Err(self.mismatch("string", found)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_str(&mut self) -> Result<String, DecodeError> {
|
fn read_str(&mut self) -> Result<String, DecodeError> {
|
||||||
match self.toml.take() {
|
match self.toml.take() {
|
||||||
Some(String(s)) => Ok(s),
|
Some(String(s)) => Ok(s),
|
||||||
ref found => Err(self.type_error("string", found)),
|
ref found => Err(self.mismatch("string", found)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,11 +494,7 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(first_error.unwrap_or_else(|| {
|
Err(first_error.unwrap_or_else(|| self.err(NoEnumVariants)))
|
||||||
DecodeError {
|
|
||||||
desc: format!("no enum variants to decode to"),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
fn read_enum_variant_arg<T>(&mut self,
|
fn read_enum_variant_arg<T>(&mut self,
|
||||||
_a_idx: uint,
|
_a_idx: uint,
|
||||||
|
@ -519,7 +527,7 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
{
|
{
|
||||||
match self.toml {
|
match self.toml {
|
||||||
Some(Table(..)) => f(self),
|
Some(Table(..)) => f(self),
|
||||||
ref found => Err(self.type_error("table", found)),
|
ref found => Err(self.mismatch("table", found)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_struct_field<T>(&mut self,
|
fn read_struct_field<T>(&mut self,
|
||||||
|
@ -529,7 +537,7 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
-> Result<T, DecodeError> {
|
-> Result<T, DecodeError> {
|
||||||
let toml = match self.toml {
|
let toml = match self.toml {
|
||||||
Some(Table(ref mut table)) => table.pop(&f_name.to_string()),
|
Some(Table(ref mut table)) => table.pop(&f_name.to_string()),
|
||||||
ref found => return Err(self.type_error("table", found)),
|
ref found => return Err(self.mismatch("table", found)),
|
||||||
};
|
};
|
||||||
f(&mut self.sub_decoder(toml, f_name))
|
f(&mut self.sub_decoder(toml, f_name))
|
||||||
}
|
}
|
||||||
|
@ -578,7 +586,7 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
{
|
{
|
||||||
let len = match self.toml {
|
let len = match self.toml {
|
||||||
Some(Array(ref arr)) => arr.len(),
|
Some(Array(ref arr)) => arr.len(),
|
||||||
ref found => return Err(self.type_error("array", found)),
|
ref found => return Err(self.mismatch("array", found)),
|
||||||
};
|
};
|
||||||
f(self, len)
|
f(self, len)
|
||||||
}
|
}
|
||||||
|
@ -588,7 +596,7 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
{
|
{
|
||||||
let toml = match self.toml {
|
let toml = match self.toml {
|
||||||
Some(Array(ref mut arr)) => mem::replace(arr.get_mut(idx), Integer(0)),
|
Some(Array(ref mut arr)) => mem::replace(arr.get_mut(idx), Integer(0)),
|
||||||
ref found => return Err(self.type_error("array", found)),
|
ref found => return Err(self.mismatch("array", found)),
|
||||||
};
|
};
|
||||||
f(&mut self.sub_decoder(Some(toml), ""))
|
f(&mut self.sub_decoder(Some(toml), ""))
|
||||||
}
|
}
|
||||||
|
@ -598,7 +606,7 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
{
|
{
|
||||||
let len = match self.toml {
|
let len = match self.toml {
|
||||||
Some(Table(ref table)) => table.len(),
|
Some(Table(ref table)) => table.len(),
|
||||||
ref found => return Err(self.type_error("table", found)),
|
ref found => return Err(self.mismatch("table", found)),
|
||||||
};
|
};
|
||||||
f(self, len)
|
f(self, len)
|
||||||
}
|
}
|
||||||
|
@ -613,12 +621,10 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
f(&mut self.sub_decoder(Some(String(key.to_string())),
|
f(&mut self.sub_decoder(Some(String(key.to_string())),
|
||||||
key.as_slice()))
|
key.as_slice()))
|
||||||
}
|
}
|
||||||
None => Err(DecodeError {
|
None => Err(self.err(ExpectedMapKey(idx))),
|
||||||
desc: format!("map key `{}` does not exist", idx),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ref found => Err(self.type_error("table", found)),
|
ref found => Err(self.mismatch("table", found)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_map_elt_val<T>(&mut self, idx: uint,
|
fn read_map_elt_val<T>(&mut self, idx: uint,
|
||||||
|
@ -632,12 +638,54 @@ impl serialize::Decoder<DecodeError> for Decoder {
|
||||||
// XXX: this shouldn't clone
|
// XXX: this shouldn't clone
|
||||||
f(&mut self.sub_decoder(Some(key.clone()), ""))
|
f(&mut self.sub_decoder(Some(key.clone()), ""))
|
||||||
}
|
}
|
||||||
None => Err(DecodeError {
|
None => Err(self.err(ExpectedMapElement(idx))),
|
||||||
desc: format!("map element `{}` does not exist", idx),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ref found => Err(self.type_error("table", found)),
|
ref found => Err(self.mismatch("table", found)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Show for DecodeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
try!(match self.kind {
|
||||||
|
ExpectedField(expected_type) => {
|
||||||
|
if expected_type == "table" {
|
||||||
|
write!(f, "expected a section")
|
||||||
|
} else {
|
||||||
|
write!(f, "expected a value of type `{}`", expected_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExpectedType(expected, found) => {
|
||||||
|
fn humanize(s: &str) -> String {
|
||||||
|
if s == "section" {
|
||||||
|
format!("a section")
|
||||||
|
} else {
|
||||||
|
format!("a value of type `{}`", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, "expected {}, but found {}",
|
||||||
|
humanize(expected),
|
||||||
|
humanize(found))
|
||||||
|
}
|
||||||
|
ExpectedMapKey(idx) => {
|
||||||
|
write!(f, "expected at least {} keys", idx + 1)
|
||||||
|
}
|
||||||
|
ExpectedMapElement(idx) => {
|
||||||
|
write!(f, "expected at least {} elements", idx + 1)
|
||||||
|
}
|
||||||
|
NoEnumVariants => {
|
||||||
|
write!(f, "expected an enum variant to decode to")
|
||||||
|
}
|
||||||
|
NilTooLong => {
|
||||||
|
write!(f, "expected 0-length string")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
match self.field {
|
||||||
|
Some(ref s) => {
|
||||||
|
write!(f, " for the key `{}`", s)
|
||||||
|
}
|
||||||
|
None => Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -849,9 +897,9 @@ mod tests {
|
||||||
match a {
|
match a {
|
||||||
Ok(..) => fail!("should not have decoded"),
|
Ok(..) => fail!("should not have decoded"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
assert_eq!(e.desc.as_slice(),
|
assert_eq!(e.to_str().as_slice(),
|
||||||
"for field `bar` expected type `integer`, but \
|
"expected a value of type `integer`, but \
|
||||||
found `float`");
|
found a value of type `float` for the key `bar`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -867,9 +915,8 @@ mod tests {
|
||||||
match a {
|
match a {
|
||||||
Ok(..) => fail!("should not have decoded"),
|
Ok(..) => fail!("should not have decoded"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
assert_eq!(e.desc.as_slice(),
|
assert_eq!(e.to_str().as_slice(),
|
||||||
"for field `bar` expected type `integer`, but \
|
"expected a value of type `integer` for the key `d`");
|
||||||
found no value");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue