Skip to main content

bincode_next/
error.rs

1//! Errors that can be encountering by Encoding and Decoding.
2// Necessary to allow dead code since we don't use all variants in all configurations
3#![allow(dead_code)]
4
5#[doc(hidden)]
6#[macro_export]
7macro_rules! bincode_error {
8    (
9        $(#[$meta:meta])*
10        $vis:vis enum $name:ident {
11            $(
12                $(#[$variant_meta:meta])*
13                $variant:ident $( { $( $(#[$field_meta:meta])* $field:ident : $ftype:ty ),* $(,)? } )? $( ( $( $(#[$tuple_meta:meta])* $tname:ident : $ttype:ty ),* $(,)? ) )?
14            ),* $(,)?
15        }
16    ) => {
17        $(#[$meta])*
18        $vis enum $name {
19            $(
20                $(#[$variant_meta])*
21                $variant $( { $( $(#[$field_meta])* $field : $ftype ),* } )? $( ( $( $(#[$tuple_meta])* $ttype ),* ) )?,
22            )*
23        }
24
25        pastey::paste! {
26            $(
27                $(#[$variant_meta])*
28                #[doc(hidden)]
29                #[cold]
30                #[track_caller]
31                #[inline(never)]
32                pub const fn [<cold_ $name:snake _ $variant:snake>]<T>(
33                    $($($field : $ftype),*)?
34                    $($($tname : $ttype),*)?
35                ) -> core::result::Result<T, $name> {
36                    core::result::Result::Err($name::$variant $( { $($field),* } )? $( ( $( $tname ),* ) )?)
37                }
38            )*
39        }
40    };
41}
42
43bincode_error! {
44    /// Errors that can be encountered by encoding a type
45    #[non_exhaustive]
46    #[derive(Debug)]
47    pub enum EncodeError {
48        /// The writer ran out of storage.
49        UnexpectedEnd,
50
51        /// The `RefCell<T>` is already borrowed
52        RefCellAlreadyBorrowed {
53            /// The inner borrow error
54            inner: core::cell::BorrowError,
55            /// the type name of the `RefCell` being encoded that is currently borrowed.
56            type_name: &'static str,
57        },
58
59        /// An uncommon error occurred, see the inner text for more information
60        Other(inner: &'static str),
61
62        /// An uncommon error occurred, see the inner text for more information
63        #[cfg(feature = "alloc")]
64        OtherString(inner: alloc::string::String),
65
66        /// A `std::path::Path` was being encoded but did not contain a valid `&str` representation
67        #[cfg(feature = "std")]
68        InvalidPathCharacters,
69
70        /// The targeted writer encountered an `std::io::Error`
71        #[cfg(feature = "std")]
72        Io {
73            /// The encountered error
74            inner: std::io::Error,
75            /// The amount of bytes that were written before the error occurred
76            index: usize,
77        },
78
79        /// The encoder tried to encode a `Mutex` or `RwLock`, but the locking failed
80        #[cfg(feature = "std")]
81        LockFailed {
82            /// The type name of the mutex for debugging purposes
83            type_name: &'static str,
84        },
85
86        /// The encoder tried to encode a `SystemTime`, but it was before `SystemTime::UNIX_EPOCH`
87        #[cfg(feature = "std")]
88        InvalidSystemTime {
89            /// The error that was thrown by the `SystemTime`
90            inner: std::time::SystemTimeError,
91            /// The `SystemTime` that caused the error
92            time: std::boxed::Box<std::time::SystemTime>,
93        },
94
95        #[cfg(feature = "serde")]
96        /// A serde-specific error that occurred while decoding.
97        Serde(inner: crate::features::serde::EncodeError),
98    }
99}
100
101impl core::fmt::Display for EncodeError {
102    fn fmt(
103        &self,
104        f: &mut core::fmt::Formatter<'_>,
105    ) -> core::fmt::Result {
106        // TODO: Improve this?
107        write!(f, "{self:?}")
108    }
109}
110
111impl core::error::Error for EncodeError {
112    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
113        match self {
114            | Self::RefCellAlreadyBorrowed { inner, .. } => Some(inner),
115            #[cfg(feature = "std")]
116            | Self::Io { inner, .. } => Some(inner),
117            #[cfg(feature = "std")]
118            | Self::InvalidSystemTime { inner, .. } => Some(inner),
119            | _ => None,
120        }
121    }
122}
123impl core::error::Error for DecodeError {
124    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
125        match self {
126            | Self::Utf8 { inner } => Some(inner),
127            | _ => None,
128        }
129    }
130}
131
132bincode_error! {
133    /// Errors that can be encountered by decoding a type
134    #[non_exhaustive]
135    #[derive(Debug)]
136    pub enum DecodeError {
137        /// The reader reached its end but more bytes were expected.
138        UnexpectedEnd {
139            /// Gives an estimate of how many extra bytes are needed.
140            ///
141            /// **Note**: this is only an estimate and not indicative of the actual bytes needed.
142            ///
143            /// **Note**: Bincode has no look-ahead mechanism. This means that this will only return the amount of bytes to be read for the current action, and not take into account the entire data structure being read.
144            additional: usize,
145        },
146
147        /// The given configuration limit was exceeded
148        LimitExceeded,
149
150        /// Invalid type was found. The decoder tried to read type `expected`, but found type `found` instead.
151        InvalidIntegerType {
152            /// The type that was being read from the reader
153            expected: IntegerType,
154            /// The type that was encoded in the data
155            found: IntegerType,
156        },
157
158        /// The decoder tried to decode any of the `NonZero*` types but the value is zero
159        NonZeroTypeIsZero {
160            /// The type that was being read from the reader
161            non_zero_type: IntegerType,
162        },
163
164        /// Invalid enum variant was found. The decoder tried to decode variant index `found`, but the variant index should be between `min` and `max`.
165        UnexpectedVariant {
166            /// The type name that was being decoded.
167            type_name: &'static str,
168
169            /// The variants that are allowed
170            allowed: &'static AllowedEnumVariants,
171
172            /// The index of the enum that the decoder encountered
173            found: u32,
174        },
175
176        /// The decoder tried to decode a `str`, but an utf8 error was encountered.
177        Utf8 {
178            /// The inner error
179            inner: core::str::Utf8Error,
180        },
181
182        /// The decoder tried to decode a `char` and failed. The given buffer contains the bytes that are read at the moment of failure.
183        InvalidCharEncoding(inner: [u8; 4]),
184
185        /// The decoder tried to decode a `bool` and failed. The given value is what is actually read.
186        InvalidBooleanValue(inner: u8),
187
188        /// Invalid CBOR "additional info" value (28-31) was found.
189        InvalidCborInfo(inner: u8),
190
191        /// The decoder tried to decode an array of length `required`, but the binary data contained an array of length `found`.
192        ArrayLengthMismatch {
193            /// The length of the array required by the rust type.
194            required: usize,
195            /// The length of the array found in the binary format.
196            found: usize,
197        },
198
199        /// The encoded value is outside of the range of the target usize type.
200        ///
201        /// This can happen if an usize was encoded on an architecture with a larger
202        /// usize type and then decoded on an architecture with a smaller one. For
203        /// example going from a 64 bit architecture to a 32 or 16 bit one may
204        /// cause this error.
205        OutsideUsizeRange(inner: u64),
206
207        /// The encoded value is outside of the range of the target isize type.
208        ///
209        /// This can happen if an isize was encoded on an architecture with a larger
210        /// isize type and then decoded on an architecture with a smaller one. For
211        /// example going from a 64 bit architecture to a 32 or 16 bit one may
212        /// cause this error.
213        OutsideIsizeRange(inner: i64),
214
215        /// Tried to decode an enum with no variants
216        EmptyEnum {
217            /// The type that was being decoded
218            type_name: &'static str,
219        },
220
221        /// The decoder tried to decode a Duration and overflowed the number of seconds.
222        InvalidDuration {
223            /// The number of seconds in the duration.
224            secs: u64,
225
226            /// The number of nanoseconds in the duration, which when converted to seconds and added to
227            /// `secs`, overflows a `u64`.
228            nanos: u32,
229        },
230
231        /// The decoder tried to decode a `SystemTime` and overflowed
232        InvalidSystemTime {
233            /// The duration which could not have been added to
234            /// [`UNIX_EPOCH`\](`std::time::SystemTime::UNIX_EPOCH`)
235            duration: core::time::Duration,
236        },
237
238        /// The decoder tried to decode a `CString`, but the incoming data contained a 0 byte
239        #[cfg(feature = "std")]
240        CStringNulError {
241            /// Nul byte position
242            position: usize,
243        },
244
245        /// The reader encountered an IO error but more bytes were expected.
246        #[cfg(feature = "std")]
247        Io {
248            /// The IO error expected
249            inner: std::io::Error,
250
251            /// Gives an estimate of how many extra bytes are needed.
252            ///
253            /// **Note**: this is only an estimate and not indicative of the actual bytes needed.
254            ///
255            /// **Note**: Bincode has no look-ahead mechanism. This means that this will only return the amount of bytes to be read for the current action, and not take into account the entire data structure being read.
256            additional: usize,
257        },
258
259        /// An uncommon error occurred, see the inner text for more information
260        Other(inner: &'static str),
261
262        /// An uncommon error occurred, see the inner text for more information
263        #[cfg(feature = "alloc")]
264        OtherString(inner: alloc::string::String),
265
266        #[cfg(feature = "serde")]
267        /// A serde-specific error that occurred while decoding.
268        Serde(inner: crate::features::serde::DecodeError),
269
270        /// The schema of the type being decoded does not match the schema of the encoded data.
271        SchemaMismatch {
272            /// The hash that was expected by the decoder
273            expected: u64,
274            /// The hash that was found in the encoded data
275            actual: u64,
276        },
277
278        /// The decoder tried to decode a map, but a duplicate key was found.
279        DuplicateMapKey,
280
281        /// The decoder tried to decode a map, but the keys were not in the correct order.
282        InvalidMapOrder,
283    }
284}
285
286#[cfg(feature = "serde")]
287impl From<crate::features::serde::DecodeError> for crate::error::DecodeError {
288    fn from(err: crate::features::serde::DecodeError) -> Self {
289        Self::Serde(err)
290    }
291}
292
293#[cfg(feature = "serde")]
294#[doc(hidden)]
295#[must_use]
296/// Helper function to convert an error into a `DecodeError`.
297pub const fn decode_err_from(e: crate::features::serde::DecodeError) -> DecodeError {
298    DecodeError::Serde(e)
299}
300
301impl core::fmt::Display for DecodeError {
302    fn fmt(
303        &self,
304        f: &mut core::fmt::Formatter<'_>,
305    ) -> core::fmt::Result {
306        // TODO: Improve this?
307        write!(f, "{self:?}")
308    }
309}
310
311impl DecodeError {
312    /// If the current error is `InvalidIntegerType`, change the `expected` and
313    /// `found` values from `Ux` to `Ix`. This is needed to have correct error
314    /// reporting in `src/varint/decode_signed.rs` since this calls
315    /// `src/varint/decode_unsigned.rs` and needs to correct the `expected` and
316    /// `found` types.
317    #[cold]
318    #[inline(never)]
319    pub(crate) fn change_integer_type_to_signed(self) -> Self {
320        match self {
321            | Self::InvalidIntegerType { expected, found } => {
322                Self::InvalidIntegerType {
323                    expected: expected.into_signed(),
324                    found: found.into_signed(),
325                }
326            },
327            | other => other,
328        }
329    }
330}
331
332/// Indicates which enum variants are allowed
333#[non_exhaustive]
334#[derive(Debug, PartialEq, Eq)]
335pub enum AllowedEnumVariants {
336    /// All values between `min` and `max` (inclusive) are allowed
337    #[allow(missing_docs)]
338    Range { min: u32, max: u32 },
339    /// Each one of these values is allowed
340    Allowed(&'static [u32]),
341}
342
343/// Integer types. Used by [`DecodeError`\]. These types have no purpose other than being shown in errors.
344#[non_exhaustive]
345#[derive(Debug, PartialEq, Eq)]
346#[allow(missing_docs)]
347pub enum IntegerType {
348    U8,
349    U16,
350    U32,
351    U64,
352    U128,
353    Usize,
354
355    I8,
356    I16,
357    I32,
358    I64,
359    I128,
360    Isize,
361
362    Reserved,
363}
364
365impl IntegerType {
366    /// Change the `Ux` value to the associated `Ix` value.
367    /// Returns the old value if `self` is already `Ix`.
368    pub(crate) const fn into_signed(self) -> Self {
369        match self {
370            | Self::U8 => Self::I8,
371            | Self::U16 => Self::I16,
372            | Self::U32 => Self::I32,
373            | Self::U64 => Self::I64,
374            | Self::U128 => Self::I128,
375            | Self::Usize => Self::Isize,
376
377            | other => other,
378        }
379    }
380}