Skip to content

Commit 0e4d525

Browse files
authored
Merge pull request rust-num#278 from serde-rs/error
Extend the Error API with line, column, category
2 parents 2b5957c + deae244 commit 0e4d525

File tree

4 files changed

+202
-90
lines changed

4 files changed

+202
-90
lines changed

json/src/de.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ impl<R: Read> Deserializer<R> {
159159
where V: de::Visitor,
160160
{
161161
if try!(self.parse_whitespace()) { // true if eof
162-
return Err(self.peek_error(ErrorCode::EOFWhileParsingValue));
162+
return Err(self.peek_error(ErrorCode::EofWhileParsingValue));
163163
}
164164

165165
let value = match try!(self.peek_or_null()) {
@@ -526,7 +526,7 @@ impl<R: Read> Deserializer<R> {
526526
Ok(())
527527
}
528528
Some(_) => Err(self.peek_error(ErrorCode::ExpectedColon)),
529-
None => Err(self.peek_error(ErrorCode::EOFWhileParsingObject)),
529+
None => Err(self.peek_error(ErrorCode::EofWhileParsingObject)),
530530
}
531531
}
532532

@@ -536,7 +536,7 @@ impl<R: Read> Deserializer<R> {
536536
match try!(self.next_char()) {
537537
Some(b']') => Ok(()),
538538
Some(_) => Err(self.error(ErrorCode::TrailingCharacters)),
539-
None => Err(self.error(ErrorCode::EOFWhileParsingList)),
539+
None => Err(self.error(ErrorCode::EofWhileParsingList)),
540540
}
541541
}
542542

@@ -546,7 +546,7 @@ impl<R: Read> Deserializer<R> {
546546
match try!(self.next_char()) {
547547
Some(b'}') => Ok(()),
548548
Some(_) => Err(self.error(ErrorCode::TrailingCharacters)),
549-
None => Err(self.error(ErrorCode::EOFWhileParsingObject)),
549+
None => Err(self.error(ErrorCode::EofWhileParsingObject)),
550550
}
551551
}
552552
}
@@ -765,7 +765,7 @@ impl<'a, R: Read + 'a> de::SeqVisitor for SeqVisitor<'a, R> {
765765
}
766766
}
767767
None => {
768-
return Err(self.de.peek_error(ErrorCode::EOFWhileParsingList));
768+
return Err(self.de.peek_error(ErrorCode::EofWhileParsingList));
769769
}
770770
}
771771

@@ -814,14 +814,14 @@ impl<'a, R: Read + 'a> de::MapVisitor for MapVisitor<'a, R> {
814814
}
815815
None => {
816816
return Err(self.de
817-
.peek_error(ErrorCode::EOFWhileParsingObject));
817+
.peek_error(ErrorCode::EofWhileParsingObject));
818818
}
819819
}
820820

821821
match try!(self.de.peek()) {
822822
Some(b'"') => Ok(Some(try!(seed.deserialize(&mut *self.de)))),
823823
Some(_) => Err(self.de.peek_error(ErrorCode::KeyMustBeAString)),
824-
None => Err(self.de.peek_error(ErrorCode::EOFWhileParsingValue)),
824+
None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)),
825825
}
826826
}
827827

json/src/error.rs

Lines changed: 153 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,149 @@ use serde::ser;
1111
/// This type represents all possible errors that can occur when serializing or
1212
/// deserializing JSON data.
1313
pub struct Error {
14+
/// This `Box` allows us to keep the size of `Error` as small as possible. A
15+
/// larger `Error` type was substantially slower due to all the functions
16+
/// that pass around `Result<T, Error>`.
1417
err: Box<ErrorImpl>,
1518
}
1619

1720
/// Alias for a `Result` with the error type `serde_json::Error`.
1821
pub type Result<T> = result::Result<T, Error>;
1922

20-
enum ErrorImpl {
21-
/// The JSON value had some syntatic error.
22-
Syntax(ErrorCode, usize, usize),
23+
impl Error {
24+
/// One-based line number at which the error was detected.
25+
///
26+
/// Characters in the first line of the input (before the first newline
27+
/// character) are in line 1.
28+
pub fn line(&self) -> usize {
29+
self.err.line
30+
}
2331

24-
/// Some IO error occurred when serializing or deserializing a value.
25-
Io(io::Error),
32+
/// One-based column number at which the error was detected.
33+
///
34+
/// The first character in the input and any characters immediately
35+
/// following a newline character are in column 1.
36+
///
37+
/// Note that errors may occur in column 0, for example if a read from an IO
38+
/// stream fails immediately following a previously read newline character.
39+
pub fn column(&self) -> usize {
40+
self.err.column
41+
}
42+
43+
/// Categorizes the cause of this error.
44+
///
45+
/// - `Category::Io` - failure to read or write bytes on an IO stream
46+
/// - `Category::Syntax` - input that is not syntactically valid JSON
47+
/// - `Category::Data` - input data that is semantically incorrect
48+
/// - `Category::Eof` - unexpected end of the input data
49+
pub fn classify(&self) -> Category {
50+
match self.err.code {
51+
ErrorCode::Message(_) => Category::Data,
52+
ErrorCode::Io(_) => Category::Io,
53+
ErrorCode::EofWhileParsingList |
54+
ErrorCode::EofWhileParsingObject |
55+
ErrorCode::EofWhileParsingString |
56+
ErrorCode::EofWhileParsingValue => Category::Eof,
57+
ErrorCode::ExpectedColon |
58+
ErrorCode::ExpectedListCommaOrEnd |
59+
ErrorCode::ExpectedObjectCommaOrEnd |
60+
ErrorCode::ExpectedSomeIdent |
61+
ErrorCode::ExpectedSomeValue |
62+
ErrorCode::ExpectedSomeString |
63+
ErrorCode::InvalidEscape |
64+
ErrorCode::InvalidNumber |
65+
ErrorCode::NumberOutOfRange |
66+
ErrorCode::InvalidUnicodeCodePoint |
67+
ErrorCode::KeyMustBeAString |
68+
ErrorCode::LoneLeadingSurrogateInHexEscape |
69+
ErrorCode::TrailingCharacters |
70+
ErrorCode::UnexpectedEndOfHexEscape |
71+
ErrorCode::RecursionLimitExceeded => Category::Syntax,
72+
}
73+
}
74+
75+
/// Returns true if this error was caused by a failure to read or write
76+
/// bytes on an IO stream.
77+
pub fn is_io(&self) -> bool {
78+
self.classify() == Category::Io
79+
}
80+
81+
/// Returns true if this error was caused by input that was not
82+
/// syntactically valid JSON.
83+
pub fn is_syntax(&self) -> bool {
84+
self.classify() == Category::Syntax
85+
}
86+
87+
/// Returns true if this error was caused by input data that was
88+
/// semantically incorrect.
89+
///
90+
/// For example, JSON containing a number is semantically incorrect when the
91+
/// type being deserialized into holds a String.
92+
pub fn is_data(&self) -> bool {
93+
self.classify() == Category::Data
94+
}
95+
96+
/// Returns true if this error was caused by prematurely reaching the end of
97+
/// the input data.
98+
///
99+
/// Callers that process streaming input may be interested in retrying the
100+
/// deserialization once more data is available.
101+
pub fn is_eof(&self) -> bool {
102+
self.classify() == Category::Eof
103+
}
104+
}
105+
106+
/// Categorizes the cause of a `serde_json::Error`.
107+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
108+
pub enum Category {
109+
/// The error was caused by a failure to read or write bytes on an IO
110+
/// stream.
111+
Io,
112+
113+
/// The error was caused by input that was not syntactically valid JSON.
114+
Syntax,
115+
116+
/// The error was caused by input data that was semantically incorrect.
117+
///
118+
/// For example, JSON containing a number is semantically incorrect when the
119+
/// type being deserialized into holds a String.
120+
Data,
121+
122+
/// The error was caused by prematurely reaching the end of the input data.
123+
///
124+
/// Callers that process streaming input may be interested in retrying the
125+
/// deserialization once more data is available.
126+
Eof,
127+
}
128+
129+
#[derive(Debug)]
130+
struct ErrorImpl {
131+
code: ErrorCode,
132+
line: usize,
133+
column: usize,
26134
}
27135

28136
// Not public API. Should be pub(crate).
29137
#[doc(hidden)]
30-
#[derive(Clone, PartialEq, Debug)]
138+
#[derive(Debug)]
31139
pub enum ErrorCode {
32140
/// Catchall for syntax error messages
33141
Message(String),
34142

143+
/// Some IO error occurred while serializing or deserializing.
144+
Io(io::Error),
145+
35146
/// EOF while parsing a list.
36-
EOFWhileParsingList,
147+
EofWhileParsingList,
37148

38149
/// EOF while parsing an object.
39-
EOFWhileParsingObject,
150+
EofWhileParsingObject,
40151

41152
/// EOF while parsing a string.
42-
EOFWhileParsingString,
153+
EofWhileParsingString,
43154

44155
/// EOF while parsing a JSON value.
45-
EOFWhileParsingValue,
156+
EofWhileParsingValue,
46157

47158
/// Expected this character to be a `':'`.
48159
ExpectedColon,
@@ -93,9 +204,9 @@ pub enum ErrorCode {
93204
impl Error {
94205
// Not public API. Should be pub(crate).
95206
#[doc(hidden)]
96-
pub fn syntax(code: ErrorCode, line: usize, col: usize) -> Self {
207+
pub fn syntax(code: ErrorCode, line: usize, column: usize) -> Self {
97208
Error {
98-
err: Box::new(ErrorImpl::Syntax(code, line, col)),
209+
err: Box::new(ErrorImpl { code: code, line: line, column: column }),
99210
}
100211
}
101212

@@ -104,8 +215,8 @@ impl Error {
104215
pub fn fix_position<F>(self, f: F) -> Self
105216
where F: FnOnce(ErrorCode) -> Error
106217
{
107-
if let ErrorImpl::Syntax(code, 0, 0) = *self.err {
108-
f(code)
218+
if self.err.line == 0 {
219+
f(self.err.code)
109220
} else {
110221
self
111222
}
@@ -115,17 +226,18 @@ impl Error {
115226
impl Display for ErrorCode {
116227
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117228
match *self {
118-
ErrorCode::Message(ref msg) => write!(f, "{}", msg),
119-
ErrorCode::EOFWhileParsingList => {
229+
ErrorCode::Message(ref msg) => f.write_str(msg),
230+
ErrorCode::Io(ref err) => Display::fmt(err, f),
231+
ErrorCode::EofWhileParsingList => {
120232
f.write_str("EOF while parsing a list")
121233
}
122-
ErrorCode::EOFWhileParsingObject => {
234+
ErrorCode::EofWhileParsingObject => {
123235
f.write_str("EOF while parsing an object")
124236
}
125-
ErrorCode::EOFWhileParsingString => {
237+
ErrorCode::EofWhileParsingString => {
126238
f.write_str("EOF while parsing a string")
127239
}
128-
ErrorCode::EOFWhileParsingValue => {
240+
ErrorCode::EofWhileParsingValue => {
129241
f.write_str("EOF while parsing a value")
130242
}
131243
ErrorCode::ExpectedColon => {
@@ -179,56 +291,44 @@ impl Display for ErrorCode {
179291

180292
impl error::Error for Error {
181293
fn description(&self) -> &str {
182-
match *self.err {
183-
ErrorImpl::Syntax(..) => {
294+
match self.err.code {
295+
ErrorCode::Io(ref err) => error::Error::description(err),
296+
_ => {
184297
// If you want a better message, use Display::fmt or to_string().
185298
"JSON error"
186299
}
187-
ErrorImpl::Io(ref error) => error::Error::description(error),
188300
}
189301
}
190302

191303
fn cause(&self) -> Option<&error::Error> {
192-
match *self.err {
193-
ErrorImpl::Io(ref error) => Some(error),
304+
match self.err.code {
305+
ErrorCode::Io(ref err) => Some(err),
194306
_ => None,
195307
}
196308
}
197309
}
198310

199311
impl Display for Error {
200-
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
201-
match *self.err {
202-
ErrorImpl::Syntax(ref code, line, col) => {
203-
if line == 0 && col == 0 {
204-
write!(fmt, "{}", code)
205-
} else {
206-
write!(fmt, "{} at line {} column {}", code, line, col)
207-
}
208-
}
209-
ErrorImpl::Io(ref error) => fmt::Display::fmt(error, fmt),
312+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
313+
Display::fmt(&*self.err, f)
314+
}
315+
}
316+
317+
impl Display for ErrorImpl {
318+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319+
if self.line == 0 {
320+
Display::fmt(&self.code, f)
321+
} else {
322+
write!(f, "{} at line {} column {}", self.code, self.line, self.column)
210323
}
211324
}
212325
}
213326

214327
// Remove two layers of verbosity from the debug representation. Humans often
215328
// end up seeing this representation because it is what unwrap() shows.
216329
impl Debug for Error {
217-
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
218-
match *self.err {
219-
ErrorImpl::Syntax(ref code, ref line, ref col) => {
220-
formatter.debug_tuple("Syntax")
221-
.field(code)
222-
.field(line)
223-
.field(col)
224-
.finish()
225-
}
226-
ErrorImpl::Io(ref io) => {
227-
formatter.debug_tuple("Io")
228-
.field(io)
229-
.finish()
230-
}
231-
}
330+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331+
Debug::fmt(&*self.err, f)
232332
}
233333
}
234334

@@ -243,31 +343,31 @@ impl From<ErrorImpl> for Error {
243343
impl From<io::Error> for Error {
244344
fn from(error: io::Error) -> Error {
245345
Error {
246-
err: Box::new(ErrorImpl::Io(error)),
346+
err: Box::new(ErrorImpl { code: ErrorCode::Io(error), line: 0, column: 0 }),
247347
}
248348
}
249349
}
250350

251351
impl From<de::value::Error> for Error {
252352
fn from(error: de::value::Error) -> Error {
253353
Error {
254-
err: Box::new(ErrorImpl::Syntax(ErrorCode::Message(error.to_string()), 0, 0)),
354+
err: Box::new(ErrorImpl { code: ErrorCode::Message(error.to_string()), line: 0, column: 0 }),
255355
}
256356
}
257357
}
258358

259359
impl de::Error for Error {
260360
fn custom<T: Display>(msg: T) -> Error {
261361
Error {
262-
err: Box::new(ErrorImpl::Syntax(ErrorCode::Message(msg.to_string()), 0, 0)),
362+
err: Box::new(ErrorImpl { code: ErrorCode::Message(msg.to_string()), line: 0, column: 0 }),
263363
}
264364
}
265365
}
266366

267367
impl ser::Error for Error {
268368
fn custom<T: Display>(msg: T) -> Error {
269369
Error {
270-
err: Box::new(ErrorImpl::Syntax(ErrorCode::Message(msg.to_string()), 0, 0)),
370+
err: Box::new(ErrorImpl { code: ErrorCode::Message(msg.to_string()), line: 0, column: 0 }),
271371
}
272372
}
273373
}

0 commit comments

Comments
 (0)