Skip to main content

cyclone_runtime_rust/
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 {}