Repository: maciejhirsz/json-rust (rust)

Issue:
Feat request: Custom Generator
Hi,

Thank you for you project!
I have a feature request : expose the Generator trait in order to create a custom PrettyWriter

Pull request description:
Custom Generator impl by publishing codegen mod
I added one test as an example and proof that it works.

Fixes #171

Patch:
diff --git a/src/codegen.rs b/src/codegen.rs
index a0e7807..b937c6b 100644
--- a/src/codegen.rs
+++ b/src/codegen.rs
@@ -38,6 +38,7 @@ static ESCAPED: [u8; 256] = [
   __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
 ];
 
+/// Default trait for serializing JSONValue into string.
 pub trait Generator {
     type T: Write;
 
@@ -181,6 +182,7 @@ pub trait Generator {
     }
 }
 
+/// In-Memory Generator, this uses a Vec to store the JSON result.
 pub struct DumpGenerator {
     code: Vec<u8>,
 }
@@ -225,6 +227,7 @@ impl Generator for DumpGenerator {
     }
 }
 
+/// Pretty In-Memory Generator, this uses a Vec to store the JSON result and add indent.
 pub struct PrettyGenerator {
     code: Vec<u8>,
     dent: u16,
@@ -288,6 +291,7 @@ impl Generator for PrettyGenerator {
     }
 }
 
+/// Writer Generator, this uses a custom writer to store the JSON result.
 pub struct WriterGenerator<'a, W: 'a + Write> {
     writer: &'a mut W
 }
@@ -314,7 +318,7 @@ impl<'a, W> Generator for WriterGenerator<'a, W> where W: Write {
     }
 }
 
-
+/// Pretty Writer Generator, this uses a custom writer to store the JSON result and add indent.
 pub struct PrettyWriterGenerator<'a, W: 'a + Write> {
     writer: &'a mut W,
     dent: u16,
diff --git a/src/lib.rs b/src/lib.rs
index ae4ca22..d4bab94 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -198,7 +198,7 @@
 
 use std::result;
 
-mod codegen;
+pub mod codegen;
 mod parser;
 mod value;
 mod error;


Test patch:
diff --git a/tests/customgen.rs b/tests/customgen.rs
new file mode 100644
index 0000000..aeb6fdb
--- /dev/null
+++ b/tests/customgen.rs
@@ -0,0 +1,90 @@
+#[macro_use]
+extern crate json;
+
+use json::codegen::Generator;
+use json::object::{self, Object};
+use json::{JsonValue, Null};
+use std::io;
+
+/// Custom generator that sort keys by name; based on DumpGenerator.
+pub struct CustomGenerator {
+    code: Vec<u8>,
+}
+
+impl CustomGenerator {
+    pub fn new() -> Self {
+        CustomGenerator {
+            code: Vec::with_capacity(1024),
+        }
+    }
+
+    pub fn consume(self) -> String {
+        // Original strings were unicode, numbers are all ASCII,
+        // therefore this is safe.
+        unsafe { String::from_utf8_unchecked(self.code) }
+    }
+}
+
+impl Generator for CustomGenerator {
+    type T = Vec<u8>;
+    #[inline(always)]
+    fn get_writer(&mut self) -> &mut Vec<u8> {
+        &mut self.code
+    }
+
+    #[inline(always)]
+    fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
+        self.code.push(min);
+        Ok(())
+    }
+    #[inline(always)]
+    fn write_object(&mut self, object: &Object) -> io::Result<()> {
+        self.write_char(b'{')?;
+        let mut entries: Vec<(&str, &JsonValue)> = Vec::new();
+        for (k, v) in object.iter() {
+            entries.push((k, v));
+        }
+
+        entries.sort_by(|(k1, _), (k2, _)| k1.partial_cmp(k2).unwrap());
+
+        let mut iter = entries.iter();
+        if let Some((key, value)) = iter.next() {
+            self.indent();
+            self.new_line()?;
+            self.write_string(key)?;
+            self.write_min(b": ", b':')?;
+            self.write_json(value)?;
+        } else {
+            self.write_char(b'}')?;
+            return Ok(());
+        }
+
+        for (key, value) in iter {
+            self.write_char(b',')?;
+            self.new_line()?;
+            self.write_string(key)?;
+            self.write_min(b": ", b':')?;
+            self.write_json(value)?;
+        }
+
+        self.dedent();
+        self.new_line()?;
+        self.write_char(b'}')
+    }
+}
+
+#[test]
+fn object_keys_sorted() {
+    let o = object! {
+        "c" => Null,
+        "b" => Null,
+        "a" => Null,
+    };
+    let mut gen = CustomGenerator::new();
+    gen.write_json(&o).expect("Can't fail");
+    let json = gen.consume();
+    let dump = o.dump();
+    assert_eq!(json, r#"{"a":null,"b":null,"c":null}"#);
+    assert_eq!(dump, r#"{"c":null,"b":null,"a":null}"#);
+    assert_ne!(json, dump);
+}