estree 0.0.5

A deserializer for the ESTree format.
Documentation
use serde_json::value::Value;
use easter::expr::Expr;
use easter::obj::DotKey;
use easter::id::IdExt;
use easter::punc::{Unop, Binop, Assop, Logop};
use unjson::ty::{Object, TyOf};
use unjson::ExtractField;
use joker::token::RegExpLiteral;

use tag::{Tag, TagOf};
use id::IntoId;
use result::Result;
use error::{Error, string_error, node_type_error, type_error};
use node::ExtractNode;
use fun::IntoFun;
use lit::{IntoStringLiteral, IntoNumberLiteral};

pub trait IntoExpr {
    fn into_expr(self) -> Result<Expr>;
    fn into_lit(self) -> Result<Expr>;
}

impl IntoExpr for Object {
    fn into_expr(mut self) -> Result<Expr> {
        let tag = try!(self.tag());
        Ok(match tag {
            Tag::Identifier => { return Ok(try!(self.into_id()).into_expr()); }
            Tag::Literal => {
                let json = try!(self.extract_field("value").map_err(Error::Json));
                match json {
                    Value::Null => Expr::Null(None),
                    Value::Bool(val) => if val { Expr::True(None) } else { Expr::False(None) },
                    Value::String(value) => Expr::String(None, value.into_string_literal()),
                    Value::I64(val) => Expr::Number(None, val.into_number_literal()),
                    Value::U64(val) => Expr::Number(None, val.into_number_literal()),
                    Value::F64(val) => Expr::Number(None, val.into_number_literal()),
                    Value::Object(_) => {
                        let mut regex = try!(self.extract_object("regex").map_err(Error::Json));
                        let pattern = try!(regex.extract_string("pattern").map_err(Error::Json));
                        let flags = try!(regex.extract_string("flags").map_err(Error::Json));
                        Expr::RegExp(None, RegExpLiteral {
                            pattern: pattern,
                            flags: flags.chars().collect()
                        })
                    }
                    _ => { return type_error("null, number, boolean, string, or object", json.ty()); }
                }
            }
            Tag::BinaryExpression => {
                let str = try!(self.extract_string("operator").map_err(Error::Json));
                let op: Binop = match str.parse() {
                    Ok(op) => op,
                    Err(_) => { return string_error("binary operator", str); }
                };
                let left = try!(self.extract_expr("left"));
                let right = try!(self.extract_expr("right"));
                Expr::Binop(None, op, Box::new(left), Box::new(right))
            }
            Tag::AssignmentExpression => {
                let str = try!(self.extract_string("operator").map_err(Error::Json));
                let op: Assop = match str.parse() {
                    Ok(op) => op,
                    Err(_) => { return string_error("assignment operator", str); }
                };
                let left = try!(self.extract_assign_patt("left"));
                let right = try!(self.extract_expr("right"));
                Expr::Assign(None, op, left, Box::new(right))
            }
            Tag::LogicalExpression => {
                let str = try!(self.extract_string("operator").map_err(Error::Json));
                let op: Logop = match str.parse() {
                    Ok(op) => op,
                    Err(_) => { return string_error("logical operator", str); }
                };
                let left = try!(self.extract_expr("left"));
                let right = try!(self.extract_expr("right"));
                Expr::Logop(None, op, Box::new(left), Box::new(right))
            }
            Tag::UnaryExpression => {
                let str = try!(self.extract_string("operator").map_err(Error::Json));
                let op: Unop = match str.parse() {
                    Ok(op) => op,
                    Err(_) => { return string_error("unary operator", str); }
                };
                let arg = try!(self.extract_expr("argument"));
                Expr::Unop(None, op, Box::new(arg))
            }
            Tag::UpdateExpression => {
                let op = try!(self.extract_string("operator").map_err(Error::Json));
                let arg = Box::new(try!(self.extract_expr("argument")));
                let prefix = try!(self.extract_bool("prefix").map_err(Error::Json));
                match (&op[..], prefix) {
                    ("++", true)  => Expr::PreInc(None, arg),
                    ("++", false) => Expr::PostInc(None, arg),
                    ("--", true)  => Expr::PreDec(None, arg),
                    ("--", false) => Expr::PostDec(None, arg),
                    _ => { return string_error("'++' or '--'", op); }
                }
            }
            Tag::MemberExpression => {
                let obj = Box::new(try!(self.extract_expr("object")));
                if try!(self.extract_bool("computed").map_err(Error::Json)) {
                    let prop = Box::new(try!(self.extract_expr("property")));
                    Expr::Brack(None, obj, prop)
                } else {
                    let id = try!(try!(self.extract_object("property").map_err(Error::Json)).into_id());
                    let key = DotKey { location: None, value: id.name.into_string() };
                    Expr::Dot(None, obj, key)
                }
            }
            Tag::CallExpression => {
                let callee = Box::new(try!(self.extract_expr("callee")));
                let args = try!(self.extract_expr_list("arguments"));
                Expr::Call(None, callee, args)
            }
            Tag::NewExpression => {
                let callee = Box::new(try!(self.extract_expr("callee")));
                let args = try!(self.extract_expr_list("arguments"));
                Expr::New(None, callee, Some(args))
            }
            Tag::ArrayExpression => {
                let elts = try!(self.extract_expr_opt_list("elements"));
                Expr::Arr(None, elts)
            }
            Tag::FunctionExpression => {
                let fun = try!(self.into_fun());
                Expr::Fun(fun)
            }
            Tag::SequenceExpression => {
                let exprs = try!(self.extract_expr_list("expressions"));
                Expr::Seq(None, exprs)
            }
            Tag::ObjectExpression => {
                let props = try!(self.extract_prop_list("properties"));
                Expr::Obj(None, props)
            }
            Tag::ConditionalExpression => {
                let test = Box::new(try!(self.extract_expr("test")));
                let cons = Box::new(try!(self.extract_expr("consequent")));
                let alt = Box::new(try!(self.extract_expr("alternate")));
                Expr::Cond(None, test, cons, alt)
            }
            Tag::ThisExpression => Expr::This(None),
            _ => { return node_type_error("expression", tag); }
        })
    }

    fn into_lit(mut self) -> Result<Expr> {
        let json = try!(self.extract_field("value").map_err(Error::Json));
        Ok(match json {
            Value::Null => Expr::Null(None),
            Value::Bool(val) => if val { Expr::True(None) } else { Expr::False(None) },
            Value::String(value) => Expr::String(None, value.into_string_literal()),
            Value::I64(val) => Expr::Number(None, val.into_number_literal()),
            Value::U64(val) => Expr::Number(None, val.into_number_literal()),
            Value::F64(val) => Expr::Number(None, val.into_number_literal()),
            Value::Object(_) => {
                let mut regex = try!(self.extract_object("regex").map_err(Error::Json));
                let pattern = try!(regex.extract_string("pattern").map_err(Error::Json));
                let flags = try!(regex.extract_string("flags").map_err(Error::Json));
                Expr::RegExp(None, RegExpLiteral {
                    pattern: pattern,
                    flags: flags.chars().collect()
                })
            }
            _ => { return type_error("null, number, boolean, string, or object", json.ty()); }
        })
    }
}