1use std::error;
4use std::fmt::{self, Debug, Display};
5use std::io;
6use std::result;
7use std::str::FromStr;
8
9use serde::de;
10use serde::ser;
11
12pub struct Error {
15 err: Box<ErrorImpl>,
19}
20
21pub type Result<T> = result::Result<T, Error>;
23
24impl Error {
25 pub fn line(&self) -> usize {
30 self.err.line
31 }
32
33 pub fn column(&self) -> usize {
41 self.err.column
42 }
43
44 pub fn classify(&self) -> Category {
51 match self.err.code {
52 ErrorCode::Message(_) => Category::Data,
53 ErrorCode::Io(_) => Category::Io,
54 ErrorCode::EofWhileParsingList
55 | ErrorCode::EofWhileParsingObject
56 | ErrorCode::EofWhileParsingString
57 | ErrorCode::EofWhileParsingValue => Category::Eof,
58 ErrorCode::ExpectedColon
59 | ErrorCode::ExpectedListCommaOrEnd
60 | ErrorCode::ExpectedObjectCommaOrEnd
61 | ErrorCode::ExpectedObjectOrArray
62 | ErrorCode::ExpectedSomeIdent
63 | ErrorCode::ExpectedSomeValue
64 | ErrorCode::ExpectedSomeString
65 | ErrorCode::InvalidEscape
66 | ErrorCode::InvalidNumber
67 | ErrorCode::NumberOutOfRange
68 | ErrorCode::InvalidUnicodeCodePoint
69 | ErrorCode::ControlCharacterWhileParsingString
70 | ErrorCode::KeyMustBeAString
71 | ErrorCode::LoneLeadingSurrogateInHexEscape
72 | ErrorCode::TrailingComma
73 | ErrorCode::TrailingCharacters
74 | ErrorCode::UnexpectedEndOfHexEscape
75 | ErrorCode::RecursionLimitExceeded => Category::Syntax,
76 }
77 }
78
79 pub fn is_io(&self) -> bool {
82 self.classify() == Category::Io
83 }
84
85 pub fn is_syntax(&self) -> bool {
88 self.classify() == Category::Syntax
89 }
90
91 pub fn is_data(&self) -> bool {
97 self.classify() == Category::Data
98 }
99
100 pub fn is_eof(&self) -> bool {
106 self.classify() == Category::Eof
107 }
108}
109
110#[derive(Copy, Clone, PartialEq, Eq, Debug)]
112pub enum Category {
113 Io,
116
117 Syntax,
119
120 Data,
125
126 Eof,
131}
132
133#[cfg_attr(feature = "cargo-clippy", allow(fallible_impl_from))]
134impl From<Error> for io::Error {
135 fn from(j: Error) -> Self {
163 if let ErrorCode::Io(err) = j.err.code {
164 err
165 } else {
166 match j.classify() {
167 Category::Io => unreachable!(),
168 Category::Syntax | Category::Data => io::Error::new(io::ErrorKind::InvalidData, j),
169 Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, j),
170 }
171 }
172 }
173}
174
175struct ErrorImpl {
176 code: ErrorCode,
177 line: usize,
178 column: usize,
179}
180
181#[doc(hidden)]
183pub enum ErrorCode {
184 Message(Box<str>),
186
187 Io(io::Error),
189
190 EofWhileParsingList,
192
193 EofWhileParsingObject,
195
196 EofWhileParsingString,
198
199 EofWhileParsingValue,
201
202 ExpectedColon,
204
205 ExpectedListCommaOrEnd,
207
208 ExpectedObjectCommaOrEnd,
210
211 ExpectedObjectOrArray,
213
214 ExpectedSomeIdent,
216
217 ExpectedSomeValue,
219
220 ExpectedSomeString,
222
223 InvalidEscape,
225
226 InvalidNumber,
228
229 NumberOutOfRange,
231
232 InvalidUnicodeCodePoint,
234
235 ControlCharacterWhileParsingString,
237
238 KeyMustBeAString,
240
241 LoneLeadingSurrogateInHexEscape,
243
244 TrailingComma,
246
247 TrailingCharacters,
249
250 UnexpectedEndOfHexEscape,
252
253 RecursionLimitExceeded,
255}
256
257impl Error {
258 #[doc(hidden)]
260 #[cold]
261 pub fn syntax(code: ErrorCode, line: usize, column: usize) -> Self {
262 Error {
263 err: Box::new(ErrorImpl {
264 code: code,
265 line: line,
266 column: column,
267 }),
268 }
269 }
270
271 #[doc(hidden)]
275 #[cold]
276 pub fn io(error: io::Error) -> Self {
277 Error {
278 err: Box::new(ErrorImpl {
279 code: ErrorCode::Io(error),
280 line: 0,
281 column: 0,
282 }),
283 }
284 }
285
286 #[doc(hidden)]
288 #[cold]
289 pub fn fix_position<F>(self, f: F) -> Self
290 where
291 F: FnOnce(ErrorCode) -> Error,
292 {
293 if self.err.line == 0 {
294 f(self.err.code)
295 } else {
296 self
297 }
298 }
299}
300
301impl Display for ErrorCode {
302 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303 match *self {
304 ErrorCode::Message(ref msg) => f.write_str(msg),
305 ErrorCode::Io(ref err) => Display::fmt(err, f),
306 ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"),
307 ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"),
308 ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
309 ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
310 ErrorCode::ExpectedColon => f.write_str("expected `:`"),
311 ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
312 ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
313 ErrorCode::ExpectedObjectOrArray => f.write_str("expected `{` or `[`"),
314 ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
315 ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
316 ErrorCode::ExpectedSomeString => f.write_str("expected string"),
317 ErrorCode::InvalidEscape => f.write_str("invalid escape"),
318 ErrorCode::InvalidNumber => f.write_str("invalid number"),
319 ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
320 ErrorCode::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"),
321 ErrorCode::ControlCharacterWhileParsingString => {
322 f.write_str("control character (\\u0000-\\u001F) found while parsing a string")
323 }
324 ErrorCode::KeyMustBeAString => f.write_str("key must be a string"),
325 ErrorCode::LoneLeadingSurrogateInHexEscape => {
326 f.write_str("lone leading surrogate in hex escape")
327 }
328 ErrorCode::TrailingComma => f.write_str("trailing comma"),
329 ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
330 ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"),
331 ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
332 }
333 }
334}
335
336impl error::Error for Error {
337 fn description(&self) -> &str {
338 match self.err.code {
339 ErrorCode::Io(ref err) => error::Error::description(err),
340 _ => {
341 "JSON error"
343 }
344 }
345 }
346
347 fn cause(&self) -> Option<&error::Error> {
348 match self.err.code {
349 ErrorCode::Io(ref err) => Some(err),
350 _ => None,
351 }
352 }
353}
354
355impl Display for Error {
356 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
357 Display::fmt(&*self.err, f)
358 }
359}
360
361impl Display for ErrorImpl {
362 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363 if self.line == 0 {
364 Display::fmt(&self.code, f)
365 } else {
366 write!(
367 f,
368 "{} at line {} column {}",
369 self.code, self.line, self.column
370 )
371 }
372 }
373}
374
375impl Debug for Error {
378 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
379 write!(
380 f,
381 "Error({:?}, line: {}, column: {})",
382 self.err.code.to_string(),
383 self.err.line,
384 self.err.column
385 )
386 }
387}
388
389impl de::Error for Error {
390 #[cold]
391 fn custom<T: Display>(msg: T) -> Error {
392 make_error(msg.to_string())
393 }
394
395 #[cold]
396 fn invalid_type(unexp: de::Unexpected, exp: &de::Expected) -> Self {
397 if let de::Unexpected::Unit = unexp {
398 Error::custom(format_args!("invalid type: null, expected {}", exp))
399 } else {
400 Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp))
401 }
402 }
403}
404
405impl ser::Error for Error {
406 #[cold]
407 fn custom<T: Display>(msg: T) -> Error {
408 make_error(msg.to_string())
409 }
410}
411
412fn make_error(mut msg: String) -> Error {
415 let (line, column) = parse_line_col(&mut msg).unwrap_or((0, 0));
416 Error {
417 err: Box::new(ErrorImpl {
418 code: ErrorCode::Message(msg.into_boxed_str()),
419 line: line,
420 column: column,
421 }),
422 }
423}
424
425fn parse_line_col(msg: &mut String) -> Option<(usize, usize)> {
426 let start_of_suffix = match msg.rfind(" at line ") {
427 Some(index) => index,
428 None => return None,
429 };
430
431 let start_of_line = start_of_suffix + " at line ".len();
433 let mut end_of_line = start_of_line;
434 while starts_with_digit(&msg[end_of_line..]) {
435 end_of_line += 1;
436 }
437
438 if !msg[end_of_line..].starts_with(" column ") {
439 return None;
440 }
441
442 let start_of_column = end_of_line + " column ".len();
444 let mut end_of_column = start_of_column;
445 while starts_with_digit(&msg[end_of_column..]) {
446 end_of_column += 1;
447 }
448
449 if end_of_column < msg.len() {
450 return None;
451 }
452
453 let line = match usize::from_str(&msg[start_of_line..end_of_line]) {
455 Ok(line) => line,
456 Err(_) => return None,
457 };
458 let column = match usize::from_str(&msg[start_of_column..end_of_column]) {
459 Ok(column) => column,
460 Err(_) => return None,
461 };
462
463 msg.truncate(start_of_suffix);
464 Some((line, column))
465}
466
467fn starts_with_digit(slice: &str) -> bool {
468 match slice.as_bytes().get(0) {
469 None => false,
470 Some(&byte) => byte >= b'0' && byte <= b'9',
471 }
472}