Repository: maciejhirsz/json-rust
Pull request #10 (2016-06-19 14:03:27)

Fix pretty printing, add stringify_pretty methods
I fixed your pretty printing which was kind of broken. It now matches the behaviour of http://jsonprettyprint.com (I don't know if there is a standard for that).

I implemented stringify_pretty functions while not breaking any of your public api. I added a test for pretty formating.

Are you interested in another PR implementing a function that works like this:

```rust
let j = json::JsonValue; // Let's pretend this is correct and it contains values.
println!("{}", j.dump(2)); // Will dump pretty json with indent 2.
println!("{}", j.dump(-1)); // Will dump minified json. Could also be 0.

// or as an alternative
println!("{}", j.dump()); // Will dump minified json.
println!("{}", j.print(2)); // Will dump pretty json unless you input 0.
```

That's the way nlohmann/json does it (in c++) and I really like it!


Patch:
diff --git a/src/codegen.rs b/src/codegen.rs
index 027414e..0526842 100644
--- a/src/codegen.rs
+++ b/src/codegen.rs
@@ -4,22 +4,24 @@ pub struct Generator {
     pub minify: bool,
     code: String,
     dent: u16,
+    step: u16
 }
 
 impl Generator {
-    pub fn new(minify: bool) -> Self {
+    pub fn new(minify: bool, step : u16) -> Self {
         Generator {
             minify: minify,
             code: String::new(),
             dent: 0,
+            step: step
         }
     }
 
     pub fn new_line(&mut self) {
         if !self.minify {
             self.code.push('\n');
-            for _ in 0..self.dent {
-                self.code.push_str("    ");
+            for _ in 0..(self.dent*self.step) {
+                self.code.push_str(" ");
             }
         }
     }
@@ -51,26 +53,33 @@ impl Generator {
             JsonValue::Null               => self.write("null"),
             JsonValue::Array(ref array)   => {
                 self.write_char('[');
+                self.indent();
                 let mut first = true;
                 for item in array {
                     if first {
                         first = false;
+                        self.new_line();
                     } else {
-                        self.write_min(", ", ",");
+                        self.write(",");
+                        self.new_line();
                     }
                     self.write_json(item);
                 }
+                self.dedent();
+                self.new_line();
                 self.write_char(']');
             },
             JsonValue::Object(ref object) => {
-                let mut first = true;
                 self.write_char('{');
                 self.indent();
+                let mut first = true;
                 for (key, value) in object.iter() {
                     if first {
                         first = false;
+                        self.new_line();
                     } else {
-                        self.write_min(", ", ",");
+                        self.write(",");
+                        self.new_line();
                     }
                     self.write(&format!("{:?}", key));
                     self.write_min(": ", ":");
diff --git a/src/error.rs b/src/error.rs
index 19355b2..ca9dc84 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,6 +1,4 @@
 use parser::Token;
-use std::error::Error;
-use std::fmt;
 
 #[derive(Debug)]
 pub enum JsonError {
@@ -26,34 +24,3 @@ impl JsonError {
         JsonError::UndefinedField(field.into())
     }
 }
-
-impl fmt::Display for JsonError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use JsonError::*;
-        
-        match *self {
-            UnexpectedToken(ref s) => write!(f, "Unexpected token: {}", s),
-            UnexpectedCharacter(c) => write!(f, "Unexpected character: {}", c),
-            UnexpectedEndOfJson => write!(f, "Unexpected end of JSON"),
-            CantCastCodepointToCharacter(i) => write!(f, "Cannot cast this codepoint to a character: {}", i),
-            ArrayIndexOutOfBounds => write!(f, "Array index out of bounds!"),
-            WrongType(ref s) => write!(f, "Wrong type: {}", s),
-            UndefinedField(ref s) => write!(f, "Undefined field: {}", s)
-        }
-    }
-}
-
-impl Error for JsonError {
-    fn description(&self) -> &str {
-        use JsonError::*;        
-        match *self {
-            UnexpectedToken(_) => "Unexpected token",
-            UnexpectedCharacter(_) => "Unexpected character",
-            UnexpectedEndOfJson => "Unexpected end of JSON",
-            CantCastCodepointToCharacter(_) => "Cannot cast this codepoint to character",
-            ArrayIndexOutOfBounds => "Array index out of bounds!",
-            WrongType(_) => "Wrong type",
-            UndefinedField(_) => "Undefined field",
-        }
-    }
-}
diff --git a/src/lib.rs b/src/lib.rs
index e9a05c9..5b14bc8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -182,13 +182,24 @@ pub type Array = Vec<JsonValue>;
 pub type Object = BTreeMap<String, JsonValue>;
 
 pub fn stringify_ref(root: &JsonValue) -> String {
-    let mut gen = Generator::new(true);
+    let mut gen = Generator::new(true, 4);
     gen.write_json(root);
     gen.consume()
 }
 
 pub fn stringify<T>(root: T) -> String where T: Into<JsonValue> {
-    let mut gen = Generator::new(true);
+    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()
+}
+
+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()
 }


Test patch:
diff --git a/tests/lib.rs b/tests/lib.rs
index c605e32..e1090c9 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -3,7 +3,7 @@ extern crate json;
 use std::collections::BTreeMap;
 use std::collections::HashMap;
 
-use json::{ stringify, parse, JsonValue, Null };
+use json::{ stringify, stringify_pretty, parse, JsonValue, Null };
 
 #[test]
 fn is_as_string() {
@@ -183,6 +183,24 @@ fn stringify_escaped_characters() {
     assert_eq!(stringify("\r\n\t\u{8}\u{c}\\/\""), r#""\r\n\t\b\f\\\/\"""#);
 }
 
+#[test]
+fn stringify_pretty_object() {
+    let object = object!{
+        "name" => "Maciej",
+        "age" => 30,
+        "parents" => object!{
+            "mother" => "Helga",
+            "father" => "Brutus"
+        },
+        "cars" => array![ "Golf", "Mercedes", "Porsche" ]
+    };
+
+    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\": \
+                \"Brutus\",\n    \"mother\": \"Helga\"\n  }\n}");
+}
+
 #[test]
 fn parse_true() {
     assert!(parse("true").unwrap().is(true));