Skip to main content

rm_frame/
error.rs

1//! Error types for the binframe crate.
2
3use core::error::Error as StdError;
4use core::fmt::{Display, Formatter, Result as FmtResult};
5
6/// Errors returned by
7///     [`Marshaler::marshal`](crate::Marshaler::marshal),
8///     [`Marshaler::unmarshal`](crate::Marshaler::unmarshal),
9/// and [`RawFrame::unmarshal`](crate::RawFrame::unmarshal).
10#[derive(Debug)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12pub enum MarshalerError {
13    /// The frame's command ID does not match the expected type.
14    ///
15    /// Returned by
16    ///     [`RawFrame::unmarshal`](crate::RawFrame::unmarshal)
17    /// when [`M::CMD_ID`](crate::Marshaler::CMD_ID) differs from the
18    /// command ID in the decoded frame.
19    InvalidCmdID {
20        /// The expected command ID for the target type.
21        expected: u16,
22        /// The actual command ID found in the frame.
23        found: u16,
24    },
25
26    /// The payload length does not match [`Marshaler::PAYLOAD_SIZE`](crate::Marshaler::PAYLOAD_SIZE).
27    ///
28    /// Returned by
29    ///     [`RawFrame::unmarshal`](crate::RawFrame::unmarshal)
30    /// when the payload size in the frame differs from the size the target type expects.
31    InvalidDataLength {
32        /// The expected payload size for the target type.
33        expected: usize,
34        /// The actual payload size found in the frame.
35        found: usize,
36    },
37
38    /// The destination buffer is too small to hold the serialized payload.
39    ///
40    /// `need` is the minimum buffer size required, in bytes.
41    BufferTooSmall {
42        /// The minimum buffer size required, in bytes.
43        need: usize,
44    },
45
46    /// An error specific to the payload type prevented successful marshaling or unmarshaling.
47    ///
48    /// This is a catch-all variant for errors that do not fit into the above categories.
49    /// The inner error type and message are determined by the specific implementation of
50    /// [`Marshaler::marshal`](crate::Marshaler::marshal) or
51    /// [`Marshaler::unmarshal`](crate::Marshaler::unmarshal).
52    Unexpected {
53        /// The error code that user defined in their marshaler implementation.
54        code: usize,
55        /// The error message that user defined in their marshaler implementation.
56        message: &'static str,
57    },
58}
59
60impl From<()> for MarshalerError {
61    fn from(_: ()) -> Self {
62        Self::Unexpected {
63            code: 0,
64            message: "Unit Type Error",
65        }
66    }
67}
68
69impl From<usize> for MarshalerError {
70    fn from(code: usize) -> Self {
71        Self::Unexpected {
72            code,
73            message: "Unexpected Error",
74        }
75    }
76}
77
78impl From<(usize,)> for MarshalerError {
79    fn from((code,): (usize,)) -> Self {
80        Self::Unexpected {
81            code,
82            message: "Unexpected Error",
83        }
84    }
85}
86
87impl From<(&'static str, usize)> for MarshalerError {
88    fn from((message, code): (&'static str, usize)) -> Self {
89        Self::Unexpected { code, message }
90    }
91}
92
93impl From<(usize, &'static str)> for MarshalerError {
94    fn from((code, message): (usize, &'static str)) -> Self {
95        Self::Unexpected { code, message }
96    }
97}
98
99/// Errors returned by [`Messager::pack`](crate::Messager::pack).
100#[derive(Debug)]
101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
102pub enum PackError {
103    /// The destination buffer is too small to hold the complete frame.
104    BufferTooSmall {
105        /// The minimum required size in bytes:
106        /// `5 (header) + 2 (CMD_ID) + PAYLOAD_SIZE + 2 (CRC16)`.
107        need: usize,
108    },
109
110    /// The marshaler returned a byte count that does not equal
111    /// [`Marshaler::PAYLOAD_SIZE`](crate::Marshaler::PAYLOAD_SIZE).
112    ///
113    /// This indicates a broken [`Marshaler`](crate::Marshaler) implementation.
114    InvalidPayloadSize {
115        /// The expected payload size for the target type.
116        expected: usize,
117        /// The actual payload size returned by the marshaler.
118        found: usize,
119    },
120
121    /// The marshaler returned an error.
122    MarshalerError(MarshalerError),
123}
124
125impl From<MarshalerError> for PackError {
126    fn from(err: MarshalerError) -> Self {
127        Self::MarshalerError(err)
128    }
129}
130
131/// Errors returned by
132///     [`Messager::unpack`](crate::Messager::unpack)
133/// and [`Messager::unmarshal`](crate::Messager::unmarshal).
134#[derive(Debug)]
135#[cfg_attr(feature = "defmt", derive(defmt::Format))]
136pub enum UnPackError {
137    /// The input does not start with the SOF byte (`0xA5`), but a SOF byte
138    /// was found later in the buffer.
139    ///
140    /// `skip` is the byte offset of the first SOF byte found.
141    /// Discard that many bytes from the input, then retry.
142    ReSync {
143        /// The byte offset of the first SOF byte found in the input buffer.
144        skip: usize,
145    },
146
147    /// No SOF byte was found anywhere in the input buffer.
148    ///
149    /// `skip` equals the buffer length.
150    /// Discard the entire buffer and wait for more data before retrying.
151    MissingHeader {
152        /// The length of the input buffer, which should be discarded.
153        skip: usize,
154    },
155
156    /// The frame is truncated; more bytes are needed to complete it.
157    ///
158    /// `read` is the number of bytes currently available.
159    /// Keep the existing bytes and wait for more data before retrying.
160    UnexpectedEnd {
161        /// The number of bytes currently available in the input buffer.
162        read: usize,
163    },
164
165    /// CRC validation failed (header CRC8 or frame CRC16).
166    ///
167    /// `at` is the cursor position when the failure was detected.
168    /// Call [`UnPackError::skip`] to determine how many bytes to discard.
169    InvalidChecksum {
170        /// The cursor position in the input buffer where the checksum failure was detected.
171        at: usize,
172    },
173
174    /// The payload could not be decoded into the target type.
175    ///
176    /// Wraps a [`MarshalerError`] from [`RawFrame::unmarshal`](crate::RawFrame::unmarshal).
177    MarshalerError(MarshalerError),
178}
179
180impl UnPackError {
181    /// Returns the number of bytes to discard before retrying a parse.
182    ///
183    /// Use this when processing a continuous byte stream to advance the read
184    /// position past invalid or incomplete data:
185    ///
186    /// | Variant | Returned value | Action |
187    /// |---------|----------------|--------|
188    /// | [`ReSync`](Self::ReSync) | offset of next SOF | discard bytes, retry |
189    /// | [`MissingHeader`](Self::MissingHeader) | buffer length | discard all, wait for data |
190    /// | [`UnexpectedEnd`](Self::UnexpectedEnd) | `0` | wait for more data at current position |
191    /// | [`InvalidChecksum`](Self::InvalidChecksum) | cursor at failure | discard frame, retry |
192    /// | [`MarshalerError`](Self::MarshalerError) | `0` | frame was consumed; handle decode error |
193    pub fn skip(&self) -> usize {
194        match self {
195            Self::MissingHeader { skip } => *skip,
196            Self::ReSync { skip } => *skip,
197            Self::UnexpectedEnd { .. } => 0,
198            Self::InvalidChecksum { at } => *at,
199            Self::MarshalerError(_) => 0,
200        }
201    }
202}
203
204impl From<MarshalerError> for UnPackError {
205    fn from(err: MarshalerError) -> Self {
206        Self::MarshalerError(err)
207    }
208}
209
210impl StdError for MarshalerError {}
211impl Display for MarshalerError {
212    fn fmt(&self, f: &mut Formatter) -> FmtResult {
213        match self {
214            Self::InvalidCmdID { expected, found } => write!(
215                f,
216                "Invalid Command ID: expected {}, found {}",
217                expected, found
218            ),
219            Self::InvalidDataLength { expected, found } => {
220                write!(
221                    f,
222                    "Invalid Data Length: expected {} bytes, found {} bytes",
223                    expected, found
224                )
225            }
226            Self::BufferTooSmall { need } => {
227                write!(f, "Buffer too Small: need {} bytes", need)
228            }
229            Self::Unexpected { code, message } => {
230                write!(
231                    f,
232                    "Unexpected Marshaler Error: code {}, message: {}",
233                    code, message
234                )
235            }
236        }
237    }
238}
239
240impl Display for PackError {
241    fn fmt(&self, f: &mut Formatter) -> FmtResult {
242        match self {
243            Self::BufferTooSmall { need } => {
244                write!(f, "Buffer too Small: need {} bytes", need)
245            }
246            Self::InvalidPayloadSize { expected, found } => {
247                write!(
248                    f,
249                    "Invalid Payload Size: expected {}, found {}",
250                    expected, found
251                )
252            }
253            Self::MarshalerError(e) => {
254                write!(f, "Marshaler Error: {}", e)
255            }
256        }
257    }
258}
259impl StdError for PackError {
260    fn source(&self) -> Option<&(dyn StdError + 'static)> {
261        match self {
262            Self::MarshalerError(e) => Some(e),
263            _ => None,
264        }
265    }
266}
267
268impl Display for UnPackError {
269    fn fmt(&self, f: &mut Formatter) -> FmtResult {
270        match self {
271            Self::ReSync { skip } => write!(f, "ReSync Needed: skip {} bytes", skip),
272            Self::MissingHeader { skip } => write!(f, "Missing Header: skip {} bytes", skip),
273            Self::UnexpectedEnd { read } => write!(f, "Unexpected End: read {} bytes", read),
274            Self::InvalidChecksum { at } => write!(f, "Invalid Checksum: at {} bytes", at),
275            Self::MarshalerError(e) => write!(f, "Marshaler Error: {}", e),
276        }
277    }
278}
279impl StdError for UnPackError {
280    fn source(&self) -> Option<&(dyn StdError + 'static)> {
281        match self {
282            Self::MarshalerError(e) => Some(e),
283            _ => None,
284        }
285    }
286}