1 //! When serializing or deserializing CBOR goes wrong. 2 use core::fmt; 3 use core::result; 4 use serde::de; 5 use serde::ser; 6 #[cfg(feature = "std")] 7 use std::error; 8 #[cfg(feature = "std")] 9 use std::io; 10 11 /// This type represents all possible errors that can occur when serializing or deserializing CBOR 12 /// data. 13 pub struct Error(ErrorImpl); 14 15 /// Alias for a `Result` with the error type `serde_cbor::Error`. 16 pub type Result<T> = result::Result<T, Error>; 17 18 /// Categorizes the cause of a `serde_cbor::Error`. 19 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 20 pub enum Category { 21 /// The error was caused by a failure to read or write bytes on an IO stream. 22 Io, 23 /// The error was caused by input that was not syntactically valid CBOR. 24 Syntax, 25 /// The error was caused by input data that was semantically incorrect. 26 Data, 27 /// The error was caused by prematurely reaching the end of the input data. 28 Eof, 29 } 30 31 impl Error { 32 /// The byte offset at which the error occurred. offset(&self) -> u6433 pub fn offset(&self) -> u64 { 34 self.0.offset 35 } 36 syntax(code: ErrorCode, offset: u64) -> Error37 pub(crate) fn syntax(code: ErrorCode, offset: u64) -> Error { 38 Error(ErrorImpl { code, offset }) 39 } 40 41 #[cfg(feature = "std")] io(error: io::Error) -> Error42 pub(crate) fn io(error: io::Error) -> Error { 43 Error(ErrorImpl { 44 code: ErrorCode::Io(error), 45 offset: 0, 46 }) 47 } 48 49 #[cfg(all(not(feature = "std"), feature = "unsealed_read_write"))] 50 /// Creates an error signalling that the underlying `Read` encountered an I/O error. io() -> Error51 pub fn io() -> Error { 52 Error(ErrorImpl { 53 code: ErrorCode::Io, 54 offset: 0, 55 }) 56 } 57 58 #[cfg(feature = "unsealed_read_write")] 59 /// Creates an error signalling that the scratch buffer was too small to fit the data. scratch_too_small(offset: u64) -> Error60 pub fn scratch_too_small(offset: u64) -> Error { 61 Error(ErrorImpl { 62 code: ErrorCode::ScratchTooSmall, 63 offset, 64 }) 65 } 66 67 #[cfg(not(feature = "unsealed_read_write"))] scratch_too_small(offset: u64) -> Error68 pub(crate) fn scratch_too_small(offset: u64) -> Error { 69 Error(ErrorImpl { 70 code: ErrorCode::ScratchTooSmall, 71 offset, 72 }) 73 } 74 75 #[cfg(feature = "unsealed_read_write")] 76 /// Creates an error with a custom message. 77 /// 78 /// **Note**: When the "std" feature is disabled, the message will be discarded. message<T: fmt::Display>(_msg: T) -> Error79 pub fn message<T: fmt::Display>(_msg: T) -> Error { 80 #[cfg(not(feature = "std"))] 81 { 82 Error(ErrorImpl { 83 code: ErrorCode::Message, 84 offset: 0, 85 }) 86 } 87 #[cfg(feature = "std")] 88 { 89 Error(ErrorImpl { 90 code: ErrorCode::Message(_msg.to_string()), 91 offset: 0, 92 }) 93 } 94 } 95 96 #[cfg(not(feature = "unsealed_read_write"))] message<T: fmt::Display>(_msg: T) -> Error97 pub(crate) fn message<T: fmt::Display>(_msg: T) -> Error { 98 #[cfg(not(feature = "std"))] 99 { 100 Error(ErrorImpl { 101 code: ErrorCode::Message, 102 offset: 0, 103 }) 104 } 105 #[cfg(feature = "std")] 106 { 107 Error(ErrorImpl { 108 code: ErrorCode::Message(_msg.to_string()), 109 offset: 0, 110 }) 111 } 112 } 113 114 #[cfg(feature = "unsealed_read_write")] 115 /// Creates an error signalling that the underlying read 116 /// encountered an end of input. eof(offset: u64) -> Error117 pub fn eof(offset: u64) -> Error { 118 Error(ErrorImpl { 119 code: ErrorCode::EofWhileParsingValue, 120 offset, 121 }) 122 } 123 124 /// Categorizes the cause of this error. classify(&self) -> Category125 pub fn classify(&self) -> Category { 126 match self.0.code { 127 #[cfg(feature = "std")] 128 ErrorCode::Message(_) => Category::Data, 129 #[cfg(not(feature = "std"))] 130 ErrorCode::Message => Category::Data, 131 #[cfg(feature = "std")] 132 ErrorCode::Io(_) => Category::Io, 133 #[cfg(not(feature = "std"))] 134 ErrorCode::Io => Category::Io, 135 ErrorCode::ScratchTooSmall => Category::Io, 136 ErrorCode::EofWhileParsingValue 137 | ErrorCode::EofWhileParsingArray 138 | ErrorCode::EofWhileParsingMap => Category::Eof, 139 ErrorCode::LengthOutOfRange 140 | ErrorCode::InvalidUtf8 141 | ErrorCode::UnassignedCode 142 | ErrorCode::UnexpectedCode 143 | ErrorCode::TrailingData 144 | ErrorCode::ArrayTooShort 145 | ErrorCode::ArrayTooLong 146 | ErrorCode::RecursionLimitExceeded 147 | ErrorCode::WrongEnumFormat 148 | ErrorCode::WrongStructFormat => Category::Syntax, 149 } 150 } 151 152 /// Returns true if this error was caused by a failure to read or write bytes on an IO stream. is_io(&self) -> bool153 pub fn is_io(&self) -> bool { 154 match self.classify() { 155 Category::Io => true, 156 _ => false, 157 } 158 } 159 160 /// Returns true if this error was caused by input that was not syntactically valid CBOR. is_syntax(&self) -> bool161 pub fn is_syntax(&self) -> bool { 162 match self.classify() { 163 Category::Syntax => true, 164 _ => false, 165 } 166 } 167 168 /// Returns true if this error was caused by data that was semantically incorrect. is_data(&self) -> bool169 pub fn is_data(&self) -> bool { 170 match self.classify() { 171 Category::Data => true, 172 _ => false, 173 } 174 } 175 176 /// Returns true if this error was caused by prematurely reaching the end of the input data. is_eof(&self) -> bool177 pub fn is_eof(&self) -> bool { 178 match self.classify() { 179 Category::Eof => true, 180 _ => false, 181 } 182 } 183 184 /// Returns true if this error was caused by the scratch buffer being too small. 185 /// 186 /// Note this being `true` implies that `is_io()` is also `true`. is_scratch_too_small(&self) -> bool187 pub fn is_scratch_too_small(&self) -> bool { 188 match self.0.code { 189 ErrorCode::ScratchTooSmall => true, 190 _ => false, 191 } 192 } 193 } 194 195 #[cfg(feature = "std")] 196 impl error::Error for Error { source(&self) -> Option<&(dyn error::Error + 'static)>197 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 198 match self.0.code { 199 ErrorCode::Io(ref err) => Some(err), 200 _ => None, 201 } 202 } 203 } 204 205 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 207 if self.0.offset == 0 { 208 fmt::Display::fmt(&self.0.code, f) 209 } else { 210 write!(f, "{} at offset {}", self.0.code, self.0.offset) 211 } 212 } 213 } 214 215 impl fmt::Debug for Error { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result216 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 217 fmt::Debug::fmt(&self.0, fmt) 218 } 219 } 220 221 impl de::Error for Error { custom<T: fmt::Display>(msg: T) -> Error222 fn custom<T: fmt::Display>(msg: T) -> Error { 223 Error::message(msg) 224 } 225 invalid_type(unexp: de::Unexpected<'_>, exp: &dyn de::Expected) -> Error226 fn invalid_type(unexp: de::Unexpected<'_>, exp: &dyn de::Expected) -> Error { 227 if let de::Unexpected::Unit = unexp { 228 Error::custom(format_args!("invalid type: null, expected {}", exp)) 229 } else { 230 Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp)) 231 } 232 } 233 } 234 235 impl ser::Error for Error { custom<T: fmt::Display>(msg: T) -> Error236 fn custom<T: fmt::Display>(msg: T) -> Error { 237 Error::message(msg) 238 } 239 } 240 241 #[cfg(feature = "std")] 242 impl From<io::Error> for Error { from(e: io::Error) -> Error243 fn from(e: io::Error) -> Error { 244 Error::io(e) 245 } 246 } 247 248 #[cfg(not(feature = "std"))] 249 impl From<core::fmt::Error> for Error { from(_: core::fmt::Error) -> Error250 fn from(_: core::fmt::Error) -> Error { 251 Error(ErrorImpl { 252 code: ErrorCode::Message, 253 offset: 0, 254 }) 255 } 256 } 257 258 #[derive(Debug)] 259 struct ErrorImpl { 260 code: ErrorCode, 261 offset: u64, 262 } 263 264 #[derive(Debug)] 265 pub(crate) enum ErrorCode { 266 #[cfg(feature = "std")] 267 Message(String), 268 #[cfg(not(feature = "std"))] 269 Message, 270 #[cfg(feature = "std")] 271 Io(io::Error), 272 #[allow(unused)] 273 #[cfg(not(feature = "std"))] 274 Io, 275 ScratchTooSmall, 276 EofWhileParsingValue, 277 EofWhileParsingArray, 278 EofWhileParsingMap, 279 LengthOutOfRange, 280 InvalidUtf8, 281 UnassignedCode, 282 UnexpectedCode, 283 TrailingData, 284 ArrayTooShort, 285 ArrayTooLong, 286 RecursionLimitExceeded, 287 WrongEnumFormat, 288 WrongStructFormat, 289 } 290 291 impl fmt::Display for ErrorCode { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 293 match *self { 294 #[cfg(feature = "std")] 295 ErrorCode::Message(ref msg) => f.write_str(msg), 296 #[cfg(not(feature = "std"))] 297 ErrorCode::Message => f.write_str("Unknown error"), 298 #[cfg(feature = "std")] 299 ErrorCode::Io(ref err) => fmt::Display::fmt(err, f), 300 #[cfg(not(feature = "std"))] 301 ErrorCode::Io => f.write_str("Unknown I/O error"), 302 ErrorCode::ScratchTooSmall => f.write_str("Scratch buffer too small"), 303 ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"), 304 ErrorCode::EofWhileParsingArray => f.write_str("EOF while parsing an array"), 305 ErrorCode::EofWhileParsingMap => f.write_str("EOF while parsing a map"), 306 ErrorCode::LengthOutOfRange => f.write_str("length out of range"), 307 ErrorCode::InvalidUtf8 => f.write_str("invalid UTF-8"), 308 ErrorCode::UnassignedCode => f.write_str("unassigned type"), 309 ErrorCode::UnexpectedCode => f.write_str("unexpected code"), 310 ErrorCode::TrailingData => f.write_str("trailing data"), 311 ErrorCode::ArrayTooShort => f.write_str("array too short"), 312 ErrorCode::ArrayTooLong => f.write_str("array too long"), 313 ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"), 314 ErrorCode::WrongEnumFormat => f.write_str("wrong enum format"), 315 ErrorCode::WrongStructFormat => f.write_str("wrong struct format"), 316 } 317 } 318 } 319