use alloc::boxed::Box;
use alloc::string::ToString;
use core::fmt::{self, Debug, Display};
use core::result;
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub struct Expectation<U, E = U> {
pub unexpected: U,
pub expected: E,
}
pub struct Error {
kind: Box<ErrorKind>,
pos: Option<usize>,
}
impl Error {
#[cold]
pub(crate) fn new(kind: Box<ErrorKind>, pos: Option<usize>) -> Self {
Self { kind, pos }
}
#[cold]
pub fn end_of_file() -> Self {
Self::new(Box::new(ErrorKind::end_of_file()), None)
}
#[cold]
pub fn invalid_type(unexpected: String, expected: String, pos: Option<usize>) -> Self {
Self::new(Box::new(ErrorKind::invalid_type(unexpected, expected)), pos)
}
#[cold]
pub fn invalid_value(unexpected: String, expected: String, pos: Option<usize>) -> Self {
Self::new(
Box::new(ErrorKind::invalid_value(unexpected, expected)),
pos,
)
}
#[cold]
pub fn invalid_length(unexpected: String, expected: String, pos: Option<usize>) -> Self {
Self::new(
Box::new(ErrorKind::invalid_length(unexpected, expected)),
pos,
)
}
#[cold]
pub fn unknown_length() -> Self {
Self::new(Box::new(ErrorKind::unknown_length()), None)
}
#[cold]
pub fn number_out_of_range(pos: Option<usize>) -> Self {
Self::new(Box::new(ErrorKind::number_out_of_range()), pos)
}
#[cold]
pub fn uncategorized(msg: impl Display, pos: Option<usize>) -> Self {
Self::new(Box::new(ErrorKind::uncategorized(msg)), pos)
}
#[cold]
pub fn depth_limit_exceeded(pos: Option<usize>) -> Self {
Self::new(Box::new(ErrorKind::depth_limit_exceeded()), pos)
}
#[cold]
pub fn utf8(err: core::str::Utf8Error, pos: Option<usize>) -> Self {
Self::new(Box::new(ErrorKind::utf8(err)), pos)
}
#[cold]
pub fn reserved_type() -> Self {
Self::new(Box::new(ErrorKind::reserved_type()), None)
}
#[cfg(feature = "std")]
pub fn io(err: std::io::Error) -> Self {
Self::new(Box::new(ErrorKind::io(err)), None)
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
pub fn pos(&self) -> Option<usize> {
self.pos
}
pub fn code(&self) -> ErrorCode {
self.kind.as_code()
}
}
impl Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(pos) = self.pos {
write!(f, "Error({:?}, position: {pos:?})", self.kind.to_string())
} else {
write!(f, "Error({:?})", self.kind.to_string())
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(pos) = self.pos {
write!(f, "{:?}, at position: {pos:?}", self.kind.to_string())
} else {
write!(f, "{:?}", self.kind.to_string(),)
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &*self.kind {
ErrorKind::UnexpectedEndOfFile => None,
ErrorKind::InvalidType(_) => None,
ErrorKind::InvalidValue(_) => None,
ErrorKind::InvalidLength(_) => None,
ErrorKind::UnknownLength => None,
ErrorKind::NumberOutOfRange => None,
ErrorKind::Uncategorized(_) => None,
ErrorKind::DepthLimitExceeded => None,
ErrorKind::Utf8(err) => Some(err),
ErrorKind::ReservedType => None,
#[cfg(feature = "std")]
ErrorKind::StdIo(err) => Some(err),
}
}
}
#[cfg(feature = "serde")]
impl serde::de::Error for Error {
#[cold]
fn custom<T>(msg: T) -> Error
where
T: Display,
{
Error::uncategorized(msg, None)
}
#[cold]
fn invalid_type(unexp: serde::de::Unexpected, exp: &dyn serde::de::Expected) -> Self {
Error::invalid_type(unexp.to_string(), exp.to_string(), None)
}
#[cold]
fn invalid_value(unexp: serde::de::Unexpected, exp: &dyn serde::de::Expected) -> Self {
Error::invalid_value(unexp.to_string(), exp.to_string(), None)
}
#[cold]
fn invalid_length(len: usize, exp: &dyn serde::de::Expected) -> Self {
Error::invalid_length(len.to_string(), exp.to_string(), None)
}
}
#[cfg(feature = "serde")]
impl serde::ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: Display,
{
Error::uncategorized(msg, None)
}
}
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum ErrorCode {
UnexpectedEndOfFile = 1,
InvalidType = 11,
InvalidValue = 21,
InvalidLength = 31,
UnknownLength = 41,
NumberOutOfRange = 51,
Uncategorized = 61,
DepthLimitExceeded = 71,
Utf8 = 81,
ReservedType = 91,
#[cfg(feature = "std")]
StdIo = 255,
}
#[derive(Debug)]
pub enum ErrorKind {
UnexpectedEndOfFile,
InvalidType(Expectation<String>),
InvalidValue(Expectation<String>),
InvalidLength(Expectation<String>),
UnknownLength,
NumberOutOfRange,
Uncategorized(String),
DepthLimitExceeded,
Utf8(core::str::Utf8Error),
ReservedType,
#[cfg(feature = "std")]
StdIo(std::io::Error),
}
impl ErrorKind {
fn end_of_file() -> Self {
Self::UnexpectedEndOfFile
}
fn invalid_type(unexpected: String, expected: String) -> Self {
Self::InvalidType(Expectation {
unexpected,
expected,
})
}
fn invalid_value(unexpected: String, expected: String) -> Self {
Self::InvalidValue(Expectation {
unexpected,
expected,
})
}
fn invalid_length(unexpected: String, expected: String) -> Self {
Self::InvalidLength(Expectation {
unexpected,
expected,
})
}
#[cold]
pub fn unknown_length() -> Self {
Self::UnknownLength
}
fn number_out_of_range() -> Self {
Self::NumberOutOfRange
}
fn uncategorized(msg: impl Display) -> Self {
Self::Uncategorized(msg.to_string())
}
fn depth_limit_exceeded() -> Self {
Self::DepthLimitExceeded
}
fn utf8(err: core::str::Utf8Error) -> Self {
Self::Utf8(err)
}
fn reserved_type() -> Self {
Self::ReservedType
}
#[cfg(feature = "std")]
fn io(err: std::io::Error) -> Self {
if err.kind() == std::io::ErrorKind::UnexpectedEof {
return Self::UnexpectedEndOfFile;
}
Self::StdIo(err)
}
pub fn as_code(&self) -> ErrorCode {
match self {
ErrorKind::UnexpectedEndOfFile => ErrorCode::UnexpectedEndOfFile,
ErrorKind::InvalidType(_) => ErrorCode::InvalidType,
ErrorKind::InvalidValue(_) => ErrorCode::InvalidValue,
ErrorKind::InvalidLength(_) => ErrorCode::InvalidLength,
ErrorKind::UnknownLength => ErrorCode::UnknownLength,
ErrorKind::NumberOutOfRange => ErrorCode::NumberOutOfRange,
ErrorKind::Uncategorized(_) => ErrorCode::Uncategorized,
ErrorKind::DepthLimitExceeded => ErrorCode::DepthLimitExceeded,
ErrorKind::Utf8(_) => ErrorCode::Utf8,
ErrorKind::ReservedType => ErrorCode::ReservedType,
ErrorKind::StdIo(_) => ErrorCode::StdIo,
}
}
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::UnexpectedEndOfFile => f.write_str("unexpected EOF while parsing"),
Self::InvalidType(unexpected) => {
write!(
f,
"expected type {}, found type {}",
unexpected.expected, unexpected.unexpected
)
}
Self::InvalidValue(unexpected) => {
write!(
f,
"expected data {}, found type {}",
unexpected.expected, unexpected.unexpected
)
}
Self::InvalidLength(unexpected) => {
write!(
f,
"expected length {}, found length {}",
unexpected.expected, unexpected.unexpected
)
}
Self::UnknownLength => f.write_str("unknown length"),
Self::NumberOutOfRange => f.write_str("unexpected EOF while parsing"),
Self::Uncategorized(msg) => f.write_str(msg),
Self::DepthLimitExceeded => {
f.write_str("a numeric cast failed due to an out-of-range error")
}
Self::Utf8(err) => Display::fmt(err, f),
Self::ReservedType => f.write_str("reserved type"),
#[cfg(feature = "std")]
Self::StdIo(err) => Display::fmt(err, f),
}
}
}