Coverage Report

Created: 2025-12-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/json/src/error.rs
Line
Count
Source
1
//! When serializing or deserializing JSON goes wrong.
2
3
use crate::io;
4
use alloc::boxed::Box;
5
use alloc::string::{String, ToString};
6
use core::fmt::{self, Debug, Display};
7
use core::result;
8
use core::str::FromStr;
9
use serde::{de, ser};
10
#[cfg(feature = "std")]
11
use std::error;
12
#[cfg(feature = "std")]
13
use std::io::ErrorKind;
14
15
/// This type represents all possible errors that can occur when serializing or
16
/// deserializing JSON data.
17
pub struct Error {
18
    /// This `Box` allows us to keep the size of `Error` as small as possible. A
19
    /// larger `Error` type was substantially slower due to all the functions
20
    /// that pass around `Result<T, Error>`.
21
    err: Box<ErrorImpl>,
22
}
23
24
/// Alias for a `Result` with the error type `serde_json::Error`.
25
pub type Result<T> = result::Result<T, Error>;
26
27
impl Error {
28
    /// One-based line number at which the error was detected.
29
    ///
30
    /// Characters in the first line of the input (before the first newline
31
    /// character) are in line 1.
32
0
    pub fn line(&self) -> usize {
33
0
        self.err.line
34
0
    }
35
36
    /// One-based column number at which the error was detected.
37
    ///
38
    /// The first character in the input and any characters immediately
39
    /// following a newline character are in column 1.
40
    ///
41
    /// Note that errors may occur in column 0, for example if a read from an
42
    /// I/O stream fails immediately following a previously read newline
43
    /// character.
44
0
    pub fn column(&self) -> usize {
45
0
        self.err.column
46
0
    }
47
48
    /// Categorizes the cause of this error.
49
    ///
50
    /// - `Category::Io` - failure to read or write bytes on an I/O stream
51
    /// - `Category::Syntax` - input that is not syntactically valid JSON
52
    /// - `Category::Data` - input data that is semantically incorrect
53
    /// - `Category::Eof` - unexpected end of the input data
54
0
    pub fn classify(&self) -> Category {
55
0
        match self.err.code {
56
0
            ErrorCode::Message(_) => Category::Data,
57
0
            ErrorCode::Io(_) => Category::Io,
58
            ErrorCode::EofWhileParsingList
59
            | ErrorCode::EofWhileParsingObject
60
            | ErrorCode::EofWhileParsingString
61
0
            | 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
0
            | ErrorCode::RecursionLimitExceeded => Category::Syntax,
81
        }
82
0
    }
83
84
    /// Returns true if this error was caused by a failure to read or write
85
    /// bytes on an I/O stream.
86
0
    pub fn is_io(&self) -> bool {
87
0
        self.classify() == Category::Io
88
0
    }
89
90
    /// Returns true if this error was caused by input that was not
91
    /// syntactically valid JSON.
92
0
    pub fn is_syntax(&self) -> bool {
93
0
        self.classify() == Category::Syntax
94
0
    }
95
96
    /// Returns true if this error was caused by input data that was
97
    /// semantically incorrect.
98
    ///
99
    /// For example, JSON containing a number is semantically incorrect when the
100
    /// type being deserialized into holds a String.
101
0
    pub fn is_data(&self) -> bool {
102
0
        self.classify() == Category::Data
103
0
    }
104
105
    /// Returns true if this error was caused by prematurely reaching the end of
106
    /// the input data.
107
    ///
108
    /// Callers that process streaming input may be interested in retrying the
109
    /// deserialization once more data is available.
110
0
    pub fn is_eof(&self) -> bool {
111
0
        self.classify() == Category::Eof
112
0
    }
113
114
    /// The kind reported by the underlying standard library I/O error, if this
115
    /// error was caused by a failure to read or write bytes on an I/O stream.
116
    ///
117
    /// # Example
118
    ///
119
    /// ```
120
    /// use serde_json::Value;
121
    /// use std::io::{self, ErrorKind, Read};
122
    /// use std::process;
123
    ///
124
    /// struct ReaderThatWillTimeOut<'a>(&'a [u8]);
125
    ///
126
    /// impl<'a> Read for ReaderThatWillTimeOut<'a> {
127
    ///     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
128
    ///         if self.0.is_empty() {
129
    ///             Err(io::Error::new(ErrorKind::TimedOut, "timed out"))
130
    ///         } else {
131
    ///             self.0.read(buf)
132
    ///         }
133
    ///     }
134
    /// }
135
    ///
136
    /// fn main() {
137
    ///     let reader = ReaderThatWillTimeOut(br#" {"k": "#);
138
    ///
139
    ///     let _: Value = match serde_json::from_reader(reader) {
140
    ///         Ok(value) => value,
141
    ///         Err(error) => {
142
    ///             if error.io_error_kind() == Some(ErrorKind::TimedOut) {
143
    ///                 // Maybe this application needs to retry certain kinds of errors.
144
    ///
145
    ///                 # return;
146
    ///             } else {
147
    ///                 eprintln!("error: {}", error);
148
    ///                 process::exit(1);
149
    ///             }
150
    ///         }
151
    ///     };
152
    /// }
153
    /// ```
154
    #[cfg(feature = "std")]
155
0
    pub fn io_error_kind(&self) -> Option<ErrorKind> {
156
0
        if let ErrorCode::Io(io_error) = &self.err.code {
157
0
            Some(io_error.kind())
158
        } else {
159
0
            None
160
        }
161
0
    }
162
}
163
164
/// Categorizes the cause of a `serde_json::Error`.
165
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
166
pub enum Category {
167
    /// The error was caused by a failure to read or write bytes on an I/O
168
    /// stream.
169
    Io,
170
171
    /// The error was caused by input that was not syntactically valid JSON.
172
    Syntax,
173
174
    /// The error was caused by input data that was semantically incorrect.
175
    ///
176
    /// For example, JSON containing a number is semantically incorrect when the
177
    /// type being deserialized into holds a String.
178
    Data,
179
180
    /// The error was caused by prematurely reaching the end of the input data.
181
    ///
182
    /// Callers that process streaming input may be interested in retrying the
183
    /// deserialization once more data is available.
184
    Eof,
185
}
186
187
#[cfg(feature = "std")]
188
#[allow(clippy::fallible_impl_from)]
189
impl From<Error> for io::Error {
190
    /// Convert a `serde_json::Error` into an `io::Error`.
191
    ///
192
    /// JSON syntax and data errors are turned into `InvalidData` I/O errors.
193
    /// EOF errors are turned into `UnexpectedEof` I/O errors.
194
    ///
195
    /// ```
196
    /// use std::io;
197
    ///
198
    /// enum MyError {
199
    ///     Io(io::Error),
200
    ///     Json(serde_json::Error),
201
    /// }
202
    ///
203
    /// impl From<serde_json::Error> for MyError {
204
    ///     fn from(err: serde_json::Error) -> MyError {
205
    ///         use serde_json::error::Category;
206
    ///         match err.classify() {
207
    ///             Category::Io => {
208
    ///                 MyError::Io(err.into())
209
    ///             }
210
    ///             Category::Syntax | Category::Data | Category::Eof => {
211
    ///                 MyError::Json(err)
212
    ///             }
213
    ///         }
214
    ///     }
215
    /// }
216
    /// ```
217
0
    fn from(j: Error) -> Self {
218
0
        if let ErrorCode::Io(err) = j.err.code {
219
0
            err
220
        } else {
221
0
            match j.classify() {
222
0
                Category::Io => unreachable!(),
223
0
                Category::Syntax | Category::Data => io::Error::new(ErrorKind::InvalidData, j),
224
0
                Category::Eof => io::Error::new(ErrorKind::UnexpectedEof, j),
225
            }
226
        }
227
0
    }
228
}
229
230
struct ErrorImpl {
231
    code: ErrorCode,
232
    line: usize,
233
    column: usize,
234
}
235
236
pub(crate) enum ErrorCode {
237
    /// Catchall for syntax error messages
238
    Message(Box<str>),
239
240
    /// Some I/O error occurred while serializing or deserializing.
241
    Io(io::Error),
242
243
    /// EOF while parsing a list.
244
    EofWhileParsingList,
245
246
    /// EOF while parsing an object.
247
    EofWhileParsingObject,
248
249
    /// EOF while parsing a string.
250
    EofWhileParsingString,
251
252
    /// EOF while parsing a JSON value.
253
    EofWhileParsingValue,
254
255
    /// Expected this character to be a `':'`.
256
    ExpectedColon,
257
258
    /// Expected this character to be either a `','` or a `']'`.
259
    ExpectedListCommaOrEnd,
260
261
    /// Expected this character to be either a `','` or a `'}'`.
262
    ExpectedObjectCommaOrEnd,
263
264
    /// Expected to parse either a `true`, `false`, or a `null`.
265
    ExpectedSomeIdent,
266
267
    /// Expected this character to start a JSON value.
268
    ExpectedSomeValue,
269
270
    /// Expected this character to be a `"`.
271
    ExpectedDoubleQuote,
272
273
    /// Invalid hex escape code.
274
    InvalidEscape,
275
276
    /// Invalid number.
277
    InvalidNumber,
278
279
    /// Number is bigger than the maximum value of its type.
280
    NumberOutOfRange,
281
282
    /// Invalid unicode code point.
283
    InvalidUnicodeCodePoint,
284
285
    /// Control character found while parsing a string.
286
    ControlCharacterWhileParsingString,
287
288
    /// Object key is not a string.
289
    KeyMustBeAString,
290
291
    /// Contents of key were supposed to be a number.
292
    ExpectedNumericKey,
293
294
    /// Object key is a non-finite float value.
295
    FloatKeyMustBeFinite,
296
297
    /// Lone leading surrogate in hex escape.
298
    LoneLeadingSurrogateInHexEscape,
299
300
    /// JSON has a comma after the last value in an array or map.
301
    TrailingComma,
302
303
    /// JSON has non-whitespace trailing characters after the value.
304
    TrailingCharacters,
305
306
    /// Unexpected end of hex escape.
307
    UnexpectedEndOfHexEscape,
308
309
    /// Encountered nesting of JSON maps and arrays more than 128 layers deep.
310
    RecursionLimitExceeded,
311
}
312
313
impl Error {
314
    #[cold]
315
34.2k
    pub(crate) fn syntax(code: ErrorCode, line: usize, column: usize) -> Self {
316
34.2k
        Error {
317
34.2k
            err: Box::new(ErrorImpl { code, line, column }),
318
34.2k
        }
319
34.2k
    }
320
321
    // Not public API. Should be pub(crate).
322
    //
323
    // Update `eager_json` crate when this function changes.
324
    #[doc(hidden)]
325
    #[cold]
326
0
    pub fn io(error: io::Error) -> Self {
327
0
        Error {
328
0
            err: Box::new(ErrorImpl {
329
0
                code: ErrorCode::Io(error),
330
0
                line: 0,
331
0
                column: 0,
332
0
            }),
333
0
        }
334
0
    }
335
336
    #[cold]
337
29.5k
    pub(crate) fn fix_position<F>(self, f: F) -> Self
338
29.5k
    where
339
29.5k
        F: FnOnce(ErrorCode) -> Error,
340
    {
341
29.5k
        if self.err.line == 0 {
342
0
            f(self.err.code)
343
        } else {
344
29.5k
            self
345
        }
346
29.5k
    }
<serde_json::error::Error>::fix_position::<<serde_json::de::Deserializer<serde_json::read::SliceRead>>::fix_position::{closure#0}>
Line
Count
Source
337
29.5k
    pub(crate) fn fix_position<F>(self, f: F) -> Self
338
29.5k
    where
339
29.5k
        F: FnOnce(ErrorCode) -> Error,
340
    {
341
29.5k
        if self.err.line == 0 {
342
0
            f(self.err.code)
343
        } else {
344
29.5k
            self
345
        }
346
29.5k
    }
Unexecuted instantiation: <serde_json::error::Error>::fix_position::<<serde_json::de::Deserializer<serde_json::read::StrRead>>::fix_position::{closure#0}>
347
}
348
349
impl Display for ErrorCode {
350
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351
0
        match self {
352
0
            ErrorCode::Message(msg) => f.write_str(msg),
353
0
            ErrorCode::Io(err) => Display::fmt(err, f),
354
0
            ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"),
355
0
            ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"),
356
0
            ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
357
0
            ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
358
0
            ErrorCode::ExpectedColon => f.write_str("expected `:`"),
359
0
            ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
360
0
            ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
361
0
            ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
362
0
            ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
363
0
            ErrorCode::ExpectedDoubleQuote => f.write_str("expected `\"`"),
364
0
            ErrorCode::InvalidEscape => f.write_str("invalid escape"),
365
0
            ErrorCode::InvalidNumber => f.write_str("invalid number"),
366
0
            ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
367
0
            ErrorCode::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"),
368
            ErrorCode::ControlCharacterWhileParsingString => {
369
0
                f.write_str("control character (\\u0000-\\u001F) found while parsing a string")
370
            }
371
0
            ErrorCode::KeyMustBeAString => f.write_str("key must be a string"),
372
            ErrorCode::ExpectedNumericKey => {
373
0
                f.write_str("invalid value: expected key to be a number in quotes")
374
            }
375
            ErrorCode::FloatKeyMustBeFinite => {
376
0
                f.write_str("float key must be finite (got NaN or +/-inf)")
377
            }
378
            ErrorCode::LoneLeadingSurrogateInHexEscape => {
379
0
                f.write_str("lone leading surrogate in hex escape")
380
            }
381
0
            ErrorCode::TrailingComma => f.write_str("trailing comma"),
382
0
            ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
383
0
            ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"),
384
0
            ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
385
        }
386
0
    }
387
}
388
389
impl serde::de::StdError for Error {
390
    #[cfg(feature = "std")]
391
0
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
392
0
        match &self.err.code {
393
0
            ErrorCode::Io(err) => err.source(),
394
0
            _ => None,
395
        }
396
0
    }
397
}
398
399
impl Display for Error {
400
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401
0
        Display::fmt(&*self.err, f)
402
0
    }
403
}
404
405
impl Display for ErrorImpl {
406
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
407
0
        if self.line == 0 {
408
0
            Display::fmt(&self.code, f)
409
        } else {
410
0
            write!(
411
0
                f,
412
0
                "{} at line {} column {}",
413
                self.code, self.line, self.column
414
            )
415
        }
416
0
    }
417
}
418
419
// Remove two layers of verbosity from the debug representation. Humans often
420
// end up seeing this representation because it is what unwrap() shows.
421
impl Debug for Error {
422
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
423
0
        write!(
424
0
            f,
425
0
            "Error({:?}, line: {}, column: {})",
426
0
            self.err.code.to_string(),
427
            self.err.line,
428
            self.err.column
429
        )
430
0
    }
431
}
432
433
impl de::Error for Error {
434
    #[cold]
435
0
    fn custom<T: Display>(msg: T) -> Error {
436
0
        make_error(msg.to_string())
437
0
    }
438
439
    #[cold]
440
0
    fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
441
0
        Error::custom(format_args!(
442
0
            "invalid type: {}, expected {}",
443
0
            JsonUnexpected(unexp),
444
            exp,
445
        ))
446
0
    }
447
448
    #[cold]
449
0
    fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
450
0
        Error::custom(format_args!(
451
0
            "invalid value: {}, expected {}",
452
0
            JsonUnexpected(unexp),
453
            exp,
454
        ))
455
0
    }
456
}
457
458
impl ser::Error for Error {
459
    #[cold]
460
    fn custom<T: Display>(msg: T) -> Error {
461
        make_error(msg.to_string())
462
    }
463
}
464
465
struct JsonUnexpected<'a>(de::Unexpected<'a>);
466
467
impl<'a> Display for JsonUnexpected<'a> {
468
0
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
469
0
        match self.0 {
470
0
            de::Unexpected::Unit => formatter.write_str("null"),
471
0
            de::Unexpected::Float(value) => write!(
472
0
                formatter,
473
0
                "floating point `{}`",
474
0
                zmij::Buffer::new().format(value),
475
            ),
476
0
            unexp => Display::fmt(&unexp, formatter),
477
        }
478
0
    }
479
}
480
481
// Parse our own error message that looks like "{} at line {} column {}" to work
482
// around erased-serde round-tripping the error through de::Error::custom.
483
0
fn make_error(mut msg: String) -> Error {
484
0
    let (line, column) = parse_line_col(&mut msg).unwrap_or((0, 0));
485
0
    Error {
486
0
        err: Box::new(ErrorImpl {
487
0
            code: ErrorCode::Message(msg.into_boxed_str()),
488
0
            line,
489
0
            column,
490
0
        }),
491
0
    }
492
0
}
493
494
0
fn parse_line_col(msg: &mut String) -> Option<(usize, usize)> {
495
0
    let start_of_suffix = match msg.rfind(" at line ") {
496
0
        Some(index) => index,
497
0
        None => return None,
498
    };
499
500
    // Find start and end of line number.
501
0
    let start_of_line = start_of_suffix + " at line ".len();
502
0
    let mut end_of_line = start_of_line;
503
0
    while starts_with_digit(&msg[end_of_line..]) {
504
0
        end_of_line += 1;
505
0
    }
506
507
0
    if !msg[end_of_line..].starts_with(" column ") {
508
0
        return None;
509
0
    }
510
511
    // Find start and end of column number.
512
0
    let start_of_column = end_of_line + " column ".len();
513
0
    let mut end_of_column = start_of_column;
514
0
    while starts_with_digit(&msg[end_of_column..]) {
515
0
        end_of_column += 1;
516
0
    }
517
518
0
    if end_of_column < msg.len() {
519
0
        return None;
520
0
    }
521
522
    // Parse numbers.
523
0
    let line = match usize::from_str(&msg[start_of_line..end_of_line]) {
524
0
        Ok(line) => line,
525
0
        Err(_) => return None,
526
    };
527
0
    let column = match usize::from_str(&msg[start_of_column..end_of_column]) {
528
0
        Ok(column) => column,
529
0
        Err(_) => return None,
530
    };
531
532
0
    msg.truncate(start_of_suffix);
533
0
    Some((line, column))
534
0
}
535
536
0
fn starts_with_digit(slice: &str) -> bool {
537
0
    match slice.as_bytes().first() {
538
0
        None => false,
539
0
        Some(&byte) => byte >= b'0' && byte <= b'9',
540
    }
541
0
}