cbor_core/error.rs
1use std::{fmt, io};
2
3use crate::DataType;
4
5/// Errors produced by this crate.
6///
7/// Errors fall into three categories:
8///
9/// **Decoding errors** are returned when input cannot be parsed as a valid CBOR
10/// value, whether the input is binary, hex, or diagnostic notation. Produced by
11/// [`Value::decode`](crate::Value::decode),
12/// [`Value::decode_hex`](crate::Value::decode_hex),
13/// [`Value::read_from`](crate::Value::read_from),
14/// [`Value::read_hex_from`](crate::Value::read_hex_from),
15/// `Value::from_str` (via [`FromStr`](std::str::FromStr)),
16/// and the iterators returned by [`DecodeOptions`](crate::DecodeOptions):
17/// [`Malformed`](Self::Malformed), [`NonDeterministic`](Self::NonDeterministic),
18/// [`UnexpectedEof`](Self::UnexpectedEof), [`LengthTooLarge`](Self::LengthTooLarge),
19/// [`NestingTooDeep`](Self::NestingTooDeep),
20/// [`InvalidUtf8`](Self::InvalidUtf8), [`InvalidHex`](Self::InvalidHex),
21/// [`InvalidBase64`](Self::InvalidBase64), [`InvalidFormat`](Self::InvalidFormat).
22///
23/// [`Malformed`](Self::Malformed) and [`NonDeterministic`](Self::NonDeterministic)
24/// apply to binary and hex input; [`InvalidFormat`](Self::InvalidFormat) is the
25/// catch-all for diagnostic-notation syntax errors and also signals trailing
26/// data after a complete single-item decode.
27///
28/// **Accessor errors** are returned by the `to_*`, `as_*`, and `into_*`
29/// methods on [`Value`](crate::Value) when the value does not match the requested type:
30/// [`IncompatibleType`](Self::IncompatibleType), [`Overflow`](Self::Overflow),
31/// [`NegativeUnsigned`](Self::NegativeUnsigned), [`Precision`](Self::Precision),
32/// [`InvalidSimpleValue`](Self::InvalidSimpleValue).
33///
34/// **Validation errors** are returned during construction of typed helpers
35/// like [`DateTime`](crate::DateTime) and [`EpochTime`](crate::EpochTime):
36/// [`InvalidFormat`](Self::InvalidFormat) (reused from the decoding category)
37/// and [`InvalidValue`](Self::InvalidValue).
38///
39/// `Error` is `Copy`, `Eq`, `Ord`, and `Hash`, so it can be matched,
40/// compared, and used as a map key without allocation. I/O errors are
41/// handled separately by [`IoError`], which wraps either an `Error` or
42/// a [`std::io::Error`]. This separation keeps `Error` small and
43/// `Copy`-able while still supporting streaming operations that can
44/// fail with I/O problems.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
46#[non_exhaustive]
47pub enum Error {
48 // --- Decoding errors ---
49 //
50 /// Binary CBOR data is structurally broken.
51 Malformed,
52 /// CBOR encoding is valid but not deterministic (non-shortest form, unsorted map keys, etc.).
53 NonDeterministic,
54 /// Input ended before a complete data item was read.
55 UnexpectedEof,
56 /// Declared length exceeds addressable memory or reasonable size.
57 LengthTooLarge,
58 /// Nesting depth of arrays, maps, or tags exceeds the recursion limit.
59 NestingTooDeep,
60 /// Text string contains invalid UTF-8.
61 InvalidUtf8,
62 /// Hex input contains invalid characters.
63 InvalidHex,
64 /// Base64 input contains invalid characters.
65 InvalidBase64,
66
67 // --- Accessor errors ---
68 //
69 /// Accessor called on a value of the wrong CBOR type.
70 IncompatibleType(DataType),
71 /// Integer does not fit in the target type.
72 Overflow,
73 /// Attempted to read a negative integer as an unsigned type.
74 NegativeUnsigned,
75 /// Float conversion would lose precision.
76 Precision,
77 /// Simple value number is in the reserved range 24-31, or does
78 /// not match the requested target type.
79 InvalidSimpleValue,
80
81 // --- Validation errors ---
82 //
83 /// Textual input did not match the expected syntax. Used for
84 /// diagnostic-notation parse errors, trailing data after a
85 /// single-item decode, and invalid date/time strings.
86 InvalidFormat,
87 /// A value violates semantic constraints.
88 InvalidValue,
89}
90
91impl fmt::Display for Error {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 Self::Malformed => write!(f, "malformed CBOR encoding"),
95 Self::NonDeterministic => write!(f, "non-deterministic CBOR encoding"),
96 Self::UnexpectedEof => write!(f, "unexpected end of input"),
97 Self::LengthTooLarge => write!(f, "length exceeds reasonable size"),
98 Self::NestingTooDeep => write!(f, "nesting exceeds recursion limit"),
99 Self::InvalidUtf8 => write!(f, "invalid UTF-8 in text string"),
100 Self::InvalidHex => write!(f, "invalid hex character"),
101 Self::InvalidBase64 => write!(f, "invalid base64 character"),
102 Self::IncompatibleType(t) => write!(f, "incompatible CBOR type {name}", name = t.name()),
103 Self::Overflow => write!(f, "integer overflow"),
104 Self::NegativeUnsigned => write!(f, "negative value for unsigned type"),
105 Self::Precision => write!(f, "float precision loss"),
106 Self::InvalidSimpleValue => write!(f, "invalid CBOR simple value"),
107 Self::InvalidFormat => write!(f, "invalid syntax for expected format"),
108 Self::InvalidValue => write!(f, "invalid value"),
109 }
110 }
111}
112
113impl std::error::Error for Error {}
114
115/// Convenience alias used throughout this crate.
116pub type Result<T> = std::result::Result<T, Error>;
117
118impl From<std::string::FromUtf8Error> for Error {
119 fn from(_error: std::string::FromUtf8Error) -> Self {
120 Self::InvalidUtf8
121 }
122}
123
124impl From<std::str::Utf8Error> for Error {
125 fn from(_error: std::str::Utf8Error) -> Self {
126 Self::InvalidUtf8
127 }
128}
129
130impl<T> From<Error> for Result<T> {
131 fn from(error: Error) -> Self {
132 Err(error)
133 }
134}
135
136/// Error type for I/O-related operations.
137///
138/// For streaming CBOR operations that may fail with either
139/// an I/O error or a data-level [`Error`].
140#[derive(Debug)]
141pub enum IoError {
142 /// Underlying I/O error from the reader or writer.
143 Io(io::Error),
144 /// CBOR-level error (malformed data, non-deterministic encoding, etc.).
145 Data(Error),
146}
147
148impl fmt::Display for IoError {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 match self {
151 Self::Io(e) => write!(f, "I/O error: {e}"),
152 Self::Data(e) => fmt::Display::fmt(e, f),
153 }
154 }
155}
156
157impl std::error::Error for IoError {
158 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
159 match self {
160 Self::Io(e) => Some(e),
161 Self::Data(e) => Some(e),
162 }
163 }
164}
165
166impl From<io::Error> for IoError {
167 fn from(error: io::Error) -> Self {
168 match error.kind() {
169 io::ErrorKind::UnexpectedEof => Error::UnexpectedEof.into(),
170 _other => Self::Io(error),
171 }
172 }
173}
174
175impl<E: Into<Error>> From<E> for IoError {
176 fn from(error: E) -> Self {
177 Self::Data(error.into())
178 }
179}
180
181impl<T> From<Error> for IoResult<T> {
182 fn from(error: Error) -> Self {
183 Err(IoError::Data(error))
184 }
185}
186
187/// Convenience alias for streaming CBOR operations.
188pub type IoResult<T> = std::result::Result<T, IoError>;
189
190pub(crate) trait WithEof {
191 fn is_eof(&self) -> bool;
192}
193
194impl WithEof for Error {
195 fn is_eof(&self) -> bool {
196 matches!(self, Error::UnexpectedEof)
197 }
198}
199
200impl WithEof for IoError {
201 fn is_eof(&self) -> bool {
202 matches!(self, IoError::Data(Error::UnexpectedEof))
203 }
204}