deku/
error.rs

1//! Error module
2
3#[cfg(feature = "descriptive-errors")]
4use alloc::borrow::Cow;
5
6use no_std_io::io::ErrorKind;
7
8/// Number of bits needed to retry parsing
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct NeedSize {
11    bits: usize,
12}
13
14impl NeedSize {
15    /// Create new [NeedSize] from bits
16    #[inline]
17    pub fn new(bits: usize) -> Self {
18        Self { bits }
19    }
20
21    /// Number of bits needed
22    #[inline]
23    pub fn bit_size(&self) -> usize {
24        self.bits
25    }
26
27    /// Number of bytes needed
28    #[inline]
29    pub fn byte_size(&self) -> usize {
30        self.bits.div_ceil(8)
31    }
32}
33
34#[cfg(feature = "descriptive-errors")]
35type DekuErrorString = Cow<'static, str>;
36
37#[cfg(not(feature = "descriptive-errors"))]
38type DekuErrorString = &'static str;
39
40/// Deku errors
41#[derive(Debug, Clone, PartialEq, Eq)]
42#[non_exhaustive]
43pub enum DekuError {
44    /// Parsing error when reading
45    Incomplete(NeedSize),
46    /// Parsing error when reading
47    Parse(DekuErrorString),
48    /// Invalid parameter
49    InvalidParam(DekuErrorString),
50    /// Assertion error from `assert` or `assert_eq` attributes
51    Assertion(DekuErrorString),
52    /// Could not resolve `id` for variant
53    IdVariantNotFound,
54    /// IO error while reading or writing
55    Io(ErrorKind),
56}
57
58/// Abstract over alloc vs no-alloc for handling of error strings
59///
60/// The macro takes the DekuError variant as an argument and performs the
61/// instantiation itself in order to cater to the no-alloc case where we discard
62/// all arguments beyond the description.
63///
64/// Invoked like:
65///
66/// ```
67/// use deku::{deku_error, DekuError};
68/// # extern crate alloc;
69/// # let bit_size = 5;
70/// # let input_size = 4;
71/// let _ = deku_error!(DekuError::Parse,
72///                     "bit size is greater than input size",
73///                     "{} exceeds {}",
74///                     bit_size,
75///                     input_size);
76/// ```
77#[cfg(feature = "descriptive-errors")]
78#[macro_export]
79macro_rules! deku_error {
80    ($p:path, $desc:expr, $fmt:expr, $($arg:expr),*) => {{
81        extern crate alloc;
82        $p(alloc::borrow::Cow::from(alloc::format!(concat!($desc, ": ", $fmt), $($arg),*)))
83    }};
84    ($p:path, $desc:expr, $fmt:expr) => {{
85        extern crate alloc;
86        $p(alloc::borrow::Cow::from(alloc::format!(concat!($desc, ": ", $fmt))))
87    }};
88    ($p:path, $desc:expr) => {{
89        extern crate alloc;
90        $p(alloc::borrow::Cow::from($desc))
91    }};
92    ($p:path) => {{ $p }};
93}
94
95/// Abstract over alloc vs no-alloc for handling of error strings
96///
97/// The macro takes the DekuError variant as an argument and performs the
98/// instantiation itself in order to cater to the no-alloc case where we discard
99/// all arguments beyond the description.
100///
101/// Invoked like:
102///
103/// ```
104/// use deku::{deku_error, DekuError};
105/// # let bit_size = 5;
106/// # let input_size = 4;
107/// let _ = deku_error!(DekuError::Parse,
108///                     "bit size is greater than input size",
109///                     "{} exceeds {}",
110///                     bit_size,
111///                     input_size);
112/// ```
113#[cfg(not(feature = "descriptive-errors"))]
114#[macro_export]
115macro_rules! deku_error {
116    ($p:path, $desc:expr, $fmt:expr, $($arg:expr),*) => {{
117        $(let _ = $arg;)*
118        $p($desc)
119    }};
120    ($p:path, $desc:expr, $fmt:expr) => {{
121        $p($desc)
122    }};
123    ($p:path, $desc:expr) => {{
124        $p($desc)
125    }};
126    ($p:path) => {{ $p }};
127}
128
129impl From<core::num::TryFromIntError> for DekuError {
130    fn from(e: core::num::TryFromIntError) -> DekuError {
131        deku_error!(DekuError::Parse, "error parsing int", "{}", e)
132    }
133}
134
135impl From<core::array::TryFromSliceError> for DekuError {
136    fn from(e: core::array::TryFromSliceError) -> DekuError {
137        deku_error!(DekuError::Parse, "error parsing from slice", "{}", e)
138    }
139}
140
141impl From<core::convert::Infallible> for DekuError {
142    fn from(_e: core::convert::Infallible) -> DekuError {
143        unreachable!();
144    }
145}
146
147#[cfg(feature = "std")]
148impl core::fmt::Display for DekuError {
149    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
150        match *self {
151            DekuError::Incomplete(ref size) => write!(
152                f,
153                "Not enough data, need {} bits (or {} bytes)",
154                size.bit_size(),
155                size.byte_size()
156            ),
157            DekuError::Parse(ref err) => write!(f, "Parse error: {err}"),
158            DekuError::InvalidParam(ref err) => write!(f, "Invalid param error: {err}"),
159            DekuError::Assertion(ref err) => write!(f, "{err}"),
160            DekuError::IdVariantNotFound => write!(f, "Could not resolve `id` for variant"),
161            DekuError::Io(ref e) => write!(f, "io errorr: {e:?}"),
162        }
163    }
164}
165
166#[cfg(not(feature = "std"))]
167impl core::error::Error for DekuError {}
168
169#[cfg(not(feature = "std"))]
170impl core::fmt::Display for DekuError {
171    fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172        Ok(())
173    }
174}
175
176#[cfg(not(feature = "std"))]
177impl From<DekuError> for no_std_io::io::Error {
178    fn from(error: DekuError) -> Self {
179        use no_std_io::io;
180        match error {
181            DekuError::Incomplete(_) => {
182                io::Error::new(io::ErrorKind::UnexpectedEof, "Short message buffer")
183            }
184            DekuError::Parse(msg) => io::Error::new(io::ErrorKind::InvalidData, msg),
185            DekuError::InvalidParam(msg) => io::Error::new(io::ErrorKind::InvalidInput, msg),
186            DekuError::Assertion(msg) => io::Error::new(io::ErrorKind::InvalidData, msg),
187            DekuError::IdVariantNotFound => {
188                io::Error::new(io::ErrorKind::NotFound, "Variant not found for ID")
189            }
190            DekuError::Io(kind) => io::Error::new(kind, "IO failure during parsing"),
191        }
192    }
193}
194
195#[cfg(feature = "std")]
196impl std::error::Error for DekuError {
197    fn cause(&self) -> Option<&dyn std::error::Error> {
198        Some(self)
199    }
200}
201
202#[cfg(feature = "std")]
203impl From<DekuError> for std::io::Error {
204    fn from(error: DekuError) -> Self {
205        use std::io;
206        match error {
207            DekuError::Incomplete(_) => io::Error::new(io::ErrorKind::UnexpectedEof, error),
208            DekuError::Parse(_) => io::Error::new(io::ErrorKind::InvalidData, error),
209            DekuError::InvalidParam(_) => io::Error::new(io::ErrorKind::InvalidInput, error),
210            DekuError::Assertion(_) => io::Error::new(io::ErrorKind::InvalidData, error),
211            DekuError::IdVariantNotFound => io::Error::new(io::ErrorKind::NotFound, error),
212            DekuError::Io(e) => io::Error::new(e, error),
213        }
214    }
215}
216
217impl From<no_std_io::io::Error> for DekuError {
218    fn from(value: no_std_io::io::Error) -> Self {
219        DekuError::Io(value.kind())
220    }
221}