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 | } |