use core::convert::Infallible;
use core::fmt::Display;
#[derive(Debug, PartialEq)]
pub struct Error<DelegateError = Infallible> {
pub(crate) offset: usize,
pub(crate) kind: ErrorKind<DelegateError>,
}
impl<DelegateError> Error<DelegateError> {
#[must_use]
pub const fn kind(&self) -> &ErrorKind<DelegateError> {
&self.kind
}
#[must_use]
pub const fn offset(&self) -> usize {
self.offset
}
}
#[cfg(feature = "std")]
impl<DelegateError> std::error::Error for Error<DelegateError> where
DelegateError: std::fmt::Debug + Display
{
}
impl<DelegateError> Display for Error<DelegateError>
where
DelegateError: Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "error at {}: {}", self.offset, self.kind)
}
}
impl Error<Infallible> {
#[must_use]
pub(crate) fn into_fallable<DelegateError>(self) -> Error<DelegateError> {
Error {
offset: self.offset,
kind: self.kind.into_fallable(),
}
}
}
impl Error<ErrorKind> {
#[must_use]
pub(crate) fn into_infallable(self) -> Error {
Error {
offset: self.offset,
kind: self.kind.into_infallable(),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
#[non_exhaustive]
pub enum ErrorKind<DelegateError = Infallible> {
Utf8,
UnexpectedEof,
ExpectedObjectKey,
ExpectedColon,
ExpectedValue,
ExpectedCommaOrEndOfObject,
ExpectedCommaOrEndOfArray,
IllegalTrailingComma,
Unexpected(u8),
TrailingNonWhitespace,
ObjectKeysMustBeStrings,
ExpectedExponent,
ExpectedDecimalDigit,
ExpectedDigit,
InvalidHexadecimal,
InvalidEscape,
UnclosedObject,
UnclosedArray,
UnclosedString,
ExpectedString,
ExpectedNumber,
RecursionLimitReached,
PayloadsShouldBeObjectOrArray,
PaylodTooLarge,
ErrorFromDelegate(DelegateError),
}
impl ErrorKind<Infallible> {
fn into_fallable<DelegateError>(self) -> ErrorKind<DelegateError> {
match self {
ErrorKind::Utf8 => ErrorKind::Utf8,
ErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof,
ErrorKind::ExpectedObjectKey => ErrorKind::ExpectedObjectKey,
ErrorKind::ExpectedColon => ErrorKind::ExpectedColon,
ErrorKind::ExpectedValue => ErrorKind::ExpectedValue,
ErrorKind::ExpectedCommaOrEndOfObject => ErrorKind::ExpectedCommaOrEndOfObject,
ErrorKind::ExpectedCommaOrEndOfArray => ErrorKind::ExpectedCommaOrEndOfArray,
ErrorKind::IllegalTrailingComma => ErrorKind::IllegalTrailingComma,
ErrorKind::Unexpected(ch) => ErrorKind::Unexpected(ch),
ErrorKind::TrailingNonWhitespace => ErrorKind::TrailingNonWhitespace,
ErrorKind::ObjectKeysMustBeStrings => ErrorKind::ObjectKeysMustBeStrings,
ErrorKind::ExpectedExponent => ErrorKind::ExpectedExponent,
ErrorKind::ExpectedDecimalDigit => ErrorKind::ExpectedDecimalDigit,
ErrorKind::ExpectedDigit => ErrorKind::ExpectedDigit,
ErrorKind::InvalidHexadecimal => ErrorKind::InvalidHexadecimal,
ErrorKind::InvalidEscape => ErrorKind::InvalidEscape,
ErrorKind::UnclosedObject => ErrorKind::UnclosedObject,
ErrorKind::UnclosedArray => ErrorKind::UnclosedArray,
ErrorKind::UnclosedString => ErrorKind::UnclosedString,
ErrorKind::ExpectedString => ErrorKind::ExpectedString,
ErrorKind::ExpectedNumber => ErrorKind::ExpectedNumber,
ErrorKind::RecursionLimitReached => ErrorKind::RecursionLimitReached,
ErrorKind::PayloadsShouldBeObjectOrArray => ErrorKind::PayloadsShouldBeObjectOrArray,
ErrorKind::PaylodTooLarge => ErrorKind::PaylodTooLarge,
ErrorKind::ErrorFromDelegate(_) => unreachable!("infallible"),
}
}
}
impl ErrorKind<ErrorKind> {
fn into_infallable(self) -> ErrorKind {
match self {
ErrorKind::Utf8 => ErrorKind::Utf8,
ErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof,
ErrorKind::ExpectedObjectKey => ErrorKind::ExpectedObjectKey,
ErrorKind::ExpectedColon => ErrorKind::ExpectedColon,
ErrorKind::ExpectedValue => ErrorKind::ExpectedValue,
ErrorKind::ExpectedCommaOrEndOfObject => ErrorKind::ExpectedCommaOrEndOfObject,
ErrorKind::ExpectedCommaOrEndOfArray => ErrorKind::ExpectedCommaOrEndOfArray,
ErrorKind::IllegalTrailingComma => ErrorKind::IllegalTrailingComma,
ErrorKind::Unexpected(ch) => ErrorKind::Unexpected(ch),
ErrorKind::TrailingNonWhitespace => ErrorKind::TrailingNonWhitespace,
ErrorKind::ObjectKeysMustBeStrings => ErrorKind::ObjectKeysMustBeStrings,
ErrorKind::ExpectedExponent => ErrorKind::ExpectedExponent,
ErrorKind::ExpectedDecimalDigit => ErrorKind::ExpectedDecimalDigit,
ErrorKind::ExpectedDigit => ErrorKind::ExpectedDigit,
ErrorKind::InvalidHexadecimal => ErrorKind::InvalidHexadecimal,
ErrorKind::InvalidEscape => ErrorKind::InvalidEscape,
ErrorKind::UnclosedObject => ErrorKind::UnclosedObject,
ErrorKind::UnclosedArray => ErrorKind::UnclosedArray,
ErrorKind::UnclosedString => ErrorKind::UnclosedString,
ErrorKind::ExpectedString => ErrorKind::ExpectedString,
ErrorKind::ExpectedNumber => ErrorKind::ExpectedNumber,
ErrorKind::RecursionLimitReached => ErrorKind::RecursionLimitReached,
ErrorKind::PayloadsShouldBeObjectOrArray => ErrorKind::PayloadsShouldBeObjectOrArray,
ErrorKind::PaylodTooLarge => ErrorKind::PaylodTooLarge,
ErrorKind::ErrorFromDelegate(kind) => kind,
}
}
}
#[test]
fn into_fallable_test() {
for kind in [
ErrorKind::Utf8,
ErrorKind::UnexpectedEof,
ErrorKind::ExpectedObjectKey,
ErrorKind::ExpectedColon,
ErrorKind::ExpectedValue,
ErrorKind::ExpectedCommaOrEndOfObject,
ErrorKind::ExpectedCommaOrEndOfArray,
ErrorKind::IllegalTrailingComma,
ErrorKind::Unexpected(b'/'),
ErrorKind::TrailingNonWhitespace,
ErrorKind::ObjectKeysMustBeStrings,
ErrorKind::ExpectedExponent,
ErrorKind::ExpectedDecimalDigit,
ErrorKind::ExpectedDigit,
ErrorKind::InvalidHexadecimal,
ErrorKind::InvalidEscape,
ErrorKind::UnclosedObject,
ErrorKind::UnclosedArray,
ErrorKind::UnclosedString,
ErrorKind::ExpectedString,
ErrorKind::ExpectedNumber,
ErrorKind::RecursionLimitReached,
ErrorKind::PayloadsShouldBeObjectOrArray,
ErrorKind::PaylodTooLarge,
] {
match (kind.clone(), kind.into_fallable::<u8>()) {
(ErrorKind::Utf8, ErrorKind::Utf8)
| (ErrorKind::UnexpectedEof, ErrorKind::UnexpectedEof)
| (ErrorKind::ExpectedObjectKey, ErrorKind::ExpectedObjectKey)
| (ErrorKind::ExpectedColon, ErrorKind::ExpectedColon)
| (ErrorKind::ExpectedValue, ErrorKind::ExpectedValue)
| (ErrorKind::ExpectedCommaOrEndOfObject, ErrorKind::ExpectedCommaOrEndOfObject)
| (ErrorKind::ExpectedCommaOrEndOfArray, ErrorKind::ExpectedCommaOrEndOfArray)
| (ErrorKind::IllegalTrailingComma, ErrorKind::IllegalTrailingComma)
| (ErrorKind::TrailingNonWhitespace, ErrorKind::TrailingNonWhitespace)
| (ErrorKind::ObjectKeysMustBeStrings, ErrorKind::ObjectKeysMustBeStrings)
| (ErrorKind::ExpectedExponent, ErrorKind::ExpectedExponent)
| (ErrorKind::ExpectedDecimalDigit, ErrorKind::ExpectedDecimalDigit)
| (ErrorKind::ExpectedDigit, ErrorKind::ExpectedDigit)
| (ErrorKind::InvalidHexadecimal, ErrorKind::InvalidHexadecimal)
| (ErrorKind::InvalidEscape, ErrorKind::InvalidEscape)
| (ErrorKind::UnclosedObject, ErrorKind::UnclosedObject)
| (ErrorKind::UnclosedArray, ErrorKind::UnclosedArray)
| (ErrorKind::UnclosedString, ErrorKind::UnclosedString)
| (ErrorKind::ExpectedString, ErrorKind::ExpectedString)
| (ErrorKind::ExpectedNumber, ErrorKind::ExpectedNumber)
| (ErrorKind::RecursionLimitReached, ErrorKind::RecursionLimitReached)
| (ErrorKind::PaylodTooLarge, ErrorKind::PaylodTooLarge)
| (
ErrorKind::PayloadsShouldBeObjectOrArray,
ErrorKind::PayloadsShouldBeObjectOrArray,
) => {}
(ErrorKind::Unexpected(before), ErrorKind::Unexpected(after)) => {
assert_eq!(before, after);
}
_ => unreachable!(),
}
}
}
#[test]
fn into_infallable_test() {
for kind in [
ErrorKind::Utf8,
ErrorKind::UnexpectedEof,
ErrorKind::ExpectedObjectKey,
ErrorKind::ExpectedColon,
ErrorKind::ExpectedValue,
ErrorKind::ExpectedCommaOrEndOfObject,
ErrorKind::ExpectedCommaOrEndOfArray,
ErrorKind::IllegalTrailingComma,
ErrorKind::Unexpected(b'/'),
ErrorKind::TrailingNonWhitespace,
ErrorKind::ObjectKeysMustBeStrings,
ErrorKind::ExpectedExponent,
ErrorKind::ExpectedDecimalDigit,
ErrorKind::ExpectedDigit,
ErrorKind::InvalidHexadecimal,
ErrorKind::InvalidEscape,
ErrorKind::UnclosedObject,
ErrorKind::UnclosedArray,
ErrorKind::UnclosedString,
ErrorKind::ExpectedString,
ErrorKind::ExpectedNumber,
ErrorKind::RecursionLimitReached,
ErrorKind::PayloadsShouldBeObjectOrArray,
ErrorKind::PaylodTooLarge,
ErrorKind::ErrorFromDelegate(ErrorKind::ExpectedColon),
] {
match (kind.clone(), kind.into_infallable()) {
(ErrorKind::Utf8, ErrorKind::Utf8)
| (ErrorKind::UnexpectedEof, ErrorKind::UnexpectedEof)
| (ErrorKind::ExpectedObjectKey, ErrorKind::ExpectedObjectKey)
| (ErrorKind::ExpectedColon, ErrorKind::ExpectedColon)
| (ErrorKind::ExpectedValue, ErrorKind::ExpectedValue)
| (ErrorKind::ExpectedCommaOrEndOfObject, ErrorKind::ExpectedCommaOrEndOfObject)
| (ErrorKind::ExpectedCommaOrEndOfArray, ErrorKind::ExpectedCommaOrEndOfArray)
| (ErrorKind::IllegalTrailingComma, ErrorKind::IllegalTrailingComma)
| (ErrorKind::TrailingNonWhitespace, ErrorKind::TrailingNonWhitespace)
| (ErrorKind::ObjectKeysMustBeStrings, ErrorKind::ObjectKeysMustBeStrings)
| (ErrorKind::ExpectedExponent, ErrorKind::ExpectedExponent)
| (ErrorKind::ExpectedDecimalDigit, ErrorKind::ExpectedDecimalDigit)
| (ErrorKind::ExpectedDigit, ErrorKind::ExpectedDigit)
| (ErrorKind::InvalidHexadecimal, ErrorKind::InvalidHexadecimal)
| (ErrorKind::InvalidEscape, ErrorKind::InvalidEscape)
| (ErrorKind::UnclosedObject, ErrorKind::UnclosedObject)
| (ErrorKind::UnclosedArray, ErrorKind::UnclosedArray)
| (ErrorKind::UnclosedString, ErrorKind::UnclosedString)
| (ErrorKind::ExpectedString, ErrorKind::ExpectedString)
| (ErrorKind::ExpectedNumber, ErrorKind::ExpectedNumber)
| (ErrorKind::RecursionLimitReached, ErrorKind::RecursionLimitReached)
| (ErrorKind::PaylodTooLarge, ErrorKind::PaylodTooLarge)
| (
ErrorKind::PayloadsShouldBeObjectOrArray,
ErrorKind::PayloadsShouldBeObjectOrArray,
) => {}
(ErrorKind::Unexpected(before), ErrorKind::Unexpected(after)) => {
assert_eq!(before, after);
}
(ErrorKind::ErrorFromDelegate(before), after) => {
assert_eq!(before, after);
}
_ => unreachable!(),
}
}
}
#[cfg(feature = "std")]
impl<DelegateError> std::error::Error for ErrorKind<DelegateError> where
DelegateError: std::fmt::Debug + Display
{
}
impl<DelegateError> Display for ErrorKind<DelegateError>
where
DelegateError: Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ErrorKind::Utf8 => f.write_str("invalid utf-8"),
ErrorKind::UnexpectedEof => f.write_str("unexpected end of file"),
ErrorKind::ExpectedObjectKey => f.write_str("expected object key"),
ErrorKind::ExpectedColon => f.write_str("expected colon (':')"),
ErrorKind::ExpectedValue => f.write_str("expected value"),
ErrorKind::ExpectedCommaOrEndOfObject => f.write_str("expected comma or end of object"),
ErrorKind::ExpectedCommaOrEndOfArray => f.write_str("expected comma or end of array"),
ErrorKind::IllegalTrailingComma => f.write_str("trailing commas are not allowed"),
ErrorKind::Unexpected(ch) => write!(f, "unexpected character: '{ch}'"),
ErrorKind::TrailingNonWhitespace => {
f.write_str("trailing non-whitespace data was encountered")
}
ErrorKind::ObjectKeysMustBeStrings => f.write_str("object keys must be strings"),
ErrorKind::ExpectedExponent => {
f.write_str("expected exponent sign or digit for number literal")
}
ErrorKind::ExpectedDecimalDigit => {
f.write_str("expected decimal digit for number literal")
}
ErrorKind::ExpectedDigit => f.write_str("expected decimal digit for number literal"),
ErrorKind::InvalidHexadecimal => {
f.write_str("invalid hexadecimal in unicode escape sequence")
}
ErrorKind::InvalidEscape => f.write_str("invalid escaped character"),
ErrorKind::UnclosedObject => f.write_str("expected end of object ('}}')"),
ErrorKind::UnclosedArray => f.write_str("expected end of array (']')"),
ErrorKind::UnclosedString => f.write_str("expected end of string ('\"')"),
ErrorKind::ExpectedString => f.write_str("expected a string"),
ErrorKind::ExpectedNumber => f.write_str("expected a number"),
ErrorKind::RecursionLimitReached => f.write_str("the recursion limit has been reached"),
ErrorKind::PayloadsShouldBeObjectOrArray => {
f.write_str("JSON only allows objects or arrays at the root")
}
ErrorKind::PaylodTooLarge => {
f.write_str("the payload is too large to be parsed into the destination structure")
}
ErrorKind::ErrorFromDelegate(err) => write!(f, "error from delegate: {err}"),
}
}
}
#[test]
#[cfg(feature = "alloc")]
fn display_tests() {
use alloc::string::ToString;
for kind in [
ErrorKind::Utf8,
ErrorKind::UnexpectedEof,
ErrorKind::ExpectedObjectKey,
ErrorKind::ExpectedColon,
ErrorKind::ExpectedValue,
ErrorKind::ExpectedCommaOrEndOfObject,
ErrorKind::ExpectedCommaOrEndOfArray,
ErrorKind::IllegalTrailingComma,
ErrorKind::Unexpected(b'/'),
ErrorKind::TrailingNonWhitespace,
ErrorKind::ObjectKeysMustBeStrings,
ErrorKind::ExpectedExponent,
ErrorKind::ExpectedDecimalDigit,
ErrorKind::ExpectedDigit,
ErrorKind::InvalidHexadecimal,
ErrorKind::InvalidEscape,
ErrorKind::UnclosedObject,
ErrorKind::UnclosedArray,
ErrorKind::UnclosedString,
ErrorKind::ExpectedString,
ErrorKind::ExpectedNumber,
ErrorKind::RecursionLimitReached,
ErrorKind::PayloadsShouldBeObjectOrArray,
ErrorKind::PaylodTooLarge,
ErrorKind::ErrorFromDelegate("oops"),
] {
Error { kind, offset: 0 }.to_string();
}
}