Repository: maciejhirsz/json-rust (rust)

Issue:
Implement `dump` and `pretty` for `Object`
I found myself several times needing to use `dump` on a `&Object` I had at hand. This is currently not possible without a clone, which is a shame.

Pull request description:
Resolves #131 - Implement pretty and dump for Object

Patch:
diff --git a/src/codegen.rs b/src/codegen.rs
index 1929b85..ea296ff 100644
--- a/src/codegen.rs
+++ b/src/codegen.rs
@@ -2,6 +2,7 @@ use std::ptr;
 use std::io::Write;
 use JsonValue;
 use number::Number;
+use object::Object;
 use std::io;
 
 use util::print_dec;
@@ -113,6 +114,34 @@ pub trait Generator {
         }
     }
 
+    fn write_object(&mut self, object: &Object) -> io::Result<()> {
+        try!(self.write_char(b'{'));
+        let mut iter = object.iter();
+
+        if let Some((key, value)) = iter.next() {
+            self.indent();
+            try!(self.new_line());
+            try!(self.write_string(key));
+            try!(self.write_min(b": ", b':'));
+            try!(self.write_json(value));
+        } else {
+            try!(self.write_char(b'}'));
+            return Ok(());
+        }
+
+        for (key, value) in iter {
+            try!(self.write_char(b','));
+            try!(self.new_line());
+            try!(self.write_string(key));
+            try!(self.write_min(b": ", b':'));
+            try!(self.write_json(value));
+        }
+
+        self.dedent();
+        try!(self.new_line());
+        self.write_char(b'}')
+    }
+
     fn write_json(&mut self, json: &JsonValue) -> io::Result<()> {
         match *json {
             JsonValue::Null               => self.write(b"null"),
@@ -145,31 +174,7 @@ pub trait Generator {
                 self.write_char(b']')
             },
             JsonValue::Object(ref object) => {
-                try!(self.write_char(b'{'));
-                let mut iter = object.iter();
-
-                if let Some((key, value)) = iter.next() {
-                    self.indent();
-                    try!(self.new_line());
-                    try!(self.write_string(key));
-                    try!(self.write_min(b": ", b':'));
-                    try!(self.write_json(value));
-                } else {
-                    try!(self.write_char(b'}'));
-                    return Ok(());
-                }
-
-                for (key, value) in iter {
-                    try!(self.write_char(b','));
-                    try!(self.new_line());
-                    try!(self.write_string(key));
-                    try!(self.write_min(b": ", b':'));
-                    try!(self.write_json(value));
-                }
-
-                self.dedent();
-                try!(self.new_line());
-                self.write_char(b'}')
+                self.write_object(object)
             }
         }
     }
diff --git a/src/object.rs b/src/object.rs
index d344587..e1bc5dc 100644
--- a/src/object.rs
+++ b/src/object.rs
@@ -1,6 +1,7 @@
 use std::{ ptr, mem, str, slice, fmt };
 use std::ops::{ Index, IndexMut, Deref };
 
+use codegen::{ DumpGenerator, Generator, PrettyGenerator };
 use value::JsonValue;
 
 const KEY_BUF_LEN: usize = 32;
@@ -490,6 +491,21 @@ impl Object {
             inner: self.store.iter_mut()
         }
     }
+
+    /// Prints out the value as JSON string.
+    pub fn dump(&self) -> String {
+        let mut gen = DumpGenerator::new();
+        gen.write_object(self).expect("Can't fail");
+        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(&self, spaces: u16) -> String {
+        let mut gen = PrettyGenerator::new(spaces);
+        gen.write_object(self).expect("Can't fail");
+        gen.consume()
+    }
 }
 
 // Custom implementation of `Clone`, as new heap allocation means


Test patch:
diff --git a/tests/stringify.rs b/tests/stringify.rs
index e031b9f..d70c39e 100644
--- a/tests/stringify.rs
+++ b/tests/stringify.rs
@@ -144,6 +144,7 @@ fn stringify_object() {
         "age" => 30
     };
 
+    assert_eq!(object.dump(), r#"{"name":"Maciej","age":30}"#);
     assert_eq!(stringify(object), r#"{"name":"Maciej","age":30}"#);
 }
 
@@ -154,6 +155,7 @@ fn stringify_raw_object() {
     object.insert("name", "Maciej".into());
     object.insert("age", 30.into());
 
+    assert_eq!(object.dump(), r#"{"name":"Maciej","age":30}"#);
     assert_eq!(stringify(object), r#"{"name":"Maciej","age":30}"#);
 }
 
@@ -193,6 +195,7 @@ fn stringify_object_with_put() {
     object["a"] = 100.into();
     object["b"] = false.into();
 
+    assert_eq!(object.dump(), r#"{"a":100,"b":false}"#);
     assert_eq!(stringify(object), r#"{"a":100,"b":false}"#);
 }
 
@@ -240,6 +243,7 @@ fn stringify_pretty_object() {
         "cars" => array![ "Golf", "Mercedes", "Porsche" ]
     };
 
-    assert_eq!(stringify_pretty(object, 2),
-               "{\n  \"name\": \"Urlich\",\n  \"age\": 50,\n  \"parents\": {\n    \"mother\": \"Helga\",\n    \"father\": \"Brutus\"\n  },\n  \"cars\": [\n    \"Golf\",\n    \"Mercedes\",\n    \"Porsche\"\n  ]\n}");
+    let expected = "{\n  \"name\": \"Urlich\",\n  \"age\": 50,\n  \"parents\": {\n    \"mother\": \"Helga\",\n    \"father\": \"Brutus\"\n  },\n  \"cars\": [\n    \"Golf\",\n    \"Mercedes\",\n    \"Porsche\"\n  ]\n}";
+    assert_eq!(object.pretty(2), expected);
+    assert_eq!(stringify_pretty(object, 2), expected);
 }