diff --git a/Cargo.toml b/Cargo.toml index acc4636..83c1930 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "bincode" version = "0.8.1" authors = ["Ty Overby ", "Francesco Mazzoli ", "David Tolnay ", "Daniel Griffen"] +# Adding ErrorKind::Invalid*Encoding is a breaking change # Adding ErrorKind::DeserializeAnyNotSupported is a breaking change publish = false diff --git a/src/de/mod.rs b/src/de/mod.rs index 396b897..0bb1667 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -56,11 +56,9 @@ impl<'de, R: BincodeRead<'de>, E: ByteOrder, S: SizeLimit> Deserializer } fn read_string(&mut self) -> Result { - String::from_utf8(try!(self.read_vec())).map_err(|err| - ErrorKind::InvalidEncoding{ - desc: "error while decoding utf8 string", - detail: Some(format!("Deserialize error: {}", err)) - }.into()) + let vec = self.read_vec()?; + String::from_utf8(vec).map_err(|e| + ErrorKind::InvalidUtf8Encoding(e.utf8_error()).into()) } } @@ -96,10 +94,7 @@ where R: BincodeRead<'de>, S: SizeLimit, E: ByteOrder { 1 => visitor.visit_bool(true), 0 => visitor.visit_bool(false), value => { - Err(ErrorKind::InvalidEncoding{ - desc: "invalid u8 when decoding bool", - detail: Some(format!("Expected 0 or 1, got {}", value)) - }.into()) + Err(ErrorKind::InvalidBoolEncoding(value).into()) } } } @@ -142,10 +137,7 @@ where R: BincodeRead<'de>, S: SizeLimit, E: ByteOrder { use std::str; let error = || { - ErrorKind::InvalidEncoding{ - desc: "invalid char encoding", - detail: None, - }.into() + ErrorKind::InvalidCharEncoding.into() }; let mut buf = [0u8; 4]; @@ -255,10 +247,7 @@ where R: BincodeRead<'de>, S: SizeLimit, E: ByteOrder { match value { 0 => visitor.visit_none(), 1 => visitor.visit_some(&mut *self), - _ => Err(ErrorKind::InvalidEncoding{ - desc: "invalid tag when decoding Option", - detail: Some(format!("Expected 0 or 1, got {}", value)) - }.into()), + v => Err(ErrorKind::InvalidTagEncoding(v as usize).into()) } } diff --git a/src/de/read.rs b/src/de/read.rs index 87aa304..8ef0403 100644 --- a/src/de/read.rs +++ b/src/de/read.rs @@ -81,10 +81,7 @@ impl <'storage> BincodeRead<'storage> for SliceReader<'storage> { let string = match ::std::str::from_utf8(&self.slice[..length]) { Ok(s) => s, - Err(_) => return Err(Box::new(ErrorKind::InvalidEncoding { - desc: "string was not valid utf8", - detail: None, - })), + Err(e) => return Err(ErrorKind::InvalidUtf8Encoding(e).into()), }; let r = visitor.visit_borrowed_str(string); self.slice = &self.slice[length..]; @@ -133,10 +130,7 @@ impl BincodeRead<'static> for IoReader where R: io::Read { let string = match ::std::str::from_utf8(&self.temp_buffer[..length]) { Ok(s) => s, - Err(_) => return Err(Box::new(::ErrorKind::InvalidEncoding { - desc: "string was not valid utf8", - detail: None, - })), + Err(e) => return Err(::ErrorKind::InvalidUtf8Encoding(e).into()), }; let r = visitor.visit_str(string); diff --git a/src/internal.rs b/src/internal.rs index 8c100f2..960a65c 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -4,8 +4,10 @@ use std::io::{self, Write, Read}; use std::{error, fmt, result}; +use std::str::Utf8Error; use ::{CountSize, SizeLimit}; use byteorder::{ByteOrder}; +use std::error::Error as StdError; pub use super::de::{ Deserializer, @@ -31,16 +33,16 @@ pub enum ErrorKind { /// If the error stems from the reader/writer that is being used /// during (de)serialization, that error will be stored and returned here. Io(io::Error), - /// If the bytes in the reader are not decodable because of an invalid - /// encoding, this error will be returned. This error is only possible - /// if a stream is corrupted. A stream produced from `encode` or `encode_into` - /// should **never** produce an InvalidEncoding error. - InvalidEncoding { - #[allow(missing_docs)] - desc: &'static str, - #[allow(missing_docs)] - detail: Option - }, + /// Returned if the deserializer attempts to deserialize a string that is not valid utf8 + InvalidUtf8Encoding(Utf8Error), + /// Returned if the deserializer attempts to deserialize a bool that was + /// not encoded as either a 1 or a 0 + InvalidBoolEncoding(u8), + /// Returned if the deserializer attempts to deserialize a char that is not in the correct format. + InvalidCharEncoding, + /// Returned if the deserializer attempts to deserialize the tag of an enum that is + /// not in the expected ranges + InvalidTagEncoding(usize), /// Serde has a deserialize_any method that lets the format hint to the /// object which route to take in deserializing. DeserializeAnyNotSupported, @@ -53,11 +55,14 @@ pub enum ErrorKind { Custom(String) } -impl error::Error for ErrorKind { +impl StdError for ErrorKind { fn description(&self) -> &str { match *self { ErrorKind::Io(ref err) => error::Error::description(err), - ErrorKind::InvalidEncoding{desc, ..} => desc, + ErrorKind::InvalidUtf8Encoding(_) => "string is not valid utf8", + ErrorKind::InvalidBoolEncoding(_) => "invalid u8 while decoding bool", + ErrorKind::InvalidCharEncoding => "char is not valid", + ErrorKind::InvalidTagEncoding(_) => "tag for enum is not valid", ErrorKind::SequenceMustHaveLength => "bincode can't encode infinite sequences", ErrorKind::DeserializeAnyNotSupported => "bincode doesn't support serde::Deserializer::deserialize_any", ErrorKind::SizeLimit => "the size limit for decoding has been reached", @@ -69,7 +74,10 @@ impl error::Error for ErrorKind { fn cause(&self) -> Option<&error::Error> { match *self { ErrorKind::Io(ref err) => Some(err), - ErrorKind::InvalidEncoding{..} => None, + ErrorKind::InvalidUtf8Encoding(_) => None, + ErrorKind::InvalidBoolEncoding(_) => None, + ErrorKind::InvalidCharEncoding => None, + ErrorKind::InvalidTagEncoding(_) => None, ErrorKind::SequenceMustHaveLength => None, ErrorKind::DeserializeAnyNotSupported => None, ErrorKind::SizeLimit => None, @@ -89,10 +97,14 @@ impl fmt::Display for ErrorKind { match *self { ErrorKind::Io(ref ioerr) => write!(fmt, "io error: {}", ioerr), - ErrorKind::InvalidEncoding{desc, detail: None}=> - write!(fmt, "invalid encoding: {}", desc), - ErrorKind::InvalidEncoding{desc, detail: Some(ref detail)}=> - write!(fmt, "invalid encoding: {} ({})", desc, detail), + ErrorKind::InvalidUtf8Encoding(ref e) => + write!(fmt, "{}: {}", self.description(), e), + ErrorKind::InvalidBoolEncoding(b) => + write!(fmt, "{}, expected 0 or 1, found {}", self.description(), b), + ErrorKind::InvalidCharEncoding => + write!(fmt, "{}", self.description()), + ErrorKind::InvalidTagEncoding(tag) => + write!(fmt, "{}, found {}", self.description(), tag), ErrorKind::SequenceMustHaveLength => write!(fmt, "bincode can only encode sequences and maps that have a knowable size ahead of time."), ErrorKind::SizeLimit => diff --git a/tests/test.rs b/tests/test.rs index db0031b..5299a01 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -195,25 +195,32 @@ fn test_fixed_size_array() { #[test] fn deserializing_errors() { - fn isize_invalid_deserialize(res: Result) { - match res.map_err(|e| *e) { - Err(ErrorKind::InvalidEncoding{..}) => {}, - Err(ErrorKind::Custom(ref s)) if s.contains("invalid encoding") => {}, - Err(ErrorKind::Custom(ref s)) if s.contains("invalid value") => {}, - other => panic!("Expecting InvalidEncoding, got {:?}", other), - } + + match *deserialize_little::(&vec![0xA][..]).unwrap_err() { + ErrorKind::InvalidBoolEncoding(0xA) => {}, + _ => panic!(), + } + match *deserialize_little::(&vec![1, 0, 0, 0, 0, 0, 0, 0, 0xFF][..]).unwrap_err() { + ErrorKind::InvalidUtf8Encoding(_) => {}, + _ => panic!(), } - isize_invalid_deserialize(deserialize_little::(&vec![0xA][..])); - isize_invalid_deserialize(deserialize_little::(&vec![1, 0, 0, 0, 0, 0, 0, 0, 0xFF][..])); // Out-of-bounds variant #[derive(Serialize, Deserialize, Debug)] enum Test { One, Two, }; - isize_invalid_deserialize(deserialize_little::(&vec![0, 0, 0, 5][..])); - isize_invalid_deserialize(deserialize_little::>(&vec![5, 0][..])); + + match *deserialize_little::(&vec![0, 0, 0, 5][..]).unwrap_err() { + // Error message comes from serde + ErrorKind::Custom(_) => {}, + _ => panic!(), + } + match *deserialize_little::>(&vec![5, 0][..]).unwrap_err() { + ErrorKind::InvalidTagEncoding(_) => {}, + _ => panic!(), + } } #[test]