auto merge of #19249 : barosl/rust/json-type-safety, r=alexcrichton

This pull request tries to improve type safety of `serialize::json::Encoder`.

Looking at #18319, I decided to test some JSON implementations in other languages. The results are as follows:

* Encoding to JSON

| Language | 111111111111111111 | 1.0 |
| --- | --- | --- |
| JavaScript™ | "111111111111111100" | "1" |
| Python | "111111111111111111" | **"1.0"** |
| Go | "111111111111111111" | "1" |
| Haskell | "111111111111111111" | "1" |
| Rust | **"111111111111111104"** | "1" |

* Decoding from JSON

| Language | "1" | "1.0" | "1.6" |
| --- | --- | --- | --- |
| JavaScript™ | 1 (Number) | 1 (Number) | 1.6 (Number) |
| Python | 1 (int) | 1.0 (float) | 1.6 (float) |
| Go | **1 (float64)** | 1 (float64) | 1.6 (float64) |
| Go (expecting `int`) | 1 (int) | **error** | error |
| Haskell (with `:: Int`) | 1 (Int) | 1 (Int) | **2 (Int)** |
| Haskell (with `:: Double`) | 1.0 (Double) | 1.0 (Double) | 1.6 (Double) |
| Rust (with `::<int>`) | 1 (int) | 1 (Int) | **1 (Int)** |
| Rust (with `::<f64>`) | 1 (f64) | 1 (f64) | 1.6 (f64) |

* The tests on Haskell were done using the [json](http://hackage.haskell.org/package/json) package.
* The error message printed by Go was: `cannot unmarshal number 1.0 into Go value of type int`

As you see, there is no uniform behavior. Every implementation follows its own principle. So I think it is reasonable to find a desirable set of behaviors for Rust.

Firstly, every implementation except the one for JavaScript is capable of handling `i64` values. It is even practical, because [Twitter API uses an i64 number to represent a tweet ID](https://dev.twitter.com/overview/api/twitter-ids-json-and-snowflake), although it is recommended to use the string version of the ID.

Secondly, looking into the Go's behavior, implicit type conversion is not allowed in their decoder. If the user expects an integer value to follow, decoding a float value will raise an error. This behavior is desirable in Rust, because we are pleased to follow the principles of strong typing.

Thirdly, Python's JSON module forces a decimal point to be printed even if the fractional part does not exist. This eases the distinction of a float value from an integer value in JSON, because by the spec there is only one type to represent numbers, `Number`.

So, I suggest the following three breaking changes:

1. Remove float preprocessing in serialize::json::Encoder

 `serialize::json::Encoder` currently uses `f64` to emit any integral type. This is possibly due to the behavior of JavaScript, which uses `f64` to represent any numeric value.

 This leads to a problem that only the integers in the range of [-2^53+1, 2^53-1] can be encoded. Therefore, `i64` and `u64` cannot be used reliably in the current implementation.

 [RFC 7159](http://tools.ietf.org/html/rfc7159) suggests that good interoperability can be achieved if the range is respected by implementations. However, it also says that implementations are allowed to set the range of number accepted. And it seems that the JSON encoders outside of the JavaScript world usually make use of `i64` values.

 This commit removes the float preprocessing done in the `emit_*` methods. It also increases performance, because transforming `f64` into String costs more than that of an integral type.

 Fixes #18319

2. Do not coerce to integer when decoding a float value

 When an integral value is expected by the user but a fractional value is found, the current implementation uses `std::num::cast()` to coerce to an integer type, losing the fractional part. This behavior is not desirable because the number loses precision without notice.

 This commit makes it raise `ExpectedError` when such a situation arises.

3. Always use a decimal point when emitting a float value

 JSON doesn't distinguish between integer and float. They are just numbers. Also, in the current implementation, a fractional number without the fractional part is encoded without a decimal point.

 Thereforce, when the value is decoded, it is first rendered as `Json`, either `I64` or `U64`. This reduces type safety, because while the original intention was to cast the value to float, it can also be casted to integer.

 As a workaround of this problem, this commit makes the encoder always emit a decimal point even if it is not necessary. If the fractional part of a float number is zero, ".0" is padded to the end of the result.
This commit is contained in:
bors 2014-12-09 10:51:49 +00:00
commit cafe296677

View File

@ -91,7 +91,7 @@
//! fn main() {
//! let object = TestStruct {
//! data_int: 1,
//! data_str: "toto".to_string(),
//! data_str: "homura".to_string(),
//! data_vector: vec![2,3,4,5],
//! };
//!
@ -178,7 +178,7 @@
//! // Serialize using `ToJson`
//! let input_data = TestStruct {
//! data_int: 1,
//! data_str: "toto".to_string(),
//! data_str: "madoka".to_string(),
//! data_vector: vec![2,3,4,5],
//! };
//! let json_obj: Json = input_data.to_json();
@ -390,7 +390,8 @@ fn spaces(wr: &mut io::Writer, mut n: uint) -> Result<(), io::IoError> {
fn fmt_number_or_null(v: f64) -> string::String {
match v.classify() {
FPNaN | FPInfinite => string::String::from_str("null"),
_ => f64::to_str_digits(v, 6u)
_ if v.fract() != 0f64 => f64::to_str_digits(v, 6u),
_ => f64::to_str_digits(v, 6u) + ".0",
}
}
@ -423,17 +424,17 @@ impl<'a> Encoder<'a> {
impl<'a> ::Encoder<io::IoError> for Encoder<'a> {
fn emit_nil(&mut self) -> EncodeResult { write!(self.writer, "null") }
fn emit_uint(&mut self, v: uint) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_u64(&mut self, v: u64) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_u32(&mut self, v: u32) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_u16(&mut self, v: u16) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_u8(&mut self, v: u8) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_uint(&mut self, v: uint) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_u64(&mut self, v: u64) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_u32(&mut self, v: u32) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_u16(&mut self, v: u16) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_u8(&mut self, v: u8) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_int(&mut self, v: int) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_i64(&mut self, v: i64) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_i32(&mut self, v: i32) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_i16(&mut self, v: i16) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_i8(&mut self, v: i8) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_int(&mut self, v: int) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_i64(&mut self, v: i64) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_i32(&mut self, v: i32) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_i16(&mut self, v: i16) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_i8(&mut self, v: i8) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_bool(&mut self, v: bool) -> EncodeResult {
if v {
@ -446,7 +447,9 @@ impl<'a> ::Encoder<io::IoError> for Encoder<'a> {
fn emit_f64(&mut self, v: f64) -> EncodeResult {
write!(self.writer, "{}", fmt_number_or_null(v))
}
fn emit_f32(&mut self, v: f32) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_f32(&mut self, v: f32) -> EncodeResult {
self.emit_f64(v as f64)
}
fn emit_char(&mut self, v: char) -> EncodeResult {
escape_char(self.writer, v)
@ -455,7 +458,9 @@ impl<'a> ::Encoder<io::IoError> for Encoder<'a> {
escape_str(self.writer, v)
}
fn emit_enum(&mut self, _name: &str, f: |&mut Encoder<'a>| -> EncodeResult) -> EncodeResult {
fn emit_enum(&mut self,
_name: &str,
f: |&mut Encoder<'a>| -> EncodeResult) -> EncodeResult {
f(self)
}
@ -624,17 +629,17 @@ impl<'a> PrettyEncoder<'a> {
impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> {
fn emit_nil(&mut self) -> EncodeResult { write!(self.writer, "null") }
fn emit_uint(&mut self, v: uint) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_u64(&mut self, v: u64) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_u32(&mut self, v: u32) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_u16(&mut self, v: u16) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_u8(&mut self, v: u8) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_uint(&mut self, v: uint) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_u64(&mut self, v: u64) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_u32(&mut self, v: u32) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_u16(&mut self, v: u16) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_u8(&mut self, v: u8) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_int(&mut self, v: int) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_i64(&mut self, v: i64) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_i32(&mut self, v: i32) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_i16(&mut self, v: i16) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_i8(&mut self, v: i8) -> EncodeResult { self.emit_f64(v as f64) }
fn emit_int(&mut self, v: int) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_i64(&mut self, v: i64) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_i32(&mut self, v: i32) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_i16(&mut self, v: i16) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_i8(&mut self, v: i8) -> EncodeResult { write!(self.writer, "{}", v) }
fn emit_bool(&mut self, v: bool) -> EncodeResult {
if v {
@ -666,7 +671,7 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> {
fn emit_enum_variant(&mut self,
name: &str,
_: uint,
_id: uint,
cnt: uint,
f: |&mut PrettyEncoder<'a>| -> EncodeResult) -> EncodeResult {
if cnt == 0 {
@ -1946,7 +1951,7 @@ macro_rules! expect(
($e:expr, Null) => ({
match $e {
Json::Null => Ok(()),
other => Err(ExpectedError("Null".to_string(),
other => Err(ExpectedError("Null".into_string(),
format!("{}", other)))
}
});
@ -1965,33 +1970,22 @@ macro_rules! read_primitive {
($name:ident, $ty:ty) => {
fn $name(&mut self) -> DecodeResult<$ty> {
match self.pop() {
Json::I64(f) => {
match num::cast(f) {
Some(f) => Ok(f),
None => Err(ExpectedError("Number".to_string(), format!("{}", f))),
}
}
Json::U64(f) => {
match num::cast(f) {
Some(f) => Ok(f),
None => Err(ExpectedError("Number".to_string(), format!("{}", f))),
}
}
Json::F64(f) => {
match num::cast(f) {
Some(f) => Ok(f),
None => Err(ExpectedError("Number".to_string(), format!("{}", f))),
}
}
Json::String(s) => {
// re: #12967.. a type w/ numeric keys (ie HashMap<uint, V> etc)
// is going to have a string here, as per JSON spec.
match std::str::from_str(s.as_slice()) {
Some(f) => Ok(f),
None => Err(ExpectedError("Number".to_string(), s)),
}
Json::I64(f) => match num::cast(f) {
Some(f) => Ok(f),
None => Err(ExpectedError("Number".into_string(), format!("{}", f))),
},
value => Err(ExpectedError("Number".to_string(), format!("{}", value)))
Json::U64(f) => match num::cast(f) {
Some(f) => Ok(f),
None => Err(ExpectedError("Number".into_string(), format!("{}", f))),
},
Json::F64(f) => Err(ExpectedError("Integer".into_string(), format!("{}", f))),
// re: #12967.. a type w/ numeric keys (ie HashMap<uint, V> etc)
// is going to have a string here, as per JSON spec.
Json::String(s) => match std::str::from_str(s.as_slice()) {
Some(f) => Ok(f),
None => Err(ExpectedError("Number".into_string(), s)),
},
value => Err(ExpectedError("Number".into_string(), format!("{}", value))),
}
}
}
@ -2027,11 +2021,11 @@ impl ::Decoder<DecoderError> for Decoder {
// is going to have a string here, as per JSON spec.
match std::str::from_str(s.as_slice()) {
Some(f) => Ok(f),
None => Err(ExpectedError("Number".to_string(), s)),
None => Err(ExpectedError("Number".into_string(), s)),
}
},
Json::Null => Ok(f64::NAN),
value => Err(ExpectedError("Number".to_string(), format!("{}", value)))
value => Err(ExpectedError("Number".into_string(), format!("{}", value)))
}
}
@ -2050,7 +2044,7 @@ impl ::Decoder<DecoderError> for Decoder {
_ => ()
}
}
Err(ExpectedError("single character string".to_string(), format!("{}", s)))
Err(ExpectedError("single character string".into_string(), format!("{}", s)))
}
fn read_str(&mut self) -> DecodeResult<string::String> {
@ -2073,32 +2067,32 @@ impl ::Decoder<DecoderError> for Decoder {
let name = match self.pop() {
Json::String(s) => s,
Json::Object(mut o) => {
let n = match o.remove(&"variant".to_string()) {
let n = match o.remove(&"variant".into_string()) {
Some(Json::String(s)) => s,
Some(val) => {
return Err(ExpectedError("String".to_string(), format!("{}", val)))
return Err(ExpectedError("String".into_string(), format!("{}", val)))
}
None => {
return Err(MissingFieldError("variant".to_string()))
return Err(MissingFieldError("variant".into_string()))
}
};
match o.remove(&"fields".to_string()) {
match o.remove(&"fields".into_string()) {
Some(Json::Array(l)) => {
for field in l.into_iter().rev() {
self.stack.push(field);
}
},
Some(val) => {
return Err(ExpectedError("Array".to_string(), format!("{}", val)))
return Err(ExpectedError("Array".into_string(), format!("{}", val)))
}
None => {
return Err(MissingFieldError("fields".to_string()))
return Err(MissingFieldError("fields".into_string()))
}
}
n
}
json => {
return Err(ExpectedError("String or Object".to_string(), format!("{}", json)))
return Err(ExpectedError("String or Object".into_string(), format!("{}", json)))
}
};
let idx = match names.iter()
@ -2447,9 +2441,9 @@ mod tests {
#[test]
fn test_decode_option_malformed() {
check_err::<OptionData>("{ \"opt\": [] }",
ExpectedError("Number".to_string(), "[]".to_string()));
ExpectedError("Number".into_string(), "[]".into_string()));
check_err::<OptionData>("{ \"opt\": false }",
ExpectedError("Number".to_string(), "false".to_string()));
ExpectedError("Number".into_string(), "false".into_string()));
}
#[deriving(PartialEq, Encodable, Decodable, Show)]
@ -2490,72 +2484,75 @@ mod tests {
#[test]
fn test_write_null() {
assert_eq!(Null.to_string().into_string(), "null");
assert_eq!(Null.to_pretty_str().into_string(), "null");
assert_eq!(Null.to_string(), "null");
assert_eq!(Null.to_pretty_str(), "null");
}
#[test]
fn test_write_i64() {
assert_eq!(U64(0).to_string().into_string(), "0");
assert_eq!(U64(0).to_pretty_str().into_string(), "0");
assert_eq!(U64(0).to_string(), "0");
assert_eq!(U64(0).to_pretty_str(), "0");
assert_eq!(U64(1234).to_string().into_string(), "1234");
assert_eq!(U64(1234).to_pretty_str().into_string(), "1234");
assert_eq!(U64(1234).to_string(), "1234");
assert_eq!(U64(1234).to_pretty_str(), "1234");
assert_eq!(I64(-5678).to_string().into_string(), "-5678");
assert_eq!(I64(-5678).to_pretty_str().into_string(), "-5678");
assert_eq!(I64(-5678).to_string(), "-5678");
assert_eq!(I64(-5678).to_pretty_str(), "-5678");
assert_eq!(U64(7650007200025252000).to_string(), "7650007200025252000");
assert_eq!(U64(7650007200025252000).to_pretty_str(), "7650007200025252000");
}
#[test]
fn test_write_f64() {
assert_eq!(F64(3.0).to_string().into_string(), "3");
assert_eq!(F64(3.0).to_pretty_str().into_string(), "3");
assert_eq!(F64(3.0).to_string(), "3.0");
assert_eq!(F64(3.0).to_pretty_str(), "3.0");
assert_eq!(F64(3.1).to_string().into_string(), "3.1");
assert_eq!(F64(3.1).to_pretty_str().into_string(), "3.1");
assert_eq!(F64(3.1).to_string(), "3.1");
assert_eq!(F64(3.1).to_pretty_str(), "3.1");
assert_eq!(F64(-1.5).to_string().into_string(), "-1.5");
assert_eq!(F64(-1.5).to_pretty_str().into_string(), "-1.5");
assert_eq!(F64(-1.5).to_string(), "-1.5");
assert_eq!(F64(-1.5).to_pretty_str(), "-1.5");
assert_eq!(F64(0.5).to_string().into_string(), "0.5");
assert_eq!(F64(0.5).to_pretty_str().into_string(), "0.5");
assert_eq!(F64(0.5).to_string(), "0.5");
assert_eq!(F64(0.5).to_pretty_str(), "0.5");
assert_eq!(F64(f64::NAN).to_string().into_string(), "null");
assert_eq!(F64(f64::NAN).to_pretty_str().into_string(), "null");
assert_eq!(F64(f64::NAN).to_string(), "null");
assert_eq!(F64(f64::NAN).to_pretty_str(), "null");
assert_eq!(F64(f64::INFINITY).to_string().into_string(), "null");
assert_eq!(F64(f64::INFINITY).to_pretty_str().into_string(), "null");
assert_eq!(F64(f64::INFINITY).to_string(), "null");
assert_eq!(F64(f64::INFINITY).to_pretty_str(), "null");
assert_eq!(F64(f64::NEG_INFINITY).to_string().into_string(), "null");
assert_eq!(F64(f64::NEG_INFINITY).to_pretty_str().into_string(), "null");
assert_eq!(F64(f64::NEG_INFINITY).to_string(), "null");
assert_eq!(F64(f64::NEG_INFINITY).to_pretty_str(), "null");
}
#[test]
fn test_write_str() {
assert_eq!(String("".to_string()).to_string().into_string(), "\"\"");
assert_eq!(String("".to_string()).to_pretty_str().into_string(), "\"\"");
assert_eq!(String("".into_string()).to_string(), "\"\"");
assert_eq!(String("".into_string()).to_pretty_str(), "\"\"");
assert_eq!(String("foo".to_string()).to_string().into_string(), "\"foo\"");
assert_eq!(String("foo".to_string()).to_pretty_str().into_string(), "\"foo\"");
assert_eq!(String("homura".into_string()).to_string(), "\"homura\"");
assert_eq!(String("madoka".into_string()).to_pretty_str(), "\"madoka\"");
}
#[test]
fn test_write_bool() {
assert_eq!(Boolean(true).to_string().into_string(), "true");
assert_eq!(Boolean(true).to_pretty_str().into_string(), "true");
assert_eq!(Boolean(true).to_string(), "true");
assert_eq!(Boolean(true).to_pretty_str(), "true");
assert_eq!(Boolean(false).to_string().into_string(), "false");
assert_eq!(Boolean(false).to_pretty_str().into_string(), "false");
assert_eq!(Boolean(false).to_string(), "false");
assert_eq!(Boolean(false).to_pretty_str(), "false");
}
#[test]
fn test_write_array() {
assert_eq!(Array(vec![]).to_string().into_string(), "[]");
assert_eq!(Array(vec![]).to_pretty_str().into_string(), "[]");
assert_eq!(Array(vec![]).to_string(), "[]");
assert_eq!(Array(vec![]).to_pretty_str(), "[]");
assert_eq!(Array(vec![Boolean(true)]).to_string().into_string(), "[true]");
assert_eq!(Array(vec![Boolean(true)]).to_string(), "[true]");
assert_eq!(
Array(vec![Boolean(true)]).to_pretty_str().into_string(),
Array(vec![Boolean(true)]).to_pretty_str(),
"\
[\n \
true\n\
@ -2565,12 +2562,12 @@ mod tests {
let long_test_array = Array(vec![
Boolean(false),
Null,
Array(vec![String("foo\nbar".to_string()), F64(3.5)])]);
Array(vec![String("foo\nbar".into_string()), F64(3.5)])]);
assert_eq!(long_test_array.to_string().into_string(),
assert_eq!(long_test_array.to_string(),
"[false,null,[\"foo\\nbar\",3.5]]");
assert_eq!(
long_test_array.to_pretty_str().into_string(),
long_test_array.to_pretty_str(),
"\
[\n \
false,\n \
@ -2585,17 +2582,17 @@ mod tests {
#[test]
fn test_write_object() {
assert_eq!(mk_object(&[]).to_string().into_string(), "{}");
assert_eq!(mk_object(&[]).to_pretty_str().into_string(), "{}");
assert_eq!(mk_object(&[]).to_string(), "{}");
assert_eq!(mk_object(&[]).to_pretty_str(), "{}");
assert_eq!(
mk_object(&[
("a".to_string(), Boolean(true))
]).to_string().into_string(),
("a".into_string(), Boolean(true))
]).to_string(),
"{\"a\":true}"
);
assert_eq!(
mk_object(&[("a".to_string(), Boolean(true))]).to_pretty_str(),
mk_object(&[("a".into_string(), Boolean(true))]).to_pretty_str(),
"\
{\n \
\"a\": true\n\
@ -2603,14 +2600,14 @@ mod tests {
);
let complex_obj = mk_object(&[
("b".to_string(), Array(vec![
mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]),
mk_object(&[("d".to_string(), String("".to_string()))])
("b".into_string(), Array(vec![
mk_object(&[("c".into_string(), String("\x0c\r".into_string()))]),
mk_object(&[("d".into_string(), String("".into_string()))])
]))
]);
assert_eq!(
complex_obj.to_string().into_string(),
complex_obj.to_string(),
"{\
\"b\":[\
{\"c\":\"\\f\\r\"},\
@ -2619,7 +2616,7 @@ mod tests {
}"
);
assert_eq!(
complex_obj.to_pretty_str().into_string(),
complex_obj.to_pretty_str(),
"\
{\n \
\"b\": [\n \
@ -2634,10 +2631,10 @@ mod tests {
);
let a = mk_object(&[
("a".to_string(), Boolean(true)),
("b".to_string(), Array(vec![
mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]),
mk_object(&[("d".to_string(), String("".to_string()))])
("a".into_string(), Boolean(true)),
("b".into_string(), Array(vec![
mk_object(&[("c".into_string(), String("\x0c\r".into_string()))]),
mk_object(&[("d".into_string(), String("".into_string()))])
]))
]);
@ -2674,7 +2671,7 @@ mod tests {
"\"Dog\""
);
let animal = Frog("Henry".to_string(), 349);
let animal = Frog("Henry".into_string(), 349);
assert_eq!(
with_str_writer(|writer| {
let mut encoder = Encoder::new(writer);
@ -2699,14 +2696,14 @@ mod tests {
#[test]
fn test_write_some() {
let value = Some("jodhpurs".to_string());
let value = Some("jodhpurs".into_string());
let s = with_str_writer(|writer| {
let mut encoder = Encoder::new(writer);
value.encode(&mut encoder).unwrap();
});
assert_eq!(s, "\"jodhpurs\"");
let value = Some("jodhpurs".to_string());
let value = Some("jodhpurs".into_string());
let s = with_str_writer(|writer| {
let mut encoder = PrettyEncoder::new(writer);
value.encode(&mut encoder).unwrap();
@ -2831,6 +2828,9 @@ mod tests {
let v: i64 = super::decode("9223372036854775807").unwrap();
assert_eq!(v, i64::MAX);
let res: DecodeResult<i64> = super::decode("765.25252");
assert_eq!(res, Err(ExpectedError("Integer".into_string(), "765.25252".into_string())));
}
#[test]
@ -2838,16 +2838,16 @@ mod tests {
assert_eq!(from_str("\""), Err(SyntaxError(EOFWhileParsingString, 1, 2)));
assert_eq!(from_str("\"lol"), Err(SyntaxError(EOFWhileParsingString, 1, 5)));
assert_eq!(from_str("\"\""), Ok(String("".to_string())));
assert_eq!(from_str("\"foo\""), Ok(String("foo".to_string())));
assert_eq!(from_str("\"\\\"\""), Ok(String("\"".to_string())));
assert_eq!(from_str("\"\\b\""), Ok(String("\x08".to_string())));
assert_eq!(from_str("\"\\n\""), Ok(String("\n".to_string())));
assert_eq!(from_str("\"\\r\""), Ok(String("\r".to_string())));
assert_eq!(from_str("\"\\t\""), Ok(String("\t".to_string())));
assert_eq!(from_str(" \"foo\" "), Ok(String("foo".to_string())));
assert_eq!(from_str("\"\\u12ab\""), Ok(String("\u12ab".to_string())));
assert_eq!(from_str("\"\\uAB12\""), Ok(String("\uAB12".to_string())));
assert_eq!(from_str("\"\""), Ok(String("".into_string())));
assert_eq!(from_str("\"foo\""), Ok(String("foo".into_string())));
assert_eq!(from_str("\"\\\"\""), Ok(String("\"".into_string())));
assert_eq!(from_str("\"\\b\""), Ok(String("\x08".into_string())));
assert_eq!(from_str("\"\\n\""), Ok(String("\n".into_string())));
assert_eq!(from_str("\"\\r\""), Ok(String("\r".into_string())));
assert_eq!(from_str("\"\\t\""), Ok(String("\t".into_string())));
assert_eq!(from_str(" \"foo\" "), Ok(String("foo".into_string())));
assert_eq!(from_str("\"\\u12ab\""), Ok(String("\u12ab".into_string())));
assert_eq!(from_str("\"\\uAB12\""), Ok(String("\uAB12".into_string())));
}
#[test]
@ -2913,7 +2913,7 @@ mod tests {
assert_eq!(t, (1u, 2, 3))
let t: (uint, string::String) = super::decode("[1, \"two\"]").unwrap();
assert_eq!(t, (1u, "two".to_string()));
assert_eq!(t, (1u, "two".into_string()));
}
#[test]
@ -2943,22 +2943,22 @@ mod tests {
assert_eq!(from_str("{}").unwrap(), mk_object(&[]));
assert_eq!(from_str("{\"a\": 3}").unwrap(),
mk_object(&[("a".to_string(), U64(3))]));
mk_object(&[("a".into_string(), U64(3))]));
assert_eq!(from_str(
"{ \"a\": null, \"b\" : true }").unwrap(),
mk_object(&[
("a".to_string(), Null),
("b".to_string(), Boolean(true))]));
("a".into_string(), Null),
("b".into_string(), Boolean(true))]));
assert_eq!(from_str("\n{ \"a\": null, \"b\" : true }\n").unwrap(),
mk_object(&[
("a".to_string(), Null),
("b".to_string(), Boolean(true))]));
("a".into_string(), Null),
("b".into_string(), Boolean(true))]));
assert_eq!(from_str(
"{\"a\" : 1.0 ,\"b\": [ true ]}").unwrap(),
mk_object(&[
("a".to_string(), F64(1.0)),
("b".to_string(), Array(vec![Boolean(true)]))
("a".into_string(), F64(1.0)),
("b".into_string(), Array(vec![Boolean(true)]))
]));
assert_eq!(from_str(
"{\
@ -2970,12 +2970,12 @@ mod tests {
]\
}").unwrap(),
mk_object(&[
("a".to_string(), F64(1.0)),
("b".to_string(), Array(vec![
("a".into_string(), F64(1.0)),
("b".into_string(), Array(vec![
Boolean(true),
String("foo\nbar".to_string()),
String("foo\nbar".into_string()),
mk_object(&[
("c".to_string(), mk_object(&[("d".to_string(), Null)]))
("c".into_string(), mk_object(&[("d".into_string(), Null)]))
])
]))
]));
@ -2994,7 +2994,7 @@ mod tests {
v,
Outer {
inner: vec![
Inner { a: (), b: 2, c: vec!["abc".to_string(), "xyz".to_string()] }
Inner { a: (), b: 2, c: vec!["abc".into_string(), "xyz".into_string()] }
]
}
);
@ -3020,7 +3020,7 @@ mod tests {
assert_eq!(value, None);
let value: Option<string::String> = super::decode("\"jodhpurs\"").unwrap();
assert_eq!(value, Some("jodhpurs".to_string()));
assert_eq!(value, Some("jodhpurs".into_string()));
}
#[test]
@ -3030,7 +3030,7 @@ mod tests {
let s = "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}";
let value: Animal = super::decode(s).unwrap();
assert_eq!(value, Frog("Henry".to_string(), 349));
assert_eq!(value, Frog("Henry".into_string(), 349));
}
#[test]
@ -3039,8 +3039,8 @@ mod tests {
\"fields\":[\"Henry\", 349]}}";
let mut map: TreeMap<string::String, Animal> = super::decode(s).unwrap();
assert_eq!(map.remove(&"a".to_string()), Some(Dog));
assert_eq!(map.remove(&"b".to_string()), Some(Frog("Henry".to_string(), 349)));
assert_eq!(map.remove(&"a".into_string()), Some(Dog));
assert_eq!(map.remove(&"b".into_string()), Some(Frog("Henry".into_string(), 349)));
}
#[test]
@ -3080,30 +3080,30 @@ mod tests {
}
#[test]
fn test_decode_errors_struct() {
check_err::<DecodeStruct>("[]", ExpectedError("Object".to_string(), "[]".to_string()));
check_err::<DecodeStruct>("[]", ExpectedError("Object".into_string(), "[]".into_string()));
check_err::<DecodeStruct>("{\"x\": true, \"y\": true, \"z\": \"\", \"w\": []}",
ExpectedError("Number".to_string(), "true".to_string()));
ExpectedError("Number".into_string(), "true".into_string()));
check_err::<DecodeStruct>("{\"x\": 1, \"y\": [], \"z\": \"\", \"w\": []}",
ExpectedError("Boolean".to_string(), "[]".to_string()));
ExpectedError("Boolean".into_string(), "[]".into_string()));
check_err::<DecodeStruct>("{\"x\": 1, \"y\": true, \"z\": {}, \"w\": []}",
ExpectedError("String".to_string(), "{}".to_string()));
ExpectedError("String".into_string(), "{}".into_string()));
check_err::<DecodeStruct>("{\"x\": 1, \"y\": true, \"z\": \"\", \"w\": null}",
ExpectedError("Array".to_string(), "null".to_string()));
ExpectedError("Array".into_string(), "null".into_string()));
check_err::<DecodeStruct>("{\"x\": 1, \"y\": true, \"z\": \"\"}",
MissingFieldError("w".to_string()));
MissingFieldError("w".into_string()));
}
#[test]
fn test_decode_errors_enum() {
check_err::<DecodeEnum>("{}",
MissingFieldError("variant".to_string()));
MissingFieldError("variant".into_string()));
check_err::<DecodeEnum>("{\"variant\": 1}",
ExpectedError("String".to_string(), "1".to_string()));
ExpectedError("String".into_string(), "1".into_string()));
check_err::<DecodeEnum>("{\"variant\": \"A\"}",
MissingFieldError("fields".to_string()));
MissingFieldError("fields".into_string()));
check_err::<DecodeEnum>("{\"variant\": \"A\", \"fields\": null}",
ExpectedError("Array".to_string(), "null".to_string()));
ExpectedError("Array".into_string(), "null".into_string()));
check_err::<DecodeEnum>("{\"variant\": \"C\", \"fields\": []}",
UnknownVariantError("C".to_string()));
UnknownVariantError("C".into_string()));
}
#[test]
@ -3388,7 +3388,7 @@ mod tests {
};
let mut decoder = Decoder::new(json_obj);
let result: Result<HashMap<uint, bool>, DecoderError> = Decodable::decode(&mut decoder);
assert_eq!(result, Err(ExpectedError("Number".to_string(), "a".to_string())));
assert_eq!(result, Err(ExpectedError("Number".into_string(), "a".into_string())));
}
fn assert_stream_equal(src: &str,
@ -3415,7 +3415,7 @@ mod tests {
r#"{ "foo":"bar", "array" : [0, 1, 2, 3, 4, 5], "idents":[null,true,false]}"#,
vec![
(ObjectStart, vec![]),
(StringValue("bar".to_string()), vec![Key("foo")]),
(StringValue("bar".into_string()), vec![Key("foo")]),
(ArrayStart, vec![Key("array")]),
(U64Value(0), vec![Key("array"), Index(0)]),
(U64Value(1), vec![Key("array"), Index(1)]),
@ -3506,7 +3506,7 @@ mod tests {
(F64Value(1.0), vec![Key("a")]),
(ArrayStart, vec![Key("b")]),
(BooleanValue(true), vec![Key("b"), Index(0)]),
(StringValue("foo\nbar".to_string()), vec![Key("b"), Index(1)]),
(StringValue("foo\nbar".into_string()), vec![Key("b"), Index(1)]),
(ObjectStart, vec![Key("b"), Index(2)]),
(ObjectStart, vec![Key("b"), Index(2), Key("c")]),
(NullValue, vec![Key("b"), Index(2), Key("c"), Key("d")]),
@ -3639,7 +3639,7 @@ mod tests {
assert!(stack.last_is_index());
assert!(stack.get(0) == Index(1));
stack.push_key("foo".to_string());
stack.push_key("foo".into_string());
assert!(stack.len() == 2);
assert!(stack.is_equal_to(&[Index(1), Key("foo")]));
@ -3651,7 +3651,7 @@ mod tests {
assert!(stack.get(0) == Index(1));
assert!(stack.get(1) == Key("foo"));
stack.push_key("bar".to_string());
stack.push_key("bar".into_string());
assert!(stack.len() == 3);
assert!(stack.is_equal_to(&[Index(1), Key("foo"), Key("bar")]));
@ -3688,8 +3688,8 @@ mod tests {
let array3 = Array(vec!(U64(1), U64(2), U64(3)));
let object = {
let mut tree_map = TreeMap::new();
tree_map.insert("a".to_string(), U64(1));
tree_map.insert("b".to_string(), U64(2));
tree_map.insert("a".into_string(), U64(1));
tree_map.insert("b".into_string(), U64(2));
Object(tree_map)
};
@ -3721,12 +3721,12 @@ mod tests {
assert_eq!((vec![1u, 2]).to_json(), array2);
assert_eq!(vec!(1u, 2, 3).to_json(), array3);
let mut tree_map = TreeMap::new();
tree_map.insert("a".to_string(), 1u);
tree_map.insert("b".to_string(), 2);
tree_map.insert("a".into_string(), 1u);
tree_map.insert("b".into_string(), 2);
assert_eq!(tree_map.to_json(), object);
let mut hash_map = HashMap::new();
hash_map.insert("a".to_string(), 1u);
hash_map.insert("b".to_string(), 2);
hash_map.insert("a".into_string(), 1u);
hash_map.insert("b".into_string(), 2);
assert_eq!(hash_map.to_json(), object);
assert_eq!(Some(15i).to_json(), I64(15));
assert_eq!(Some(15u).to_json(), U64(15));
@ -3769,7 +3769,7 @@ mod tests {
}
fn big_json() -> string::String {
let mut src = "[\n".to_string();
let mut src = "[\n".into_string();
for _ in range(0i, 500) {
src.push_str(r#"{ "a": true, "b": null, "c":3.1415, "d": "Hello world", "e": \
[1,2,3]},"#);