cyclone_runtime/error.rs
1//! Decode errors.
2//!
3//! Every failure mode of [`Reader`](crate::Reader) is one of the variants of
4//! [`DecodeError`]. The runtime never panics on malformed input: a byte stream
5//! that does not satisfy the Specification produces an `Err`, not an abort.
6
7use core::fmt;
8
9/// A byte stream that does not satisfy the Cyclone Specification.
10///
11/// The Specification (RFC-0002 §10) names the error conditions but not the way
12/// an implementation reports them. This runtime reports them as values.
13///
14/// | Variant | Condition on the byte stream |
15/// |---------|------------------------------|
16/// | [`UnexpectedEof`](Self::UnexpectedEof) | fewer bytes remain than the value requires |
17/// | [`InvalidBool`](Self::InvalidBool) | a `bool` byte that is neither `0x00` nor `0x01` |
18/// | [`InvalidUtf8`](Self::InvalidUtf8) | a `String` byte region that is not valid UTF-8 |
19/// | [`LengthOverflow`](Self::LengthOverflow) | a length/count field beyond the configured limit |
20///
21/// `InvalidEnum` is deliberately absent: the set of valid values of an enum is
22/// schema knowledge, so validating it belongs to the generated codec, not to a
23/// runtime that knows nothing about schemas.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum DecodeError {
26 /// Fewer bytes remain in the buffer than the value being read requires.
27 UnexpectedEof {
28 /// Number of bytes the read needed.
29 needed: usize,
30 /// Number of bytes actually left in the buffer.
31 remaining: usize,
32 },
33
34 /// A `bool` was encoded as a byte other than `0x00` or `0x01`.
35 ///
36 /// A conforming decoder MUST NOT read this as "non-zero means true"
37 /// (RFC-0002 §2.4). The offending byte is carried for diagnostics.
38 InvalidBool(u8),
39
40 /// The byte region of a `String` is not valid UTF-8 (RFC-0002 §3).
41 InvalidUtf8,
42
43 /// A length or count field exceeded the configured [`Limits`](crate::Limits).
44 ///
45 /// This is the configurable, non-normative guard of RFC-0002 §12. The
46 /// normative check - a length larger than the bytes actually remaining -
47 /// surfaces as [`UnexpectedEof`](Self::UnexpectedEof); both are applied,
48 /// and both reject before any memory is allocated.
49 LengthOverflow {
50 /// The length or count read from the byte stream.
51 length: usize,
52 /// The configured limit it exceeded.
53 limit: usize,
54 },
55}
56
57impl fmt::Display for DecodeError {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 match *self {
60 DecodeError::UnexpectedEof { needed, remaining } => {
61 write!(f, "unexpected eof: needed {needed} bytes, {remaining} remaining")
62 }
63 DecodeError::InvalidBool(byte) => {
64 write!(f, "invalid bool: 0x{byte:02X} is neither 0x00 nor 0x01")
65 }
66 DecodeError::InvalidUtf8 => f.write_str("invalid utf-8 in string"),
67 DecodeError::LengthOverflow { length, limit } => {
68 write!(f, "length overflow: length {length} exceeds limit {limit}")
69 }
70 }
71 }
72}
73
74impl std::error::Error for DecodeError {}