1use core::fmt;
4
5pub type Result<T> = core::result::Result<T, Error>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum Error {
12 UnexpectedEnd {
14 additional: usize,
16 },
17
18 InvalidData {
20 message: &'static str,
22 },
23
24 InvalidIntegerType {
26 expected: IntegerType,
28 found: IntegerType,
30 },
31
32 InvalidBooleanValue(u8),
34
35 InvalidCharEncoding([u8; 4]),
37
38 #[cfg(feature = "alloc")]
40 Utf8 {
41 inner: core::str::Utf8Error,
43 },
44
45 LimitExceeded {
47 limit: u64,
49 found: u64,
51 },
52
53 #[cfg(feature = "std")]
55 Io {
56 kind: std::io::ErrorKind,
58 message: String,
60 },
61
62 Custom {
64 message: &'static str,
66 },
67
68 #[cfg(feature = "alloc")]
70 OwnedCustom {
71 message: alloc::string::String,
73 },
74
75 OutsideUsizeRange(u64),
77
78 NonZeroTypeIsZero {
80 non_zero_type: IntegerType,
82 },
83
84 UnexpectedVariant {
86 found: u32,
88 type_name: &'static str,
90 },
91
92 #[cfg(feature = "std")]
100 InvalidDuration {
101 secs: u64,
103 nanos: u32,
105 },
106
107 #[cfg(feature = "std")]
115 InvalidSystemTime {
116 duration: std::time::Duration,
118 },
119
120 #[cfg(feature = "checksum")]
122 ChecksumMismatch {
123 expected: u32,
125 found: u32,
127 },
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132#[non_exhaustive]
133pub enum IntegerType {
134 U8,
136 U16,
138 U32,
140 U64,
142 U128,
144 Usize,
146 I8,
148 I16,
150 I32,
152 I64,
154 I128,
156 Isize,
158 Reserved,
160}
161
162impl IntegerType {
163 #[allow(dead_code)]
165 pub(crate) const fn into_signed(self) -> Self {
166 match self {
167 Self::U8 => Self::I8,
168 Self::U16 => Self::I16,
169 Self::U32 => Self::I32,
170 Self::U64 => Self::I64,
171 Self::U128 => Self::I128,
172 Self::Usize => Self::Isize,
173 other => other,
174 }
175 }
176}
177
178impl fmt::Display for IntegerType {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 match self {
181 Self::U8 => write!(f, "u8"),
182 Self::U16 => write!(f, "u16"),
183 Self::U32 => write!(f, "u32"),
184 Self::U64 => write!(f, "u64"),
185 Self::U128 => write!(f, "u128"),
186 Self::Usize => write!(f, "usize"),
187 Self::I8 => write!(f, "i8"),
188 Self::I16 => write!(f, "i16"),
189 Self::I32 => write!(f, "i32"),
190 Self::I64 => write!(f, "i64"),
191 Self::I128 => write!(f, "i128"),
192 Self::Isize => write!(f, "isize"),
193 Self::Reserved => write!(f, "reserved"),
194 }
195 }
196}
197
198impl fmt::Display for Error {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 match self {
201 Error::UnexpectedEnd { additional } => {
202 write!(
203 f,
204 "Unexpected end of input (need {} more bytes)",
205 additional
206 )
207 }
208 Error::InvalidData { message } => write!(f, "Invalid data: {}", message),
209 Error::InvalidIntegerType { expected, found } => {
210 write!(
211 f,
212 "Invalid integer type: expected {}, found {}",
213 expected, found
214 )
215 }
216 Error::InvalidBooleanValue(v) => write!(f, "Invalid boolean value: {}", v),
217 Error::InvalidCharEncoding(bytes) => {
218 write!(f, "Invalid char encoding: {:?}", bytes)
219 }
220 #[cfg(feature = "alloc")]
221 Error::Utf8 { inner } => write!(
222 f,
223 "UTF-8 error at byte offset {}: {}",
224 inner.valid_up_to(),
225 inner
226 ),
227 Error::LimitExceeded { limit, found } => {
228 write!(f, "limit exceeded: found {} but limit is {}", found, limit)
229 }
230 #[cfg(feature = "std")]
231 Error::Io { kind, message } => write!(f, "IO error ({:?}): {}", kind, message),
232 Error::Custom { message } => write!(f, "{}", message),
233 #[cfg(feature = "alloc")]
234 Error::OwnedCustom { message } => write!(f, "{}", message),
235 Error::OutsideUsizeRange(v) => {
236 write!(f, "Value {} outside usize range", v)
237 }
238 Error::NonZeroTypeIsZero { non_zero_type } => {
239 write!(f, "NonZero{} type decoded as zero", non_zero_type)
240 }
241 Error::UnexpectedVariant { found, type_name } => {
242 write!(
243 f,
244 "unexpected variant for type `{}`: found discriminant {}",
245 type_name, found
246 )
247 }
248 #[cfg(feature = "std")]
249 Error::InvalidDuration { secs, nanos } => {
250 write!(
251 f,
252 "Invalid duration: {} seconds, {} nanoseconds (nanos must be < 1,000,000,000)",
253 secs, nanos
254 )
255 }
256 #[cfg(feature = "std")]
257 Error::InvalidSystemTime { duration } => {
258 write!(f, "Invalid SystemTime: {:?} before UNIX_EPOCH", duration)
259 }
260 #[cfg(feature = "checksum")]
261 Error::ChecksumMismatch { expected, found } => {
262 write!(
263 f,
264 "Checksum mismatch: expected 0x{:08x}, found 0x{:08x}",
265 expected, found
266 )
267 }
268 }
269 }
270}
271
272#[cfg(feature = "std")]
273impl std::error::Error for Error {
274 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
275 match self {
276 Error::Utf8 { inner } => Some(inner),
279 _ => None,
280 }
281 }
282}
283
284#[cfg(feature = "std")]
285impl From<std::io::Error> for Error {
286 fn from(err: std::io::Error) -> Self {
287 Error::Io {
288 kind: err.kind(),
289 message: err.to_string(),
290 }
291 }
292}
293
294impl From<core::str::Utf8Error> for Error {
295 fn from(inner: core::str::Utf8Error) -> Self {
296 #[cfg(feature = "alloc")]
297 {
298 Error::Utf8 { inner }
299 }
300 #[cfg(not(feature = "alloc"))]
301 {
302 let _ = inner;
303 Error::InvalidData {
304 message: "UTF-8 decoding error",
305 }
306 }
307 }
308}
309
310#[cfg(all(test, feature = "std"))]
311mod tests {
312 use super::*;
313 use std::error::Error as StdError;
314
315 #[test]
316 fn test_source_chains_utf8_inner_error() {
317 let bytes = [core::hint::black_box(0xFFu8), 0xFFu8];
323 let utf8_err = core::str::from_utf8(&bytes).expect_err("invalid utf-8");
324 let err: Error = utf8_err.into();
325 assert!(matches!(err, Error::Utf8 { .. }));
326
327 let source = StdError::source(&err);
328 assert!(source.is_some(), "Utf8 variant must expose its inner error");
329
330 let downcast = source
332 .expect("source present")
333 .downcast_ref::<core::str::Utf8Error>();
334 assert!(downcast.is_some(), "source must be the wrapped Utf8Error");
335 }
336
337 #[test]
338 fn test_source_none_for_non_wrapping_variants() {
339 let io = Error::from(std::io::Error::other("boom"));
341 assert!(StdError::source(&io).is_none());
342
343 let invalid = Error::InvalidData { message: "nope" };
344 assert!(StdError::source(&invalid).is_none());
345
346 let outside = Error::OutsideUsizeRange(u64::MAX);
347 assert!(StdError::source(&outside).is_none());
348 }
349}