]>
Commit | Line | Data |
---|---|---|
7cac9316 XL |
1 | // Copyright 2017 Serde Developers |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
4 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
5 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
6 | // option. This file may not be copied, modified, or distributed | |
7 | // except according to those terms. | |
8 | ||
8bb4bdeb XL |
9 | //! When serializing or deserializing JSON goes wrong. |
10 | ||
11 | use std::error; | |
12 | use std::fmt::{self, Debug, Display}; | |
13 | use std::io; | |
14 | use std::result; | |
15 | ||
16 | use serde::de; | |
17 | use serde::ser; | |
18 | ||
19 | /// This type represents all possible errors that can occur when serializing or | |
20 | /// deserializing JSON data. | |
21 | pub struct Error { | |
7cac9316 XL |
22 | /// This `Box` allows us to keep the size of `Error` as small as possible. A |
23 | /// larger `Error` type was substantially slower due to all the functions | |
24 | /// that pass around `Result<T, Error>`. | |
8bb4bdeb XL |
25 | err: Box<ErrorImpl>, |
26 | } | |
27 | ||
28 | /// Alias for a `Result` with the error type `serde_json::Error`. | |
29 | pub type Result<T> = result::Result<T, Error>; | |
30 | ||
7cac9316 XL |
31 | impl Error { |
32 | /// One-based line number at which the error was detected. | |
33 | /// | |
34 | /// Characters in the first line of the input (before the first newline | |
35 | /// character) are in line 1. | |
36 | pub fn line(&self) -> usize { | |
37 | self.err.line | |
38 | } | |
8bb4bdeb | 39 | |
7cac9316 XL |
40 | /// One-based column number at which the error was detected. |
41 | /// | |
42 | /// The first character in the input and any characters immediately | |
43 | /// following a newline character are in column 1. | |
44 | /// | |
45 | /// Note that errors may occur in column 0, for example if a read from an IO | |
46 | /// stream fails immediately following a previously read newline character. | |
47 | pub fn column(&self) -> usize { | |
48 | self.err.column | |
49 | } | |
50 | ||
51 | /// Categorizes the cause of this error. | |
52 | /// | |
53 | /// - `Category::Io` - failure to read or write bytes on an IO stream | |
54 | /// - `Category::Syntax` - input that is not syntactically valid JSON | |
55 | /// - `Category::Data` - input data that is semantically incorrect | |
56 | /// - `Category::Eof` - unexpected end of the input data | |
57 | pub fn classify(&self) -> Category { | |
58 | match self.err.code { | |
59 | ErrorCode::Message(_) => Category::Data, | |
60 | ErrorCode::Io(_) => Category::Io, | |
61 | ErrorCode::EofWhileParsingList | | |
62 | ErrorCode::EofWhileParsingObject | | |
63 | ErrorCode::EofWhileParsingString | | |
64 | ErrorCode::EofWhileParsingValue => Category::Eof, | |
65 | ErrorCode::ExpectedColon | | |
66 | ErrorCode::ExpectedListCommaOrEnd | | |
67 | ErrorCode::ExpectedObjectCommaOrEnd | | |
68 | ErrorCode::ExpectedObjectOrArray | | |
69 | ErrorCode::ExpectedSomeIdent | | |
70 | ErrorCode::ExpectedSomeValue | | |
71 | ErrorCode::ExpectedSomeString | | |
72 | ErrorCode::InvalidEscape | | |
73 | ErrorCode::InvalidNumber | | |
74 | ErrorCode::NumberOutOfRange | | |
75 | ErrorCode::InvalidUnicodeCodePoint | | |
76 | ErrorCode::KeyMustBeAString | | |
77 | ErrorCode::LoneLeadingSurrogateInHexEscape | | |
78 | ErrorCode::TrailingCharacters | | |
79 | ErrorCode::UnexpectedEndOfHexEscape | | |
80 | ErrorCode::RecursionLimitExceeded => Category::Syntax, | |
81 | } | |
82 | } | |
83 | ||
84 | /// Returns true if this error was caused by a failure to read or write | |
85 | /// bytes on an IO stream. | |
86 | pub fn is_io(&self) -> bool { | |
87 | self.classify() == Category::Io | |
88 | } | |
89 | ||
90 | /// Returns true if this error was caused by input that was not | |
91 | /// syntactically valid JSON. | |
92 | pub fn is_syntax(&self) -> bool { | |
93 | self.classify() == Category::Syntax | |
94 | } | |
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 | pub fn is_data(&self) -> bool { | |
102 | self.classify() == Category::Data | |
103 | } | |
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 | pub fn is_eof(&self) -> bool { | |
111 | self.classify() == Category::Eof | |
112 | } | |
113 | } | |
114 | ||
115 | /// Categorizes the cause of a `serde_json::Error`. | |
116 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
117 | pub enum Category { | |
118 | /// The error was caused by a failure to read or write bytes on an IO | |
119 | /// stream. | |
120 | Io, | |
121 | ||
122 | /// The error was caused by input that was not syntactically valid JSON. | |
123 | Syntax, | |
124 | ||
125 | /// The error was caused by input data that was semantically incorrect. | |
126 | /// | |
127 | /// For example, JSON containing a number is semantically incorrect when the | |
128 | /// type being deserialized into holds a String. | |
129 | Data, | |
130 | ||
131 | /// The error was caused by prematurely reaching the end of the input data. | |
132 | /// | |
133 | /// Callers that process streaming input may be interested in retrying the | |
134 | /// deserialization once more data is available. | |
135 | Eof, | |
136 | } | |
137 | ||
138 | impl From<Error> for io::Error { | |
139 | /// Convert a `serde_json::Error` into an `io::Error`. | |
140 | /// | |
141 | /// JSON syntax and data errors are turned into `InvalidData` IO errors. | |
142 | /// EOF errors are turned into `UnexpectedEof` IO errors. | |
143 | /// | |
144 | /// ```rust | |
145 | /// use std::io; | |
146 | /// | |
147 | /// enum MyError { | |
148 | /// Io(io::Error), | |
149 | /// Json(serde_json::Error), | |
150 | /// } | |
151 | /// | |
152 | /// impl From<serde_json::Error> for MyError { | |
153 | /// fn from(err: serde_json::Error) -> MyError { | |
154 | /// use serde_json::error::Category; | |
155 | /// match err.classify() { | |
156 | /// Category::Io => { | |
157 | /// MyError::Io(err.into()) | |
158 | /// } | |
159 | /// Category::Syntax | Category::Data | Category::Eof => { | |
160 | /// MyError::Json(err) | |
161 | /// } | |
162 | /// } | |
163 | /// } | |
164 | /// } | |
165 | /// ``` | |
166 | fn from(j: Error) -> Self { | |
167 | if let ErrorCode::Io(err) = j.err.code { | |
168 | err | |
169 | } else { | |
170 | match j.classify() { | |
171 | Category::Io => unreachable!(), | |
172 | Category::Syntax | Category::Data => io::Error::new(io::ErrorKind::InvalidData, j), | |
173 | Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, j), | |
174 | } | |
175 | } | |
176 | } | |
177 | } | |
178 | ||
179 | #[derive(Debug)] | |
180 | struct ErrorImpl { | |
181 | code: ErrorCode, | |
182 | line: usize, | |
183 | column: usize, | |
8bb4bdeb XL |
184 | } |
185 | ||
186 | // Not public API. Should be pub(crate). | |
187 | #[doc(hidden)] | |
7cac9316 | 188 | #[derive(Debug)] |
8bb4bdeb XL |
189 | pub enum ErrorCode { |
190 | /// Catchall for syntax error messages | |
191 | Message(String), | |
192 | ||
7cac9316 XL |
193 | /// Some IO error occurred while serializing or deserializing. |
194 | Io(io::Error), | |
195 | ||
8bb4bdeb | 196 | /// EOF while parsing a list. |
7cac9316 | 197 | EofWhileParsingList, |
8bb4bdeb XL |
198 | |
199 | /// EOF while parsing an object. | |
7cac9316 | 200 | EofWhileParsingObject, |
8bb4bdeb XL |
201 | |
202 | /// EOF while parsing a string. | |
7cac9316 | 203 | EofWhileParsingString, |
8bb4bdeb XL |
204 | |
205 | /// EOF while parsing a JSON value. | |
7cac9316 | 206 | EofWhileParsingValue, |
8bb4bdeb XL |
207 | |
208 | /// Expected this character to be a `':'`. | |
209 | ExpectedColon, | |
210 | ||
7cac9316 | 211 | /// Expected this character to be either a `','` or a `']'`. |
8bb4bdeb XL |
212 | ExpectedListCommaOrEnd, |
213 | ||
7cac9316 | 214 | /// Expected this character to be either a `','` or a `'}'`. |
8bb4bdeb XL |
215 | ExpectedObjectCommaOrEnd, |
216 | ||
7cac9316 XL |
217 | /// Expected this character to be either a `'{'` or a `'['`. |
218 | ExpectedObjectOrArray, | |
219 | ||
8bb4bdeb XL |
220 | /// Expected to parse either a `true`, `false`, or a `null`. |
221 | ExpectedSomeIdent, | |
222 | ||
223 | /// Expected this character to start a JSON value. | |
224 | ExpectedSomeValue, | |
225 | ||
226 | /// Expected this character to start a JSON string. | |
227 | ExpectedSomeString, | |
228 | ||
229 | /// Invalid hex escape code. | |
230 | InvalidEscape, | |
231 | ||
232 | /// Invalid number. | |
233 | InvalidNumber, | |
234 | ||
235 | /// Number is bigger than the maximum value of its type. | |
236 | NumberOutOfRange, | |
237 | ||
238 | /// Invalid unicode code point. | |
239 | InvalidUnicodeCodePoint, | |
240 | ||
241 | /// Object key is not a string. | |
242 | KeyMustBeAString, | |
243 | ||
244 | /// Lone leading surrogate in hex escape. | |
245 | LoneLeadingSurrogateInHexEscape, | |
246 | ||
247 | /// JSON has non-whitespace trailing characters after the value. | |
248 | TrailingCharacters, | |
249 | ||
250 | /// Unexpected end of hex excape. | |
251 | UnexpectedEndOfHexEscape, | |
252 | ||
253 | /// Encountered nesting of JSON maps and arrays more than 128 layers deep. | |
254 | RecursionLimitExceeded, | |
255 | } | |
256 | ||
257 | impl Error { | |
258 | // Not public API. Should be pub(crate). | |
259 | #[doc(hidden)] | |
7cac9316 XL |
260 | pub fn syntax(code: ErrorCode, line: usize, column: usize) -> Self { |
261 | Error { | |
262 | err: Box::new( | |
263 | ErrorImpl { | |
264 | code: code, | |
265 | line: line, | |
266 | column: column, | |
267 | }, | |
268 | ), | |
269 | } | |
270 | } | |
271 | ||
272 | // Not public API. Should be pub(crate). | |
273 | #[doc(hidden)] | |
274 | pub fn io(error: io::Error) -> Self { | |
8bb4bdeb | 275 | Error { |
7cac9316 XL |
276 | err: Box::new( |
277 | ErrorImpl { | |
278 | code: ErrorCode::Io(error), | |
279 | line: 0, | |
280 | column: 0, | |
281 | }, | |
282 | ), | |
8bb4bdeb XL |
283 | } |
284 | } | |
285 | ||
286 | // Not public API. Should be pub(crate). | |
287 | #[doc(hidden)] | |
288 | pub fn fix_position<F>(self, f: F) -> Self | |
7cac9316 XL |
289 | where |
290 | F: FnOnce(ErrorCode) -> Error, | |
8bb4bdeb | 291 | { |
7cac9316 XL |
292 | if self.err.line == 0 { |
293 | f(self.err.code) | |
8bb4bdeb XL |
294 | } else { |
295 | self | |
296 | } | |
297 | } | |
298 | } | |
299 | ||
300 | impl Display for ErrorCode { | |
301 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
302 | match *self { | |
7cac9316 XL |
303 | ErrorCode::Message(ref msg) => f.write_str(msg), |
304 | ErrorCode::Io(ref err) => Display::fmt(err, f), | |
305 | ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"), | |
306 | ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"), | |
307 | ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"), | |
308 | ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"), | |
309 | ErrorCode::ExpectedColon => f.write_str("expected `:`"), | |
310 | ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"), | |
311 | ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"), | |
312 | ErrorCode::ExpectedObjectOrArray => f.write_str("expected `{` or `[`"), | |
313 | ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"), | |
314 | ErrorCode::ExpectedSomeValue => f.write_str("expected value"), | |
315 | ErrorCode::ExpectedSomeString => f.write_str("expected string"), | |
316 | ErrorCode::InvalidEscape => f.write_str("invalid escape"), | |
317 | ErrorCode::InvalidNumber => f.write_str("invalid number"), | |
318 | ErrorCode::NumberOutOfRange => f.write_str("number out of range"), | |
319 | ErrorCode::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"), | |
320 | ErrorCode::KeyMustBeAString => f.write_str("key must be a string"), | |
8bb4bdeb XL |
321 | ErrorCode::LoneLeadingSurrogateInHexEscape => { |
322 | f.write_str("lone leading surrogate in hex escape") | |
323 | } | |
7cac9316 XL |
324 | ErrorCode::TrailingCharacters => f.write_str("trailing characters"), |
325 | ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"), | |
326 | ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"), | |
8bb4bdeb XL |
327 | } |
328 | } | |
329 | } | |
330 | ||
331 | impl error::Error for Error { | |
332 | fn description(&self) -> &str { | |
7cac9316 XL |
333 | match self.err.code { |
334 | ErrorCode::Io(ref err) => error::Error::description(err), | |
335 | _ => { | |
8bb4bdeb XL |
336 | // If you want a better message, use Display::fmt or to_string(). |
337 | "JSON error" | |
338 | } | |
8bb4bdeb XL |
339 | } |
340 | } | |
341 | ||
342 | fn cause(&self) -> Option<&error::Error> { | |
7cac9316 XL |
343 | match self.err.code { |
344 | ErrorCode::Io(ref err) => Some(err), | |
8bb4bdeb XL |
345 | _ => None, |
346 | } | |
347 | } | |
348 | } | |
349 | ||
350 | impl Display for Error { | |
7cac9316 XL |
351 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
352 | Display::fmt(&*self.err, f) | |
8bb4bdeb XL |
353 | } |
354 | } | |
355 | ||
7cac9316 XL |
356 | impl Display for ErrorImpl { |
357 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
358 | if self.line == 0 { | |
359 | Display::fmt(&self.code, f) | |
360 | } else { | |
361 | write!( | |
362 | f, | |
363 | "{} at line {} column {}", | |
364 | self.code, | |
365 | self.line, | |
366 | self.column | |
367 | ) | |
8bb4bdeb XL |
368 | } |
369 | } | |
370 | } | |
371 | ||
7cac9316 XL |
372 | // Remove two layers of verbosity from the debug representation. Humans often |
373 | // end up seeing this representation because it is what unwrap() shows. | |
374 | impl Debug for Error { | |
375 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
376 | Debug::fmt(&*self.err, f) | |
8bb4bdeb XL |
377 | } |
378 | } | |
379 | ||
380 | impl de::Error for Error { | |
381 | fn custom<T: Display>(msg: T) -> Error { | |
382 | Error { | |
7cac9316 XL |
383 | err: Box::new( |
384 | ErrorImpl { | |
385 | code: ErrorCode::Message(msg.to_string()), | |
386 | line: 0, | |
387 | column: 0, | |
388 | }, | |
389 | ), | |
8bb4bdeb XL |
390 | } |
391 | } | |
392 | } | |
393 | ||
394 | impl ser::Error for Error { | |
395 | fn custom<T: Display>(msg: T) -> Error { | |
396 | Error { | |
7cac9316 XL |
397 | err: Box::new( |
398 | ErrorImpl { | |
399 | code: ErrorCode::Message(msg.to_string()), | |
400 | line: 0, | |
401 | column: 0, | |
402 | }, | |
403 | ), | |
8bb4bdeb XL |
404 | } |
405 | } | |
406 | } |