diff --git a/serde2/src/json/mod.rs b/serde2/src/json/mod.rs index faf57ff7..4c1c6716 100644 --- a/serde2/src/json/mod.rs +++ b/serde2/src/json/mod.rs @@ -1,6 +1,15 @@ pub use self::de::{Deserializer, from_str}; pub use self::error::{Error, ErrorCode}; -pub use self::ser::{Serializer, to_vec, to_string, escape_str}; +pub use self::ser::{ + Serializer, + to_writer, + to_writer_pretty, + to_vec, + to_vec_pretty, + to_string, + to_string_pretty, + escape_str, +}; pub use self::value::{Value, to_value, from_value}; pub mod builder; diff --git a/serde2/src/json/ser.rs b/serde2/src/json/ser.rs index 036b7e50..092f1a1f 100644 --- a/serde2/src/json/ser.rs +++ b/serde2/src/json/ser.rs @@ -8,6 +8,15 @@ use ser; /// A structure for implementing serialization to JSON. pub struct Serializer { writer: W, + format: Format, + current_indent: usize, + indent: usize, +} + +#[derive(Copy, PartialEq)] +enum Format { + Compact, + Pretty, } impl Serializer { @@ -17,6 +26,21 @@ impl Serializer { pub fn new(writer: W) -> Serializer { Serializer { writer: writer, + format: Format::Compact, + current_indent: 0, + indent: 0, + } + } + + /// Creates a new JSON visitr whose output will be written to the writer + /// specified. + #[inline] + pub fn new_pretty(writer: W) -> Serializer { + Serializer { + writer: writer, + format: Format::Pretty, + current_indent: 0, + indent: 2, } } @@ -35,26 +59,74 @@ impl ser::Serializer for Serializer { fn visit(&mut self, value: &T) -> io::Result<()> where T: ser::Serialize, { - value.visit(&mut Visitor { writer: &mut self.writer }) + value.visit(&mut Visitor { + writer: &mut self.writer, + format: self.format, + current_indent: self.current_indent, + indent: self.indent, + }) } } struct Visitor<'a, W: 'a> { writer: &'a mut W, + format: Format, + current_indent: usize, + indent: usize, } -impl<'a, W: io::Write> ser::Visitor for Visitor<'a, W> { +impl<'a, W> Visitor<'a, W> where W: io::Write, { + fn serialize_sep(&mut self, first: bool) -> io::Result<()> { + match self.format { + Format::Compact => { + if first { + Ok(()) + } else { + self.writer.write_all(b",") + } + } + Format::Pretty => { + if first { + self.current_indent += self.indent; + try!(self.writer.write_all(b"\n")); + } else { + try!(self.writer.write_all(b",\n")); + } + + spaces(&mut self.writer, self.current_indent) + } + } + } + + fn serialize_colon(&mut self) -> io::Result<()> { + match self.format { + Format::Compact => self.writer.write_all(b":"), + Format::Pretty => self.writer.write_all(b": "), + } + } + + fn serialize_end(&mut self, current_indent: usize, s: &[u8]) -> io::Result<()> { + if self.format == Format::Pretty && current_indent != self.current_indent { + self.current_indent -= self.indent; + try!(self.writer.write(b"\n")); + try!(spaces(&mut self.writer, self.current_indent)); + } + + self.writer.write_all(s) + } +} + +impl<'a, W> ser::Visitor for Visitor<'a, W> where W: io::Write, { type Value = (); type Error = io::Error; #[inline] fn visit_bool(&mut self, value: bool) -> io::Result<()> { if value { - try!(self.writer.write(b"true")); + self.writer.write_all(b"true") } else { - try!(self.writer.write(b"false")); + self.writer.write_all(b"false") } - Ok(()) } #[inline] @@ -141,50 +213,53 @@ impl<'a, W: io::Write> ser::Visitor for Visitor<'a, W> { #[inline] fn visit_unit(&mut self) -> io::Result<()> { - try!(self.writer.write(b"null")); - Ok(()) + self.writer.write_all(b"null") } #[inline] fn visit_enum_unit(&mut self, _name: &str, variant: &str) -> io::Result<()> { - try!(self.writer.write(b"{")); + let current_indent = self.current_indent; + + try!(self.writer.write_all(b"{")); + try!(self.serialize_sep(true)); try!(self.visit_str(variant)); - try!(self.writer.write(b":[]}")); - Ok(()) + try!(self.serialize_colon()); + try!(self.writer.write_all(b"[]")); + self.serialize_end(current_indent, b"}") } #[inline] fn visit_seq(&mut self, mut visitor: V) -> io::Result<()> where V: ser::SeqVisitor, { - try!(self.writer.write(b"[")); + let current_indent = self.current_indent; + + try!(self.writer.write_all(b"[")); while let Some(()) = try!(visitor.visit(self)) { } - try!(self.writer.write(b"]")); - Ok(()) + self.serialize_end(current_indent, b"]") } #[inline] fn visit_enum_seq(&mut self, _name: &str, variant: &str, visitor: V) -> io::Result<()> where V: ser::SeqVisitor, { - try!(self.writer.write(b"{")); - try!(self.visit_str(variant)); - try!(self.writer.write(b":")); - try!(self.visit_seq(visitor)); - try!(self.writer.write(b"}")); + let current_indent = self.current_indent; - Ok(()) + try!(self.writer.write_all(b"{")); + try!(self.serialize_sep(true)); + try!(self.visit_str(variant)); + try!(self.serialize_colon()); + try!(self.visit_seq(visitor)); + self.serialize_end(current_indent, b"}") } #[inline] fn visit_seq_elt(&mut self, first: bool, value: T) -> io::Result<()> where T: ser::Serialize, { - if !first { - try!(self.writer.write(b",")); - } + try!(self.serialize_sep(first)); value.visit(self) } @@ -193,26 +268,27 @@ impl<'a, W: io::Write> ser::Visitor for Visitor<'a, W> { fn visit_map(&mut self, mut visitor: V) -> io::Result<()> where V: ser::MapVisitor, { - try!(self.writer.write(b"{")); + let current_indent = self.current_indent; + + try!(self.writer.write_all(b"{")); while let Some(()) = try!(visitor.visit(self)) { } - try!(self.writer.write(b"}")); - - Ok(()) + self.serialize_end(current_indent, b"}") } #[inline] fn visit_enum_map(&mut self, _name: &str, variant: &str, visitor: V) -> io::Result<()> where V: ser::MapVisitor, { - try!(self.writer.write(b"{")); - try!(self.visit_str(variant)); - try!(self.writer.write(b":")); - try!(self.visit_map(visitor)); - try!(self.writer.write(b"}")); + let current_indent = self.current_indent; - Ok(()) + try!(self.writer.write_all(b"{")); + try!(self.serialize_sep(true)); + try!(self.visit_str(variant)); + try!(self.serialize_colon()); + try!(self.visit_map(visitor)); + self.serialize_end(current_indent, b"}") } #[inline] @@ -220,14 +296,10 @@ impl<'a, W: io::Write> ser::Visitor for Visitor<'a, W> { where K: ser::Serialize, V: ser::Serialize, { - if !first { - try!(self.writer.write(b",")); - } - + try!(self.serialize_sep(first)); try!(key.visit(self)); - try!(self.writer.write(b":")); - try!(value.visit(self)); - Ok(()) + try!(self.serialize_colon()); + value.visit(self) } } @@ -235,7 +307,7 @@ impl<'a, W: io::Write> ser::Visitor for Visitor<'a, W> { pub fn escape_bytes(wr: &mut W, bytes: &[u8]) -> io::Result<()> where W: io::Write { - try!(wr.write(b"\"")); + try!(wr.write_all(b"\"")); let mut start = 0; @@ -252,19 +324,19 @@ pub fn escape_bytes(wr: &mut W, bytes: &[u8]) -> io::Result<()> }; if start < i { - try!(wr.write(&bytes[start..i])); + try!(wr.write_all(&bytes[start..i])); } - try!(wr.write(escaped)); + try!(wr.write_all(escaped)); start = i + 1; } if start != bytes.len() { - try!(wr.write(&bytes[start..])); + try!(wr.write_all(&bytes[start..])); } - try!(wr.write(b"\"")); + try!(wr.write_all(b"\"")); Ok(()) } @@ -288,22 +360,18 @@ fn fmt_f32_or_null(wr: &mut W, value: f32) -> io::Result<()> where W: io::Write { match value.classify() { - FpCategory::Nan | FpCategory::Infinite => try!(wr.write(b"null")), - _ => try!(wr.write(f32::to_str_digits(value, 6).as_bytes())), - }; - - Ok(()) + FpCategory::Nan | FpCategory::Infinite => wr.write_all(b"null"), + _ => wr.write_all(f32::to_str_digits(value, 6).as_bytes()), + } } fn fmt_f64_or_null(wr: &mut W, value: f64) -> io::Result<()> where W: io::Write { match value.classify() { - FpCategory::Nan | FpCategory::Infinite => try!(wr.write(b"null")), - _ => try!(wr.write(f64::to_str_digits(value, 6).as_bytes())), - }; - - Ok(()) + FpCategory::Nan | FpCategory::Infinite => wr.write_all(b"null"), + _ => wr.write_all(f64::to_str_digits(value, 6).as_bytes()), + } } /// Encode the specified struct into a json `[u8]` writer. @@ -317,6 +385,17 @@ pub fn to_writer(writer: &mut W, value: &T) -> io::Result<()> Ok(()) } +/// Encode the specified struct into a json `[u8]` writer. +#[inline] +pub fn to_writer_pretty(writer: &mut W, value: &T) -> io::Result<()> + where W: io::Write, + T: ser::Serialize, +{ + let mut ser = Serializer::new_pretty(writer); + try!(ser::Serializer::visit(&mut ser, value)); + Ok(()) +} + /// Encode the specified struct into a json `[u8]` buffer. #[inline] pub fn to_vec(value: &T) -> Vec @@ -329,6 +408,18 @@ pub fn to_vec(value: &T) -> Vec writer } +/// Encode the specified struct into a json `[u8]` buffer. +#[inline] +pub fn to_vec_pretty(value: &T) -> Vec + where T: ser::Serialize, +{ + // We are writing to a Vec, which doesn't fail. So we can ignore + // the error. + let mut writer = Vec::with_capacity(128); + to_writer_pretty(&mut writer, value).unwrap(); + writer +} + /// Encode the specified struct into a json `String` buffer. #[inline] pub fn to_string(value: &T) -> Result @@ -337,3 +428,30 @@ pub fn to_string(value: &T) -> Result let vec = to_vec(value); String::from_utf8(vec) } + +/// Encode the specified struct into a json `String` buffer. +#[inline] +pub fn to_string_pretty(value: &T) -> Result + where T: ser::Serialize +{ + let vec = to_vec_pretty(value); + String::from_utf8(vec) +} + +fn spaces(wr: &mut W, mut n: usize) -> io::Result<()> + where W: io::Write, +{ + const LEN: usize = 16; + const BUF: &'static [u8; LEN] = &[b' '; 16]; + + while n >= LEN { + try!(wr.write_all(BUF)); + n -= LEN; + } + + if n > 0 { + wr.write_all(&BUF[..n]) + } else { + Ok(()) + } +} diff --git a/serde2/tests/test_json.rs b/serde2/tests/test_json.rs index e69dbb7e..7c31414a 100644 --- a/serde2/tests/test_json.rs +++ b/serde2/tests/test_json.rs @@ -54,9 +54,9 @@ struct Outer { inner: Vec, } -fn test_encode_ok< - T: PartialEq + Debug + ser::Serialize ->(errors: &[(T, &str)]) { +fn test_encode_ok(errors: &[(T, &str)]) + where T: PartialEq + Debug + ser::Serialize, +{ for &(ref value, out) in errors { let out = out.to_string(); @@ -69,21 +69,20 @@ fn test_encode_ok< } } -/* -fn test_pretty_encode_ok< - T: PartialEq + Debug + ser::Serialize>, io::Error> ->(errors: &[(T, &str)]) { +fn test_pretty_encode_ok(errors: &[(T, &str)]) + where T: PartialEq + Debug + ser::Serialize, +{ for &(ref value, out) in errors { let out = out.to_string(); - let s = json::to_pretty_string(value).unwrap(); + let s = json::to_string_pretty(value).unwrap(); assert_eq!(s, out); - let s = json::to_pretty_string(&value.to_json()).unwrap(); + let v = to_value(&value); + let s = json::to_string_pretty(&v).unwrap(); assert_eq!(s, out); } } -*/ #[test] fn test_write_null() { @@ -91,7 +90,7 @@ fn test_write_null() { ((), "null"), ]; test_encode_ok(tests); - //test_pretty_encode_ok(tests); + test_pretty_encode_ok(tests); } #[test] @@ -102,7 +101,7 @@ fn test_write_i64() { (-1234i64, "-1234"), ]; test_encode_ok(tests); - //test_pretty_encode_ok(tests); + test_pretty_encode_ok(tests); } #[test] @@ -114,7 +113,7 @@ fn test_write_f64() { (0.5, "0.5"), ]; test_encode_ok(tests); - //test_pretty_encode_ok(tests); + test_pretty_encode_ok(tests); } #[test] @@ -124,7 +123,7 @@ fn test_write_str() { ("foo", "\"foo\""), ]; test_encode_ok(tests); - //test_pretty_encode_ok(tests); + test_pretty_encode_ok(tests); } #[test] @@ -134,22 +133,21 @@ fn test_write_bool() { (false, "false"), ]; test_encode_ok(tests); - //test_pretty_encode_ok(tests); + test_pretty_encode_ok(tests); } #[test] fn test_write_list() { test_encode_ok(&[ - (vec!(), "[]"), - (vec!(true), "[true]"), - (vec!(true, false), "[true,false]"), + (vec![], "[]"), + (vec![true], "[true]"), + (vec![true, false], "[true,false]"), ]); - /* test_pretty_encode_ok(&[ - (vec!(), "[]"), + (vec![], "[]"), ( - vec!(true), + vec![true], concat!( "[\n", " true\n", @@ -157,7 +155,7 @@ fn test_write_list() { ), ), ( - vec!(true, false), + vec![true, false], concat!( "[\n", " true,\n", @@ -166,7 +164,6 @@ fn test_write_list() { ), ), ]); - */ let long_test_list = Value::Array(vec![ Value::Bool(false), @@ -174,15 +171,12 @@ fn test_write_list() { Value::Array(vec![Value::String("foo\nbar".to_string()), Value::F64(3.5)])]); test_encode_ok(&[ - (long_test_list, "[false,null,[\"foo\\nbar\",3.5]]"), + ( + long_test_list.clone(), + "[false,null,[\"foo\\nbar\",3.5]]", + ), ]); - /* - let long_test_list = Value::Array(vec![ - Value::Bool(false), - Value::Null, - Value::Array(vec![Value::String("foo\nbar".to_string()), Value::F64(3.5)])]); - test_pretty_encode_ok(&[ ( long_test_list, @@ -195,10 +189,9 @@ fn test_write_list() { " 3.5\n", " ]\n", "]" - ) + ), ) ]); - */ } #[test] @@ -214,7 +207,6 @@ fn test_write_object() { "{\"a\":true,\"b\":false}"), ]); - /* test_pretty_encode_ok(&[ (treemap!(), "{}"), ( @@ -238,13 +230,12 @@ fn test_write_object() { ), ), ]); - */ let complex_obj = Value::Object(treemap!( - "b".to_string() => Value::Array(vec!( + "b".to_string() => Value::Array(vec![ Value::Object(treemap!("c".to_string() => Value::String("\x0c\r".to_string()))), Value::Object(treemap!("d".to_string() => Value::String("".to_string()))) - )) + ]) )); test_encode_ok(&[ @@ -259,7 +250,6 @@ fn test_write_object() { ), ]); - /* test_pretty_encode_ok(&[ ( complex_obj.clone(), @@ -277,7 +267,6 @@ fn test_write_object() { ), ) ]); - */ } #[test] @@ -289,7 +278,6 @@ fn test_write_tuple() { ), ]); - /* test_pretty_encode_ok(&[ ( (5,), @@ -300,7 +288,6 @@ fn test_write_tuple() { ), ), ]); - */ test_encode_ok(&[ ( @@ -309,7 +296,6 @@ fn test_write_tuple() { ), ]); - /* test_pretty_encode_ok(&[ ( (5, (6, "abc")), @@ -324,7 +310,6 @@ fn test_write_tuple() { ), ), ]); - */ } #[test] @@ -335,15 +320,15 @@ fn test_write_enum() { "{\"Dog\":[]}", ), ( - Animal::Frog("Henry".to_string(), vec!()), + Animal::Frog("Henry".to_string(), vec![]), "{\"Frog\":[\"Henry\",[]]}", ), ( - Animal::Frog("Henry".to_string(), vec!(349)), + Animal::Frog("Henry".to_string(), vec![349]), "{\"Frog\":[\"Henry\",[349]]}", ), ( - Animal::Frog("Henry".to_string(), vec!(349, 102)), + Animal::Frog("Henry".to_string(), vec![349, 102]), "{\"Frog\":[\"Henry\",[349,102]]}", ), ( @@ -352,7 +337,6 @@ fn test_write_enum() { ), ]); - /* test_pretty_encode_ok(&[ ( Animal::Dog, @@ -363,7 +347,7 @@ fn test_write_enum() { ), ), ( - Animal::Frog("Henry".to_string(), vec!()), + Animal::Frog("Henry".to_string(), vec![]), concat!( "{\n", " \"Frog\": [\n", @@ -374,7 +358,7 @@ fn test_write_enum() { ), ), ( - Animal::Frog("Henry".to_string(), vec!(349)), + Animal::Frog("Henry".to_string(), vec![349]), concat!( "{\n", " \"Frog\": [\n", @@ -387,7 +371,7 @@ fn test_write_enum() { ), ), ( - Animal::Frog("Henry".to_string(), vec!(349, 102)), + Animal::Frog("Henry".to_string(), vec![349, 102]), concat!( "{\n", " \"Frog\": [\n", @@ -401,7 +385,6 @@ fn test_write_enum() { ), ), ]); - */ } #[test] @@ -413,10 +396,9 @@ fn test_write_option() { test_encode_ok(&[ (None, "null"), - (Some(vec!("foo", "bar")), "[\"foo\",\"bar\"]"), + (Some(vec!["foo", "bar"]), "[\"foo\",\"bar\"]"), ]); - /* test_pretty_encode_ok(&[ (None, "null"), (Some("jodhpurs"), "\"jodhpurs\""), @@ -425,7 +407,7 @@ fn test_write_option() { test_pretty_encode_ok(&[ (None, "null"), ( - Some(vec!("foo", "bar")), + Some(vec!["foo", "bar"]), concat!( "[\n", " \"foo\",\n", @@ -434,7 +416,6 @@ fn test_write_option() { ), ), ]); - */ } fn test_parse_ok<'a, T>(errors: &[(&'a str, T)]) @@ -730,11 +711,11 @@ fn test_parse_enum() { (" { \"Dog\" : [ ] } ", Animal::Dog), ( "{\"Frog\":[\"Henry\",[]]}", - Animal::Frog("Henry".to_string(), vec!()), + Animal::Frog("Henry".to_string(), vec![]), ), ( " { \"Frog\": [ \"Henry\" , [ 349, 102 ] ] } ", - Animal::Frog("Henry".to_string(), vec!(349, 102)), + Animal::Frog("Henry".to_string(), vec![349, 102]), ), ( "{\"Cat\": {\"age\": 5, \"name\": \"Kate\"}}", @@ -746,7 +727,6 @@ fn test_parse_enum() { ), ]); - /* test_parse_ok(&[ ( concat!( @@ -757,11 +737,10 @@ fn test_parse_enum() { ), treemap!( "a".to_string() => Animal::Dog, - "b".to_string() => Animal::Frog("Henry".to_string(), vec!()) + "b".to_string() => Animal::Frog("Henry".to_string(), vec![]) ) ), ]); - */ } #[test] diff --git a/serde2/tests/test_json_builder.rs b/serde2/tests/test_json_builder.rs index ed1733d5..354f9440 100644 --- a/serde2/tests/test_json_builder.rs +++ b/serde2/tests/test_json_builder.rs @@ -1,5 +1,3 @@ -#![feature(test)] - extern crate serde2; use std::collections::BTreeMap;