json-codec 0.1.0

JSON Codec implementation
Documentation
//! JSON ([RFC 7159](http://tools.ietf.org/html/rfc7159)) encoder.
//!
//! # Usage example
//!
//! ```
//! #![feature(question_mark)]
//! use json::Encoder;
//! use std::io::Cursor;
//!
//! let e = Encoder::new(Cursor::new(Vec::new()))
//!     .object(|e| {
//!         e.key("key1")?.array(|mut e| {
//!              for i in 0 .. 10 {
//!                 e = e.bool(i % 2 == 0)?;
//!              }
//!              Ok(e)
//!          })?
//!          .key("key2")?.string("\"hello world\"")?
//!          .key("key3")?.object(|e| {
//!              e.key("inner1")?.bool(true)?
//!               .key("inner2")?.array(|e| {
//!                   e.string("\u{2764}\u{fe0f}")?
//!                    .string("again")?
//!                    .bool(false)?
//!                    .usize(1024)?
//!                    .u8(90)?
//!                    .f64(1.23442e-9)?
//!                    .null()
//!               })
//!          })
//!     });
//! assert!(e.is_ok())

use ast::Json;
use std::char::EncodeUtf8;
use std::error::Error;
use std::fmt;
use std::io::{self, Write};
use std::borrow::Borrow;
use std::str::Chars;

// Encoder //////////////////////////////////////////////////////////////////

/// JSON encoder over any `Write`-type.
pub struct Encoder<W> {
    writer: W,
    stack: Stack
}

// Macros ///////////////////////////////////////////////////////////////////

macro_rules! number {
    ($name: ident, f64) => {
        pub fn $name(mut self, x: f64) -> EncodeResult<Encoder<W>> {
            if x.is_nan() || x.is_infinite() {
                return Err(EncodeError::InvalidFloat)
            }
            try!(self.comma_array());
            try!(self.writer.write_all(x.to_string().as_bytes()));
            Ok(self)
        }
    };
    ($name: ident, $ty: ty) => {
        pub fn $name(mut self, x: $ty) -> EncodeResult<Encoder<W>> {
            try!(self.comma_array());
            try!(self.writer.write_all(x.to_string().as_bytes()));
            Ok(self)
        }
    }
}

impl<W: Write> Encoder<W> {
    pub fn new(w: W) -> Encoder<W> {
        Encoder { writer: w, stack: Stack::new() }
    }

    pub fn into_writer(self) -> W {
        self.writer
    }

    pub fn writer(&self) -> &W {
        &self.writer
    }

    pub fn encode(mut self, j: &Json) -> EncodeResult<Encoder<W>> {
        match *j {
            Json::Null          => self = try!(self.null()),
            Json::Bool(x)       => self = try!(self.bool(x)),
            Json::Number(x)     => self = try!(self.f64(x)),
            Json::String(ref x) => self = try!(self.string(x.as_ref())),
            Json::Array(ref xs) =>
                self = try!(self.array(|mut e| {
                    for x in xs {
                        e = try!(e.encode(x))
                    }
                    Ok(e)
                })),
            Json::Object(ref xs) =>
                self = try!(self.object(|mut e| {
                    for (k, v) in xs {
                        e = try!(e.key(k.as_ref()));
                        e = try!(e.encode(v))
                    }
                    Ok(e)
                }))
        }
        Ok(self)
    }

    number!(u8, u8);
    number!(u16, u16);
    number!(u32, u32);
    number!(u64, u64);
    number!(usize, usize);

    number!(i8, i8);
    number!(i16, i16);
    number!(i32, i32);
    number!(i64, i64);
    number!(isize, isize);

    number!(f64, f64);

    pub fn bool(mut self, x: bool) -> EncodeResult<Encoder<W>> {
        try!(self.comma_array());
        try!(self.writer.write_all(if x { b"true" } else { b"false" }));
        Ok(self)
    }

    pub fn null(mut self) -> EncodeResult<Encoder<W>> {
        try!(self.comma_array());
        try!(self.writer.write_all(b"null"));
        Ok(self)
    }

    pub fn string<S: Borrow<str>>(mut self, s: S) -> EncodeResult<Encoder<W>> {
        try!(self.comma_array());
        try!(self.writer.write_all(b"\""));
        for x in Bytes::new(EscapedChars::new(s.borrow())) {
            try!(self.writer.write_all(&[x]));
        }
        try!(self.writer.write_all(b"\""));
        Ok(self)
    }

    pub fn key<S: Borrow<str>>(mut self, key: S) -> EncodeResult<Encoder<W>> {
        try!(self.comma_object());
        let mut e = try!(self.string(key.borrow()));
        try!(e.writer.write_all(b":"));
        Ok(e)
    }

    pub fn array<F>(mut self, mut f: F) -> EncodeResult<Encoder<W>>
    where
        F: FnMut(Encoder<W>) -> EncodeResult<Encoder<W>>
    {
        try!(self.comma_array());
        try!(self.writer.write_all(b"["));
        self.stack.push(Scope::A(false));
        let mut e = try!(f(self));
        e.stack.pop();
        try!(e.writer.write_all(b"]"));
        Ok(e)
    }

    pub fn object<F>(mut self, mut f: F) -> EncodeResult<Encoder<W>>
    where
        F: FnMut(Encoder<W>) -> EncodeResult<Encoder<W>>
    {
        try!(self.comma_array());
        try!(self.writer.write_all(b"{"));
        self.stack.push(Scope::O(false));
        let mut e = try!(f(self));
        e.stack.pop();
        try!(e.writer.write_all(b"}"));
        Ok(e)
    }

    fn comma_array(&mut self) -> EncodeResult<()> {
        match self.stack.top() {
            Some(Scope::A(true))  => self.writer.write_all(b",").map_err(From::from),
            Some(Scope::A(false)) => {
                self.stack.set();
                Ok(())
            }
            _ => Ok(())
        }
    }

    fn comma_object(&mut self) -> EncodeResult<()> {
        match self.stack.top() {
            Some(Scope::O(true))  => self.writer.write_all(b",").map_err(From::from),
            Some(Scope::O(false)) => {
                self.stack.set();
                Ok(())
            }
            _ => Ok(())
        }
    }
}

// Stack & Scope ////////////////////////////////////////////////////////////

#[derive(Debug, Copy, Clone)]
enum Scope {
    A(bool), // Array
    O(bool)  // Object
}

// Stack to track if commas need to be written. For every array a new
// `Scope::A` is pushed onto the stack, and for every object a new
// `Scope::O`. The various `Encoder` methods check the current scope to
// decide if a comma should be written.
struct Stack(Vec<Scope>);

impl Stack {
    fn new() -> Stack {
        Stack(Vec::new())
    }

    fn push(&mut self, s: Scope) {
        self.0.push(s)
    }

    fn pop(&mut self) {
        self.0.pop();
    }

    fn top(&self) -> Option<Scope> {
        self.0.last().map(|x| *x)
    }

    fn set(&mut self) {
        self.0.last_mut().map(|x| {
            match *x {
                Scope::A(_) => *x = Scope::A(true),
                Scope::O(_) => *x = Scope::O(true)
            }
        });
    }
}

// Encoder Error Type ///////////////////////////////////////////////////////

pub type EncodeResult<A> = Result<A, EncodeError>;

#[derive(Debug)]
pub enum EncodeError {
    Io(io::Error),
    /// A float value such as `NAN` or `INFINITY` was used.
    InvalidFloat
}

impl fmt::Display for EncodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            EncodeError::Io(ref e)    => write!(f, "i/o: {:?}", e),
            EncodeError::InvalidFloat => write!(f, "invalid f64 (NaN | Infinity)")
        }
    }
}

impl Error for EncodeError {
    fn description(&self) -> &str {
        "EncodeError"
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            EncodeError::Io(ref e) => Some(e),
            _                      => None

        }
    }
}

impl From<io::Error> for EncodeError {
    fn from(e: io::Error) -> EncodeError {
        EncodeError::Io(e)
    }
}

// Character conversion support /////////////////////////////////////////////

struct EscapedChars<'r> {
    source: Chars<'r>,
    buffer: [u8; 5],
    index:  Option<(usize, usize)>
}

impl<'r> EscapedChars<'r> {
    fn new(s: &'r str) -> EscapedChars<'r> {
        EscapedChars {
            source: s.chars(),
            buffer: [0; 5],
            index:  None
        }
    }

    fn chr(x: u8) -> u8 {
        match x {
            0x0 ... 0x9 => b'0' + x,
            0xA ... 0xF => b'A' + x - 0xA,
            _           => panic!("{} > 0xF", x)
        }
    }
}

impl<'r> Iterator for EscapedChars<'r> {
    type Item = char;

    fn next(&mut self) -> Option<char> {
        match self.index {
            None         => (),
            Some((i, e)) =>
                if i < e {
                    self.index = Some((i + 1, e));
                    return Some(self.buffer[i] as char)
                } else {
                    self.index = None
                }
        }
        match self.source.next() {
            Some(x@'\\') | Some(x@'"') => {
                self.buffer[0] = x as u8;
                self.index = Some((0, 1));
                Some('\\')
            }
            Some('\n') => {
                self.buffer[0] = b'n';
                self.index = Some((0, 1));
                Some('\\')
            }
            Some('\t') => {
                self.buffer[0] = b't';
                self.index = Some((0, 1));
                Some('\\')
            }
            Some('\r') => {
                self.buffer[0] = b'r';
                self.index = Some((0, 1));
                Some('\\')
            }
            Some(x@'\x00' ... '\x1F') | Some(x@'\x7F') => {
                self.buffer[0] = b'u';
                self.buffer[1] = b'0';
                self.buffer[2] = b'0';
                self.buffer[3] = Self::chr(x as u8 >> 4);
                self.buffer[4] = Self::chr(x as u8 & 0x0F);
                self.index = Some((0, 5));
                Some('\\')
            }
            x => x
        }
    }
}

struct Bytes<I> {
    source:  I,
    current: Option<EncodeUtf8>
}

impl<I> Bytes<I> {
    pub fn new(i: I) -> Bytes<I> {
        Bytes {
            source:  i,
            current: None
        }
    }
}

impl<I: Iterator<Item=char>> Iterator for Bytes<I> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        match self.current.as_mut().and_then(EncodeUtf8::next) {
            None => {
                self.current = self.source.next().map(char::encode_utf8);
                self.current.as_mut().and_then(EncodeUtf8::next)
            }
            x => x
        }
    }
}