Skip to main content

sacp_cbor/
error.rs

1use core::fmt;
2
3/// A structured error code identifying the reason a CBOR item was rejected.
4///
5/// This enum is intentionally stable and string-free to support `no_std` and to remain hot-path friendly.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7#[non_exhaustive]
8pub enum ErrorCode {
9    /// Invalid configured limits.
10    InvalidLimits,
11
12    /// Unexpected end-of-input while decoding.
13    UnexpectedEof,
14    /// Arithmetic overflow while computing a length/offset.
15    LengthOverflow,
16    /// Input contains trailing bytes after the single CBOR data item.
17    TrailingBytes,
18    /// Memory allocation failed while decoding into owned structures.
19    AllocationFailed,
20    /// Array builder length mismatch (encoder).
21    ArrayLenMismatch,
22    /// Map builder length mismatch (encoder).
23    MapLenMismatch,
24
25    /// Nesting depth limit exceeded.
26    DepthLimitExceeded,
27    /// Total items limit exceeded.
28    TotalItemsLimitExceeded,
29    /// Array length exceeds limits.
30    ArrayLenLimitExceeded,
31    /// Map length exceeds limits.
32    MapLenLimitExceeded,
33    /// Byte string length exceeds limits.
34    BytesLenLimitExceeded,
35    /// Text string length exceeds limits.
36    TextLenLimitExceeded,
37    /// Total input length exceeds limits.
38    MessageLenLimitExceeded,
39
40    /// Reserved additional-info value (28..30) was used.
41    ReservedAdditionalInfo,
42    /// Indefinite-length encoding was used where forbidden.
43    IndefiniteLengthForbidden,
44    /// Non-canonical (non-shortest) integer/length encoding was used.
45    NonCanonicalEncoding,
46
47    /// Map key was not a CBOR text string.
48    MapKeyMustBeText,
49    /// Duplicate map key detected.
50    DuplicateMapKey,
51    /// Map keys are not in canonical order.
52    NonCanonicalMapOrder,
53    /// Set elements are not in strict canonical set order.
54    NonCanonicalSetOrder,
55
56    /// A forbidden tag was used, or the tag structure is malformed.
57    ForbiddenOrMalformedTag,
58    /// Bignum magnitude is not canonical (empty or leading zero).
59    BignumNotCanonical,
60    /// Bignum was used for a value within the safe integer range.
61    BignumMustBeOutsideSafeRange,
62
63    /// Unsupported CBOR simple value.
64    UnsupportedSimpleValue,
65    /// Integer outside the `int_safe` range.
66    IntegerOutsideSafeRange,
67
68    /// Invalid UTF-8 in a text string.
69    Utf8Invalid,
70
71    /// Float64 negative zero encoding is forbidden.
72    NegativeZeroForbidden,
73    /// Float64 NaN encoding is not canonical.
74    NonCanonicalNaN,
75
76    /// Serde conversion failed.
77    SerdeError,
78
79    /// Expected a map at the current location.
80    ExpectedMap,
81    /// Expected an array at the current location.
82    ExpectedArray,
83    /// Expected an integer (safe or bignum) at the current location.
84    ExpectedInteger,
85    /// Expected a text string at the current location.
86    ExpectedText,
87    /// Expected a byte string at the current location.
88    ExpectedBytes,
89    /// Expected a boolean at the current location.
90    ExpectedBool,
91    /// Expected a null at the current location.
92    ExpectedNull,
93    /// Expected a float64 at the current location.
94    ExpectedFloat,
95    /// Expected a CBOR value matching an untagged enum variant.
96    ExpectedEnum,
97    /// Unknown enum variant key.
98    UnknownEnumVariant,
99
100    /// Patch operations overlap or conflict.
101    PatchConflict,
102    /// Array index is out of bounds.
103    IndexOutOfBounds,
104
105    /// Invalid query arguments (e.g., output slice length mismatch).
106    InvalidQuery,
107    /// Required key missing from map.
108    MissingKey,
109    /// Malformed canonical CBOR during query traversal.
110    MalformedCanonical,
111}
112
113/// An SACP-CBOR/1 error with structured classification, a stable code, and a byte offset.
114///
115/// Offsets refer to the byte position where the error was detected.
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub struct CborError {
118    /// The error code.
119    pub code: ErrorCode,
120    /// Byte offset into the input where the error was detected.
121    pub offset: usize,
122}
123
124impl CborError {
125    /// Construct a decode error at `offset`.
126    #[inline]
127    #[must_use]
128    pub const fn new(code: ErrorCode, offset: usize) -> Self {
129        Self { code, offset }
130    }
131}
132
133impl fmt::Display for CborError {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        let msg = match self.code {
136            ErrorCode::InvalidLimits => "invalid CBOR limits",
137
138            ErrorCode::UnexpectedEof => "unexpected end of input",
139            ErrorCode::LengthOverflow => "length overflow",
140            ErrorCode::TrailingBytes => "trailing bytes after single CBOR item",
141            ErrorCode::AllocationFailed => "allocation failed",
142            ErrorCode::ArrayLenMismatch => "array length mismatch",
143            ErrorCode::MapLenMismatch => "map length mismatch",
144
145            ErrorCode::DepthLimitExceeded => "nesting depth limit exceeded",
146            ErrorCode::TotalItemsLimitExceeded => "total items limit exceeded",
147            ErrorCode::ArrayLenLimitExceeded => "array length exceeds decode limits",
148            ErrorCode::MapLenLimitExceeded => "map length exceeds decode limits",
149            ErrorCode::BytesLenLimitExceeded => "byte string length exceeds decode limits",
150            ErrorCode::TextLenLimitExceeded => "text string length exceeds decode limits",
151            ErrorCode::MessageLenLimitExceeded => "input length exceeds decode limits",
152
153            ErrorCode::ReservedAdditionalInfo => "reserved additional info value",
154            ErrorCode::IndefiniteLengthForbidden => "indefinite length forbidden",
155            ErrorCode::NonCanonicalEncoding => "non-canonical integer/length encoding",
156
157            ErrorCode::MapKeyMustBeText => "map keys must be text strings",
158            ErrorCode::DuplicateMapKey => "duplicate map key",
159            ErrorCode::NonCanonicalMapOrder => "non-canonical map key order",
160            ErrorCode::NonCanonicalSetOrder => "non-canonical set element order",
161
162            ErrorCode::ForbiddenOrMalformedTag => "forbidden or malformed CBOR tag",
163            ErrorCode::BignumNotCanonical => {
164                "bignum magnitude must be canonical (non-empty, no leading zero)"
165            }
166            ErrorCode::BignumMustBeOutsideSafeRange => "bignum must be outside int_safe range",
167
168            ErrorCode::UnsupportedSimpleValue => "unsupported CBOR simple value",
169            ErrorCode::IntegerOutsideSafeRange => "integer outside int_safe range",
170
171            ErrorCode::Utf8Invalid => "text must be valid UTF-8",
172
173            ErrorCode::NegativeZeroForbidden => "negative zero forbidden",
174            ErrorCode::NonCanonicalNaN => "non-canonical NaN encoding",
175            ErrorCode::SerdeError => "serde conversion failed",
176
177            ErrorCode::ExpectedMap => "expected CBOR map",
178            ErrorCode::ExpectedArray => "expected CBOR array",
179            ErrorCode::ExpectedInteger => "expected CBOR integer",
180            ErrorCode::ExpectedText => "expected CBOR text string",
181            ErrorCode::ExpectedBytes => "expected CBOR byte string",
182            ErrorCode::ExpectedBool => "expected CBOR bool",
183            ErrorCode::ExpectedNull => "expected CBOR null",
184            ErrorCode::ExpectedFloat => "expected CBOR float64",
185            ErrorCode::ExpectedEnum => "expected CBOR enum value",
186            ErrorCode::UnknownEnumVariant => "unknown CBOR enum variant",
187            ErrorCode::PatchConflict => "patch operations conflict",
188            ErrorCode::IndexOutOfBounds => "array index out of bounds",
189            ErrorCode::InvalidQuery => "invalid query arguments",
190            ErrorCode::MissingKey => "missing required map key",
191            ErrorCode::MalformedCanonical => "malformed canonical CBOR",
192        };
193
194        write!(f, "cbor error at {}: {msg}", self.offset)
195    }
196}
197
198#[cfg(feature = "std")]
199impl std::error::Error for CborError {}