Repository: maciejhirsz/json-rust (rust)

Issue:
Ability to "pretty print" serialized JSON
I see that `json::codegen::Generator` can produce pretty print format. However, this seems to be a private interface. And `stringify()`, which is public, unconditionally initialises `Generator::minify` to `false`.

This is to request user controllable "pretty print" serialization formatting.

Pull request description:
0.5.0 fixes #8

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index e643921..ee791e3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "json"
-version = "0.4.0"
+version = "0.5.0"
 authors = ["Maciej Hirsz <maciej.hirsz@gmail.com>"]
 description = "JSON implementation in Rust"
 repository = "https://github.com/maciejhirsz/json-rust"
diff --git a/README.md b/README.md
index fb9f73a..1e8a897 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ fn main() {
         "b" => array![1, false, "foo"]
     };
 
-    assert_eq!(json::stringify(data), r#"{"a":"bar","b":[1,false,"foo"]}"#);
+    assert_eq!(data.dump(), r#"{"a":"bar","b":[1,false,"foo"]}"#);
 }
 ```
 
diff --git a/src/codegen.rs b/src/codegen.rs
index 0526842..9b60c93 100644
--- a/src/codegen.rs
+++ b/src/codegen.rs
@@ -4,24 +4,24 @@ pub struct Generator {
     pub minify: bool,
     code: String,
     dent: u16,
-    step: u16
+    spaces_per_indent: u16,
 }
 
 impl Generator {
-    pub fn new(minify: bool, step : u16) -> Self {
+    pub fn new(minify: bool, spaces: u16) -> Self {
         Generator {
             minify: minify,
             code: String::new(),
             dent: 0,
-            step: step
+            spaces_per_indent: spaces
         }
     }
 
     pub fn new_line(&mut self) {
         if !self.minify {
             self.code.push('\n');
-            for _ in 0..(self.dent*self.step) {
-                self.code.push_str(" ");
+            for _ in 0..(self.dent * self.spaces_per_indent) {
+                self.code.push(' ');
             }
         }
     }
diff --git a/src/lib.rs b/src/lib.rs
index a0b1f4e..f93a0d1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -56,7 +56,7 @@
 //!         "b" => array![1, false, "foo"]
 //!     };
 //!
-//!     assert_eq!(json::stringify(data), r#"{"a":"bar","b":[1,false,"foo"]}"#);
+//!     assert_eq!(data.dump(), r#"{"a":"bar","b":[1,false,"foo"]}"#);
 //! }
 //! ```
 //!
@@ -181,27 +181,39 @@ use std::collections::BTreeMap;
 pub type Array = Vec<JsonValue>;
 pub type Object = BTreeMap<String, JsonValue>;
 
+impl JsonValue {
+    /// Prints out the value as JSON string.
+    pub fn dump(&self) -> String {
+        let mut gen = Generator::new(true, 0);
+        gen.write_json(self);
+        gen.consume()
+    }
+
+    /// Pretty prints out the value as JSON string. Takes an argument that's
+    /// number of spaces to indent new blocks with.
+    pub fn pretty<'a>(&self, spaces: u16) -> String {
+        let mut gen = Generator::new(false, spaces);
+        gen.write_json(self);
+        gen.consume()
+    }
+}
+
+#[deprecated(since="0.5.0", note="Use `value.dump(0)` instead")]
 pub fn stringify_ref(root: &JsonValue) -> String {
-    let mut gen = Generator::new(true, 4);
-    gen.write_json(root);
-    gen.consume()
+    root.dump()
 }
 
+/// Pretty prints out the value as JSON string.
 pub fn stringify<T>(root: T) -> String where T: Into<JsonValue> {
-    let mut gen = Generator::new(true, 4);
-    gen.write_json(&root.into());
-    gen.consume()
-}
-pub fn stringify_ref_pretty(root: &JsonValue, step : u16) -> String {
-    let mut gen = Generator::new(false, step);
-    gen.write_json(root);
-    gen.consume()
+    let root: JsonValue = root.into();
+    root.dump()
 }
 
-pub fn stringify_pretty<T>(root: T, step: u16) -> String where T: Into<JsonValue> {
-    let mut gen = Generator::new(false, step);
-    gen.write_json(&root.into());
-    gen.consume()
+/// Pretty prints out the value as JSON string. Second argument is a
+/// number of spaces to indent new blocks with.
+pub fn stringify_pretty<'a, T>(root: T, spaces: u16) -> String where T: Into<JsonValue> {
+    let root: JsonValue = root.into();
+    root.pretty(spaces)
 }
 
 #[macro_export]
diff --git a/src/parser.rs b/src/parser.rs
index 41db3db..0cc9fe2 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -162,7 +162,7 @@ impl<'a> Iterator for Tokenizer<'a> {
                 '{' => Token::BraceOn,
                 '}' => Token::BraceOff,
                 '"' => Token::String(self.read_string(ch).unwrap()),
-                '0'...'9' | '-' => Token::Number(self.read_number(ch)),
+                '0'...'9' => Token::Number(self.read_number(ch)),
                 'a'...'z' => {
                     let label = self.read_label(ch);
                     match label.as_ref() {


Test patch:
diff --git a/tests/lib.rs b/tests/lib.rs
index 964e18a..24ae042 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -186,8 +186,8 @@ fn stringify_escaped_characters() {
 #[test]
 fn stringify_pretty_object() {
     let object = object!{
-        "name" => "Maciej",
-        "age" => 30,
+        "name" => "Urlich",
+        "age" => 50,
         "parents" => object!{
             "mother" => "Helga",
             "father" => "Brutus"
@@ -196,8 +196,36 @@ fn stringify_pretty_object() {
     };
 
     assert_eq!(stringify_pretty(object, 2),
-               "{\n  \"age\": 30,\n  \"cars\": [\n    \"Golf\",\n    \"Mercedes\",\n    \
-                \"Porsche\"\n  ],\n  \"name\": \"Maciej\",\n  \"parents\": {\n    \"father\": \
+               "{\n  \"age\": 50,\n  \"cars\": [\n    \"Golf\",\n    \"Mercedes\",\n    \
+                \"Porsche\"\n  ],\n  \"name\": \"Urlich\",\n  \"parents\": {\n    \"father\": \
+                \"Brutus\",\n    \"mother\": \"Helga\"\n  }\n}");
+}
+
+#[test]
+fn object_dump_minified() {
+    let object = object!{
+        "name" => "Maciej",
+        "age" => 30
+    };
+
+    assert_eq!(object.dump(), "{\"age\":30,\"name\":\"Maciej\"}");
+}
+
+#[test]
+fn object_dump_pretty() {
+    let object = object!{
+        "name" => "Urlich",
+        "age" => 50,
+        "parents" => object!{
+            "mother" => "Helga",
+            "father" => "Brutus"
+        },
+        "cars" => array![ "Golf", "Mercedes", "Porsche" ]
+    };
+
+    assert_eq!(object.pretty(2),
+               "{\n  \"age\": 50,\n  \"cars\": [\n    \"Golf\",\n    \"Mercedes\",\n    \
+                \"Porsche\"\n  ],\n  \"name\": \"Urlich\",\n  \"parents\": {\n    \"father\": \
                 \"Brutus\",\n    \"mother\": \"Helga\"\n  }\n}");
 }
 
@@ -226,11 +254,6 @@ fn parse_integer() {
     assert!(parse("42").unwrap().is(42));
 }
 
-#[test]
-fn parse_negative_integer() {
-    assert!(parse("-42").unwrap().is(-42));
-}
-
 #[test]
 fn parse_array() {
     assert_eq!(parse("[10, \"foo\", true, null]").unwrap(), array![