Repository: maciejhirsz/json-rust (rust)

Issue:
Implement Index for Object
Sometimes I like to pass around JSON `Object`'s after filtering them out of a bigger JSON structure.

As it stands right now, if I want to access its fields with the same easy indexing syntax, I first have to put i `JsonValue` again, which feels kind of pointless.

Would you accept a PR that also implements `Index` directly for `Object`?

Pull request:
Implement `Index` and `IndexMut` for `Object`
closes #104

Patch:
diff --git a/src/object.rs b/src/object.rs
index 3d68073..b77feaa 100644
--- a/src/object.rs
+++ b/src/object.rs
@@ -1,8 +1,10 @@
 use std::{ ptr, mem, str, slice, fmt };
+use std::ops::{ Index, IndexMut, Deref };
 
 use value::JsonValue;
 
 const KEY_BUF_LEN: usize = 32;
+static NULL: JsonValue = JsonValue::Null;
 
 struct Node {
     // Internal buffer to store keys that fit within `KEY_BUF_LEN`,
@@ -575,3 +577,90 @@ impl<'a> DoubleEndedIterator for IterMut<'a> {
         self.inner.next_back().map(|node| (node.key_str(), &mut node.value))
     }
 }
+
+/// Implements indexing by `&str` to easily access object members:
+///
+/// ## Example
+///
+/// ```
+/// # #[macro_use]
+/// # extern crate json;
+/// # use json::JsonValue;
+/// #
+/// # fn main() {
+/// let value = object!{
+///     "foo" => "bar"
+/// };
+///
+/// if let JsonValue::Object(object) = value {
+///   assert!(object["foo"] == "bar");
+/// }
+/// # }
+/// ```
+// TODO: doc
+impl<'a> Index<&'a str> for Object {
+    type Output = JsonValue;
+
+    fn index(&self, index: &str) -> &JsonValue {
+            match self.get(index) {
+                Some(value) => value,
+                _ => &NULL
+            }
+    }
+}
+
+impl Index<String> for Object {
+    type Output = JsonValue;
+
+    fn index(&self, index: String) -> &JsonValue {
+        self.index(index.deref())
+    }
+}
+
+impl<'a> Index<&'a String> for Object {
+    type Output = JsonValue;
+
+    fn index(&self, index: &String) -> &JsonValue {
+        self.index(index.deref())
+    }
+}
+
+/// Implements mutable indexing by `&str` to easily modify object members:
+///
+/// ## Example
+///
+/// ```
+/// # #[macro_use]
+/// # extern crate json;
+/// # use json::JsonValue;
+/// #
+/// # fn main() {
+/// let value = object!{};
+///
+/// if let JsonValue::Object(mut object) = value {
+///   object["foo"] = 42.into();
+///
+///   assert!(object["foo"] == 42);
+/// }
+/// # }
+/// ```
+impl<'a> IndexMut<&'a str> for Object {
+    fn index_mut(&mut self, index: &str) -> &mut JsonValue {
+        if self.get(index).is_none() {
+            self.insert(index, JsonValue::Null);
+        }
+        self.get_mut(index).unwrap()
+    }
+}
+
+impl IndexMut<String> for Object {
+    fn index_mut(&mut self, index: String) -> &mut JsonValue {
+        self.index_mut(index.deref())
+    }
+}
+
+impl<'a> IndexMut<&'a String> for Object {
+    fn index_mut(&mut self, index: &String) -> &mut JsonValue {
+        self.index_mut(index.deref())
+    }
+}
diff --git a/src/value/mod.rs b/src/value/mod.rs
index 3597ee8..2d1f58b 100644
--- a/src/value/mod.rs
+++ b/src/value/mod.rs
@@ -586,10 +586,7 @@ impl<'a> Index<&'a str> for JsonValue {
 
     fn index(&self, index: &str) -> &JsonValue {
         match *self {
-            JsonValue::Object(ref object) => match object.get(index) {
-                Some(value) => value,
-                _ => &NULL
-            },
+            JsonValue::Object(ref object) => &object[index],
             _ => &NULL
         }
     }
@@ -631,10 +628,7 @@ impl<'a> IndexMut<&'a str> for JsonValue {
     fn index_mut(&mut self, index: &str) -> &mut JsonValue {
         match *self {
             JsonValue::Object(ref mut object) => {
-                if object.get(index).is_none() {
-                    object.insert(index, JsonValue::Null);
-                }
-                object.get_mut(index).unwrap()
+                &mut object[index]
             },
             _ => {
                 *self = JsonValue::new_object();


Test patch:
diff --git a/tests/value.rs b/tests/value.rs
index 0cd385a..e508e8d 100644
--- a/tests/value.rs
+++ b/tests/value.rs
@@ -429,6 +429,83 @@ fn index_mut_by_string_ref() {
     assert_eq!(data["foo"], "bar");
 }
 
+#[test]
+fn object_index_by_str() {
+    let val = object!{
+        "foo" => "bar"
+    };
+    if let JsonValue::Object(data) = val {
+        assert_eq!(data["foo"], "bar");
+    }
+}
+
+#[test]
+fn object_index_by_string() {
+    let val = object!{
+        "foo" => "bar"
+    };
+
+    if let JsonValue::Object(data) = val {
+        assert_eq!(data["foo".to_string()], "bar");
+    }
+}
+
+#[test]
+fn object_index_by_string_ref() {
+    let val = object!{
+        "foo" => "bar"
+    };
+
+    let key = "foo".to_string();
+    let ref key_ref = key;
+
+    if let JsonValue::Object(data) = val {
+        assert_eq!(data[key_ref], "bar");
+    }
+}
+
+#[test]
+fn object_index_mut_by_str() {
+    let val = object!{
+        "foo" => Null
+    };
+
+    if let JsonValue::Object(mut data) = val {
+        data["foo"] = "bar".into();
+
+        assert_eq!(data["foo"], "bar");
+    }
+}
+
+#[test]
+fn object_index_mut_by_string() {
+    let val = object!{
+        "foo" => Null
+    };
+
+    if let JsonValue::Object(mut data) = val {
+        data["foo".to_string()] = "bar".into();
+
+        assert_eq!(data["foo"], "bar");
+    }
+}
+
+#[test]
+fn object_index_mut_by_string_ref() {
+    let val = object!{
+        "foo" => Null
+    };
+
+    let key = "foo".to_string();
+    let ref key_ref = key;
+
+    if let JsonValue::Object(mut data) = val {
+        data[key_ref] = "bar".into();
+
+        assert_eq!(data["foo"], "bar");
+    }
+}
+
 #[test]
 fn fmt_string() {
     let data: JsonValue = "foobar".into();