cs_serde_cbor/
error.rs

1//! When serializing or deserializing CBOR goes wrong.
2use 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
11/// This type represents all possible errors that can occur when serializing or deserializing CBOR
12/// data.
13pub struct Error(ErrorImpl);
14
15/// Alias for a `Result` with the error type `serde_cbor::Error`.
16pub type Result<T> = result::Result<T, Error>;
17
18/// Categorizes the cause of a `serde_cbor::Error`.
19#[derive(Copy, Clone, Debug, Eq, PartialEq)]
20pub enum Category {
21    /// The error was caused by a failure to read or write bytes on an IO stream.
22    Io,
23    /// The error was caused by input that was not syntactically valid CBOR.
24    Syntax,
25    /// The error was caused by input data that was semantically incorrect.
26    Data,
27    /// The error was caused by prematurely reaching the end of the input data.
28    Eof,
29}
30
31impl Error {
32    /// The byte offset at which the error occurred.
33    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    /// Creates an error signalling that the underlying `Read` encountered an I/O error.
51    pub fn io() -> Error {
52        Error(ErrorImpl {
53            code: ErrorCode::Io,
54            offset: 0,
55        })
56    }
57
58    #[cfg(feature = "unsealed_read_write")]
59    /// Creates an error signalling that the scratch buffer was too small to fit the data.
60    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    /// Creates an error with a custom message.
77    ///
78    /// **Note**: When the "std" feature is disabled, the message will be discarded.
79    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    /// Creates an error signalling that the underlying read
116    /// encountered an end of input.
117    pub fn eof(offset: u64) -> Error {
118        Error(ErrorImpl {
119            code: ErrorCode::EofWhileParsingValue,
120            offset,
121        })
122    }
123
124    /// Categorizes the cause of this error.
125    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    /// Returns true if this error was caused by a failure to read or write bytes on an IO stream.
153    pub fn is_io(&self) -> bool {
154        match self.classify() {
155            Category::Io => true,
156            _ => false,
157        }
158    }
159
160    /// Returns true if this error was caused by input that was not syntactically valid CBOR.
161    pub fn is_syntax(&self) -> bool {
162        match self.classify() {
163            Category::Syntax => true,
164            _ => false,
165        }
166    }
167
168    /// Returns true if this error was caused by data that was semantically incorrect.
169    pub fn is_data(&self) -> bool {
170        match self.classify() {
171            Category::Data => true,
172            _ => false,
173        }
174    }
175
176    /// Returns true if this error was caused by prematurely reaching the end of the input data.
177    pub fn is_eof(&self) -> bool {
178        match self.classify() {
179            Category::Eof => true,
180            _ => false,
181        }
182    }
183
184    /// Returns true if this error was caused by the scratch buffer being too small.
185    ///
186    /// Note this being `true` implies that `is_io()` is also `true`.
187    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}