1use core::{
6 fmt::{self, Debug, Display},
7 result,
8};
9use std::{borrow::Cow, error, fmt::Result as FmtResult, str::FromStr};
10
11use serde::{
12 de::{self, Unexpected},
13 ser,
14};
15use sonic_number::Error as NumberError;
16use thiserror::Error as ErrorTrait;
17
18use crate::reader::Position;
19
20pub struct Error {
23 err: Box<ErrorImpl>,
27}
28
29pub type Result<T> = result::Result<T, Error>;
31
32impl Error {
33 pub fn line(&self) -> usize {
38 self.err.line
39 }
40
41 pub fn column(&self) -> usize {
50 self.err.column
51 }
52
53 pub fn io_error_kind(&self) -> Option<std::io::ErrorKind> {
56 if let ErrorCode::Io(io_error) = &self.err.code {
57 Some(io_error.kind())
58 } else {
59 None
60 }
61 }
62
63 pub fn classify(&self) -> Category {
70 match self.err.code {
71 ErrorCode::Message(_) | ErrorCode::UnexpectedVisitType => Category::TypeUnmatched,
72 ErrorCode::GetInEmptyObject
73 | ErrorCode::GetInEmptyArray
74 | ErrorCode::GetIndexOutOfArray
75 | ErrorCode::GetUnknownKeyInObject => Category::NotFound,
76 ErrorCode::Io(_) => Category::Io,
77 ErrorCode::EofWhileParsing => Category::Eof,
78 ErrorCode::ExpectedColon
79 | ErrorCode::ExpectedObjectCommaOrEnd
80 | ErrorCode::InvalidEscape
81 | ErrorCode::InvalidJsonValue
82 | ErrorCode::InvalidLiteral
83 | ErrorCode::InvalidUTF8
84 | ErrorCode::InvalidNumber
85 | ErrorCode::NumberOutOfRange
86 | ErrorCode::InvalidUnicodeCodePoint
87 | ErrorCode::ControlCharacterWhileParsingString
88 | ErrorCode::TrailingComma
89 | ErrorCode::TrailingCharacters
90 | ErrorCode::ExpectObjectKeyOrEnd
91 | ErrorCode::ExpectedArrayCommaOrEnd
92 | ErrorCode::ExpectedArrayStart
93 | ErrorCode::ExpectedObjectStart
94 | ErrorCode::InvalidSurrogateUnicodeCodePoint
95 | ErrorCode::SerExpectKeyIsStrOrNum(_)
96 | ErrorCode::FloatMustBeFinite
97 | ErrorCode::ExpectedQuote
98 | ErrorCode::ExpectedNumericKey
99 | ErrorCode::RecursionLimitExceeded => Category::Syntax,
100 }
101 }
102
103 pub fn is_io(&self) -> bool {
106 self.classify() == Category::Io
107 }
108
109 pub fn is_syntax(&self) -> bool {
112 self.classify() == Category::Syntax
113 }
114
115 pub fn is_unmatched_type(&self) -> bool {
119 self.classify() == Category::TypeUnmatched
120 }
121
122 pub fn is_not_found(&self) -> bool {
128 self.classify() == Category::NotFound
129 }
130
131 pub fn is_eof(&self) -> bool {
137 self.classify() == Category::Eof
138 }
139
140 pub fn offset(&self) -> usize {
142 self.err.index
143 }
144}
145
146#[allow(clippy::fallible_impl_from)]
147impl From<Error> for std::io::Error {
148 fn from(j: Error) -> Self {
153 match j.err.code {
154 ErrorCode::Io(err) => err,
155 ErrorCode::EofWhileParsing => std::io::Error::new(std::io::ErrorKind::UnexpectedEof, j),
156 _ => std::io::Error::new(std::io::ErrorKind::InvalidData, j),
157 }
158 }
159}
160
161#[derive(Copy, Clone, PartialEq, Eq, Debug)]
163#[non_exhaustive]
164pub enum Category {
165 Io,
168
169 Syntax,
171
172 TypeUnmatched,
177
178 NotFound,
184
185 Eof,
190}
191
192struct ErrorImpl {
193 code: ErrorCode,
194 index: usize,
195 line: usize,
196 column: usize,
197 descript: Option<String>,
199}
200
201#[derive(ErrorTrait, Debug)]
202#[non_exhaustive]
203pub enum ErrorCode {
204 #[error("{0}")]
205 Message(Cow<'static, str>),
206
207 #[error("io error while serializing or deserializing")]
208 Io(std::io::Error),
209
210 #[error("EOF while parsing")]
211 EofWhileParsing,
212
213 #[error("Expected this character to be a ':' while parsing")]
214 ExpectedColon,
215
216 #[error("Expected this character to be either a ',' or a ']' while parsing")]
217 ExpectedArrayCommaOrEnd,
218
219 #[error("Expected this character to be either a ',' or a '}}' while parsing")]
220 ExpectedObjectCommaOrEnd,
221
222 #[error("Invalid literal (`true`, `false`, or a `null`) while parsing")]
223 InvalidLiteral,
224
225 #[error("Invalid JSON value")]
226 InvalidJsonValue,
227
228 #[error("Expected this character to be '{{'")]
229 ExpectedObjectStart,
230
231 #[error("Expected this character to be '['")]
232 ExpectedArrayStart,
233
234 #[error("Invalid escape chars")]
235 InvalidEscape,
236
237 #[error("Invalid number")]
238 InvalidNumber,
239
240 #[error("Number is bigger than the maximum value of its type")]
241 NumberOutOfRange,
242
243 #[error("Invalid unicode code point")]
244 InvalidUnicodeCodePoint,
245
246 #[error("Invalid UTF-8 characters in json")]
247 InvalidUTF8,
248
249 #[error("Control character found while parsing a string")]
250 ControlCharacterWhileParsingString,
251
252 #[error("Expected this character to be '\"' or '}}'")]
253 ExpectObjectKeyOrEnd,
254
255 #[error("JSON has a comma after the last value in an array or object")]
256 TrailingComma,
257
258 #[error("JSON has non-whitespace trailing characters after the value")]
259 TrailingCharacters,
260
261 #[error("Encountered nesting of JSON maps and arrays more than 128 layers deep")]
262 RecursionLimitExceeded,
263
264 #[error("Get value from an empty object")]
265 GetInEmptyObject,
266
267 #[error("Get unknown key from the object")]
268 GetUnknownKeyInObject,
269
270 #[error("Get value from an empty array")]
271 GetInEmptyArray,
272
273 #[error("Get index out of the array")]
274 GetIndexOutOfArray,
275
276 #[error("Unexpected visited type")]
277 UnexpectedVisitType,
278
279 #[error("Invalid surrogate Unicode code point")]
280 InvalidSurrogateUnicodeCodePoint,
281
282 #[error("Float number must be finite, not be Infinity or NaN")]
283 FloatMustBeFinite,
284
285 #[error("Expect a numeric key in Value")]
286 ExpectedNumericKey,
287
288 #[error("Expect a quote")]
289 ExpectedQuote,
290
291 #[error("Expected the key to be string/bool/number when serializing map, now is {0}")]
292 SerExpectKeyIsStrOrNum(Unexpected<'static>),
293}
294
295impl From<NumberError> for ErrorCode {
296 fn from(err: NumberError) -> Self {
297 match err {
298 NumberError::InvalidNumber => ErrorCode::InvalidNumber,
299 NumberError::FloatMustBeFinite => ErrorCode::FloatMustBeFinite,
300 }
301 }
302}
303
304impl Error {
305 #[cold]
306 pub(crate) fn syntax(code: ErrorCode, json: &[u8], index: usize) -> Self {
307 let position = Position::from_index(index, json);
308 let mut start = index.saturating_sub(8);
310 let mut end = if index + 8 > json.len() {
311 json.len()
312 } else {
313 index + 8
314 };
315
316 while start > 0 && index - start <= 16 && (json[start] & 0b1100_0000) == 0b1000_0000 {
318 start -= 1;
319 }
320
321 while end < json.len() && end - index <= 16 && (json[end - 1] & 0b1100_0000) == 0b1000_0000
323 {
324 end += 1;
325 }
326
327 let fragment = String::from_utf8_lossy(&json[start..end]).to_string();
328 let left = index - start;
329 let right = if end - index > 1 {
330 end - (index + 1)
331 } else {
332 0
333 };
334 let mask = ".".repeat(left) + "^" + &".".repeat(right);
335 let descript = format!("\n\n\t{}\n\t{}\n", fragment, mask);
336
337 Error {
338 err: Box::new(ErrorImpl {
339 code,
340 line: position.line,
341 column: position.column,
342 index,
343 descript: Some(descript),
344 }),
345 }
346 }
347
348 #[cold]
349 pub(crate) fn ser_error(code: ErrorCode) -> Self {
350 Error {
351 err: Box::new(ErrorImpl {
352 code,
353 line: 0,
354 column: 0,
355 index: 0,
356 descript: None,
357 }),
358 }
359 }
360
361 #[cold]
362 pub(crate) fn io(error: std::io::Error) -> Self {
363 Error {
364 err: Box::new(ErrorImpl {
365 code: ErrorCode::Io(error),
366 line: 0,
367 index: 0,
368 column: 0,
369 descript: None,
370 }),
371 }
372 }
373
374 #[cold]
375 pub(crate) fn error_code(self) -> ErrorCode {
376 self.err.code
377 }
378}
379
380impl serde::de::StdError for Error {
381 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
382 match &self.err.code {
383 ErrorCode::Io(err) => err.source(),
384 _ => None,
385 }
386 }
387}
388
389impl Display for Error {
390 fn fmt(&self, f: &mut fmt::Formatter) -> FmtResult {
391 Display::fmt(&*self.err, f)
392 }
393}
394
395impl Display for ErrorImpl {
396 fn fmt(&self, f: &mut fmt::Formatter) -> FmtResult {
397 if self.line != 0 {
398 write!(
399 f,
400 "{} at line {} column {}{}",
401 self.code,
402 self.line,
403 self.column,
404 self.descript.as_ref().unwrap_or(&"".to_string())
405 )
406 } else {
407 write!(f, "{}", self.code)
408 }
409 }
410}
411
412impl Debug for Error {
415 fn fmt(&self, f: &mut fmt::Formatter) -> FmtResult {
416 Display::fmt(&self, f)
417 }
418}
419
420impl de::Error for Error {
421 #[cold]
422 fn custom<T: Display>(msg: T) -> Error {
423 make_error(msg.to_string())
424 }
425
426 #[cold]
427 fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
428 if let de::Unexpected::Unit = unexp {
429 Error::custom(format_args!("invalid type: null, expected {}", exp))
430 } else {
431 Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp))
432 }
433 }
434}
435
436impl ser::Error for Error {
437 #[cold]
438 fn custom<T: Display>(msg: T) -> Error {
439 make_error(msg.to_string())
440 }
441}
442
443#[cold]
445pub(crate) fn make_error(mut msg: String) -> Error {
446 let (line, column) = parse_line_col(&mut msg).unwrap_or((0, 0));
447 Error {
448 err: Box::new(ErrorImpl {
449 code: ErrorCode::Message(msg.into()),
450 line,
451 index: 0,
452 column,
453 descript: None,
454 }),
455 }
456}
457
458fn parse_line_col(msg: &mut String) -> Option<(usize, usize)> {
459 let start_of_suffix = msg.rfind(" at line ")?;
460
461 let start_of_line = start_of_suffix + " at line ".len();
463 let mut end_of_line = start_of_line;
464 while starts_with_digit(&msg[end_of_line..]) {
465 end_of_line += 1;
466 }
467
468 if !msg[end_of_line..].starts_with(" column ") {
469 return None;
470 }
471
472 let start_of_column = end_of_line + " column ".len();
474 let mut end_of_column = start_of_column;
475 while starts_with_digit(&msg[end_of_column..]) {
476 end_of_column += 1;
477 }
478
479 if end_of_column < msg.len() {
480 return None;
481 }
482
483 let line = match usize::from_str(&msg[start_of_line..end_of_line]) {
485 Ok(line) => line,
486 Err(_) => return None,
487 };
488 let column = match usize::from_str(&msg[start_of_column..end_of_column]) {
489 Ok(column) => column,
490 Err(_) => return None,
491 };
492
493 msg.truncate(start_of_suffix);
494 Some((line, column))
495}
496
497fn starts_with_digit(slice: &str) -> bool {
498 match slice.as_bytes().first() {
499 None => false,
500 Some(&byte) => byte.is_ascii_digit(),
501 }
502}
503
504pub(crate) fn invalid_utf8(json: &[u8], index: usize) -> Error {
505 Error::syntax(ErrorCode::InvalidUTF8, json, index)
506}
507
508#[cfg(test)]
509mod test {
510 use crate::{from_slice, from_str, Deserialize};
511
512 #[test]
513 fn test_serde_errors_display() {
514 #[allow(unused)]
515 #[derive(Debug, Deserialize)]
516 struct Foo {
517 a: Vec<i32>,
518 c: String,
519 }
520
521 let err = from_str::<Foo>("{ \"b\":[]}").unwrap_err();
522 assert_eq!(
523 format!("{}", err),
524 "missing field `a` at line 1 column 8\n\n\t{ \"b\":[]}\n\t........^\n"
525 );
526
527 let err = from_str::<Foo>("{\"a\": [1, 2x, 3, 4, 5]}").unwrap_err();
528 println!("{}", err);
529 assert_eq!(
530 format!("{}", err),
531 "Expected this character to be either a ',' or a ']' while parsing at line 1 column \
532 11\n\n\t\": [1, 2x, 3, 4,\n\t........^.......\n"
533 );
534
535 let err = from_str::<Foo>("{\"a\": null}").unwrap_err();
536 assert_eq!(
537 format!("{}", err),
538 "invalid type: null, expected a sequence at line 1 column 9\n\n\t\"a\": \
539 null}\n\t........^.\n"
540 );
541
542 let err = from_str::<Foo>("{\"a\": [1,2,3 }").unwrap_err();
543 assert_eq!(
544 format!("{}", err),
545 "Expected this character to be either a ',' or a ']' while parsing at line 1 column \
546 14\n\n\t[1,2,3 }\n\t........^\n"
547 );
548
549 let err = from_str::<Foo>("{\"a\": [\"123\"]}").unwrap_err();
550 assert_eq!(
551 format!("{}", err),
552 "invalid type: string \"123\", expected i32 at line 1 column 11\n\n\t\": \
553 [\"123\"]}\n\t........^..\n"
554 );
555
556 let err = from_str::<Foo>("{\"a\": [").unwrap_err();
557 assert_eq!(
558 format!("{}", err),
559 "EOF while parsing at line 1 column 6\n\n\t{\"a\": [\n\t......^\n"
560 );
561
562 let err = from_str::<Foo>("{\"a\": [000]}").unwrap_err();
563 assert_eq!(
564 format!("{}", err),
565 "Expected this character to be either a ',' or a ']' while parsing at line 1 column \
566 8\n\n\t{\"a\": [000]}\n\t........^...\n"
567 );
568
569 let err = from_str::<Foo>("{\"a\": [-]}").unwrap_err();
570 assert_eq!(
571 format!("{}", err),
572 "Invalid number at line 1 column 7\n\n\t{\"a\": [-]}\n\t.......^..\n"
573 );
574
575 let err = from_str::<Foo>("{\"a\": [-1.23e]}").unwrap_err();
576 assert_eq!(
577 format!("{}", err),
578 "Invalid number at line 1 column 12\n\n\t: [-1.23e]}\n\t........^..\n"
579 );
580
581 let err = from_str::<Foo>("{\"c\": \"哈哈哈哈哈哈}").unwrap_err();
582 assert_eq!(
583 format!("{}", err),
584 "EOF while parsing at line 1 column 25\n\n\t哈哈哈}\n\t.........^\n"
585 );
586
587 let err = from_slice::<Foo>(b"{\"b\":\"\x80\"}").unwrap_err();
588 assert_eq!(
589 format!("{}", err),
590 "Invalid UTF-8 characters in json at line 1 column 6\n\n\t{\"b\":\"�\"}\n\t......^..\n"
591 );
592 }
593
594 #[test]
595 fn test_other_errors() {
596 let err = crate::Value::try_from(f64::NAN).unwrap_err();
597 assert_eq!(
598 format!("{}", err),
599 "NaN or Infinity is not a valid JSON value"
600 );
601 }
602}