jed/
lib.rs

1//#![deny(missing_docs)]
2//#![cfg_attr(all(test, feature = "nightly"), feature(test))]
3//#![cfg_attr(all(feature = "nightly"), feature(io))]
4
5//! jed creates Json iterators over instances of io.Read
6
7extern crate rustc_serialize;
8
9use std::io::{ /*Chars,*/ Read };
10use std::iter::Iterator;
11use rustc_serialize::json::{ Json, Builder };
12
13// workaround imports for std::io::Read::chars()
14use std::error::Error;
15use std::{ fmt, io, result, str };
16
17/// An iterator over the Json elements of an io::Read stream
18pub struct Iter<R> {
19  inner: R
20}
21
22impl<R: Read> Iter<R> {
23  /// Create a new Iter instance
24  pub fn new(inner: R) -> Iter<R> {
25    Iter { inner: inner }
26  }
27}
28
29impl<R: Read> Iterator for Iter<R> {
30  type Item = Json;
31
32  fn next(&mut self) -> Option<Json> {
33    let ref mut inner = self.inner;
34    let mut chars = Chars { inner: inner };
35    let mut buf = String::new();
36    while let Some(Ok(c)) = chars.next() {
37      buf.push(c);
38      match c {
39        '}' | ']' =>
40          match Builder::new(buf.chars()).build() {
41            Ok(j) => return Some(j),
42            _ => ()
43          },
44        _ => ()
45      }
46    }
47    None
48  }
49}
50
51/// work arounds until read::chars() stablizes
52
53static UTF8_CHAR_WIDTH: [u8; 256] = [
541,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
551,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
561,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
571,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
581,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
591,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
601,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
611,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
620,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
630,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
640,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
650,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
660,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
672,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
683,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
694,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
70];
71
72#[inline]
73fn utf8_char_width(b: u8) -> usize {
74    return UTF8_CHAR_WIDTH[b as usize] as usize;
75}
76
77struct Chars<R> {
78  inner: R,
79}
80
81#[derive(Debug)]
82enum CharsError {
83  NotUtf8,
84  Other(io::Error),
85}
86
87impl<R: Read> Iterator for Chars<R> {
88  type Item = result::Result<char, CharsError>;
89
90  fn next(&mut self) -> Option<result::Result<char, CharsError>> {
91    let mut buf = [0];
92    let first_byte = match self.inner.read(&mut buf) {
93      Ok(0) => return None,
94      Ok(..) => buf[0],
95      Err(e) => return Some(Err(CharsError::Other(e))),
96    };
97    let width = utf8_char_width(first_byte);
98    if width == 1 { return Some(Ok(first_byte as char)) }
99    if width == 0 { return Some(Err(CharsError::NotUtf8)) }
100    let mut buf = [first_byte, 0, 0, 0];
101    {
102      let mut start = 1;
103      while start < width {
104        match self.inner.read(&mut buf[start..width]) {
105          Ok(0) => return Some(Err(CharsError::NotUtf8)),
106          Ok(n) => start += n,
107          Err(e) => return Some(Err(CharsError::Other(e))),
108        }
109      }
110    }
111    Some(match str::from_utf8(&buf[..width]).ok() {
112      Some(s) => {
113        let v: Vec<char> = s.chars().collect();
114        Ok(v[0])
115      },
116      None => Err(CharsError::NotUtf8),
117    })
118  }
119}
120
121impl Error for CharsError {
122  fn description(&self) -> &str {
123    match *self {
124      CharsError::NotUtf8 => "invalid utf8 encoding",
125      CharsError::Other(ref e) => Error::description(e),
126    }
127  }
128  fn cause(&self) -> Option<&Error> {
129    match *self {
130      CharsError::NotUtf8 => None,
131      CharsError::Other(ref e) => e.cause(),
132    }
133  }
134}
135
136impl fmt::Display for CharsError {
137  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138    match *self {
139      CharsError::NotUtf8 => {
140        "byte stream did not contain valid utf8".fmt(f)
141      }
142      CharsError::Other(ref e) => e.fmt(f),
143    }
144  }
145}
146
147#[cfg(test)]
148mod tests {
149  use super::Iter;
150  #[cfg(feature = "nightly")]
151  use test::Bencher;
152  use std::io::{ empty, BufReader };
153
154  #[test]
155  fn test_not_json_iter() {
156    let reader = BufReader::new("bogus".as_bytes());
157    assert_eq!(Iter::new(reader).count(), 0);
158  }
159
160  #[test]
161  fn test_empty_iter() {
162    assert_eq!(Iter::new(empty()).count(), 0);
163  }
164
165  #[test]
166  fn test_ary_iter() {
167    let reader = BufReader::new("[][]".as_bytes());
168    assert_eq!(Iter::new(reader).count(), 2)
169  }
170
171  #[test]
172  fn test_obj_iter() {
173    let reader = BufReader::new("{}{}".as_bytes());
174    assert_eq!(Iter::new(reader).count(), 2)
175  }
176
177  #[cfg(feature = "nightly")]
178  #[bench]
179  fn bench_iter(b: &mut Bencher) {
180    b.iter(|| Iter::new(BufReader::new("{'foo':'bar'}{'foo':'baz'}".as_bytes())).count())
181  }
182}