Repository: maciejhirsz/json-rust (rust)

Issue:
JsonValue::contains() on objects
It would be nice to either add a `contains_key` method for objects or extend `contains` to work with object keys. 

Pull request description:
0.10.2
Closes #86, #88, #89

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 1382a8f..cc37567 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "json"
-version = "0.10.1"
+version = "0.10.2"
 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 37440d9..5926f6e 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 Parse and serialize [JSON](http://json.org/) with ease.
 
 **[Changelog](https://github.com/maciejhirsz/json-rust/releases) -**
-**[Complete Documentation](http://json.rs/doc/json/) -**
+**[Complete Documentation](http://terhix.com/doc/json/) -**
 **[Cargo](https://crates.io/crates/json) -**
 **[Repository](https://github.com/maciejhirsz/json-rust)**
 
diff --git a/src/codegen.rs b/src/codegen.rs
index d611991..1929b85 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 std::io;
 
 use util::print_dec;
 
@@ -42,19 +43,19 @@ pub trait Generator {
     fn get_writer(&mut self) -> &mut Self::T;
 
     #[inline(always)]
-    fn write(&mut self, slice: &[u8]) {
-        self.get_writer().write_all(slice).unwrap();
+    fn write(&mut self, slice: &[u8]) -> io::Result<()> {
+        self.get_writer().write_all(slice)
     }
 
     #[inline(always)]
-    fn write_char(&mut self, ch: u8) {
-        self.get_writer().write_all(&[ch]).unwrap();
+    fn write_char(&mut self, ch: u8) -> io::Result<()> {
+        self.get_writer().write_all(&[ch])
     }
 
-    fn write_min(&mut self, slice: &[u8], min: u8);
+    fn write_min(&mut self, slice: &[u8], min: u8) -> io::Result<()>;
 
     #[inline(always)]
-    fn new_line(&mut self) {}
+    fn new_line(&mut self) -> io::Result<()> { Ok(()) }
 
     #[inline(always)]
     fn indent(&mut self) {}
@@ -63,28 +64,28 @@ pub trait Generator {
     fn dedent(&mut self) {}
 
     #[inline(never)]
-    fn write_string_complex(&mut self, string: &str, mut start: usize) {
-        self.write(string[ .. start].as_bytes());
+    fn write_string_complex(&mut self, string: &str, mut start: usize) -> io::Result<()> {
+        try!(self.write(string[ .. start].as_bytes()));
 
         for (index, ch) in string.bytes().enumerate().skip(start) {
             let escape = ESCAPED[ch as usize];
             if escape > 0 {
-                self.write(string[start .. index].as_bytes());
-                self.write(&[b'\\', escape]);
+                try!(self.write(string[start .. index].as_bytes()));
+                try!(self.write(&[b'\\', escape]));
                 start = index + 1;
             }
             if escape == b'u' {
-                write!(self.get_writer(), "{:04x}", ch).unwrap();
+                try!(write!(self.get_writer(), "{:04x}", ch));
             }
         }
-        self.write(string[start ..].as_bytes());
+        try!(self.write(string[start ..].as_bytes()));
 
-        self.write_char(b'"');
+        self.write_char(b'"')
     }
 
     #[inline(always)]
-    fn write_string(&mut self, string: &str) {
-        self.write_char(b'"');
+    fn write_string(&mut self, string: &str) -> io::Result<()> {
+        try!(self.write_char(b'"'));
 
         for (index, ch) in string.bytes().enumerate() {
             if ESCAPED[ch as usize] > 0 {
@@ -92,15 +93,14 @@ pub trait Generator {
             }
         }
 
-        self.write(string.as_bytes());
-        self.write_char(b'"');
+        try!(self.write(string.as_bytes()));
+        self.write_char(b'"')
     }
 
     #[inline(always)]
-    fn write_number(&mut self, num: &Number) {
+    fn write_number(&mut self, num: &Number) -> io::Result<()> {
         if num.is_nan() {
-            self.write(b"null");
-            return;
+            return self.write(b"null");
         }
         let (positive, mantissa, exponent) = num.as_parts();
         unsafe {
@@ -109,11 +109,11 @@ pub trait Generator {
                 positive,
                 mantissa,
                 exponent
-            ).unwrap();
+            )
         }
     }
 
-    fn write_json(&mut self, json: &JsonValue) {
+    fn write_json(&mut self, json: &JsonValue) -> io::Result<()> {
         match *json {
             JsonValue::Null               => self.write(b"null"),
             JsonValue::Short(ref short)   => self.write_string(short.as_str()),
@@ -122,54 +122,54 @@ pub trait Generator {
             JsonValue::Boolean(true)      => self.write(b"true"),
             JsonValue::Boolean(false)     => self.write(b"false"),
             JsonValue::Array(ref array)   => {
-                self.write_char(b'[');
+                try!(self.write_char(b'['));
                 let mut iter = array.iter();
 
                 if let Some(item) = iter.next() {
                     self.indent();
-                    self.new_line();
-                    self.write_json(item);
+                    try!(self.new_line());
+                    try!(self.write_json(item));
                 } else {
-                    self.write_char(b']');
-                    return;
+                    try!(self.write_char(b']'));
+                    return Ok(());
                 }
 
                 for item in iter {
-                    self.write_char(b',');
-                    self.new_line();
-                    self.write_json(item);
+                    try!(self.write_char(b','));
+                    try!(self.new_line());
+                    try!(self.write_json(item));
                 }
 
                 self.dedent();
-                self.new_line();
-                self.write_char(b']');
+                try!(self.new_line());
+                self.write_char(b']')
             },
             JsonValue::Object(ref object) => {
-                self.write_char(b'{');
+                try!(self.write_char(b'{'));
                 let mut iter = object.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);
+                    try!(self.new_line());
+                    try!(self.write_string(key));
+                    try!(self.write_min(b": ", b':'));
+                    try!(self.write_json(value));
                 } else {
-                    self.write_char(b'}');
-                    return;
+                    try!(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);
+                    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();
-                self.new_line();
-                self.write_char(b'}');
+                try!(self.new_line());
+                self.write_char(b'}')
             }
         }
     }
@@ -196,13 +196,15 @@ impl DumpGenerator {
 impl Generator for DumpGenerator {
     type T = Vec<u8>;
 
-    fn write(&mut self, slice: &[u8]) {
+    fn write(&mut self, slice: &[u8]) -> io::Result<()> {
         extend_from_slice(&mut self.code, slice);
+        Ok(())
     }
 
     #[inline(always)]
-    fn write_char(&mut self, ch: u8) {
-        self.code.push(ch)
+    fn write_char(&mut self, ch: u8) -> io::Result<()> {
+        self.code.push(ch);
+        Ok(())
     }
 
     #[inline(always)]
@@ -211,8 +213,9 @@ impl Generator for DumpGenerator {
     }
 
     #[inline(always)]
-    fn write_min(&mut self, _: &[u8], min: u8) {
+    fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
         self.code.push(min);
+        Ok(())
     }
 }
 
@@ -240,13 +243,15 @@ impl Generator for PrettyGenerator {
     type T = Vec<u8>;
 
     #[inline(always)]
-    fn write(&mut self, slice: &[u8]) {
+    fn write(&mut self, slice: &[u8]) -> io::Result<()> {
         extend_from_slice(&mut self.code, slice);
+        Ok(())
     }
 
     #[inline(always)]
-    fn write_char(&mut self, ch: u8) {
-        self.code.push(ch)
+    fn write_char(&mut self, ch: u8) -> io::Result<()> {
+        self.code.push(ch);
+        Ok(())
     }
 
     #[inline(always)]
@@ -255,15 +260,17 @@ impl Generator for PrettyGenerator {
     }
 
     #[inline(always)]
-    fn write_min(&mut self, slice: &[u8], _: u8) {
-        self.code.extend_from_slice(slice);
+    fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
+        extend_from_slice(&mut self.code, slice);
+        Ok(())
     }
 
-    fn new_line(&mut self) {
+    fn new_line(&mut self) -> io::Result<()> {
         self.code.push(b'\n');
         for _ in 0..(self.dent * self.spaces_per_indent) {
             self.code.push(b' ');
         }
+        Ok(())
     }
 
     fn indent(&mut self) {
@@ -296,8 +303,55 @@ impl<'a, W> Generator for WriterGenerator<'a, W> where W: Write {
     }
 
     #[inline(always)]
-    fn write_min(&mut self, _: &[u8], min: u8) {
-        self.writer.write_all(&[min]).unwrap();
+    fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
+        self.writer.write_all(&[min])
+    }
+}
+
+
+pub struct PrettyWriterGenerator<'a, W: 'a + Write> {
+    writer: &'a mut W,
+    dent: u16,
+    spaces_per_indent: u16,
+}
+
+impl<'a, W> PrettyWriterGenerator<'a, W> where W: 'a + Write {
+    pub fn new(writer: &'a mut W, spaces: u16) -> Self {
+        PrettyWriterGenerator {
+            writer: writer,
+            dent: 0,
+            spaces_per_indent: spaces,
+        }
+    }
+}
+
+impl<'a, W> Generator for PrettyWriterGenerator<'a, W> where W: Write {
+    type T = W;
+
+    #[inline(always)]
+    fn get_writer(&mut self) -> &mut W {
+        &mut self.writer
+    }
+
+    #[inline(always)]
+    fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
+        self.writer.write_all(slice)
+    }
+
+    fn new_line(&mut self) -> io::Result<()> {
+        try!(self.write_char(b'\n'));
+        for _ in 0..(self.dent * self.spaces_per_indent) {
+            try!(self.write_char(b' '));
+        }
+        Ok(())
+    }
+
+    fn indent(&mut self) {
+        self.dent += 1;
+    }
+
+    fn dedent(&mut self) {
+        self.dent -= 1;
     }
 }
 
diff --git a/src/value/mod.rs b/src/value/mod.rs
index 0f452b0..bd882ec 100644
--- a/src/value/mod.rs
+++ b/src/value/mod.rs
@@ -2,13 +2,13 @@ use { Result, Error };
 
 use std::ops::{ Index, IndexMut, Deref };
 use std::{ fmt, mem, usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32 };
-use std::io::Write;
+use std::io::{ self, Write };
 
 use short::Short;
 use number::Number;
 use object::Object;
 use iterators::{ Members, MembersMut, Entries, EntriesMut };
-use codegen::{ Generator, PrettyGenerator, DumpGenerator, WriterGenerator };
+use codegen::{ Generator, PrettyGenerator, DumpGenerator, WriterGenerator, PrettyWriterGenerator };
 
 mod implements;
 
@@ -90,7 +90,7 @@ impl JsonValue {
     /// Prints out the value as JSON string.
     pub fn dump(&self) -> String {
         let mut gen = DumpGenerator::new();
-        gen.write_json(self);
+        gen.write_json(self).expect("Can't fail");
         gen.consume()
     }
 
@@ -98,14 +98,29 @@ impl JsonValue {
     /// number of spaces to indent new blocks with.
     pub fn pretty(&self, spaces: u16) -> String {
         let mut gen = PrettyGenerator::new(spaces);
-        gen.write_json(self);
+        gen.write_json(self).expect("Can't fail");
         gen.consume()
     }
 
-    /// Dumps the JSON as byte stream into an instance of `std::io::Write`.
+    /// Writes the JSON as byte stream into an implementor of `std::io::Write`.
+    ///
+    /// This method is deprecated as it will panic on io errors, use `write` instead.
+    #[deprecated(since="0.10.2", note="use `JsonValue::write` instead")]
     pub fn to_writer<W: Write>(&self, writer: &mut W) {
         let mut gen = WriterGenerator::new(writer);
-        gen.write_json(self);
+        gen.write_json(self).expect("Deprecated");
+    }
+
+    /// Writes the JSON as byte stream into an implementor of `std::io::Write`.
+    pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+        let mut gen = WriterGenerator::new(writer);
+        gen.write_json(self)
+    }
+
+    /// Writes the JSON as byte stream into an implementor of `std::io::Write`.
+    pub fn write_pretty<W: Write>(&self, writer: &mut W, spaces: u16) -> io::Result<()> {
+        let mut gen = PrettyWriterGenerator::new(writer, spaces);
+        gen.write_json(self)
     }
 
     pub fn is_string(&self) -> bool {
@@ -375,12 +390,18 @@ impl JsonValue {
     }
 
     /// Works on `JsonValue::Array` - checks if the array contains a value
-    pub fn contains<T>(&self, item: T) -> bool where T: Into<JsonValue> {
+    pub fn contains<T>(&self, item: T) -> bool where T: PartialEq<JsonValue> {
         match *self {
-            JsonValue::Array(ref vec) => {
-                vec.contains(&item.into())
-            },
-            _ => false
+            JsonValue::Array(ref vec) => vec.iter().any(|member| item == *member),
+            _                         => false
+        }
+    }
+
+    /// Works on `JsonValue::Object` - checks if the object has a key
+    pub fn has_key(&self, key: &str) -> bool {
+        match *self {
+            JsonValue::Object(ref object) => object.get(key).is_some(),
+            _                             => false
         }
     }
 


Test patch:
diff --git a/tests/value.rs b/tests/value.rs
index 6ba0fa3..77a0326 100644
--- a/tests/value.rs
+++ b/tests/value.rs
@@ -517,7 +517,20 @@ fn writer_generator() {
 
     let mut buf = Vec::new();
 
-    data.to_writer(&mut buf);
+    data.write(&mut buf).expect("Can't fail with a Vec");
 
     assert_eq!(String::from_utf8(buf).unwrap(), r#"{"foo":["bar",100,true]}"#);
 }
+
+#[test]
+fn pretty_writer_generator() {
+    let data = object!{
+        "foo" => array!["bar", 100, true]
+    };
+
+    let mut buf = Vec::new();
+
+    data.write_pretty(&mut buf, 4).expect("Can't fail with a Vec");
+
+    assert_eq!(String::from_utf8(buf).unwrap(), "{\n    \"foo\": [\n        \"bar\",\n        100,\n        true\n    ]\n}");
+}