lilliput_core/
error.rs

1//! When encoding or decoding Lilliput goes wrong.
2
3use alloc::boxed::Box;
4use alloc::string::ToString;
5use core::fmt::{self, Debug, Display};
6use core::result;
7
8/// Alias for a `Result` with the error type `Error`.
9pub type Result<T> = result::Result<T, Error>;
10
11/// An expectation.
12#[derive(Debug)]
13pub struct Expectation<U, E = U> {
14    /// The unexpected value.
15    pub unexpected: U,
16    /// The expected value.
17    pub expected: E,
18}
19
20/// A minimal representation of all possible errors that can occur.
21pub struct Error {
22    kind: Box<ErrorKind>,
23    pos: Option<usize>,
24}
25
26impl Error {
27    #[cold]
28    pub(crate) fn new(kind: Box<ErrorKind>, pos: Option<usize>) -> Self {
29        Self { kind, pos }
30    }
31
32    /// EOF while parsing.
33    #[cold]
34    pub fn end_of_file() -> Self {
35        Self::new(Box::new(ErrorKind::end_of_file()), None)
36    }
37
38    /// A mismatch occurred between the decoded and expected value types.
39    #[cold]
40    pub fn invalid_type(unexpected: String, expected: String, pos: Option<usize>) -> Self {
41        Self::new(Box::new(ErrorKind::invalid_type(unexpected, expected)), pos)
42    }
43
44    /// The enclosed I/O error occurred while trying to read the encoded
45    /// MessagePack data.
46    #[cold]
47    pub fn invalid_value(unexpected: String, expected: String, pos: Option<usize>) -> Self {
48        Self::new(
49            Box::new(ErrorKind::invalid_value(unexpected, expected)),
50            pos,
51        )
52    }
53
54    /// A decoded sequence/map did not have the enclosed expected length.
55    #[cold]
56    pub fn invalid_length(unexpected: String, expected: String, pos: Option<usize>) -> Self {
57        Self::new(
58            Box::new(ErrorKind::invalid_length(unexpected, expected)),
59            pos,
60        )
61    }
62
63    /// An encoded sequence/map did not provide a length.
64    #[cold]
65    pub fn unknown_length() -> Self {
66        Self::new(Box::new(ErrorKind::unknown_length()), None)
67    }
68
69    /// A numeric cast failed due to an out-of-range error.
70    #[cold]
71    pub fn number_out_of_range(pos: Option<usize>) -> Self {
72        Self::new(Box::new(ErrorKind::number_out_of_range()), pos)
73    }
74
75    /// An otherwise uncategorized error occurred.
76    #[cold]
77    pub fn uncategorized(msg: impl Display, pos: Option<usize>) -> Self {
78        Self::new(Box::new(ErrorKind::uncategorized(msg)), pos)
79    }
80
81    /// The depth limit was exceeded.
82    #[cold]
83    pub fn depth_limit_exceeded(pos: Option<usize>) -> Self {
84        Self::new(Box::new(ErrorKind::depth_limit_exceeded()), pos)
85    }
86
87    /// An encoded string could not be parsed as UTF-8.
88    #[cold]
89    pub fn utf8(err: core::str::Utf8Error, pos: Option<usize>) -> Self {
90        Self::new(Box::new(ErrorKind::utf8(err)), pos)
91    }
92
93    /// Reserved type.
94    #[cold]
95    pub fn reserved_type() -> Self {
96        Self::new(Box::new(ErrorKind::reserved_type()), None)
97    }
98
99    /// A `std::io::Error`.
100    #[cfg(feature = "std")]
101    pub fn io(err: std::io::Error) -> Self {
102        Self::new(Box::new(ErrorKind::io(err)), None)
103    }
104
105    /// Returns the error's kind.
106    pub fn kind(&self) -> &ErrorKind {
107        &self.kind
108    }
109
110    /// Returns the error's position.
111    pub fn pos(&self) -> Option<usize> {
112        self.pos
113    }
114
115    /// Returns the error's code.
116    pub fn code(&self) -> ErrorCode {
117        self.kind.as_code()
118    }
119}
120
121impl Debug for Error {
122    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123        // Humans often end up seeing this representation because it is what `.unwrap()` shows.
124        if let Some(pos) = self.pos {
125            write!(f, "Error({:?}, position: {pos:?})", self.kind.to_string())
126        } else {
127            write!(f, "Error({:?})", self.kind.to_string())
128        }
129    }
130}
131
132impl Display for Error {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        // Humans often end up seeing this representation because it is what `.unwrap()` shows.
135        if let Some(pos) = self.pos {
136            write!(f, "{:?}, at position: {pos:?}", self.kind.to_string())
137        } else {
138            write!(f, "{:?}", self.kind.to_string(),)
139        }
140    }
141}
142
143impl std::error::Error for Error {
144    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
145        match &*self.kind {
146            ErrorKind::UnexpectedEndOfFile => None,
147            ErrorKind::InvalidType(_) => None,
148            ErrorKind::InvalidValue(_) => None,
149            ErrorKind::InvalidLength(_) => None,
150            ErrorKind::UnknownLength => None,
151            ErrorKind::NumberOutOfRange => None,
152            ErrorKind::Uncategorized(_) => None,
153            ErrorKind::DepthLimitExceeded => None,
154            ErrorKind::Utf8(err) => Some(err),
155            ErrorKind::ReservedType => None,
156            #[cfg(feature = "std")]
157            ErrorKind::StdIo(err) => Some(err),
158        }
159    }
160}
161
162#[cfg(feature = "serde")]
163impl serde::de::Error for Error {
164    #[cold]
165    fn custom<T>(msg: T) -> Error
166    where
167        T: Display,
168    {
169        Error::uncategorized(msg, None)
170    }
171
172    #[cold]
173    fn invalid_type(unexp: serde::de::Unexpected, exp: &dyn serde::de::Expected) -> Self {
174        Error::invalid_type(unexp.to_string(), exp.to_string(), None)
175    }
176
177    #[cold]
178    fn invalid_value(unexp: serde::de::Unexpected, exp: &dyn serde::de::Expected) -> Self {
179        Error::invalid_value(unexp.to_string(), exp.to_string(), None)
180    }
181
182    #[cold]
183    fn invalid_length(len: usize, exp: &dyn serde::de::Expected) -> Self {
184        Error::invalid_length(len.to_string(), exp.to_string(), None)
185    }
186}
187
188#[cfg(feature = "serde")]
189impl serde::ser::Error for Error {
190    fn custom<T>(msg: T) -> Self
191    where
192        T: Display,
193    {
194        Error::uncategorized(msg, None)
195    }
196}
197
198/// This type represents all possible errors that can occur when serializing or
199/// deserializing Lilliput data.
200#[repr(u8)]
201#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
202pub enum ErrorCode {
203    /// Unexpected EOF while parsing.
204    UnexpectedEndOfFile = 1,
205    /// A mismatch occurred between the decoded and expected value types.
206    InvalidType = 11,
207    /// The enclosed I/O error occurred while trying to read the encoded
208    /// MessagePack data.
209    InvalidValue = 21,
210    /// A decoded sequence/map did not have the enclosed expected length.
211    InvalidLength = 31,
212    /// An encoded sequence/map did not provide a length.
213    UnknownLength = 41,
214    /// A numeric cast failed due to an out-of-range error.
215    NumberOutOfRange = 51,
216    /// An otherwise uncategorized error occurred.
217    Uncategorized = 61,
218    /// The depth limit was exceeded.
219    DepthLimitExceeded = 71,
220    /// An encoded string could not be parsed as UTF-8.
221    Utf8 = 81,
222    /// Reserved type
223    ReservedType = 91,
224    /// `std::io::Error`.
225    #[cfg(feature = "std")]
226    StdIo = 255,
227}
228
229/// This type represents all possible errors that can occur when serializing or
230/// deserializing Lilliput data.
231#[derive(Debug)]
232pub enum ErrorKind {
233    /// Unexpected EOF while parsing.
234    UnexpectedEndOfFile,
235    /// A mismatch occurred between the decoded and expected value types.
236    InvalidType(Expectation<String>),
237    /// The enclosed I/O error occurred while trying to read the encoded
238    /// MessagePack data.
239    InvalidValue(Expectation<String>),
240    /// A decoded sequence/map did not have the enclosed expected length.
241    InvalidLength(Expectation<String>),
242    /// An encoded sequence/map did not provide a length.
243    UnknownLength,
244    /// A numeric cast failed due to an out-of-range error.
245    NumberOutOfRange,
246    /// An otherwise uncategorized error occurred.
247    Uncategorized(String),
248    /// The depth limit was exceeded.
249    DepthLimitExceeded,
250    /// An encoded string could not be parsed as UTF-8.
251    Utf8(core::str::Utf8Error),
252    /// ReservedType.
253    ReservedType,
254    /// `std::io::Error`.
255    #[cfg(feature = "std")]
256    StdIo(std::io::Error),
257}
258
259impl ErrorKind {
260    /// EOF while parsing.
261    fn end_of_file() -> Self {
262        Self::UnexpectedEndOfFile
263    }
264
265    /// A mismatch occurred between the decoded and expected value types.
266    fn invalid_type(unexpected: String, expected: String) -> Self {
267        Self::InvalidType(Expectation {
268            unexpected,
269            expected,
270        })
271    }
272
273    /// The enclosed I/O error occurred while trying to read the encoded
274    /// MessagePack data.
275    fn invalid_value(unexpected: String, expected: String) -> Self {
276        Self::InvalidValue(Expectation {
277            unexpected,
278            expected,
279        })
280    }
281
282    /// A decoded sequence/map did not have the enclosed expected length.
283    fn invalid_length(unexpected: String, expected: String) -> Self {
284        Self::InvalidLength(Expectation {
285            unexpected,
286            expected,
287        })
288    }
289
290    /// An encoded sequence/map did not provide a length.
291    #[cold]
292    pub fn unknown_length() -> Self {
293        Self::UnknownLength
294    }
295
296    /// A numeric cast failed due to an out-of-range error.
297    fn number_out_of_range() -> Self {
298        Self::NumberOutOfRange
299    }
300
301    /// An otherwise uncategorized error occurred.
302    fn uncategorized(msg: impl Display) -> Self {
303        Self::Uncategorized(msg.to_string())
304    }
305
306    /// The depth limit was exceeded.
307    fn depth_limit_exceeded() -> Self {
308        Self::DepthLimitExceeded
309    }
310
311    /// An encoded string could not be parsed as UTF-8.
312    fn utf8(err: core::str::Utf8Error) -> Self {
313        Self::Utf8(err)
314    }
315
316    /// Reserved type.
317    fn reserved_type() -> Self {
318        Self::ReservedType
319    }
320
321    #[cfg(feature = "std")]
322    fn io(err: std::io::Error) -> Self {
323        if err.kind() == std::io::ErrorKind::UnexpectedEof {
324            return Self::UnexpectedEndOfFile;
325        }
326
327        Self::StdIo(err)
328    }
329
330    /// Returns the error's code.
331    pub fn as_code(&self) -> ErrorCode {
332        match self {
333            ErrorKind::UnexpectedEndOfFile => ErrorCode::UnexpectedEndOfFile,
334            ErrorKind::InvalidType(_) => ErrorCode::InvalidType,
335            ErrorKind::InvalidValue(_) => ErrorCode::InvalidValue,
336            ErrorKind::InvalidLength(_) => ErrorCode::InvalidLength,
337            ErrorKind::UnknownLength => ErrorCode::UnknownLength,
338            ErrorKind::NumberOutOfRange => ErrorCode::NumberOutOfRange,
339            ErrorKind::Uncategorized(_) => ErrorCode::Uncategorized,
340            ErrorKind::DepthLimitExceeded => ErrorCode::DepthLimitExceeded,
341            ErrorKind::Utf8(_) => ErrorCode::Utf8,
342            ErrorKind::ReservedType => ErrorCode::ReservedType,
343            ErrorKind::StdIo(_) => ErrorCode::StdIo,
344        }
345    }
346}
347
348impl Display for ErrorKind {
349    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
350        match self {
351            Self::UnexpectedEndOfFile => f.write_str("unexpected EOF while parsing"),
352            Self::InvalidType(unexpected) => {
353                write!(
354                    f,
355                    "expected type {}, found type {}",
356                    unexpected.expected, unexpected.unexpected
357                )
358            }
359            Self::InvalidValue(unexpected) => {
360                write!(
361                    f,
362                    "expected data {}, found type {}",
363                    unexpected.expected, unexpected.unexpected
364                )
365            }
366            Self::InvalidLength(unexpected) => {
367                write!(
368                    f,
369                    "expected length {}, found length {}",
370                    unexpected.expected, unexpected.unexpected
371                )
372            }
373            Self::UnknownLength => f.write_str("unknown length"),
374            Self::NumberOutOfRange => f.write_str("unexpected EOF while parsing"),
375            Self::Uncategorized(msg) => f.write_str(msg),
376            Self::DepthLimitExceeded => {
377                f.write_str("a numeric cast failed due to an out-of-range error")
378            }
379            Self::Utf8(err) => Display::fmt(err, f),
380            Self::ReservedType => f.write_str("reserved type"),
381            #[cfg(feature = "std")]
382            Self::StdIo(err) => Display::fmt(err, f),
383        }
384    }
385}