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);
19
20pub type Result<T> = result::Result<T, Error>;
22
23#[derive(Copy, Clone, Debug, Eq, PartialEq)]
25pub enum Category {
26 Io,
28 Syntax,
30 Data,
32 Eof,
34}
35
36impl Error {
37 #[must_use]
39 pub const fn offset(&self) -> u64 {
40 self.0.offset
41 }
42
43 pub(crate) fn syntax(code: ErrorCode, offset: u64) -> Self {
44 Self(ErrorImpl {
45 code,
46 offset,
47 #[cfg(feature = "std")]
48 _backtrace: std::backtrace::Backtrace::capture(),
49 })
50 }
51
52 #[cfg(feature = "std")]
53 pub(crate) fn io(error: io::Error) -> Self {
54 Self(ErrorImpl {
55 code: ErrorCode::Io(error),
56 offset: 0,
57 _backtrace: std::backtrace::Backtrace::capture(),
58 })
59 }
60
61 #[cfg(all(not(feature = "std"), feature = "unsealed_read_write"))]
62 #[must_use]
64 pub fn io() -> Self {
65 Self(ErrorImpl {
66 code: ErrorCode::Io,
67 offset: 0,
68 })
69 }
70
71 #[cfg(feature = "unsealed_read_write")]
72 #[must_use]
74 pub fn scratch_too_small(offset: u64) -> Self {
75 Self(ErrorImpl {
76 code: ErrorCode::ScratchTooSmall,
77 offset,
78 #[cfg(feature = "std")]
79 _backtrace: std::backtrace::Backtrace::capture(),
80 })
81 }
82
83 #[cfg(not(feature = "unsealed_read_write"))]
84 pub(crate) fn scratch_too_small(offset: u64) -> Self {
85 Self(ErrorImpl {
86 code: ErrorCode::ScratchTooSmall,
87 offset,
88 #[cfg(feature = "std")]
89 _backtrace: std::backtrace::Backtrace::capture(),
90 })
91 }
92
93 #[cfg(feature = "unsealed_read_write")]
94 pub fn message<T: fmt::Display>(msg: T) -> Self {
98 #[cfg(not(feature = "std"))]
99 {
100 let _ = msg;
101 Self(ErrorImpl {
102 code: ErrorCode::Message,
103 offset: 0,
104 })
105 }
106 #[cfg(feature = "std")]
107 {
108 Self(ErrorImpl {
109 code: ErrorCode::Message(msg.to_string()),
110 offset: 0,
111 _backtrace: std::backtrace::Backtrace::capture(),
112 })
113 }
114 }
115
116 #[cfg(not(feature = "unsealed_read_write"))]
117 pub(crate) fn message<T: fmt::Display>(msg: T) -> Self {
118 #[cfg(not(feature = "std"))]
119 {
120 let _ = msg;
121 Self(ErrorImpl {
122 code: ErrorCode::Message,
123 offset: 0,
124 })
125 }
126 #[cfg(feature = "std")]
127 {
128 Self(ErrorImpl {
129 code: ErrorCode::Message(msg.to_string()),
130 offset: 0,
131 _backtrace: std::backtrace::Backtrace::capture(),
132 })
133 }
134 }
135
136 #[cfg(feature = "unsealed_read_write")]
137 #[must_use]
140 pub fn eof(offset: u64) -> Self {
141 Self(ErrorImpl {
142 code: ErrorCode::EofWhileParsingValue,
143 offset,
144 #[cfg(feature = "std")]
145 _backtrace: std::backtrace::Backtrace::capture(),
146 })
147 }
148
149 #[must_use]
151 pub const fn classify(&self) -> Category {
152 match self.0.code {
153 #[cfg(feature = "std")]
154 ErrorCode::Message(_) => Category::Data,
155 #[cfg(not(feature = "std"))]
156 ErrorCode::Message => Category::Data,
157 #[cfg(feature = "std")]
158 ErrorCode::Io(_) => Category::Io,
159 #[cfg(not(feature = "std"))]
160 ErrorCode::Io => Category::Io,
161 ErrorCode::ScratchTooSmall => Category::Io,
162 ErrorCode::EofWhileParsingValue
163 | ErrorCode::EofWhileParsingArray
164 | ErrorCode::EofWhileParsingMap => Category::Eof,
165 ErrorCode::LengthOutOfRange
166 | ErrorCode::InvalidUtf8
167 | ErrorCode::UnassignedCode
168 | ErrorCode::UnexpectedCode
169 | ErrorCode::TrailingData
170 | ErrorCode::ArrayTooShort
171 | ErrorCode::ArrayTooLong
172 | ErrorCode::RecursionLimitExceeded
173 | ErrorCode::WrongEnumFormat
174 | ErrorCode::WrongStructFormat
175 | ErrorCode::ArraySizeLimitExceeded
176 | ErrorCode::MapSizeLimitExceeded
177 | ErrorCode::IndefiniteIterationLimitExceeded => Category::Syntax,
178 }
179 }
180
181 #[must_use]
183 pub const fn is_io(&self) -> bool {
184 matches!(self.classify(), Category::Io)
185 }
186
187 #[must_use]
189 pub const fn is_syntax(&self) -> bool {
190 matches!(self.classify(), Category::Syntax)
191 }
192
193 #[must_use]
195 pub const fn is_data(&self) -> bool {
196 matches!(self.classify(), Category::Data)
197 }
198
199 #[must_use]
201 pub const fn is_eof(&self) -> bool {
202 matches!(self.classify(), Category::Eof)
203 }
204
205 #[must_use]
209 pub const fn is_scratch_too_small(&self) -> bool {
210 matches!(self.0.code, ErrorCode::ScratchTooSmall)
211 }
212}
213
214#[cfg(feature = "std")]
215impl error::Error for Error {
216 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
217 match self.0.code {
218 ErrorCode::Io(ref err) => Some(err),
219 _ => None,
220 }
221 }
222
223 }
226
227impl fmt::Display for Error {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 if self.0.offset == 0 {
230 fmt::Display::fmt(&self.0.code, f)
231 } else {
232 write!(f, "{} at offset {}", self.0.code, self.0.offset)
233 }
234 }
235}
236
237impl fmt::Debug for Error {
238 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
239 fmt::Debug::fmt(&self.0, fmt)
240 }
241}
242
243impl de::Error for Error {
244 fn custom<T: fmt::Display>(msg: T) -> Self {
245 Self::message(msg)
246 }
247
248 fn invalid_type(unexp: de::Unexpected<'_>, exp: &dyn de::Expected) -> Self {
249 if unexp == de::Unexpected::Unit {
250 Self::custom(format_args!("invalid type: null, expected {exp}"))
251 } else {
252 Self::custom(format_args!("invalid type: {unexp}, expected {exp}"))
253 }
254 }
255}
256
257impl ser::Error for Error {
258 fn custom<T: fmt::Display>(msg: T) -> Self {
259 Self::message(msg)
260 }
261}
262
263#[cfg(feature = "std")]
264impl From<io::Error> for Error {
265 fn from(e: io::Error) -> Self {
266 Self::io(e)
267 }
268}
269
270#[cfg(not(feature = "std"))]
271impl From<core::fmt::Error> for Error {
272 fn from(_: core::fmt::Error) -> Self {
273 Self(ErrorImpl {
274 code: ErrorCode::Message,
275 offset: 0,
276 })
277 }
278}
279
280#[derive(Debug)]
281struct ErrorImpl {
282 code: ErrorCode,
283 offset: u64,
284 #[cfg(feature = "std")]
285 _backtrace: std::backtrace::Backtrace,
286}
287
288#[derive(Debug)]
289pub(crate) enum ErrorCode {
290 #[cfg(feature = "std")]
291 Message(String),
292 #[cfg(not(feature = "std"))]
293 Message,
294 #[cfg(feature = "std")]
295 Io(io::Error),
296 #[allow(unused)]
297 #[cfg(not(feature = "std"))]
298 Io,
299 ScratchTooSmall,
300 EofWhileParsingValue,
301 EofWhileParsingArray,
302 EofWhileParsingMap,
303 LengthOutOfRange,
304 InvalidUtf8,
305 UnassignedCode,
306 UnexpectedCode,
307 TrailingData,
308 ArrayTooShort,
309 ArrayTooLong,
310 RecursionLimitExceeded,
311 WrongEnumFormat,
312 WrongStructFormat,
313 ArraySizeLimitExceeded,
314 MapSizeLimitExceeded,
315 IndefiniteIterationLimitExceeded,
316}
317
318impl fmt::Display for ErrorCode {
319 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
320 match *self {
321 #[cfg(feature = "std")]
322 Self::Message(ref msg) => f.write_str(msg),
323 #[cfg(not(feature = "std"))]
324 Self::Message => f.write_str("Unknown error"),
325 #[cfg(feature = "std")]
326 Self::Io(ref err) => fmt::Display::fmt(err, f),
327 #[cfg(not(feature = "std"))]
328 Self::Io => f.write_str("Unknown I/O error"),
329 Self::ScratchTooSmall => f.write_str("Scratch buffer too small"),
330 Self::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
331 Self::EofWhileParsingArray => f.write_str("EOF while parsing an array"),
332 Self::EofWhileParsingMap => f.write_str("EOF while parsing a map"),
333 Self::LengthOutOfRange => f.write_str("length out of range"),
334 Self::InvalidUtf8 => f.write_str("invalid UTF-8"),
335 Self::UnassignedCode => f.write_str("unassigned type"),
336 Self::UnexpectedCode => f.write_str("unexpected code"),
337 Self::TrailingData => f.write_str("trailing data"),
338 Self::ArrayTooShort => f.write_str("array too short"),
339 Self::ArrayTooLong => f.write_str("array too long"),
340 Self::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
341 Self::WrongEnumFormat => f.write_str("wrong enum format"),
342 Self::WrongStructFormat => f.write_str("wrong struct format"),
343 Self::ArraySizeLimitExceeded => f.write_str("array size limit exceeded"),
344 Self::MapSizeLimitExceeded => f.write_str("map size limit exceeded"),
345 Self::IndefiniteIterationLimitExceeded => {
346 f.write_str("indefinite-length iteration limit exceeded")
347 }
348 }
349 }
350}