use core::fmt;
use core::result;
use serde::de;
use serde::ser;
#[cfg(feature = "std")]
use std::error;
#[cfg(feature = "std")]
use std::io;
pub struct Error(ErrorImpl);
pub type Result<T> = result::Result<T, Error>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Category {
Io,
Syntax,
Data,
Eof,
}
impl Error {
#[must_use]
pub const fn offset(&self) -> u64 {
self.0.offset
}
pub(crate) fn syntax(code: ErrorCode, offset: u64) -> Self {
Self(ErrorImpl {
code,
offset,
#[cfg(feature = "std")]
_backtrace: std::backtrace::Backtrace::capture(),
})
}
#[cfg(feature = "std")]
pub(crate) fn io(error: io::Error) -> Self {
Self(ErrorImpl {
code: ErrorCode::Io(error),
offset: 0,
_backtrace: std::backtrace::Backtrace::capture(),
})
}
#[cfg(all(not(feature = "std"), feature = "unsealed_read_write"))]
#[must_use]
pub fn io() -> Self {
Self(ErrorImpl {
code: ErrorCode::Io,
offset: 0,
})
}
#[cfg(feature = "unsealed_read_write")]
#[must_use]
pub fn scratch_too_small(offset: u64) -> Self {
Self(ErrorImpl {
code: ErrorCode::ScratchTooSmall,
offset,
#[cfg(feature = "std")]
_backtrace: std::backtrace::Backtrace::capture(),
})
}
#[cfg(not(feature = "unsealed_read_write"))]
pub(crate) fn scratch_too_small(offset: u64) -> Self {
Self(ErrorImpl {
code: ErrorCode::ScratchTooSmall,
offset,
#[cfg(feature = "std")]
_backtrace: std::backtrace::Backtrace::capture(),
})
}
#[cfg(feature = "unsealed_read_write")]
pub fn message<T: fmt::Display>(msg: T) -> Self {
#[cfg(not(feature = "std"))]
{
let _ = msg;
Self(ErrorImpl {
code: ErrorCode::Message,
offset: 0,
})
}
#[cfg(feature = "std")]
{
Self(ErrorImpl {
code: ErrorCode::Message(msg.to_string()),
offset: 0,
_backtrace: std::backtrace::Backtrace::capture(),
})
}
}
#[cfg(not(feature = "unsealed_read_write"))]
pub(crate) fn message<T: fmt::Display>(msg: T) -> Self {
#[cfg(not(feature = "std"))]
{
let _ = msg;
Self(ErrorImpl {
code: ErrorCode::Message,
offset: 0,
})
}
#[cfg(feature = "std")]
{
Self(ErrorImpl {
code: ErrorCode::Message(msg.to_string()),
offset: 0,
_backtrace: std::backtrace::Backtrace::capture(),
})
}
}
#[cfg(feature = "unsealed_read_write")]
#[must_use]
pub fn eof(offset: u64) -> Self {
Self(ErrorImpl {
code: ErrorCode::EofWhileParsingValue,
offset,
#[cfg(feature = "std")]
_backtrace: std::backtrace::Backtrace::capture(),
})
}
#[must_use]
pub const 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
| ErrorCode::ArraySizeLimitExceeded
| ErrorCode::MapSizeLimitExceeded
| ErrorCode::IndefiniteIterationLimitExceeded => Category::Syntax,
}
}
#[must_use]
pub const fn is_io(&self) -> bool {
matches!(self.classify(), Category::Io)
}
#[must_use]
pub const fn is_syntax(&self) -> bool {
matches!(self.classify(), Category::Syntax)
}
#[must_use]
pub const fn is_data(&self) -> bool {
matches!(self.classify(), Category::Data)
}
#[must_use]
pub const fn is_eof(&self) -> bool {
matches!(self.classify(), Category::Eof)
}
#[must_use]
pub const fn is_scratch_too_small(&self) -> bool {
matches!(self.0.code, ErrorCode::ScratchTooSmall)
}
}
#[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) -> Self {
Self::message(msg)
}
fn invalid_type(unexp: de::Unexpected<'_>, exp: &dyn de::Expected) -> Self {
if unexp == de::Unexpected::Unit {
Self::custom(format_args!("invalid type: null, expected {exp}"))
} else {
Self::custom(format_args!("invalid type: {unexp}, expected {exp}"))
}
}
}
impl ser::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Self::message(msg)
}
}
#[cfg(feature = "std")]
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Self::io(e)
}
}
#[cfg(not(feature = "std"))]
impl From<core::fmt::Error> for Error {
fn from(_: core::fmt::Error) -> Self {
Self(ErrorImpl {
code: ErrorCode::Message,
offset: 0,
})
}
}
#[derive(Debug)]
struct ErrorImpl {
code: ErrorCode,
offset: u64,
#[cfg(feature = "std")]
_backtrace: std::backtrace::Backtrace,
}
#[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,
ArraySizeLimitExceeded,
MapSizeLimitExceeded,
IndefiniteIterationLimitExceeded,
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
#[cfg(feature = "std")]
Self::Message(ref msg) => f.write_str(msg),
#[cfg(not(feature = "std"))]
Self::Message => f.write_str("Unknown error"),
#[cfg(feature = "std")]
Self::Io(ref err) => fmt::Display::fmt(err, f),
#[cfg(not(feature = "std"))]
Self::Io => f.write_str("Unknown I/O error"),
Self::ScratchTooSmall => f.write_str("Scratch buffer too small"),
Self::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
Self::EofWhileParsingArray => f.write_str("EOF while parsing an array"),
Self::EofWhileParsingMap => f.write_str("EOF while parsing a map"),
Self::LengthOutOfRange => f.write_str("length out of range"),
Self::InvalidUtf8 => f.write_str("invalid UTF-8"),
Self::UnassignedCode => f.write_str("unassigned type"),
Self::UnexpectedCode => f.write_str("unexpected code"),
Self::TrailingData => f.write_str("trailing data"),
Self::ArrayTooShort => f.write_str("array too short"),
Self::ArrayTooLong => f.write_str("array too long"),
Self::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
Self::WrongEnumFormat => f.write_str("wrong enum format"),
Self::WrongStructFormat => f.write_str("wrong struct format"),
Self::ArraySizeLimitExceeded => f.write_str("array size limit exceeded"),
Self::MapSizeLimitExceeded => f.write_str("map size limit exceeded"),
Self::IndefiniteIterationLimitExceeded => {
f.write_str("indefinite-length iteration limit exceeded")
}
}
}
}