Skip to content

Commit ce45bb7

Browse files
committed
auto merge of #10625 : huonw/rust/json-errors, r=alexcrichton
Fixes #4244.
2 parents 738eb9b + b052f28 commit ce45bb7

File tree

1 file changed

+126
-25
lines changed

1 file changed

+126
-25
lines changed

src/libextra/json.rs

Lines changed: 126 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ impl<T : Iterator<char>> Parser<T> {
804804
while !self.eof() {
805805
self.parse_whitespace();
806806

807-
if self.ch != '"' {
807+
if self.ch != '\"' {
808808
return self.error(~"key must be a string");
809809
}
810810

@@ -866,12 +866,34 @@ pub fn Decoder(json: Json) -> Decoder {
866866
}
867867
}
868868

869+
impl Decoder {
870+
fn err(&self, msg: &str) -> ! {
871+
fail!("JSON decode error: {}", msg);
872+
}
873+
fn missing_field(&self, field: &str, object: ~Object) -> ! {
874+
self.err(format!("missing required '{}' field in object: {}",
875+
field, Object(object).to_str()))
876+
}
877+
fn expected(&self, expected: &str, found: &Json) -> ! {
878+
let found_s = match *found {
879+
Null => "null",
880+
List(*) => "list",
881+
Object(*) => "object",
882+
Number(*) => "number",
883+
String(*) => "string",
884+
Boolean(*) => "boolean"
885+
};
886+
self.err(format!("expected {expct} but found {fnd}: {val}",
887+
expct=expected, fnd=found_s, val=found.to_str()))
888+
}
889+
}
890+
869891
impl serialize::Decoder for Decoder {
870892
fn read_nil(&mut self) -> () {
871893
debug!("read_nil");
872894
match self.stack.pop() {
873895
Null => (),
874-
value => fail!("not a null: {:?}", value)
896+
value => self.expected("null", &value)
875897
}
876898
}
877899

@@ -891,33 +913,38 @@ impl serialize::Decoder for Decoder {
891913
debug!("read_bool");
892914
match self.stack.pop() {
893915
Boolean(b) => b,
894-
value => fail!("not a boolean: {:?}", value)
916+
value => self.expected("boolean", &value)
895917
}
896918
}
897919

898920
fn read_f64(&mut self) -> f64 {
899921
debug!("read_f64");
900922
match self.stack.pop() {
901923
Number(f) => f,
902-
value => fail!("not a number: {:?}", value)
924+
value => self.expected("number", &value)
903925
}
904926
}
905927
fn read_f32(&mut self) -> f32 { self.read_f64() as f32 }
906928
fn read_f32(&mut self) -> f32 { self.read_f64() as f32 }
907929

908930
fn read_char(&mut self) -> char {
909-
let mut v = ~[];
910931
let s = self.read_str();
911-
for c in s.iter() { v.push(c) }
912-
if v.len() != 1 { fail!("string must have one character") }
913-
v[0]
932+
{
933+
let mut it = s.iter();
934+
match (it.next(), it.next()) {
935+
// exactly one character
936+
(Some(c), None) => return c,
937+
_ => ()
938+
}
939+
}
940+
self.expected("single character string", &String(s))
914941
}
915942

916943
fn read_str(&mut self) -> ~str {
917944
debug!("read_str");
918945
match self.stack.pop() {
919946
String(s) => s,
920-
json => fail!("not a string: {:?}", json)
947+
value => self.expected("string", &value)
921948
}
922949
}
923950

@@ -933,26 +960,34 @@ impl serialize::Decoder for Decoder {
933960
debug!("read_enum_variant(names={:?})", names);
934961
let name = match self.stack.pop() {
935962
String(s) => s,
936-
Object(o) => {
937-
let n = match o.find(&~"variant").expect("invalidly encoded json") {
938-
&String(ref s) => s.clone(),
939-
_ => fail!("invalidly encoded json"),
963+
Object(mut o) => {
964+
let n = match o.pop(&~"variant") {
965+
Some(String(s)) => s,
966+
Some(val) => self.expected("string", &val),
967+
None => self.missing_field("variant", o)
940968
};
941-
match o.find(&~"fields").expect("invalidly encoded json") {
942-
&List(ref l) => {
943-
for field in l.rev_iter() {
969+
match o.pop(&~"fields") {
970+
Some(List(l)) => {
971+
for field in l.move_rev_iter() {
944972
self.stack.push(field.clone());
945973
}
946974
},
947-
_ => fail!("invalidly encoded json")
975+
Some(val) => self.expected("list", &val),
976+
None => {
977+
// re-insert the variant field so we're
978+
// printing the "whole" struct in the error
979+
// message... ick.
980+
o.insert(~"variant", String(n));
981+
self.missing_field("fields", o);
982+
}
948983
}
949984
n
950985
}
951-
ref json => fail!("invalid variant: {:?}", *json),
986+
json => self.expected("string or object", &json)
952987
};
953988
let idx = match names.iter().position(|n| str::eq_slice(*n, name)) {
954989
Some(idx) => idx,
955-
None => fail!("Unknown variant name: {}", name),
990+
None => self.err(format!("unknown variant name: {}", name))
956991
};
957992
f(self, idx)
958993
}
@@ -999,10 +1034,9 @@ impl serialize::Decoder for Decoder {
9991034
-> T {
10001035
debug!("read_struct_field(name={}, idx={})", name, idx);
10011036
match self.stack.pop() {
1002-
Object(obj) => {
1003-
let mut obj = obj;
1037+
Object(mut obj) => {
10041038
let value = match obj.pop(&name.to_owned()) {
1005-
None => fail!("no such field: {}", name),
1039+
None => self.missing_field(name, obj),
10061040
Some(json) => {
10071041
self.stack.push(json);
10081042
f(self)
@@ -1011,7 +1045,7 @@ impl serialize::Decoder for Decoder {
10111045
self.stack.push(Object(obj));
10121046
value
10131047
}
1014-
value => fail!("not an object: {:?}", value)
1048+
value => self.expected("object", &value)
10151049
}
10161050
}
10171051

@@ -1058,7 +1092,7 @@ impl serialize::Decoder for Decoder {
10581092
}
10591093
len
10601094
}
1061-
_ => fail!("not a list"),
1095+
value => self.expected("list", &value)
10621096
};
10631097
f(self, len)
10641098
}
@@ -1079,7 +1113,7 @@ impl serialize::Decoder for Decoder {
10791113
}
10801114
len
10811115
}
1082-
json => fail!("not an object: {:?}", json),
1116+
value => self.expected("object", &value)
10831117
};
10841118
f(self, len)
10851119
}
@@ -1936,4 +1970,71 @@ mod tests {
19361970
col: 8u,
19371971
msg: @~"EOF while parsing object"}));
19381972
}
1973+
1974+
#[deriving(Decodable)]
1975+
struct DecodeStruct {
1976+
x: f64,
1977+
y: bool,
1978+
z: ~str,
1979+
w: ~[DecodeStruct]
1980+
}
1981+
#[deriving(Decodable)]
1982+
enum DecodeEnum {
1983+
A(f64),
1984+
B(~str)
1985+
}
1986+
fn check_err<T: Decodable<Decoder>>(to_parse: &'static str, expected_error: &str) {
1987+
use std::task;
1988+
let res = task::try(|| {
1989+
// either fails in `decode` (which is what we want), or
1990+
// returns Some(error_message)/None if the string was
1991+
// invalid or valid JSON.
1992+
match from_str(to_parse) {
1993+
Err(e) => Some(e.to_str()),
1994+
Ok(json) => {
1995+
let _: T = Decodable::decode(&mut Decoder(json));
1996+
None
1997+
}
1998+
}
1999+
});
2000+
match res {
2001+
Ok(Some(parse_error)) => fail!("`{}` is not valid json: {}",
2002+
to_parse, parse_error),
2003+
Ok(None) => fail!("`{}` parsed & decoded ok, expecting error `{}`",
2004+
to_parse, expected_error),
2005+
Err(e) => {
2006+
let err = e.as_ref::<~str>().unwrap();
2007+
assert!(err.contains(expected_error),
2008+
"`{}` errored incorrectly, found `{}` expecting `{}`",
2009+
to_parse, *err, expected_error);
2010+
}
2011+
}
2012+
}
2013+
#[test]
2014+
fn test_decode_errors_struct() {
2015+
check_err::<DecodeStruct>("[]", "object but found list");
2016+
check_err::<DecodeStruct>("{\"x\": true, \"y\": true, \"z\": \"\", \"w\": []}",
2017+
"number but found boolean");
2018+
check_err::<DecodeStruct>("{\"x\": 1, \"y\": [], \"z\": \"\", \"w\": []}",
2019+
"boolean but found list");
2020+
check_err::<DecodeStruct>("{\"x\": 1, \"y\": true, \"z\": {}, \"w\": []}",
2021+
"string but found object");
2022+
check_err::<DecodeStruct>("{\"x\": 1, \"y\": true, \"z\": \"\", \"w\": null}",
2023+
"list but found null");
2024+
check_err::<DecodeStruct>("{\"x\": 1, \"y\": true, \"z\": \"\"}",
2025+
"'w' field in object");
2026+
}
2027+
#[test]
2028+
fn test_decode_errors_enum() {
2029+
check_err::<DecodeEnum>("{}",
2030+
"'variant' field in object");
2031+
check_err::<DecodeEnum>("{\"variant\": 1}",
2032+
"string but found number");
2033+
check_err::<DecodeEnum>("{\"variant\": \"A\"}",
2034+
"'fields' field in object");
2035+
check_err::<DecodeEnum>("{\"variant\": \"A\", \"fields\": null}",
2036+
"list but found null");
2037+
check_err::<DecodeEnum>("{\"variant\": \"C\", \"fields\": []}",
2038+
"unknown variant name");
2039+
}
19392040
}

0 commit comments

Comments
 (0)