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}