1use core::fmt;
3use core::result;
4use serde::de;
5use serde::ser;
6#[cfg(feature = "std")]
7use std::error;
8#[cfg(feature = "std")]
9use std::io;
10
11pub struct Error(ErrorImpl);
14
15pub type Result<T> = result::Result<T, Error>;
17
18#[derive(Copy, Clone, Debug, Eq, PartialEq)]
20pub enum Category {
21 Io,
23 Syntax,
25 Data,
27 Eof,
29}
30
31impl Error {
32 pub fn offset(&self) -> u64 {
34 self.0.offset
35 }
36
37 pub(crate) fn syntax(code: ErrorCode, offset: u64) -> Error {
38 Error(ErrorImpl { code, offset })
39 }
40
41 #[cfg(feature = "std")]
42 pub(crate) fn io(error: io::Error) -> Error {
43 Error(ErrorImpl {
44 code: ErrorCode::Io(error),
45 offset: 0,
46 })
47 }
48
49 #[cfg(all(not(feature = "std"), feature = "unsealed_read_write"))]
50 pub fn io() -> Error {
52 Error(ErrorImpl {
53 code: ErrorCode::Io,
54 offset: 0,
55 })
56 }
57
58 #[cfg(feature = "unsealed_read_write")]
59 pub fn scratch_too_small(offset: u64) -> Error {
61 Error(ErrorImpl {
62 code: ErrorCode::ScratchTooSmall,
63 offset,
64 })
65 }
66
67 #[cfg(not(feature = "unsealed_read_write"))]
68 pub(crate) fn scratch_too_small(offset: u64) -> Error {
69 Error(ErrorImpl {
70 code: ErrorCode::ScratchTooSmall,
71 offset,
72 })
73 }
74
75 #[cfg(feature = "unsealed_read_write")]
76 pub fn message<T: fmt::Display>(_msg: T) -> Error {
80 #[cfg(not(feature = "std"))]
81 {
82 Error(ErrorImpl {
83 code: ErrorCode::Message,
84 offset: 0,
85 })
86 }
87 #[cfg(feature = "std")]
88 {
89 Error(ErrorImpl {
90 code: ErrorCode::Message(_msg.to_string()),
91 offset: 0,
92 })
93 }
94 }
95
96 #[cfg(not(feature = "unsealed_read_write"))]
97 pub(crate) fn message<T: fmt::Display>(_msg: T) -> Error {
98 #[cfg(not(feature = "std"))]
99 {
100 Error(ErrorImpl {
101 code: ErrorCode::Message,
102 offset: 0,
103 })
104 }
105 #[cfg(feature = "std")]
106 {
107 Error(ErrorImpl {
108 code: ErrorCode::Message(_msg.to_string()),
109 offset: 0,
110 })
111 }
112 }
113
114 #[cfg(feature = "unsealed_read_write")]
115 pub fn eof(offset: u64) -> Error {
118 Error(ErrorImpl {
119 code: ErrorCode::EofWhileParsingValue,
120 offset,
121 })
122 }
123
124 pub fn classify(&self) -> Category {
126 match self.0.code {
127 #[cfg(feature = "std")]
128 ErrorCode::Message(_) => Category::Data,
129 #[cfg(not(feature = "std"))]
130 ErrorCode::Message => Category::Data,
131 #[cfg(feature = "std")]
132 ErrorCode::Io(_) => Category::Io,
133 #[cfg(not(feature = "std"))]
134 ErrorCode::Io => Category::Io,
135 ErrorCode::ScratchTooSmall => Category::Io,
136 ErrorCode::EofWhileParsingValue
137 | ErrorCode::EofWhileParsingArray
138 | ErrorCode::EofWhileParsingMap => Category::Eof,
139 ErrorCode::LengthOutOfRange
140 | ErrorCode::InvalidUtf8
141 | ErrorCode::UnassignedCode
142 | ErrorCode::UnexpectedCode
143 | ErrorCode::TrailingData
144 | ErrorCode::ArrayTooShort
145 | ErrorCode::ArrayTooLong
146 | ErrorCode::RecursionLimitExceeded
147 | ErrorCode::WrongEnumFormat
148 | ErrorCode::WrongStructFormat => Category::Syntax,
149 }
150 }
151
152 pub fn is_io(&self) -> bool {
154 match self.classify() {
155 Category::Io => true,
156 _ => false,
157 }
158 }
159
160 pub fn is_syntax(&self) -> bool {
162 match self.classify() {
163 Category::Syntax => true,
164 _ => false,
165 }
166 }
167
168 pub fn is_data(&self) -> bool {
170 match self.classify() {
171 Category::Data => true,
172 _ => false,
173 }
174 }
175
176 pub fn is_eof(&self) -> bool {
178 match self.classify() {
179 Category::Eof => true,
180 _ => false,
181 }
182 }
183
184 pub fn is_scratch_too_small(&self) -> bool {
188 match self.0.code {
189 ErrorCode::ScratchTooSmall => true,
190 _ => false,
191 }
192 }
193}
194
195#[cfg(feature = "std")]
196impl error::Error for Error {
197 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
198 match self.0.code {
199 ErrorCode::Io(ref err) => Some(err),
200 _ => None,
201 }
202 }
203}
204
205impl fmt::Display for Error {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 if self.0.offset == 0 {
208 fmt::Display::fmt(&self.0.code, f)
209 } else {
210 write!(f, "{} at offset {}", self.0.code, self.0.offset)
211 }
212 }
213}
214
215impl fmt::Debug for Error {
216 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
217 fmt::Debug::fmt(&self.0, fmt)
218 }
219}
220
221impl de::Error for Error {
222 fn custom<T: fmt::Display>(msg: T) -> Error {
223 Error::message(msg)
224 }
225
226 fn invalid_type(unexp: de::Unexpected<'_>, exp: &dyn de::Expected) -> Error {
227 if let de::Unexpected::Unit = unexp {
228 Error::custom(format_args!("invalid type: null, expected {}", exp))
229 } else {
230 Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp))
231 }
232 }
233}
234
235impl ser::Error for Error {
236 fn custom<T: fmt::Display>(msg: T) -> Error {
237 Error::message(msg)
238 }
239}
240
241#[cfg(feature = "std")]
242impl From<io::Error> for Error {
243 fn from(e: io::Error) -> Error {
244 Error::io(e)
245 }
246}
247
248#[cfg(not(feature = "std"))]
249impl From<core::fmt::Error> for Error {
250 fn from(_: core::fmt::Error) -> Error {
251 Error(ErrorImpl {
252 code: ErrorCode::Message,
253 offset: 0,
254 })
255 }
256}
257
258#[derive(Debug)]
259struct ErrorImpl {
260 code: ErrorCode,
261 offset: u64,
262}
263
264#[derive(Debug)]
265pub(crate) enum ErrorCode {
266 #[cfg(feature = "std")]
267 Message(String),
268 #[cfg(not(feature = "std"))]
269 Message,
270 #[cfg(feature = "std")]
271 Io(io::Error),
272 #[allow(unused)]
273 #[cfg(not(feature = "std"))]
274 Io,
275 ScratchTooSmall,
276 EofWhileParsingValue,
277 EofWhileParsingArray,
278 EofWhileParsingMap,
279 LengthOutOfRange,
280 InvalidUtf8,
281 UnassignedCode,
282 UnexpectedCode,
283 TrailingData,
284 ArrayTooShort,
285 ArrayTooLong,
286 RecursionLimitExceeded,
287 WrongEnumFormat,
288 WrongStructFormat,
289}
290
291impl fmt::Display for ErrorCode {
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 match *self {
294 #[cfg(feature = "std")]
295 ErrorCode::Message(ref msg) => f.write_str(msg),
296 #[cfg(not(feature = "std"))]
297 ErrorCode::Message => f.write_str("Unknown error"),
298 #[cfg(feature = "std")]
299 ErrorCode::Io(ref err) => fmt::Display::fmt(err, f),
300 #[cfg(not(feature = "std"))]
301 ErrorCode::Io => f.write_str("Unknown I/O error"),
302 ErrorCode::ScratchTooSmall => f.write_str("Scratch buffer too small"),
303 ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
304 ErrorCode::EofWhileParsingArray => f.write_str("EOF while parsing an array"),
305 ErrorCode::EofWhileParsingMap => f.write_str("EOF while parsing a map"),
306 ErrorCode::LengthOutOfRange => f.write_str("length out of range"),
307 ErrorCode::InvalidUtf8 => f.write_str("invalid UTF-8"),
308 ErrorCode::UnassignedCode => f.write_str("unassigned type"),
309 ErrorCode::UnexpectedCode => f.write_str("unexpected code"),
310 ErrorCode::TrailingData => f.write_str("trailing data"),
311 ErrorCode::ArrayTooShort => f.write_str("array too short"),
312 ErrorCode::ArrayTooLong => f.write_str("array too long"),
313 ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
314 ErrorCode::WrongEnumFormat => f.write_str("wrong enum format"),
315 ErrorCode::WrongStructFormat => f.write_str("wrong struct format"),
316 }
317 }
318}