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}