1 //! When serializing or deserializing CBOR goes wrong.
2 use core::fmt;
3 use core::result;
4 use serde::de;
5 use serde::ser;
6 #[cfg(feature = "std")]
7 use std::error;
8 #[cfg(feature = "std")]
9 use std::io;
10 
11 /// This type represents all possible errors that can occur when serializing or deserializing CBOR
12 /// data.
13 pub struct Error(ErrorImpl);
14 
15 /// Alias for a `Result` with the error type `serde_cbor::Error`.
16 pub type Result<T> = result::Result<T, Error>;
17 
18 /// Categorizes the cause of a `serde_cbor::Error`.
19 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
20 pub 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 
31 impl Error {
32     /// The byte offset at which the error occurred.
offset(&self) -> u6433     pub fn offset(&self) -> u64 {
34         self.0.offset
35     }
36 
syntax(code: ErrorCode, offset: u64) -> Error37     pub(crate) fn syntax(code: ErrorCode, offset: u64) -> Error {
38         Error(ErrorImpl { code, offset })
39     }
40 
41     #[cfg(feature = "std")]
io(error: io::Error) -> Error42     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.
io() -> Error51     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.
scratch_too_small(offset: u64) -> Error60     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"))]
scratch_too_small(offset: u64) -> Error68     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.
message<T: fmt::Display>(_msg: T) -> Error79     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"))]
message<T: fmt::Display>(_msg: T) -> Error97     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.
eof(offset: u64) -> Error117     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.
classify(&self) -> Category125     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.
is_io(&self) -> bool153     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.
is_syntax(&self) -> bool161     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.
is_data(&self) -> bool169     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.
is_eof(&self) -> bool177     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`.
is_scratch_too_small(&self) -> bool187     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")]
196 impl error::Error for Error {
source(&self) -> Option<&(dyn error::Error + 'static)>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 
205 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result206     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 
215 impl fmt::Debug for Error {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result216     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
217         fmt::Debug::fmt(&self.0, fmt)
218     }
219 }
220 
221 impl de::Error for Error {
custom<T: fmt::Display>(msg: T) -> Error222     fn custom<T: fmt::Display>(msg: T) -> Error {
223         Error::message(msg)
224     }
225 
invalid_type(unexp: de::Unexpected<'_>, exp: &dyn de::Expected) -> Error226     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 
235 impl ser::Error for Error {
custom<T: fmt::Display>(msg: T) -> Error236     fn custom<T: fmt::Display>(msg: T) -> Error {
237         Error::message(msg)
238     }
239 }
240 
241 #[cfg(feature = "std")]
242 impl From<io::Error> for Error {
from(e: io::Error) -> Error243     fn from(e: io::Error) -> Error {
244         Error::io(e)
245     }
246 }
247 
248 #[cfg(not(feature = "std"))]
249 impl From<core::fmt::Error> for Error {
from(_: core::fmt::Error) -> Error250     fn from(_: core::fmt::Error) -> Error {
251         Error(ErrorImpl {
252             code: ErrorCode::Message,
253             offset: 0,
254         })
255     }
256 }
257 
258 #[derive(Debug)]
259 struct ErrorImpl {
260     code: ErrorCode,
261     offset: u64,
262 }
263 
264 #[derive(Debug)]
265 pub(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 
291 impl fmt::Display for ErrorCode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result292     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 }
319