| //! When serializing or deserializing CBOR goes wrong. |
| use core::fmt; |
| use core::result; |
| use serde::de; |
| use serde::ser; |
| #[cfg(feature = "std")] |
| use std::error; |
| #[cfg(feature = "std")] |
| use std::io; |
| |
| /// This type represents all possible errors that can occur when serializing or deserializing CBOR |
| /// data. |
| pub struct Error(ErrorImpl); |
| |
| /// Alias for a `Result` with the error type `serde_cbor::Error`. |
| pub type Result<T> = result::Result<T, Error>; |
| |
| /// Categorizes the cause of a `serde_cbor::Error`. |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
| pub enum Category { |
| /// The error was caused by a failure to read or write bytes on an IO stream. |
| Io, |
| /// The error was caused by input that was not syntactically valid CBOR. |
| Syntax, |
| /// The error was caused by input data that was semantically incorrect. |
| Data, |
| /// The error was caused by prematurely reaching the end of the input data. |
| Eof, |
| } |
| |
| impl Error { |
| /// The byte offset at which the error occurred. |
| pub fn offset(&self) -> u64 { |
| self.0.offset |
| } |
| |
| pub(crate) fn syntax(code: ErrorCode, offset: u64) -> Error { |
| Error(ErrorImpl { code, offset }) |
| } |
| |
| #[cfg(feature = "std")] |
| pub(crate) fn io(error: io::Error) -> Error { |
| Error(ErrorImpl { |
| code: ErrorCode::Io(error), |
| offset: 0, |
| }) |
| } |
| |
| #[cfg(all(not(feature = "std"), feature = "unsealed_read_write"))] |
| /// Creates an error signalling that the underlying `Read` encountered an I/O error. |
| pub fn io() -> Error { |
| Error(ErrorImpl { |
| code: ErrorCode::Io, |
| offset: 0, |
| }) |
| } |
| |
| #[cfg(feature = "unsealed_read_write")] |
| /// Creates an error signalling that the scratch buffer was too small to fit the data. |
| pub fn scratch_too_small(offset: u64) -> Error { |
| Error(ErrorImpl { |
| code: ErrorCode::ScratchTooSmall, |
| offset, |
| }) |
| } |
| |
| #[cfg(not(feature = "unsealed_read_write"))] |
| pub(crate) fn scratch_too_small(offset: u64) -> Error { |
| Error(ErrorImpl { |
| code: ErrorCode::ScratchTooSmall, |
| offset, |
| }) |
| } |
| |
| #[cfg(feature = "unsealed_read_write")] |
| /// Creates an error with a custom message. |
| /// |
| /// **Note**: When the "std" feature is disabled, the message will be discarded. |
| pub fn message<T: fmt::Display>(_msg: T) -> Error { |
| #[cfg(not(feature = "std"))] |
| { |
| Error(ErrorImpl { |
| code: ErrorCode::Message, |
| offset: 0, |
| }) |
| } |
| #[cfg(feature = "std")] |
| { |
| Error(ErrorImpl { |
| code: ErrorCode::Message(_msg.to_string()), |
| offset: 0, |
| }) |
| } |
| } |
| |
| #[cfg(not(feature = "unsealed_read_write"))] |
| pub(crate) fn message<T: fmt::Display>(_msg: T) -> Error { |
| #[cfg(not(feature = "std"))] |
| { |
| Error(ErrorImpl { |
| code: ErrorCode::Message, |
| offset: 0, |
| }) |
| } |
| #[cfg(feature = "std")] |
| { |
| Error(ErrorImpl { |
| code: ErrorCode::Message(_msg.to_string()), |
| offset: 0, |
| }) |
| } |
| } |
| |
| #[cfg(feature = "unsealed_read_write")] |
| /// Creates an error signalling that the underlying read |
| /// encountered an end of input. |
| pub fn eof(offset: u64) -> Error { |
| Error(ErrorImpl { |
| code: ErrorCode::EofWhileParsingValue, |
| offset, |
| }) |
| } |
| |
| /// Categorizes the cause of this error. |
| pub fn classify(&self) -> Category { |
| match self.0.code { |
| #[cfg(feature = "std")] |
| ErrorCode::Message(_) => Category::Data, |
| #[cfg(not(feature = "std"))] |
| ErrorCode::Message => Category::Data, |
| #[cfg(feature = "std")] |
| ErrorCode::Io(_) => Category::Io, |
| #[cfg(not(feature = "std"))] |
| ErrorCode::Io => Category::Io, |
| ErrorCode::ScratchTooSmall => Category::Io, |
| ErrorCode::EofWhileParsingValue |
| | ErrorCode::EofWhileParsingArray |
| | ErrorCode::EofWhileParsingMap => Category::Eof, |
| ErrorCode::LengthOutOfRange |
| | ErrorCode::InvalidUtf8 |
| | ErrorCode::UnassignedCode |
| | ErrorCode::UnexpectedCode |
| | ErrorCode::TrailingData |
| | ErrorCode::ArrayTooShort |
| | ErrorCode::ArrayTooLong |
| | ErrorCode::RecursionLimitExceeded |
| | ErrorCode::WrongEnumFormat |
| | ErrorCode::WrongStructFormat => Category::Syntax, |
| } |
| } |
| |
| /// Returns true if this error was caused by a failure to read or write bytes on an IO stream. |
| pub fn is_io(&self) -> bool { |
| match self.classify() { |
| Category::Io => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns true if this error was caused by input that was not syntactically valid CBOR. |
| pub fn is_syntax(&self) -> bool { |
| match self.classify() { |
| Category::Syntax => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns true if this error was caused by data that was semantically incorrect. |
| pub fn is_data(&self) -> bool { |
| match self.classify() { |
| Category::Data => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns true if this error was caused by prematurely reaching the end of the input data. |
| pub fn is_eof(&self) -> bool { |
| match self.classify() { |
| Category::Eof => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns true if this error was caused by the scratch buffer being too small. |
| /// |
| /// Note this being `true` implies that `is_io()` is also `true`. |
| pub fn is_scratch_too_small(&self) -> bool { |
| match self.0.code { |
| ErrorCode::ScratchTooSmall => true, |
| _ => false, |
| } |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl error::Error for Error { |
| fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
| match self.0.code { |
| ErrorCode::Io(ref err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl fmt::Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| if self.0.offset == 0 { |
| fmt::Display::fmt(&self.0.code, f) |
| } else { |
| write!(f, "{} at offset {}", self.0.code, self.0.offset) |
| } |
| } |
| } |
| |
| impl fmt::Debug for Error { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| fmt::Debug::fmt(&self.0, fmt) |
| } |
| } |
| |
| impl de::Error for Error { |
| fn custom<T: fmt::Display>(msg: T) -> Error { |
| Error::message(msg) |
| } |
| |
| fn invalid_type(unexp: de::Unexpected<'_>, exp: &dyn de::Expected) -> Error { |
| if let de::Unexpected::Unit = unexp { |
| Error::custom(format_args!("invalid type: null, expected {}", exp)) |
| } else { |
| Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp)) |
| } |
| } |
| } |
| |
| impl ser::Error for Error { |
| fn custom<T: fmt::Display>(msg: T) -> Error { |
| Error::message(msg) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl From<io::Error> for Error { |
| fn from(e: io::Error) -> Error { |
| Error::io(e) |
| } |
| } |
| |
| #[cfg(not(feature = "std"))] |
| impl From<core::fmt::Error> for Error { |
| fn from(_: core::fmt::Error) -> Error { |
| Error(ErrorImpl { |
| code: ErrorCode::Message, |
| offset: 0, |
| }) |
| } |
| } |
| |
| #[derive(Debug)] |
| struct ErrorImpl { |
| code: ErrorCode, |
| offset: u64, |
| } |
| |
| #[derive(Debug)] |
| pub(crate) enum ErrorCode { |
| #[cfg(feature = "std")] |
| Message(String), |
| #[cfg(not(feature = "std"))] |
| Message, |
| #[cfg(feature = "std")] |
| Io(io::Error), |
| #[allow(unused)] |
| #[cfg(not(feature = "std"))] |
| Io, |
| ScratchTooSmall, |
| EofWhileParsingValue, |
| EofWhileParsingArray, |
| EofWhileParsingMap, |
| LengthOutOfRange, |
| InvalidUtf8, |
| UnassignedCode, |
| UnexpectedCode, |
| TrailingData, |
| ArrayTooShort, |
| ArrayTooLong, |
| RecursionLimitExceeded, |
| WrongEnumFormat, |
| WrongStructFormat, |
| } |
| |
| impl fmt::Display for ErrorCode { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match *self { |
| #[cfg(feature = "std")] |
| ErrorCode::Message(ref msg) => f.write_str(msg), |
| #[cfg(not(feature = "std"))] |
| ErrorCode::Message => f.write_str("Unknown error"), |
| #[cfg(feature = "std")] |
| ErrorCode::Io(ref err) => fmt::Display::fmt(err, f), |
| #[cfg(not(feature = "std"))] |
| ErrorCode::Io => f.write_str("Unknown I/O error"), |
| ErrorCode::ScratchTooSmall => f.write_str("Scratch buffer too small"), |
| ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"), |
| ErrorCode::EofWhileParsingArray => f.write_str("EOF while parsing an array"), |
| ErrorCode::EofWhileParsingMap => f.write_str("EOF while parsing a map"), |
| ErrorCode::LengthOutOfRange => f.write_str("length out of range"), |
| ErrorCode::InvalidUtf8 => f.write_str("invalid UTF-8"), |
| ErrorCode::UnassignedCode => f.write_str("unassigned type"), |
| ErrorCode::UnexpectedCode => f.write_str("unexpected code"), |
| ErrorCode::TrailingData => f.write_str("trailing data"), |
| ErrorCode::ArrayTooShort => f.write_str("array too short"), |
| ErrorCode::ArrayTooLong => f.write_str("array too long"), |
| ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"), |
| ErrorCode::WrongEnumFormat => f.write_str("wrong enum format"), |
| ErrorCode::WrongStructFormat => f.write_str("wrong struct format"), |
| } |
| } |
| } |