Repository: maciejhirsz/json-rust (rust)

Issue:
Using Strings as index
Right now it's kind of verbose to do this:

``` rust
let j_test = object!{
    "hi" => 123,
    "na" => 345
};
let mut j_test2 = json::JsonValue::new_object();
for (key, val) in j_test.entries() {
    j_test2["subsub"][key] = val.as_u32().into();
}
```

Error:
``` rust
trait bound `json::JsonValue: std::ops::Index<&std::string::String>` is not satisfied
```

You have to write
``` rust
j_test2["subsub"][&**key] = val.as_u32().into();
```

And if you use ref you have to write:

``` rust
for (ref key, ref val) in j_test.entries() {
    j_test2["subsub"][&***key] = val.as_u32().into();
}
```

As an idea for improvements. 

Pull request:
0.7.3
Closes #28 

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index acc06e3..6be768d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "json"
-version = "0.7.2"
+version = "0.7.3"
 authors = ["Maciej Hirsz <maciej.hirsz@gmail.com>"]
 description = "JSON implementation in Rust"
 repository = "https://github.com/maciejhirsz/json-rust"
diff --git a/src/codegen.rs b/src/codegen.rs
index 53e2ddd..04799e1 100644
--- a/src/codegen.rs
+++ b/src/codegen.rs
@@ -1,6 +1,8 @@
 use JsonValue;
 
 pub trait Generator {
+    fn current_index(&self) -> usize;
+
     fn new_line(&mut self) {}
 
     fn write(&mut self, slice: &[u8]);
@@ -34,31 +36,35 @@ pub trait Generator {
         self.write_char(b'"');
     }
 
-    fn write_digits_from_u64(&mut self, mut num: u64, length: &mut u8) {
+    fn write_digits_from_u64(&mut self, mut num: u64) {
         let digit = (num % 10) as u8;
         if num > 9 {
             num /= 10;
-            self.write_digits_from_u64(num, length);
+            self.write_digits_from_u64(num);
         }
-        *length += 1;
         self.write_char(digit + b'0');
     }
 
     fn write_number(&mut self, mut num: f64) {
-        let mut length = 0;
         if num < 0.0 {
             num = -num;
             self.write_char(b'-');
         }
 
-        self.write_digits_from_u64(num as u64, &mut length);
+        let start = self.current_index();
+
+        self.write_digits_from_u64(num as u64);
 
         let mut fract = num.fract();
+
         if fract < 1e-16 {
             return;
         }
 
+        let mut length = self.current_index() - start;
+
         fract *= 10.0;
+
         self.write_char(b'.');
         self.write_char((fract as u8) + b'0');
         fract = fract.fract();
@@ -76,7 +82,8 @@ pub trait Generator {
         match *json {
             JsonValue::String(ref string) => self.write_string(string),
             JsonValue::Number(ref number) => self.write_number(*number),
-            JsonValue::Boolean(ref value) => self.write(if *value { b"true" } else { b"false" }),
+            JsonValue::Boolean(true)      => self.write(b"true"),
+            JsonValue::Boolean(false)     => self.write(b"false"),
             JsonValue::Null               => self.write(b"null"),
             JsonValue::Array(ref array)   => {
                 self.write_char(b'[');
@@ -135,6 +142,10 @@ impl DumpGenerator {
 }
 
 impl Generator for DumpGenerator {
+    fn current_index(&self) -> usize {
+        self.code.len()
+    }
+
     fn write(&mut self, slice: &[u8]) {
         self.code.extend_from_slice(slice);
     }
@@ -169,6 +180,10 @@ impl PrettyGenerator {
 }
 
 impl Generator for PrettyGenerator {
+    fn current_index(&self) -> usize {
+        self.code.len()
+    }
+
     fn new_line(&mut self) {
         self.code.push(b'\n');
         for _ in 0..(self.dent * self.spaces_per_indent) {
diff --git a/src/lib.rs b/src/lib.rs
index 456860d..5020ffd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,12 +15,12 @@
 //! some keys under some conditions. Mapping that to idiomatic Rust structs
 //! introduces friction.
 //!
-//! This crate intends to avoid that friction by extensively using static dispatch
-//! and hiding type information behind enums, while still giving you all the
-//! guarantees of safe Rust code.
+//! This crate intends to avoid that friction.
 //!
-//! ```
-//! let data = json::parse(r#"
+//! ```rust
+//! # #[macro_use] extern crate json;
+//! # fn main() {
+//! let parsed = json::parse(r#"
 //!
 //! {
 //!     "code": 200,
@@ -36,51 +36,64 @@
 //!
 //! "#).unwrap();
 //!
-//! assert!(data["code"] == 200);
-//! assert!(data["success"] == true);
-//! assert!(data["payload"]["features"].is_array());
-//! assert!(data["payload"]["features"][0] == "awesome");
-//! assert!(data["payload"]["features"].contains("easyAPI"));
+//! let instantiated = object!{
+//!     "code" => 200,
+//!     "success" => true,
+//!     "payload" => object!{
+//!         "features" => array![
+//!             "awesome",
+//!             "easyAPI",
+//!             "lowLearningCurve"
+//!         ]
+//!     }
+//! };
 //!
-//! // Error resilient: non-existent values default to null
-//! assert!(data["this"]["does"]["not"]["exist"].is_null());
+//! assert_eq!(parsed, instantiated);
+//! # }
 //! ```
 //!
-//! ## Create JSON data without defining structs
+//! ## First class citizen
 //!
-//! ```
-//! #[macro_use]
-//! extern crate json;
+//! Using macros and easy indexing, it's easy to work with the data.
 //!
-//! fn main() {
-//!     let data = object!{
-//!         "a" => "bar",
-//!         "b" => array![1, false, "foo"]
-//!     };
+//! ```rust
+//! # #[macro_use] extern crate json;
+//! # fn main() {
+//! let mut data = object!{
+//!     "foo" => false,
+//!     "bar" => json::Null,
+//!     "answer" => 42,
+//!     "list" => array![json::Null, "world", true]
+//! };
 //!
-//!     assert_eq!(data.dump(), r#"{"a":"bar","b":[1,false,"foo"]}"#);
-//! }
-//! ```
+//! // Partial equality is implemented for most raw types:
+//! assert!(data["foo"] == false);
 //!
-//! ## Mutate simply by assigning new values
+//! // And it's type aware, `null` and `false` are different values:
+//! assert!(data["bar"] != false);
 //!
-//! ```
-//! let mut data = json::parse(r#"
+//! // But you can use any Rust number types:
+//! assert!(data["answer"] == 42);
+//! assert!(data["answer"] == 42.0);
+//! assert!(data["answer"] == 42isize);
 //!
-//! {
-//!     "name": "Bob",
-//!     "isAwesome": false
-//! }
+//! // Access nested structures, arrays and objects:
+//! assert!(data["list"][0].is_null());
+//! assert!(data["list"][1] == "world");
+//! assert!(data["list"][2] == true);
 //!
-//! "#).unwrap();
+//! // Error resilient - accessing properties that don't exist yield null:
+//! assert!(data["this"]["does"]["not"]["exist"].is_null());
 //!
-//! data["isAwesome"] = true.into();
-//! data["likes"] = "Rust".into();
+//! // Mutate by assigning:
+//! data["list"][0] = "Hello".into();
 //!
-//! assert_eq!(data.dump(), r#"{"isAwesome":true,"likes":"Rust","name":"Bob"}"#);
+//! // Use the `dump` method to serialize the data:
+//! assert_eq!(data.dump(), r#"{"answer":42,"bar":null,"foo":false,"list":["Hello","world",true]}"#);
 //!
-//! // Pretty print the output
+//! // Or pretty print it out:
 //! println!("{:#}", data);
+//! # }
 //! ```
 //!
 //! ## Serialize with `json::stringify(value)`
diff --git a/src/parser.rs b/src/parser.rs
index c164840..e242ec2 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,5 +1,6 @@
 use std::char;
 use std::str;
+use std::iter::Enumerate;
 use std::str::Bytes;
 use std::collections::BTreeMap;
 use { JsonValue, JsonError, JsonResult };
@@ -18,10 +19,10 @@ pub enum Token {
     Null,
 }
 
-macro_rules! expect_char {
+macro_rules! sequence {
     ($tok:ident, $( $ch:pat ),*) => {
         $(
-            match $tok.source.next() {
+            match $tok.next_byte() {
                 Some($ch) => {},
                 Some(ch)  => return Err(JsonError::unexpected_character(ch)),
                 None      => return Err(JsonError::UnexpectedEndOfJson)
@@ -48,36 +49,48 @@ macro_rules! read_num {
 }
 
 struct Tokenizer<'a> {
-    source: Bytes<'a>,
+    source: Enumerate<Bytes<'a>>,
     buffer: Vec<u8>,
     left_over: Option<u8>,
+    current_index: usize,
 }
 
 impl<'a> Tokenizer<'a> {
     pub fn new(source: &'a str) -> Self {
         Tokenizer {
-            source: source.bytes(),
+            source: source.bytes().enumerate(),
             buffer: Vec::with_capacity(512),
             left_over: None,
+            current_index: 0,
         }
     }
 
+    #[inline(always)]
+    fn next_byte(&mut self) -> Option<u8> {
+        self.source.next().map(|(index, byte)| {
+            self.current_index = index;
+            byte
+        })
+    }
+
+    #[inline(always)]
     fn peek_byte(&mut self) -> Option<u8> {
         if self.left_over.is_none() {
-            self.left_over = self.source.next();
+            self.left_over = self.next_byte();
         }
 
         return self.left_over;
     }
 
-    fn next_byte(&mut self) -> Option<u8> {
+    #[inline(always)]
+    fn checked_next_byte(&mut self) -> Option<u8> {
         if self.left_over.is_some() {
             let byte = self.left_over;
             self.left_over = None;
             return byte;
         }
 
-        self.source.next()
+        self.next_byte()
     }
 
     #[inline(always)]
@@ -85,6 +98,11 @@ impl<'a> Tokenizer<'a> {
         self.next_byte().ok_or(JsonError::UnexpectedEndOfJson)
     }
 
+    #[inline(always)]
+    fn checked_expect_byte(&mut self) -> JsonResult<u8> {
+        self.checked_next_byte().ok_or(JsonError::UnexpectedEndOfJson)
+    }
+
     fn read_char_as_hexnumber(&mut self) -> JsonResult<u32> {
         let ch = try!(self.expect_byte());
         Ok(match ch {
@@ -162,8 +180,7 @@ impl<'a> Tokenizer<'a> {
         let mut num = num as f64;
 
         if let Some(b'.') = self.peek_byte() {
-            self.next_byte();
-
+            self.left_over = None;
             let mut precision = -1;
 
             read_num!(self, digit, {
@@ -172,7 +189,7 @@ impl<'a> Tokenizer<'a> {
             });
         }
 
-        match self.next_byte() {
+        match self.checked_next_byte() {
             Some(b'e') | Some(b'E') => {
                 let mut e = 0;
                 let sign = match self.next_byte() {
@@ -184,7 +201,15 @@ impl<'a> Tokenizer<'a> {
                     },
                 };
 
-                read_num!(self, digit, e = e * 10 + digit as i32);
+                while let Some(ch) = self.checked_next_byte() {
+                    match ch {
+                        b'0' ... b'9' => e = e * 10 + (ch - b'0') as i32,
+                        ch => {
+                            self.left_over = Some(ch);
+                            break;
+                        }
+                    }
+                }
 
                 num *= 10f64.powi(e * sign);
             },
@@ -196,7 +221,7 @@ impl<'a> Tokenizer<'a> {
 
     fn next(&mut self) -> JsonResult<Token> {
         loop {
-            let ch = try!(self.expect_byte());
+            let ch = try!(self.checked_expect_byte());
             return Ok(match ch {
                 b',' => Token::Comma,
                 b':' => Token::Colon,
@@ -207,15 +232,15 @@ impl<'a> Tokenizer<'a> {
                 b'"' => Token::String(try!(self.read_string())),
                 b'0' ... b'9' | b'-' => Token::Number(try!(self.read_number(ch))),
                 b't' => {
-                    expect_char!(self, b'r', b'u', b'e');
+                    sequence!(self, b'r', b'u', b'e');
                     Token::Boolean(true)
                 },
                 b'f' => {
-                    expect_char!(self, b'a', b'l', b's', b'e');
+                    sequence!(self, b'a', b'l', b's', b'e');
                     Token::Boolean(false)
                 },
                 b'n' => {
-                    expect_char!(self, b'u', b'l', b'l');
+                    sequence!(self, b'u', b'l', b'l');
                     Token::Null
                 },
                 // whitespace
@@ -228,14 +253,14 @@ impl<'a> Tokenizer<'a> {
 
 macro_rules! expect {
     ($parser:ident, $token:pat => $value:ident) => (
-        match $parser.consume() {
+        match $parser.tokenizer.next() {
             Ok($token) => $value,
             Ok(token)  => return Err(JsonError::unexpected_token(token)),
             Err(error) => return Err(error),
         }
     );
     ($parser:ident, $token:pat) => ({
-        match $parser.consume() {
+        match $parser.tokenizer.next() {
             Ok($token) => {},
             Ok(token)  => return Err(JsonError::unexpected_token(token)),
             Err(error) => return Err(error),
@@ -254,10 +279,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn consume(&mut self) -> JsonResult<Token> {
-        self.tokenizer.next()
-    }
-
     #[must_use]
     fn ensure_end(&mut self) -> JsonResult<()> {
         match self.tokenizer.next() {
@@ -268,15 +289,15 @@ impl<'a> Parser<'a> {
     }
 
     fn array(&mut self) -> JsonResult<JsonValue> {
-        let mut array = Vec::new();
+        let mut array = Vec::with_capacity(20);
 
-        match try!(self.consume()) {
+        match try!(self.tokenizer.next()) {
             Token::BracketOff => return Ok(JsonValue::Array(array)),
             token             => array.push(try!(self.value_from(token))),
         }
 
         loop {
-            match try!(self.consume()) {
+            match try!(self.tokenizer.next()) {
                 Token::Comma => {
                     array.push(try!(self.value()));
                     continue
@@ -292,7 +313,7 @@ impl<'a> Parser<'a> {
     fn object(&mut self) -> JsonResult<JsonValue> {
         let mut object = BTreeMap::new();
 
-        match try!(self.consume()) {
+        match try!(self.tokenizer.next()) {
             Token::BraceOff    => return Ok(JsonValue::Object(object)),
             Token::String(key) => {
                 expect!(self, Token::Colon);
@@ -302,7 +323,7 @@ impl<'a> Parser<'a> {
         }
 
         loop {
-            match try!(self.consume()) {
+            match try!(self.tokenizer.next()) {
                 Token::Comma => {
                     let key = expect!(self,
                         Token::String(key) => key
@@ -334,7 +355,7 @@ impl<'a> Parser<'a> {
     }
 
     fn value(&mut self) -> JsonResult<JsonValue> {
-        let token = try!(self.consume());
+        let token = try!(self.tokenizer.next());
         self.value_from(token)
     }
 }
diff --git a/src/value.rs b/src/value.rs
index 4eaba76..7054ecc 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -1,5 +1,5 @@
 use std::collections::BTreeMap;
-use std::ops::{ Index, IndexMut };
+use std::ops::{ Index, IndexMut, Deref };
 use iterators::{ Members, MembersMut, Entries, EntriesMut };
 use { JsonResult, JsonError };
 use std::{ usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32 };
@@ -496,6 +496,22 @@ impl<'a> Index<&'a str> for JsonValue {
     }
 }
 
+impl Index<String> for JsonValue {
+    type Output = JsonValue;
+
+    fn index(&self, index: String) -> &JsonValue {
+        self.index(index.deref())
+    }
+}
+
+impl<'a> Index<&'a String> for JsonValue {
+    type Output = JsonValue;
+
+    fn index(&self, index: &String) -> &JsonValue {
+        self.index(index.deref())
+    }
+}
+
 /// Implements mutable indexing by `&str` to easily modify object members:
 ///
 /// ```
@@ -526,3 +542,15 @@ impl<'a> IndexMut<&'a str> for JsonValue {
         }
     }
 }
+
+impl IndexMut<String> for JsonValue {
+    fn index_mut(&mut self, index: String) -> &mut JsonValue {
+        self.index_mut(index.deref())
+    }
+}
+
+impl<'a> IndexMut<&'a String> for JsonValue {
+    fn index_mut(&mut self, index: &String) -> &mut JsonValue {
+        self.index_mut(index.deref())
+    }
+}


Test patch:
diff --git a/tests/lib.rs b/tests/lib.rs
index 86d1ac9..2fc34f1 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -629,6 +629,72 @@ fn null_len() {
     assert_eq!(data.len(), 0);
 }
 
+#[test]
+fn index_by_str() {
+    let data = object!{
+        "foo" => "bar"
+    };
+
+    assert_eq!(data["foo"], "bar");
+}
+
+#[test]
+fn index_by_string() {
+    let data = object!{
+        "foo" => "bar"
+    };
+
+    assert_eq!(data["foo".to_string()], "bar");
+}
+
+#[test]
+fn index_by_string_ref() {
+    let data = object!{
+        "foo" => "bar"
+    };
+
+    let key = "foo".to_string();
+    let ref key_ref = key;
+
+    assert_eq!(data[key_ref], "bar");
+}
+
+#[test]
+fn index_mut_by_str() {
+    let mut data = object!{
+        "foo" => Null
+    };
+
+    data["foo"] = "bar".into();
+
+    assert_eq!(data["foo"], "bar");
+}
+
+#[test]
+fn index_mut_by_string() {
+    let mut data = object!{
+        "foo" => Null
+    };
+
+    data["foo".to_string()] = "bar".into();
+
+    assert_eq!(data["foo"], "bar");
+}
+
+#[test]
+fn index_mut_by_string_ref() {
+    let mut data = object!{
+        "foo" => Null
+    };
+
+    let key = "foo".to_string();
+    let ref key_ref = key;
+
+    data[key_ref] = "bar".into();
+
+    assert_eq!(data["foo"], "bar");
+}
+
 #[test]
 fn fmt_string() {
     let data: JsonValue = "foobar".into();