1use crate::io;
4use alloc::boxed::Box;
5use alloc::string::{String, ToString};
6use core::fmt::{self, Debug, Display};
7use core::result;
8use core::str::FromStr;
9use serde::{de, ser};
10#[cfg(feature = "std")]
11use std::error;
12#[cfg(feature = "std")]
13use std::io::ErrorKind;
14
15pub struct Error {
18 err: Box<ErrorImpl>,
22}
23
24pub type Result<T> = result::Result<T, Error>;
26
27impl Error {
28 pub fn line(&self) -> usize {
33 self.err.line
34 }
35
36 pub fn column(&self) -> usize {
45 self.err.column
46 }
47
48 pub fn classify(&self) -> Category {
55 match self.err.code {
56 ErrorCode::Message(_) => Category::Data,
57 ErrorCode::Io(_) => Category::Io,
58 ErrorCode::EofWhileParsingList
59 | ErrorCode::EofWhileParsingObject
60 | ErrorCode::EofWhileParsingString
61 | ErrorCode::EofWhileParsingValue => Category::Eof,
62 ErrorCode::ExpectedColon
63 | ErrorCode::ExpectedListCommaOrEnd
64 | ErrorCode::ExpectedObjectCommaOrEnd
65 | ErrorCode::ExpectedSomeIdent
66 | ErrorCode::ExpectedSomeValue
67 | ErrorCode::ExpectedDoubleQuote
68 | ErrorCode::InvalidEscape
69 | ErrorCode::InvalidNumber
70 | ErrorCode::NumberOutOfRange
71 | ErrorCode::InvalidUnicodeCodePoint
72 | ErrorCode::ControlCharacterWhileParsingString
73 | ErrorCode::KeyMustBeAString
74 | ErrorCode::ExpectedNumericKey
75 | ErrorCode::FloatKeyMustBeFinite
76 | ErrorCode::LoneLeadingSurrogateInHexEscape
77 | ErrorCode::TrailingComma
78 | ErrorCode::TrailingCharacters
79 | ErrorCode::UnexpectedEndOfHexEscape
80 | ErrorCode::RecursionLimitExceeded => Category::Syntax,
81 }
82 }
83
84 pub fn is_io(&self) -> bool {
87 self.classify() == Category::Io
88 }
89
90 pub fn is_syntax(&self) -> bool {
93 self.classify() == Category::Syntax
94 }
95
96 pub fn is_data(&self) -> bool {
102 self.classify() == Category::Data
103 }
104
105 pub fn is_eof(&self) -> bool {
111 self.classify() == Category::Eof
112 }
113
114 #[cfg(feature = "std")]
155 pub fn io_error_kind(&self) -> Option<ErrorKind> {
156 if let ErrorCode::Io(io_error) = &self.err.code {
157 Some(io_error.kind())
158 } else {
159 None
160 }
161 }
162}
163
164#[derive(Copy, Clone, PartialEq, Eq, Debug)]
166pub enum Category {
167 Io,
170
171 Syntax,
173
174 Data,
179
180 Eof,
185}
186
187#[cfg(feature = "std")]
188#[allow(clippy::fallible_impl_from)]
189impl From<Error> for io::Error {
190 fn from(j: Error) -> Self {
218 if let ErrorCode::Io(err) = j.err.code {
219 err
220 } else {
221 match j.classify() {
222 Category::Io => unreachable!(),
223 Category::Syntax | Category::Data => io::Error::new(ErrorKind::InvalidData, j),
224 Category::Eof => io::Error::new(ErrorKind::UnexpectedEof, j),
225 }
226 }
227 }
228}
229
230struct ErrorImpl {
231 code: ErrorCode,
232 line: usize,
233 column: usize,
234}
235
236pub(crate) enum ErrorCode {
237 Message(Box<str>),
239
240 Io(io::Error),
242
243 EofWhileParsingList,
245
246 EofWhileParsingObject,
248
249 EofWhileParsingString,
251
252 EofWhileParsingValue,
254
255 ExpectedColon,
257
258 ExpectedListCommaOrEnd,
260
261 ExpectedObjectCommaOrEnd,
263
264 ExpectedSomeIdent,
266
267 ExpectedSomeValue,
269
270 ExpectedDoubleQuote,
272
273 InvalidEscape,
275
276 InvalidNumber,
278
279 NumberOutOfRange,
281
282 InvalidUnicodeCodePoint,
284
285 ControlCharacterWhileParsingString,
287
288 KeyMustBeAString,
290
291 ExpectedNumericKey,
293
294 FloatKeyMustBeFinite,
296
297 LoneLeadingSurrogateInHexEscape,
299
300 TrailingComma,
302
303 TrailingCharacters,
305
306 UnexpectedEndOfHexEscape,
308
309 RecursionLimitExceeded,
311}
312
313impl Error {
314 #[cold]
315 pub(crate) fn syntax(code: ErrorCode, line: usize, column: usize) -> Self {
316 Error {
317 err: Box::new(ErrorImpl { code, line, column }),
318 }
319 }
320
321 #[doc(hidden)]
325 #[cold]
326 pub fn io(error: io::Error) -> Self {
327 Error {
328 err: Box::new(ErrorImpl {
329 code: ErrorCode::Io(error),
330 line: 0,
331 column: 0,
332 }),
333 }
334 }
335
336 #[cold]
337 pub(crate) fn fix_position<F>(self, f: F) -> Self
338 where
339 F: FnOnce(ErrorCode) -> Error,
340 {
341 if self.err.line == 0 {
342 f(self.err.code)
343 } else {
344 self
345 }
346 }
347}
348
349impl Display for ErrorCode {
350 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351 match self {
352 ErrorCode::Message(msg) => f.write_str(msg),
353 ErrorCode::Io(err) => Display::fmt(err, f),
354 ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"),
355 ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"),
356 ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
357 ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
358 ErrorCode::ExpectedColon => f.write_str("expected `:`"),
359 ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
360 ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
361 ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
362 ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
363 ErrorCode::ExpectedDoubleQuote => f.write_str("expected `\"`"),
364 ErrorCode::InvalidEscape => f.write_str("invalid escape"),
365 ErrorCode::InvalidNumber => f.write_str("invalid number"),
366 ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
367 ErrorCode::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"),
368 ErrorCode::ControlCharacterWhileParsingString => {
369 f.write_str("control character (\\u0000-\\u001F) found while parsing a string")
370 }
371 ErrorCode::KeyMustBeAString => f.write_str("key must be a string"),
372 ErrorCode::ExpectedNumericKey => {
373 f.write_str("invalid value: expected key to be a number in quotes")
374 }
375 ErrorCode::FloatKeyMustBeFinite => {
376 f.write_str("float key must be finite (got NaN or +/-inf)")
377 }
378 ErrorCode::LoneLeadingSurrogateInHexEscape => {
379 f.write_str("lone leading surrogate in hex escape")
380 }
381 ErrorCode::TrailingComma => f.write_str("trailing comma"),
382 ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
383 ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"),
384 ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
385 }
386 }
387}
388
389impl serde::de::StdError for Error {
390 #[cfg(feature = "std")]
391 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
392 match &self.err.code {
393 ErrorCode::Io(err) => err.source(),
394 _ => None,
395 }
396 }
397}
398
399impl Display for Error {
400 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401 Display::fmt(&*self.err, f)
402 }
403}
404
405impl Display for ErrorImpl {
406 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
407 if self.line == 0 {
408 Display::fmt(&self.code, f)
409 } else {
410 write!(
411 f,
412 "{} at line {} column {}",
413 self.code, self.line, self.column
414 )
415 }
416 }
417}
418
419impl Debug for Error {
422 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
423 write!(
424 f,
425 "Error({:?}, line: {}, column: {})",
426 self.err.code.to_string(),
427 self.err.line,
428 self.err.column
429 )
430 }
431}
432
433impl de::Error for Error {
434 #[cold]
435 fn custom<T: Display>(msg: T) -> Error {
436 make_error(msg.to_string())
437 }
438
439 #[cold]
440 fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
441 Error::custom(format_args!(
442 "invalid type: {}, expected {}",
443 JsonUnexpected(unexp),
444 exp,
445 ))
446 }
447
448 #[cold]
449 fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
450 Error::custom(format_args!(
451 "invalid value: {}, expected {}",
452 JsonUnexpected(unexp),
453 exp,
454 ))
455 }
456}
457
458impl ser::Error for Error {
459 #[cold]
460 fn custom<T: Display>(msg: T) -> Error {
461 make_error(msg.to_string())
462 }
463}
464
465struct JsonUnexpected<'a>(de::Unexpected<'a>);
466
467impl<'a> Display for JsonUnexpected<'a> {
468 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
469 match self.0 {
470 de::Unexpected::Unit => formatter.write_str("null"),
471 de::Unexpected::Float(value) => write!(
472 formatter,
473 "floating point `{}`",
474 ryu::Buffer::new().format(value),
475 ),
476 unexp => Display::fmt(&unexp, formatter),
477 }
478 }
479}
480
481fn make_error(mut msg: String) -> Error {
484 let (line, column) = parse_line_col(&mut msg).unwrap_or((0, 0));
485 Error {
486 err: Box::new(ErrorImpl {
487 code: ErrorCode::Message(msg.into_boxed_str()),
488 line,
489 column,
490 }),
491 }
492}
493
494fn parse_line_col(msg: &mut String) -> Option<(usize, usize)> {
495 let start_of_suffix = match msg.rfind(" at line ") {
496 Some(index) => index,
497 None => return None,
498 };
499
500 let start_of_line = start_of_suffix + " at line ".len();
502 let mut end_of_line = start_of_line;
503 while starts_with_digit(&msg[end_of_line..]) {
504 end_of_line += 1;
505 }
506
507 if !msg[end_of_line..].starts_with(" column ") {
508 return None;
509 }
510
511 let start_of_column = end_of_line + " column ".len();
513 let mut end_of_column = start_of_column;
514 while starts_with_digit(&msg[end_of_column..]) {
515 end_of_column += 1;
516 }
517
518 if end_of_column < msg.len() {
519 return None;
520 }
521
522 let line = match usize::from_str(&msg[start_of_line..end_of_line]) {
524 Ok(line) => line,
525 Err(_) => return None,
526 };
527 let column = match usize::from_str(&msg[start_of_column..end_of_column]) {
528 Ok(column) => column,
529 Err(_) => return None,
530 };
531
532 msg.truncate(start_of_suffix);
533 Some((line, column))
534}
535
536fn starts_with_digit(slice: &str) -> bool {
537 match slice.as_bytes().first() {
538 None => false,
539 Some(&byte) => byte >= b'0' && byte <= b'9',
540 }
541}