1use std::fmt::{self, Debug, Display};
2
3pub struct Error {
5 inner: Box<ErrorImpl>,
6}
7
8struct ErrorImpl {
9 code: ErrorCode,
10 offset: Option<usize>,
11}
12
13#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum Category {
17 Syntax,
19 Data,
22 Eof,
24}
25
26pub(crate) enum ErrorCode {
27 Message(Box<str>),
28 EofWhileParsing,
29 ExpectedChar(char),
30 UnexpectedChar(char),
31 InvalidEscape(char),
32 InvalidNumber,
33 NumberOutOfRange,
34 TrailingCharacters,
35 RecursionLimitExceeded,
36 KeyMustBeAString,
37}
38
39impl Error {
40 pub(crate) fn syntax(code: ErrorCode, offset: usize) -> Self {
41 Error {
42 inner: Box::new(ErrorImpl {
43 code,
44 offset: Some(offset),
45 }),
46 }
47 }
48
49 pub(crate) fn data(code: ErrorCode) -> Self {
50 Error {
51 inner: Box::new(ErrorImpl { code, offset: None }),
52 }
53 }
54
55 pub(crate) fn fix_offset(mut self, offset: usize) -> Self {
56 self.inner.offset.get_or_insert(offset);
57 self
58 }
59
60 #[must_use]
65 pub fn offset(&self) -> Option<usize> {
66 self.inner.offset
67 }
68
69 #[must_use]
70 pub fn classify(&self) -> Category {
71 match self.inner.code {
72 ErrorCode::Message(_) | ErrorCode::KeyMustBeAString | ErrorCode::NumberOutOfRange => {
73 Category::Data
74 }
75 ErrorCode::EofWhileParsing => Category::Eof,
76 ErrorCode::ExpectedChar(_)
77 | ErrorCode::UnexpectedChar(_)
78 | ErrorCode::InvalidEscape(_)
79 | ErrorCode::InvalidNumber
80 | ErrorCode::TrailingCharacters
81 | ErrorCode::RecursionLimitExceeded => Category::Syntax,
82 }
83 }
84
85 #[must_use]
86 pub fn is_syntax(&self) -> bool {
87 self.classify() == Category::Syntax
88 }
89
90 #[must_use]
91 pub fn is_data(&self) -> bool {
92 self.classify() == Category::Data
93 }
94
95 #[must_use]
96 pub fn is_eof(&self) -> bool {
97 self.classify() == Category::Eof
98 }
99}
100
101impl Display for ErrorCode {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 ErrorCode::Message(msg) => f.write_str(msg),
105 ErrorCode::EofWhileParsing => f.write_str("unexpected end of input"),
106 ErrorCode::ExpectedChar(c) => write!(f, "expected `{c}`"),
107 ErrorCode::UnexpectedChar(c) => write!(f, "unexpected character `{c}`"),
108 ErrorCode::InvalidEscape(c) => write!(f, "invalid escape `!{c}`"),
109 ErrorCode::InvalidNumber => f.write_str("invalid number"),
110 ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
111 ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
112 ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
113 ErrorCode::KeyMustBeAString => {
114 f.write_str("map key must be a string, integer, char or bool")
115 }
116 }
117 }
118}
119
120impl Display for Error {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self.inner.offset {
123 Some(offset) => write!(f, "{} at offset {offset}", self.inner.code),
124 None => Display::fmt(&self.inner.code, f),
125 }
126 }
127}
128
129impl Debug for Error {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 match self.inner.offset {
132 Some(offset) => write!(
133 f,
134 "Error({:?}, offset: {offset})",
135 self.inner.code.to_string()
136 ),
137 None => write!(f, "Error({:?})", self.inner.code.to_string()),
138 }
139 }
140}
141
142impl std::error::Error for Error {}
143
144impl serde::ser::Error for Error {
145 fn custom<T: Display>(msg: T) -> Self {
146 Error::data(ErrorCode::Message(msg.to_string().into_boxed_str()))
147 }
148}
149
150impl serde::de::Error for Error {
151 fn custom<T: Display>(msg: T) -> Self {
152 Error::data(ErrorCode::Message(msg.to_string().into_boxed_str()))
153 }
154}