<commit_before>use std::io::{Write, Result};

use util::join;
use parser::html::Comparator;

use super::Generator;
use super::ast::{Code, Statement, Expression};

pub trait Emit {
    fn emit(&mut self, code: &Code) -> Result<()>;
}

impl<'a, W:Write+'a> Generator<'a, W> {
    fn write_indent(&mut self, indent: u32) -> Result<()> {
        // TODO(tailhook) Is there a beter way ?
        for _ in 0..indent {
            try!(self.buf.write_all(b" "));
        }
        Ok(())
    }
    fn emit_expression(&mut self, expr: &Expression, indent: u32)
        -> Result<()>
    {
        let nindent = self.indent + indent;
        match expr {
            &Expression::Str(ref s) => {
                try!(self.buf.write_all(b"\""));
                for ch in s.chars() {
                    try!(self.buf.write_all(
                        ch.escape_default().collect::<String>().as_bytes()));
                }
                try!(self.buf.write_all(b"\""));
            }
            &Expression::Num(ref s) => {
                try!(write!(self.buf, "{}", s));
            }
            &Expression::Object(ref pairs) => {
                try!(self.buf.write_all(b"{"));
                if pairs.len() == 0 {
                } else if pairs.len() == 1 {
                    try!(write!(self.buf, "{}: ", pairs[0].0));
                    try!(self.emit_expression(&pairs[0].1, indent));
                } else {
                    try!(self.buf.write_all(b"\n"));
                    for &(ref key, ref value) in pairs.iter() {
                        try!(self.write_indent(nindent));
                        try!(write!(self.buf, "{}: ", key));
                        try!(self.emit_expression(value, nindent));
                        try!(self.buf.write_all(b",\n"));
                    }
                    try!(self.write_indent(indent));
                }
                try!(self.buf.write_all(b"}"));
            }
            &Expression::List(ref lst) => {
                try!(self.buf.write_all(b"["));
                if lst.len() == 0 {
                } else if lst.len() == 1 {
                    try!(self.emit_expression(&lst[0], indent));
                } else {
                    try!(self.buf.write_all(b"\n"));
                    for item in lst.iter() {
                        try!(self.write_indent(nindent));
                        try!(self.emit_expression(item, nindent));
                        try!(self.buf.write_all(b",\n"));
                    }
                    try!(self.write_indent(indent));
                }
                try!(self.buf.write_all(b"]"));
            }
            &Expression::Name(ref s) => {
                try!(write!(self.buf, "{}", s));
            }
            &Expression::Attr(ref parent, ref attr) => {
                try!(self.emit_expression(parent, indent));
                try!(write!(self.buf, ".{}", attr));
            }
            &Expression::Call(ref parent, ref args) => {
                try!(self.emit_expression(parent, indent));
                try!(self.buf.write_all(b"("));
                if args.len() > 0 {
                    try!(self.emit_expression(&args[0], indent));
                    for i in args[1..].iter() {
                        try!(self.buf.write_all(b", "));
                        try!(self.emit_expression(i, indent));
                    }
                }
                try!(self.buf.write_all(b")"));
            }
            &Expression::New(ref val) => {
                try!(write!(self.buf, "new "));
                try!(self.emit_expression(val, indent));
            }
            &Expression::Or(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " || "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::And(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " && "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Add(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " + "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Sub(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " - "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Mul(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " * "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Div(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " / "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Comparison(op, ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " {} ", match op {
                    Comparator::Eq => "===",
                    Comparator::NotEq => "!==",
                    Comparator::Less => "<",
                    Comparator::LessEq => "<=",
                    Comparator::Greater => ">",
                    Comparator::GreaterEq => ">=",
                }));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Function(ref name, ref params, ref body) => {
                try!(write!(self.buf, "function {name}({params}) {{\n",
                    name=name.as_ref().unwrap_or(&String::from("")),
                    params=join(params.iter().map(|x| &x.name), ", ")));
                // TODO(tailhook) default values
                try!(self.emit_statements(&body, nindent));
                try!(self.write_indent(indent));
                try!(self.buf.write_all(b"}"));
            }
            &Expression::AssignAttr(ref expr, ref attr, ref value) => {
                try!(self.emit_expression(expr, indent));
                try!(write!(self.buf, ".{} = ", attr));
                try!(self.emit_expression(value, indent));
            }
            &Expression::Ternary(ref cond, ref left, ref right) => {
                try!(write!(self.buf, "(("));
                try!(self.emit_expression(cond, indent));
                try!(write!(self.buf, ")?("));
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, "):("));
                try!(self.emit_expression(right, indent));
                try!(write!(self.buf, "))"));
            }
        }
        Ok(())
    }
    fn emit_statements(&mut self, stmts: &Vec<Statement>, indent: u32)
        -> Result<()>
    {
        let nindent = indent + self.indent;
        for stmt in stmts.iter() {
            match stmt {
                &Statement::Expr(ref expr) => {
                    try!(self.write_indent(indent));
                    try!(self.emit_expression(expr, nindent));
                    try!(self.buf.write_all(b"\n"));
                }
                &Statement::Return(ref expr) => {
                    try!(self.write_indent(indent));
                    try!(self.buf.write_all(b"return "));
                    try!(self.emit_expression(expr, nindent));
                    try!(self.buf.write_all(b";\n"));
                }
                &Statement::Var(ref name, ref expr) => {
                    try!(self.write_indent(indent));
                    try!(write!(self.buf, "var {} = ", name));
                    try!(self.emit_expression(expr, nindent));
                    try!(self.buf.write_all(b";\n"));
                }
                &Statement::Function(ref name, ref params, ref body) => {
                    try!(self.write_indent(indent));
                    try!(write!(self.buf, "function {name}({params}) {{\n",
                        name=name,
                        params=join(params.iter().map(|x| &x.name), ", ")));
                    // TODO(tailhook) default values
                    try!(self.emit_statements(&body, nindent));
                    try!(self.write_indent(indent));
                    try!(self.buf.write_all(b"}\n"));
                }
            }
        }
        Ok(())
    }
}

impl<'a, W:Write+'a> Emit for Generator<'a, W> {
    fn emit(&mut self, code: &Code) -> Result<()> {
        try!(self.emit_statements(&code.statements, 0));
        Ok(())
    }
}
<commit_msg>Fix escaping of strings in JS<commit_after>use std::io::{Write, Result};

use util::join;
use parser::html::Comparator;

use super::Generator;
use super::ast::{Code, Statement, Expression};

pub trait Emit {
    fn emit(&mut self, code: &Code) -> Result<()>;
}

impl<'a, W:Write+'a> Generator<'a, W> {
    fn write_indent(&mut self, indent: u32) -> Result<()> {
        // TODO(tailhook) Is there a beter way ?
        for _ in 0..indent {
            try!(self.buf.write_all(b" "));
        }
        Ok(())
    }
    fn emit_expression(&mut self, expr: &Expression, indent: u32)
        -> Result<()>
    {
        let nindent = self.indent + indent;
        match expr {
            &Expression::Str(ref s) => {
                try!(self.buf.write_all(b"\""));
                for ch in s.chars() {
                    match ch {
                        '\r' => { try!(write!(self.buf, "\\r")); }
                        '\n' => { try!(write!(self.buf, "\\n")); }
                        '\t' => { try!(write!(self.buf, "\\t")); }
                        '\x00'...'\x1f' => { try!(write!(self.buf, "\\x{:02}",
                                                         ch as u8)) }
                        _ => { try!(write!(self.buf, "{}", ch)) }
                    }
                }
                try!(self.buf.write_all(b"\""));
            }
            &Expression::Num(ref s) => {
                try!(write!(self.buf, "{}", s));
            }
            &Expression::Object(ref pairs) => {
                try!(self.buf.write_all(b"{"));
                if pairs.len() == 0 {
                } else if pairs.len() == 1 {
                    try!(write!(self.buf, "{}: ", pairs[0].0));
                    try!(self.emit_expression(&pairs[0].1, indent));
                } else {
                    try!(self.buf.write_all(b"\n"));
                    for &(ref key, ref value) in pairs.iter() {
                        try!(self.write_indent(nindent));
                        try!(write!(self.buf, "{}: ", key));
                        try!(self.emit_expression(value, nindent));
                        try!(self.buf.write_all(b",\n"));
                    }
                    try!(self.write_indent(indent));
                }
                try!(self.buf.write_all(b"}"));
            }
            &Expression::List(ref lst) => {
                try!(self.buf.write_all(b"["));
                if lst.len() == 0 {
                } else if lst.len() == 1 {
                    try!(self.emit_expression(&lst[0], indent));
                } else {
                    try!(self.buf.write_all(b"\n"));
                    for item in lst.iter() {
                        try!(self.write_indent(nindent));
                        try!(self.emit_expression(item, nindent));
                        try!(self.buf.write_all(b",\n"));
                    }
                    try!(self.write_indent(indent));
                }
                try!(self.buf.write_all(b"]"));
            }
            &Expression::Name(ref s) => {
                try!(write!(self.buf, "{}", s));
            }
            &Expression::Attr(ref parent, ref attr) => {
                try!(self.emit_expression(parent, indent));
                try!(write!(self.buf, ".{}", attr));
            }
            &Expression::Call(ref parent, ref args) => {
                try!(self.emit_expression(parent, indent));
                try!(self.buf.write_all(b"("));
                if args.len() > 0 {
                    try!(self.emit_expression(&args[0], indent));
                    for i in args[1..].iter() {
                        try!(self.buf.write_all(b", "));
                        try!(self.emit_expression(i, indent));
                    }
                }
                try!(self.buf.write_all(b")"));
            }
            &Expression::New(ref val) => {
                try!(write!(self.buf, "new "));
                try!(self.emit_expression(val, indent));
            }
            &Expression::Or(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " || "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::And(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " && "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Add(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " + "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Sub(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " - "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Mul(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " * "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Div(ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " / "));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Comparison(op, ref left, ref right) => {
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, " {} ", match op {
                    Comparator::Eq => "===",
                    Comparator::NotEq => "!==",
                    Comparator::Less => "<",
                    Comparator::LessEq => "<=",
                    Comparator::Greater => ">",
                    Comparator::GreaterEq => ">=",
                }));
                try!(self.emit_expression(right, indent));
            }
            &Expression::Function(ref name, ref params, ref body) => {
                try!(write!(self.buf, "function {name}({params}) {{\n",
                    name=name.as_ref().unwrap_or(&String::from("")),
                    params=join(params.iter().map(|x| &x.name), ", ")));
                // TODO(tailhook) default values
                try!(self.emit_statements(&body, nindent));
                try!(self.write_indent(indent));
                try!(self.buf.write_all(b"}"));
            }
            &Expression::AssignAttr(ref expr, ref attr, ref value) => {
                try!(self.emit_expression(expr, indent));
                try!(write!(self.buf, ".{} = ", attr));
                try!(self.emit_expression(value, indent));
            }
            &Expression::Ternary(ref cond, ref left, ref right) => {
                try!(write!(self.buf, "(("));
                try!(self.emit_expression(cond, indent));
                try!(write!(self.buf, ")?("));
                try!(self.emit_expression(left, indent));
                try!(write!(self.buf, "):("));
                try!(self.emit_expression(right, indent));
                try!(write!(self.buf, "))"));
            }
        }
        Ok(())
    }
    fn emit_statements(&mut self, stmts: &Vec<Statement>, indent: u32)
        -> Result<()>
    {
        let nindent = indent + self.indent;
        for stmt in stmts.iter() {
            match stmt {
                &Statement::Expr(ref expr) => {
                    try!(self.write_indent(indent));
                    try!(self.emit_expression(expr, nindent));
                    try!(self.buf.write_all(b"\n"));
                }
                &Statement::Return(ref expr) => {
                    try!(self.write_indent(indent));
                    try!(self.buf.write_all(b"return "));
                    try!(self.emit_expression(expr, nindent));
                    try!(self.buf.write_all(b";\n"));
                }
                &Statement::Var(ref name, ref expr) => {
                    try!(self.write_indent(indent));
                    try!(write!(self.buf, "var {} = ", name));
                    try!(self.emit_expression(expr, nindent));
                    try!(self.buf.write_all(b";\n"));
                }
                &Statement::Function(ref name, ref params, ref body) => {
                    try!(self.write_indent(indent));
                    try!(write!(self.buf, "function {name}({params}) {{\n",
                        name=name,
                        params=join(params.iter().map(|x| &x.name), ", ")));
                    // TODO(tailhook) default values
                    try!(self.emit_statements(&body, nindent));
                    try!(self.write_indent(indent));
                    try!(self.buf.write_all(b"}\n"));
                }
            }
        }
        Ok(())
    }
}

impl<'a, W:Write+'a> Emit for Generator<'a, W> {
    fn emit(&mut self, code: &Code) -> Result<()> {
        try!(self.emit_statements(&code.statements, 0));
        Ok(())
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

pub use self::lua::lua_Alloc;
pub use self::lua::lua_CFunction;
pub use self::lua::lua_Ctx;
pub use self::lua::lua_Debug;
pub use self::lua::lua_Hook;
pub use self::lua::lua_Integer;
pub use self::lua::lua_KFunction;
pub use self::lua::lua_Number;
pub use self::lua::lua_Reader;
pub use self::lua::lua_State;
pub use self::lua::lua_Unsigned;
pub use self::lua::lua_Writer;

pub use self::lua::lua_absindex;
pub use self::lua::lua_arith;
pub use self::lua::lua_atpanic;
pub use self::lua::lua_call;
pub use self::lua::lua_callk;
pub use self::lua::lua_checkstack;
pub use self::lua::lua_close;
pub use self::lua::lua_compare;
pub use self::lua::lua_concat;
pub use self::lua::lua_copy;
pub use self::lua::lua_createtable;
pub use self::lua::lua_dump;
pub use self::lua::lua_error;
pub use self::lua::lua_gc;
pub use self::lua::lua_getallocf;
pub use self::lua::lua_getextraspace;
pub use self::lua::lua_getfield;
pub use self::lua::lua_getglobal;
pub use self::lua::lua_gethook;
pub use self::lua::lua_gethookcount;
pub use self::lua::lua_gethookmask;
pub use self::lua::lua_getinfo;
pub use self::lua::lua_getlocal;
pub use self::lua::lua_getmetatable;
pub use self::lua::lua_getstack;
pub use self::lua::lua_gettable;
pub use self::lua::lua_gettop;
pub use self::lua::lua_getupvalue;
pub use self::lua::lua_getuservalue;
pub use self::lua::lua_insert;
pub use self::lua::lua_isboolean;
pub use self::lua::lua_iscfunction;
pub use self::lua::lua_isfunction;
pub use self::lua::lua_isinteger;
pub use self::lua::lua_islightuserdata;
pub use self::lua::lua_isnil;
pub use self::lua::lua_isnone;
pub use self::lua::lua_isnoneornil;
pub use self::lua::lua_isnumber;
pub use self::lua::lua_isstring;
pub use self::lua::lua_istable;
pub use self::lua::lua_isthread;
pub use self::lua::lua_isuserdata;
pub use self::lua::lua_isyieldable;
pub use self::lua::lua_len;
pub use self::lua::lua_load;
pub use self::lua::lua_newstate;
pub use self::lua::lua_newtable;
pub use self::lua::lua_newthread;
pub use self::lua::lua_newuserdata;
pub use self::lua::lua_next;
pub use self::luaconf::lua_numtointeger;
pub use self::lua::lua_pcall;
pub use self::lua::lua_pcallk;
pub use self::lua::lua_pop;
pub use self::lua::lua_pushboolean;
pub use self::lua::lua_pushcclosure;
pub use self::lua::lua_pushcfunction;
pub use self::lua::lua_pushfstring;
pub use self::lua::lua_pushglobaltable;
pub use self::lua::lua_pushinteger;
pub use self::lua::lua_pushlightuserdata;
pub use self::lua::lua_pushliteral;
pub use self::lua::lua_pushlstring;
pub use self::lua::lua_pushnil;
pub use self::lua::lua_pushnumber;
pub use self::lua::lua_pushstring;
pub use self::lua::lua_pushthread;
pub use self::lua::lua_pushvalue;
//pub use self::lua::lua_pushvfstring; -- not implemented
pub use self::lua::lua_rawequal;
pub use self::lua::lua_rawget;
pub use self::lua::lua_rawgeti;
pub use self::lua::lua_rawgetp;
pub use self::lua::lua_rawlen;
pub use self::lua::lua_rawset;
pub use self::lua::lua_rawseti;
pub use self::lua::lua_rawsetp;
pub use self::lua::lua_register;
pub use self::lua::lua_remove;
pub use self::lua::lua_replace;
pub use self::lua::lua_resume;
pub use self::lua::lua_rotate;
pub use self::lua::lua_setallocf;
pub use self::lua::lua_setfield;
pub use self::lua::lua_setglobal;
pub use self::lua::lua_sethook;
pub use self::lua::lua_setlocal;
pub use self::lua::lua_setmetatable;
pub use self::lua::lua_settable;
pub use self::lua::lua_settop;
pub use self::lua::lua_setupvalue;
pub use self::lua::lua_setuservalue;
pub use self::lua::lua_status;
pub use self::lua::lua_strtonum;
pub use self::lua::lua_toboolean;
pub use self::lua::lua_tocfunction;
pub use self::lua::lua_tointeger;
pub use self::lua::lua_tointegerx;
pub use self::lua::lua_tolstring;
pub use self::lua::lua_tonumber;
pub use self::lua::lua_tonumberx;
pub use self::lua::lua_topointer;
pub use self::lua::lua_tostring;
pub use self::lua::lua_tothread;
pub use self::lua::lua_touserdata;
pub use self::lua::lua_type;
pub use self::lua::lua_typename;
pub use self::lua::lua_upvalueid;
pub use self::lua::lua_upvalueindex;
pub use self::lua::lua_upvaluejoin;
pub use self::lua::lua_version;
pub use self::lua::lua_xmove;
pub use self::lua::lua_yield;
pub use self::lua::lua_yieldk;

pub use self::lauxlib::luaL_Buffer;
pub use self::lauxlib::luaL_Reg;
pub use self::lauxlib::luaL_Stream;

pub use self::lauxlib::luaL_addchar;
pub use self::lauxlib::luaL_addlstring;
pub use self::lauxlib::luaL_addsize;
pub use self::lauxlib::luaL_addstring;
pub use self::lauxlib::luaL_addvalue;
pub use self::lauxlib::luaL_argcheck;
pub use self::lauxlib::luaL_argerror;
pub use self::lauxlib::luaL_buffinit;
pub use self::lauxlib::luaL_buffinitsize;
pub use self::lauxlib::luaL_callmeta;
pub use self::lauxlib::luaL_checkany;
pub use self::lauxlib::luaL_checkint;
pub use self::lauxlib::luaL_checkinteger;
pub use self::lauxlib::luaL_checklong;
pub use self::lauxlib::luaL_checklstring;
pub use self::lauxlib::luaL_checknumber;
pub use self::lauxlib::luaL_checkoption;
pub use self::lauxlib::luaL_checkstack;
pub use self::lauxlib::luaL_checkstring;
pub use self::lauxlib::luaL_checktype;
pub use self::lauxlib::luaL_checkudata;
pub use self::lauxlib::luaL_checkversion;
pub use self::lauxlib::luaL_dofile;
pub use self::lauxlib::luaL_dostring;
pub use self::lauxlib::luaL_error;
pub use self::lauxlib::luaL_execresult;
pub use self::lauxlib::luaL_fileresult;
pub use self::lauxlib::luaL_getmetafield;
pub use self::lauxlib::luaL_getmetatable;
pub use self::lauxlib::luaL_getsubtable;
pub use self::lauxlib::luaL_gsub;
pub use self::lauxlib::luaL_len;
pub use self::lauxlib::luaL_loadbuffer;
pub use self::lauxlib::luaL_loadbufferx;
pub use self::lauxlib::luaL_loadfile;
pub use self::lauxlib::luaL_loadfilex;
pub use self::lauxlib::luaL_loadstring;
pub use self::lauxlib::luaL_newlib;
pub use self::lauxlib::luaL_newlibtable;
pub use self::lauxlib::luaL_newmetatable;
pub use self::lauxlib::luaL_newstate;
pub use self::lualib::luaL_openlibs;
pub use self::lauxlib::luaL_optint;
pub use self::lauxlib::luaL_optinteger;
pub use self::lauxlib::luaL_optlong;
pub use self::lauxlib::luaL_optlstring;
pub use self::lauxlib::luaL_optnumber;
pub use self::lauxlib::luaL_optstring;
pub use self::lauxlib::luaL_prepbuffer;
pub use self::lauxlib::luaL_prepbuffsize;
pub use self::lauxlib::luaL_pushresult;
pub use self::lauxlib::luaL_pushresultsize;
pub use self::lauxlib::luaL_ref;
pub use self::lauxlib::luaL_requiref;
pub use self::lauxlib::luaL_setfuncs;
pub use self::lauxlib::luaL_setmetatable;
pub use self::lauxlib::luaL_testudata;
pub use self::lauxlib::luaL_tolstring;
pub use self::lauxlib::luaL_traceback;
pub use self::lauxlib::luaL_typename;
pub use self::lauxlib::luaL_unref;
pub use self::lauxlib::luaL_where;

// commonly used constants from lua.h
pub use self::lua::LUA_MULTRET;
pub use self::lua::LUA_REGISTRYINDEX;

pub use self::lua::{LUA_RIDX_MAINTHREAD, LUA_RIDX_GLOBALS};

pub use self::lua::{LUA_OPADD, LUA_OPSUB, LUA_OPMUL, LUA_OPDIV, LUA_OPIDIV};
pub use self::lua::{LUA_OPMOD, LUA_OPPOW, LUA_OPUNM};
pub use self::lua::{LUA_OPBNOT, LUA_OPBAND, LUA_OPBOR, LUA_OPBXOR, LUA_OPSHL, LUA_OPSHR};
pub use self::lua::{LUA_OPEQ, LUA_OPLT, LUA_OPLE};

pub use self::lua::{LUA_OK, LUA_ERRRUN, LUA_ERRMEM, LUA_ERRERR, LUA_ERRGCMM};

pub use self::lua::{LUA_TNONE, LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING};
pub use self::lua::{LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA};

pub use self::lua::{LUA_HOOKCALL, LUA_HOOKRET, LUA_HOOKTAILCALL, LUA_HOOKLINE, LUA_HOOKCOUNT};

pub use self::lua::{LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCCOUNTB};
pub use self::lua::{LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, LUA_GCISRUNNING};

// constants from lauxlib.h
pub use self::lauxlib::{LUA_REFNIL, LUA_NOREF};
pub use self::lauxlib::{LUA_ERRFILE, LUA_FILEHANDLE};

// constants from lualib.h
pub use self::lualib::{
  LUA_COLIBNAME, LUA_TABLIBNAME, LUA_IOLIBNAME, LUA_OSLIBNAME, LUA_STRLIBNAME,
  LUA_UTF8LIBNAME, LUA_BITLIBNAME, LUA_MATHLIBNAME, LUA_DBLIBNAME, LUA_LOADLIBNAME
};

mod glue;
pub mod luaconf;
pub mod lua;
pub mod lauxlib;
pub mod lualib;

<commit_msg>Simplify ffi re-exports<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 J.C. Moyer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

// This is more or less in the order it appears in the Lua manual, with the
// exception of constants, which appear scattered throughout the manual text.

// luaconf.h functions
pub use self::luaconf::lua_numtointeger;

// C API types
pub use self::lua::{
  lua_Alloc,
  lua_CFunction,
  lua_Ctx,
  lua_Debug,
  lua_Hook,
  lua_Integer,
  lua_KFunction,
  lua_Number,
  lua_Reader,
  lua_State,
  lua_Unsigned,
  lua_Writer
};

// C API functions
pub use self::lua::{
  lua_absindex,
  lua_arith,
  lua_atpanic,
  lua_call,
  lua_callk,
  lua_checkstack,
  lua_close,
  lua_compare,
  lua_concat,
  lua_copy,
  lua_createtable,
  lua_dump,
  lua_error,
  lua_gc,
  lua_getallocf,
  lua_getextraspace,
  lua_getfield,
  lua_getglobal,
  lua_gethook,
  lua_gethookcount,
  lua_gethookmask,
  lua_getinfo,
  lua_getlocal,
  lua_getmetatable,
  lua_getstack,
  lua_gettable,
  lua_gettop,
  lua_getupvalue,
  lua_getuservalue,
  lua_insert,
  lua_isboolean,
  lua_iscfunction,
  lua_isfunction,
  lua_isinteger,
  lua_islightuserdata,
  lua_isnil,
  lua_isnone,
  lua_isnoneornil,
  lua_isnumber,
  lua_isstring,
  lua_istable,
  lua_isthread,
  lua_isuserdata,
  lua_isyieldable,
  lua_len,
  lua_load,
  lua_newstate,
  lua_newtable,
  lua_newthread,
  lua_newuserdata,
  lua_next,
  lua_pcall,
  lua_pcallk,
  lua_pop,
  lua_pushboolean,
  lua_pushcclosure,
  lua_pushcfunction,
  lua_pushfstring,
  lua_pushglobaltable,
  lua_pushinteger,
  lua_pushlightuserdata,
  lua_pushliteral,
  lua_pushlstring,
  lua_pushnil,
  lua_pushnumber,
  lua_pushstring,
  lua_pushthread,
  lua_pushvalue,
  // omitted: lua_pushvfstring
  lua_rawequal,
  lua_rawget,
  lua_rawgeti,
  lua_rawgetp,
  lua_rawlen,
  lua_rawset,
  lua_rawseti,
  lua_rawsetp,
  lua_register,
  lua_remove,
  lua_replace,
  lua_resume,
  lua_rotate,
  lua_setallocf,
  lua_setfield,
  lua_setglobal,
  lua_sethook,
  lua_setlocal,
  lua_setmetatable,
  lua_settable,
  lua_settop,
  lua_setupvalue,
  lua_setuservalue,
  lua_status,
  lua_strtonum,
  lua_toboolean,
  lua_tocfunction,
  lua_tointeger,
  lua_tointegerx,
  lua_tolstring,
  lua_tonumber,
  lua_tonumberx,
  lua_topointer,
  lua_tostring,
  lua_tothread,
  lua_touserdata,
  lua_type,
  lua_typename,
  lua_upvalueid,
  lua_upvalueindex,
  lua_upvaluejoin,
  lua_version,
  lua_xmove,
  lua_yield,
  lua_yieldk
};

// auxiliary library types
pub use self::lauxlib::{
  luaL_Buffer,
  luaL_Reg,
  luaL_Stream
};

// auxiliary library functions
pub use self::lauxlib::{
  luaL_addchar,
  luaL_addlstring,
  luaL_addsize,
  luaL_addstring,
  luaL_addvalue,
  luaL_argcheck,
  luaL_argerror,
  luaL_buffinit,
  luaL_buffinitsize,
  luaL_callmeta,
  luaL_checkany,
  luaL_checkint,
  luaL_checkinteger,
  luaL_checklong,
  luaL_checklstring,
  luaL_checknumber,
  luaL_checkoption,
  luaL_checkstack,
  luaL_checkstring,
  luaL_checktype,
  luaL_checkudata,
  luaL_checkversion,
  luaL_dofile,
  luaL_dostring,
  luaL_error,
  luaL_execresult,
  luaL_fileresult,
  luaL_getmetafield,
  luaL_getmetatable,
  luaL_getsubtable,
  luaL_gsub,
  luaL_len,
  luaL_loadbuffer,
  luaL_loadbufferx,
  luaL_loadfile,
  luaL_loadfilex,
  luaL_loadstring,
  luaL_newlib,
  luaL_newlibtable,
  luaL_newmetatable,
  luaL_newstate,
  luaL_optint,
  luaL_optinteger,
  luaL_optlong,
  luaL_optlstring,
  luaL_optnumber,
  luaL_optstring,
  luaL_prepbuffer,
  luaL_prepbuffsize,
  luaL_pushresult,
  luaL_pushresultsize,
  luaL_ref,
  luaL_requiref,
  luaL_setfuncs,
  luaL_setmetatable,
  luaL_testudata,
  luaL_tolstring,
  luaL_traceback,
  luaL_typename,
  luaL_unref,
  luaL_where
};

// lualib.h functions
pub use self::lualib::{
  luaL_openlibs
};

// constants from lua.h
pub use self::lua::{
  LUA_MULTRET,
  LUA_REGISTRYINDEX,

  LUA_RIDX_MAINTHREAD, LUA_RIDX_GLOBALS,

  LUA_OPADD, LUA_OPSUB, LUA_OPMUL, LUA_OPDIV, LUA_OPIDIV,
  LUA_OPMOD, LUA_OPPOW, LUA_OPUNM,
  LUA_OPBNOT, LUA_OPBAND, LUA_OPBOR, LUA_OPBXOR, LUA_OPSHL, LUA_OPSHR,
  LUA_OPEQ, LUA_OPLT, LUA_OPLE,

  LUA_OK, LUA_ERRRUN, LUA_ERRMEM, LUA_ERRERR, LUA_ERRGCMM,

  LUA_TNONE, LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE,
  LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA,

  LUA_HOOKCALL, LUA_HOOKRET, LUA_HOOKTAILCALL, LUA_HOOKLINE, LUA_HOOKCOUNT,

  LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCCOUNTB,
  LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, LUA_GCISRUNNING
};

// constants from lauxlib.h
pub use self::lauxlib::{
  LUA_REFNIL, LUA_NOREF,
  LUA_ERRFILE,
  LUA_FILEHANDLE
};

// constants from lualib.h
pub use self::lualib::{
  LUA_COLIBNAME, LUA_TABLIBNAME, LUA_IOLIBNAME, LUA_OSLIBNAME, LUA_STRLIBNAME,
  LUA_UTF8LIBNAME, LUA_BITLIBNAME, LUA_MATHLIBNAME, LUA_DBLIBNAME, LUA_LOADLIBNAME
};

mod glue;
pub mod luaconf;
pub mod lua;
pub mod lauxlib;
pub mod lualib;

<|endoftext|>


<commit_before>use {Nl,NlSerState,NlDeState,SerError,DeError};
use ffi::CtrlCmd;
use nlhdr::{NlAttrHdr,AttrHandle};

/// Struct representing generic netlink header and payload
#[derive(Debug,PartialEq)]
pub struct GenlHdr {
    /// Generic netlink message command
    pub cmd: CtrlCmd,
    /// Version of generic netlink family protocol
    pub version: u8,
    reserved: u16,
    /// Attributes included in generic netlink message
    attrs: Vec<u8>,
}

impl GenlHdr {
    /// Create new generic netlink packet
    pub fn new<T>(cmd: CtrlCmd, version: u8, mut attrs: Vec<NlAttrHdr<T>>) -> Result<Self, SerError>
                  where T: Nl {
        let mut state = NlSerState::new();
        for item in attrs.iter_mut() {
            item.serialize(&mut state)?
        }
        Ok(GenlHdr {
            cmd,
            version,
            reserved: 0,
            attrs: state.into_inner(),
        })
    }

    /// Get handle for attribute parsing and traversal
    pub fn get_attr_handle<T>(&self) -> AttrHandle<T> {
        AttrHandle::Bin(self.attrs.clone())
    }
}

impl Default for GenlHdr {
    fn default() -> Self {
        GenlHdr {
            cmd: CtrlCmd::Unspec,
            version: 0,
            reserved: 0,
            attrs: Vec::new(),
        }
    }
}

impl Nl for GenlHdr {
    fn serialize(&self, state: &mut NlSerState) -> Result<(), SerError> {
        self.cmd.serialize(state)?;
        self.version.serialize(state)?;
        self.reserved.serialize(state)?;
        self.attrs.serialize(state)?;
        Ok(())
    }

    fn deserialize(state: &mut NlDeState) -> Result<Self, DeError> {
        let mut genl = GenlHdr::default();
        genl.cmd = CtrlCmd::deserialize(state)?;
        genl.version = u8::deserialize(state)?;
        genl.reserved = u16::deserialize(state)?;
        genl.attrs = Vec::<u8>::deserialize(state)?;
        Ok(genl)
    }

    fn size(&self) -> usize {
        self.cmd.size() + self.version.size() + self.reserved.size()
            + self.attrs.size()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use byteorder::{NativeEndian,WriteBytesExt};
    use std::io::{Cursor,Write};
    use ffi::CtrlAttr;

    #[test]
    pub fn test_serialize() {
        let attr = vec![NlAttrHdr::new_binary_payload(None, CtrlAttr::FamilyId,
                                                        vec![0, 1, 2, 3, 4, 5, 0, 0]
                                                      )];
        let genl = GenlHdr::new(CtrlCmd::Getops, 2,
                                    attr).unwrap();
        let mut state = NlSerState::new();
        genl.serialize(&mut state).unwrap();
        let v = Vec::with_capacity(genl.asize());
        let v_final = {
            let mut c = Cursor::new(v);
            c.write_u8(CtrlCmd::Getops.into()).unwrap();
            c.write_u8(2).unwrap();
            c.write_u16::<NativeEndian>(0).unwrap();
            c.write_u16::<NativeEndian>(12).unwrap();
            c.write_u16::<NativeEndian>(CtrlAttr::FamilyId.into()).unwrap();
            c.write_all(&vec![0, 1, 2, 3, 4, 5, 0, 0]).unwrap();
            c.into_inner()
        };
        assert_eq!(&state.into_inner(), &v_final)
    }

    #[test]
    pub fn test_deserialize() {
        let genl_mock = GenlHdr::new(CtrlCmd::Getops, 2,
                                     vec![NlAttrHdr::new_str_payload(None,
                                            CtrlAttr::FamilyId, "AAAAAAA"
                                        ).unwrap()]
                                     ).unwrap();
        let v = Vec::new();
        let v_final = {
            let mut c = Cursor::new(v);
            c.write_u8(CtrlCmd::Getops.into()).unwrap();
            c.write_u8(2).unwrap();
            c.write_u16::<NativeEndian>(0).unwrap();
            c.write_u16::<NativeEndian>(12).unwrap();
            c.write_u16::<NativeEndian>(CtrlAttr::FamilyId.into()).unwrap();
            c.write(&vec![65, 65, 65, 65, 65, 65, 65, 0]).unwrap();
            c.into_inner()
        };
        let mut state = NlDeState::new(&v_final);
        let genl = GenlHdr::deserialize(&mut state).unwrap();
        assert_eq!(genl, genl_mock)
    }
}
<commit_msg>Remove stray default implementation<commit_after>use {Nl,NlSerState,NlDeState,SerError,DeError};
use ffi::CtrlCmd;
use nlhdr::{NlAttrHdr,AttrHandle};

/// Struct representing generic netlink header and payload
#[derive(Debug,PartialEq)]
pub struct GenlHdr {
    /// Generic netlink message command
    pub cmd: CtrlCmd,
    /// Version of generic netlink family protocol
    pub version: u8,
    reserved: u16,
    /// Attributes included in generic netlink message
    attrs: Vec<u8>,
}

impl GenlHdr {
    /// Create new generic netlink packet
    pub fn new<T>(cmd: CtrlCmd, version: u8, mut attrs: Vec<NlAttrHdr<T>>) -> Result<Self, SerError>
                  where T: Nl {
        let mut state = NlSerState::new();
        for item in attrs.iter_mut() {
            item.serialize(&mut state)?
        }
        Ok(GenlHdr {
            cmd,
            version,
            reserved: 0,
            attrs: state.into_inner(),
        })
    }

    /// Get handle for attribute parsing and traversal
    pub fn get_attr_handle<T>(&self) -> AttrHandle<T> {
        AttrHandle::Bin(self.attrs.clone())
    }
}

impl Nl for GenlHdr {
    fn serialize(&self, state: &mut NlSerState) -> Result<(), SerError> {
        self.cmd.serialize(state)?;
        self.version.serialize(state)?;
        self.reserved.serialize(state)?;
        self.attrs.serialize(state)?;
        Ok(())
    }

    fn deserialize(state: &mut NlDeState) -> Result<Self, DeError> {
        Ok(GenlHdr {
            cmd: CtrlCmd::deserialize(state)?,
            version: u8::deserialize(state)?,
            reserved: u16::deserialize(state)?,
            attrs: Vec::<u8>::deserialize(state)?,
        })
    }

    fn size(&self) -> usize {
        self.cmd.size() + self.version.size() + self.reserved.size()
            + self.attrs.size()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use byteorder::{NativeEndian,WriteBytesExt};
    use std::io::{Cursor,Write};
    use ffi::CtrlAttr;

    #[test]
    pub fn test_serialize() {
        let attr = vec![NlAttrHdr::new_binary_payload(None, CtrlAttr::FamilyId,
                                                        vec![0, 1, 2, 3, 4, 5, 0, 0]
                                                      )];
        let genl = GenlHdr::new(CtrlCmd::Getops, 2,
                                    attr).unwrap();
        let mut state = NlSerState::new();
        genl.serialize(&mut state).unwrap();
        let v = Vec::with_capacity(genl.asize());
        let v_final = {
            let mut c = Cursor::new(v);
            c.write_u8(CtrlCmd::Getops.into()).unwrap();
            c.write_u8(2).unwrap();
            c.write_u16::<NativeEndian>(0).unwrap();
            c.write_u16::<NativeEndian>(12).unwrap();
            c.write_u16::<NativeEndian>(CtrlAttr::FamilyId.into()).unwrap();
            c.write_all(&vec![0, 1, 2, 3, 4, 5, 0, 0]).unwrap();
            c.into_inner()
        };
        assert_eq!(&state.into_inner(), &v_final)
    }

    #[test]
    pub fn test_deserialize() {
        let genl_mock = GenlHdr::new(CtrlCmd::Getops, 2,
                                     vec![NlAttrHdr::new_str_payload(None,
                                            CtrlAttr::FamilyId, "AAAAAAA"
                                        ).unwrap()]
                                     ).unwrap();
        let v = Vec::new();
        let v_final = {
            let mut c = Cursor::new(v);
            c.write_u8(CtrlCmd::Getops.into()).unwrap();
            c.write_u8(2).unwrap();
            c.write_u16::<NativeEndian>(0).unwrap();
            c.write_u16::<NativeEndian>(12).unwrap();
            c.write_u16::<NativeEndian>(CtrlAttr::FamilyId.into()).unwrap();
            c.write(&vec![65, 65, 65, 65, 65, 65, 65, 0]).unwrap();
            c.into_inner()
        };
        let mut state = NlDeState::new(&v_final);
        let genl = GenlHdr::deserialize(&mut state).unwrap();
        assert_eq!(genl, genl_mock)
    }
}
<|endoftext|>


<commit_before>//! Provider for finding definitions
use iron::prelude::*;
use iron::status;
use iron::mime::Mime;

use rustc_serialize::json;

use engine::{SemanticEngine, Definition, Context, CursorPosition, Buffer};
use super::EngineProvider;

/// Given a location, return where the identifier is defined
///
/// Possible responses include
///
/// - `200 OK` the request was successful and a JSON object is returned.
/// - `204 No Content` the request was successful, but no match was found.
/// - `400 Bad Request` the request payload was malformed
/// - `500 Internal Server Error` some unexpected error occurred
pub fn find(req: &mut Request) -> IronResult<Response> {

    // Parse the request. If the request doesn't parse properly, the request is invalid, and a 400
    // BadRequest is returned.
    let fdr = match req.get::<::bodyparser::Struct<FindDefinitionRequest>>() {
        Ok(Some(s)) => {
            trace!("definition::find parsed FindDefinitionRequest");
            s
        },
        Ok(None) => {
            trace!("definition::find failed parsing FindDefinitionRequest");
            return Ok(Response::with(status::BadRequest))
        },
        Err(err) => {
            trace!("definition::find received error while parsing FindDefinitionRequest");
            return Err(IronError::new(err, status::InternalServerError))
        }
    };

    let mutex = req.get::<::persistent::Write<EngineProvider>>().unwrap();
    let engine = mutex.lock().unwrap();
    match engine.find_definition(&fdr.context()) {
        // 200 OK; found the definition
        Ok(Some(definition)) => {
            trace!("definition::find got a match");
            let res = FindDefinitionResponse::from(definition);
            let content_type = "application/json".parse::<Mime>().unwrap();
            Ok(Response::with((content_type, status::Ok, json::encode(&res).unwrap())))
        },

        // 204 No Content; Everything went ok, but the definition was not found.
        Ok(None) => {
            trace!("definition::find did not find a match");
            Ok(Response::with(status::NoContent))
        },

        // 500 Internal Server Error; Error occurred while searching for the definition
        Err(err) => {
            trace!("definition::find encountered an error");
            Err(IronError::new(err, status::InternalServerError))
        }
    }
}

impl From<Definition> for FindDefinitionResponse {
    fn from(def: Definition) -> FindDefinitionResponse {
        FindDefinitionResponse {
            file_path: def.file_path,
            column: def.position.col,
            line: def.position.line,
            text: def.text,
            context: def.text_context,
            kind: def.dtype,
        }
    }
}

#[derive(Debug, RustcDecodable, Clone)]
struct FindDefinitionRequest {
    pub buffers: Vec<Buffer>,
    pub file_path: String,
    pub column: usize,
    pub line: usize,
}

impl FindDefinitionRequest {
    pub fn context(self) -> Context {
        let cursor = CursorPosition { line: self.line, col: self.column };
        Context::new(self.buffers, cursor, self.file_path)
    }
}

#[test]
fn find_definition_request_from_json() {
    let s = stringify!({
        "file_path": "src.rs",
        "buffers": [{
            "file_path": "src.rs",
            "contents": "fn foo() {}\nfn bar() {}\nfn main() {\nfoo();\n}"
        }],
        "line": 4,
        "column": 3
    });

    let req = json::decode::<FindDefinitionRequest>(s).unwrap();
    assert_eq!(req.file_path, "src.rs");
    assert_eq!(req.line, 4);
    assert_eq!(req.column, 3);
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
struct FindDefinitionResponse {
    pub file_path: String,
    pub column: usize,
    pub line: usize,
    pub text: String,
    pub context: String,
    pub kind: String,
}
<commit_msg>Fix PoisonError unwrap in http/definition<commit_after>//! Provider for finding definitions
use iron::prelude::*;
use iron::status;
use iron::mime::Mime;

use rustc_serialize::json;

use engine::{SemanticEngine, Definition, Context, CursorPosition, Buffer};
use super::EngineProvider;

/// Given a location, return where the identifier is defined
///
/// Possible responses include
///
/// - `200 OK` the request was successful and a JSON object is returned.
/// - `204 No Content` the request was successful, but no match was found.
/// - `400 Bad Request` the request payload was malformed
/// - `500 Internal Server Error` some unexpected error occurred
pub fn find(req: &mut Request) -> IronResult<Response> {

    // Parse the request. If the request doesn't parse properly, the request is invalid, and a 400
    // BadRequest is returned.
    let fdr = match req.get::<::bodyparser::Struct<FindDefinitionRequest>>() {
        Ok(Some(s)) => {
            trace!("definition::find parsed FindDefinitionRequest");
            s
        },
        Ok(None) => {
            trace!("definition::find failed parsing FindDefinitionRequest");
            return Ok(Response::with(status::BadRequest))
        },
        Err(err) => {
            trace!("definition::find received error while parsing FindDefinitionRequest");
            return Err(IronError::new(err, status::InternalServerError))
        }
    };

    let mutex = req.get::<::persistent::Write<EngineProvider>>().unwrap();
    let engine = mutex.lock().unwrap_or_else(|e| e.into_inner());
    match engine.find_definition(&fdr.context()) {
        // 200 OK; found the definition
        Ok(Some(definition)) => {
            trace!("definition::find got a match");
            let res = FindDefinitionResponse::from(definition);
            let content_type = "application/json".parse::<Mime>().unwrap();
            Ok(Response::with((content_type, status::Ok, json::encode(&res).unwrap())))
        },

        // 204 No Content; Everything went ok, but the definition was not found.
        Ok(None) => {
            trace!("definition::find did not find a match");
            Ok(Response::with(status::NoContent))
        },

        // 500 Internal Server Error; Error occurred while searching for the definition
        Err(err) => {
            trace!("definition::find encountered an error");
            Err(IronError::new(err, status::InternalServerError))
        }
    }
}

impl From<Definition> for FindDefinitionResponse {
    fn from(def: Definition) -> FindDefinitionResponse {
        FindDefinitionResponse {
            file_path: def.file_path,
            column: def.position.col,
            line: def.position.line,
            text: def.text,
            context: def.text_context,
            kind: def.dtype,
        }
    }
}

#[derive(Debug, RustcDecodable, Clone)]
struct FindDefinitionRequest {
    pub buffers: Vec<Buffer>,
    pub file_path: String,
    pub column: usize,
    pub line: usize,
}

impl FindDefinitionRequest {
    pub fn context(self) -> Context {
        let cursor = CursorPosition { line: self.line, col: self.column };
        Context::new(self.buffers, cursor, self.file_path)
    }
}

#[test]
fn find_definition_request_from_json() {
    let s = stringify!({
        "file_path": "src.rs",
        "buffers": [{
            "file_path": "src.rs",
            "contents": "fn foo() {}\nfn bar() {}\nfn main() {\nfoo();\n}"
        }],
        "line": 4,
        "column": 3
    });

    let req = json::decode::<FindDefinitionRequest>(s).unwrap();
    assert_eq!(req.file_path, "src.rs");
    assert_eq!(req.line, 4);
    assert_eq!(req.column, 3);
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
struct FindDefinitionResponse {
    pub file_path: String,
    pub column: usize,
    pub line: usize,
    pub text: String,
    pub context: String,
    pub kind: String,
}
<|endoftext|>


<commit_before>//! Tidy check to ensure `#[test]` is not used directly inside `libcore`.
//!
//! `#![no_core]` libraries cannot be tested directly due to duplicating lang
//! item. All tests must be written externally in `libcore/tests`.

use std::path::Path;

pub fn check(path: &Path, bad: &mut bool) {
    let libcore_path = path.join("libcore");
    super::walk(
        &libcore_path,
        &mut |subpath| t!(subpath.strip_prefix(&libcore_path)).starts_with("tests"),
        &mut |entry, contents| {
            let subpath = entry.path();
            if let Some("rs") = subpath.extension().and_then(|e| e.to_str()) {
                for line in contents.lines() {
                    let line = line.trim();
                    if !line.starts_with("//") && line.contains("#[test]") {
                        tidy_error!(
                            bad,
                            "`{}` contains `#[test]`; libcore tests must be placed inside \
                            `src/libcore/tests/`",
                            subpath.display()
                        );
                    }
                }
            }
        },
    );
}
<commit_msg>tidy: Add a check for inline unit tests and benchmarks<commit_after>//! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside
//! `libcore` or `liballoc`.
//!
//! `#![no_std]` libraries cannot be tested directly due to duplicating lang
//! items. All tests and benchmarks must be written externally in `libcore/{tests,benches}`
//! or `liballoc/{tests,benches}`.
//!
//! Outside of libcore and liballoc tests and benchmarks should be outlined into separate files
//! named `tests.rs` or `benches.rs`, or directories named `tests` or `benches` unconfigured
//! during normal build.

use std::path::Path;

pub fn check(root_path: &Path, bad: &mut bool) {
    let libcore = &root_path.join("libcore");
    let liballoc = &root_path.join("liballoc");
    let libcore_tests = &root_path.join("libcore/tests");
    let liballoc_tests = &root_path.join("liballoc/tests");
    let libcore_benches = &root_path.join("libcore/benches");
    let liballoc_benches = &root_path.join("liballoc/benches");
    let is_core_or_alloc = |path: &Path| {
        let is_core = path.starts_with(libcore) &&
                      !(path.starts_with(libcore_tests) || path.starts_with(libcore_benches));
        let is_alloc = path.starts_with(liballoc) &&
                       !(path.starts_with(liballoc_tests) || path.starts_with(liballoc_benches));
        is_core || is_alloc
    };
    let fixme = [
        "liballoc",
        "libpanic_unwind/dwarf",
        "librustc",
        "librustc_data_structures",
        "librustc_incremental/persist",
        "librustc_lexer/src",
        "librustc_target/spec",
        "librustdoc",
        "libserialize",
        "libstd",
        "libsyntax",
        "libsyntax_pos",
        "libterm/terminfo",
        "libtest",
        "tools/compiletest/src",
        "tools/tidy/src",
    ];

    let mut skip = |path: &Path| {
        let file_name = path.file_name().unwrap_or_default();
        if path.is_dir() {
            super::filter_dirs(path) ||
            path.ends_with("src/test") ||
            path.ends_with("src/doc") ||
            (file_name == "tests" || file_name == "benches") && !is_core_or_alloc(path) ||
            fixme.iter().any(|p| path.ends_with(p))
        } else {
            let extension = path.extension().unwrap_or_default();
            extension != "rs" ||
            (file_name == "tests.rs" || file_name == "benches.rs") && !is_core_or_alloc(path)
        }
    };

    super::walk(
        root_path,
        &mut skip,
        &mut |entry, contents| {
            let path = entry.path();
            let is_libcore = path.starts_with(libcore);
            let is_liballoc = path.starts_with(liballoc);
            for (i, line) in contents.lines().enumerate() {
                let line = line.trim();
                let is_test = || line.contains("#[test]") && !line.contains("`#[test]");
                let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]");
                if !line.starts_with("//") && (is_test() || is_bench()) {
                    let explanation = if is_libcore {
                        "libcore unit tests and benchmarks must be placed into \
                         `libcore/tests` or `libcore/benches`"
                    } else if is_liballoc {
                        "liballoc unit tests and benchmarks must be placed into \
                         `liballoc/tests` or `liballoc/benches`"
                    } else {
                        "unit tests and benchmarks must be placed into \
                         separate files or directories named \
                         `tests.rs`, `benches.rs`, `tests` or `benches`"
                    };
                    let name = if is_test() { "test" } else { "bench" };
                    tidy_error!(
                        bad, "`{}:{}` contains `#[{}]`; {}",
                        path.display(), i + 1, name, explanation,
                    );
                    return;
                }
            }
        },
    );
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::rc::Rc;
use fnv::FnvHashMap;
use super::Value;

#[derive(Debug, PartialEq)]
struct EnvInner {
    parent: Option<Env>,
    values: FnvHashMap<String, Value>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Env(Rc<RefCell<EnvInner>>);

impl Env {
    pub fn root() -> Env {
        let inner = EnvInner {
            parent: None,
            values: FnvHashMap::default(),
        };

        Env(Rc::new(RefCell::new(inner)))
    }

    pub fn child(&self) -> Env {
        let inner = EnvInner {
            parent: Some(self.clone()),
            values: FnvHashMap::default(),
        };

        Env(Rc::new(RefCell::new(inner)))
    }

    pub fn declare(&mut self, name: String, value: Value) {
        self.0.borrow_mut().values.insert(name, value);
    }

    pub fn assign(&mut self, name: String, value: Value) -> Result<(), String> {
        let mut inner = self.0.borrow_mut();
        if inner.values.contains_key(&name) {
            inner.values.insert(name, value);
            Ok(())
        } else {
            match inner.parent {
                Some(ref mut parent) => parent.assign(name, value),
                None => Err(format!("Variable {} has not been defined", name)),
            }
        }
    }

    pub fn get(&self, name: &str) -> Option<Value> {
        // TODO: Can we do this without cloning? Do we even need to?
        // Once we're interning strings and storing references to objects,
        // Value can probably just impl Copy
        let inner = self.0.borrow();
        match inner.values.get(name) {
            Some(value) => Some(value.clone()),
            None => match inner.parent {
                Some(ref parent) => parent.get(name),
                None => None,
            },
        }
    }
}
<commit_msg>Add note about reference cycles in current implementation of  env.rs<commit_after>use super::Value;
use fnv::FnvHashMap;
use std::cell::RefCell;
use std::rc::Rc;

// TODO: This implementation of environments causes reference cycles (and in turn, memory leaks).
// To fix this, I'll need to either make the `parent` field use weak pointers or switch to a
// different structure (e.g. referring to environments by an id/index).
//
// See: https://doc.rust-lang.org/book/second-edition/ch15-06-reference-cycles.html

#[derive(Debug, PartialEq)]
struct EnvInner {
    parent: Option<Env>,
    values: FnvHashMap<String, Value>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Env(Rc<RefCell<EnvInner>>);

impl Env {
    pub fn root() -> Env {
        let inner = EnvInner {
            parent: None,
            values: FnvHashMap::default(),
        };

        Env(Rc::new(RefCell::new(inner)))
    }

    pub fn child(&self) -> Env {
        let inner = EnvInner {
            parent: Some(self.clone()),
            values: FnvHashMap::default(),
        };

        Env(Rc::new(RefCell::new(inner)))
    }

    pub fn declare(&mut self, name: String, value: Value) {
        self.0.borrow_mut().values.insert(name, value);
    }

    pub fn assign(&mut self, name: String, value: Value) -> Result<(), String> {
        let mut inner = self.0.borrow_mut();
        if inner.values.contains_key(&name) {
            inner.values.insert(name, value);
            Ok(())
        } else {
            match inner.parent {
                Some(ref mut parent) => parent.assign(name, value),
                None => Err(format!("Variable {} has not been defined", name)),
            }
        }
    }

    pub fn get(&self, name: &str) -> Option<Value> {
        // TODO: Can we do this without cloning? Do we even need to?
        // Once we're interning strings and storing references to objects,
        // Value can probably just impl Copy
        let inner = self.0.borrow();
        match inner.values.get(name) {
            Some(value) => Some(value.clone()),
            None => match inner.parent {
                Some(ref parent) => parent.get(name),
                None => None,
            },
        }
    }
}
<|endoftext|>


<commit_before>use tcod;
use tcod::{BackgroundFlag, Console, Key, KeyCode};
use tcod::{KEY_PRESSED, FONT_LAYOUT_ASCII_INROW, FONT_TYPE_GREYSCALE};

use engine::Game;
use util::units::Direction;

#[deriving(PartialEq)]
pub enum State {
    Running,
    Exited
}

pub struct GUI {
    game: Game,
    width: int,
    height: int,
    pub console: Console,
    state: State,
}

impl GUI {
    pub fn new(game: Game) -> GUI {
        let (width, height) = (100, 50);

        tcod::system::set_fps(60);
        Console::set_custom_font(Path::new("/home/jonny/code/rslike/assets/fonts/terminal12x12_gs_ro.png"), FONT_LAYOUT_ASCII_INROW | FONT_TYPE_GREYSCALE, 0, 0);
        let console = Console::init_root(width, height, "Libtcod Rust Tutorial", false);

        GUI {
            game: game,
            width: width,
            height: height,
            console: console,
            state: State::Running,
        }
    }

    pub fn run(&mut self) {
        while !self.window_closed() && !self.exited() {
            self.render();
            self.handle_input();
        }
    }

    fn handle_input(&mut self) {
        let check_key = Console::check_for_keypress(KEY_PRESSED);

        if let Some(keypress) = check_key {
            match keypress.key {
                Key::Special(KeyCode::Up) => {
                    self.game.walk(Direction::Up);
                },
                Key::Special(KeyCode::Down) => {
                    self.game.walk(Direction::Down);
                },
                Key::Special(KeyCode::Left) => {
                    self.game.walk(Direction::Left);
                },
                Key::Special(KeyCode::Right) => {
                    self.game.walk(Direction::Right);
                },
                Key::Special(KeyCode::Escape) => {
                    self.state = State::Exited;
                },
                _ => {}
            }
        }

    }

    fn render(&mut self) {
        self.clear();

        let repr = self.game.world.player.repr;
        let pos = self.game.world.player.pos;

        self.console.put_char(pos.x, pos.y, repr, BackgroundFlag::Set);

        self.flush();
    }

    pub fn clear(&mut self) {
        self.console.clear();
    }

    pub fn flush(&mut self) {
        Console::flush();
    }

    pub fn exited(&self) -> bool {
        self.state == State::Exited
    }

    pub fn window_closed(&self) -> bool {
        Console::window_closed()
    }
}
<commit_msg>Replace width/height in GUI with Size<commit_after>use tcod;
use tcod::{BackgroundFlag, Console, Key, KeyCode};
use tcod::{KEY_PRESSED, FONT_LAYOUT_ASCII_INROW, FONT_TYPE_GREYSCALE};

use engine::Game;
use util::units::{Direction, Size};

#[deriving(PartialEq)]
pub enum State {
    Running,
    Exited
}

pub struct GUI {
    pub size: Size,
    pub console: Console,
    game: Game,
    state: State,
}

impl GUI {
    pub fn new(game: Game) -> GUI {
        let width = 100;
        let height = 50;
        let size = Size::new(width, height);

        tcod::system::set_fps(60);
        Console::set_custom_font(Path::new("/home/jonny/code/rslike/assets/fonts/terminal12x12_gs_ro.png"), FONT_LAYOUT_ASCII_INROW | FONT_TYPE_GREYSCALE, 0, 0);
        let console = Console::init_root(width as int, height as int, "Libtcod Rust Tutorial", false);

        GUI {
            game: game,
            size: size,
            console: console,
            state: State::Running,
        }
    }

    pub fn run(&mut self) {
        while !self.window_closed() && !self.exited() {
            self.render();
            self.handle_input();
        }
    }

    fn handle_input(&mut self) {
        let check_key = Console::check_for_keypress(KEY_PRESSED);

        if let Some(keypress) = check_key {
            match keypress.key {
                Key::Special(KeyCode::Up) => {
                    self.game.walk(Direction::Up);
                },
                Key::Special(KeyCode::Down) => {
                    self.game.walk(Direction::Down);
                },
                Key::Special(KeyCode::Left) => {
                    self.game.walk(Direction::Left);
                },
                Key::Special(KeyCode::Right) => {
                    self.game.walk(Direction::Right);
                },
                Key::Special(KeyCode::Escape) => {
                    self.state = State::Exited;
                },
                _ => {}
            }
        }

    }

    fn render(&mut self) {
        self.clear();

        let repr = self.game.world.player.repr;
        let pos = self.game.world.player.pos;

        self.console.put_char(pos.x, pos.y, repr, BackgroundFlag::Set);

        self.flush();
    }

    pub fn clear(&mut self) {
        self.console.clear();
    }

    pub fn flush(&mut self) {
        Console::flush();
    }

    pub fn exited(&self) -> bool {
        self.state == State::Exited
    }

    pub fn window_closed(&self) -> bool {
        Console::window_closed()
    }
}
<|endoftext|>


<commit_before>//!Request handlers.

use context::Context;
use response::Response;

///A trait for request handlers.
pub trait Handler: Send + Sync + 'static {
    ///Handle a request from the client. Panicking within this method is
    ///discouraged, to allow the server to run smoothly.
    fn handle_request(&self, context: Context, response: Response);
}

impl<F: Fn(Context, Response) + Send + Sync + 'static> Handler for F {
    fn handle_request(&self, context: Context, response: Response) {
        self(context, response);
    }
}<commit_msg>Implement Handler for Arc and Box<commit_after>//!Request handlers.

use context::Context;
use response::Response;
use std::sync::Arc;

///A trait for request handlers.
pub trait Handler: Send + Sync + 'static {
    ///Handle a request from the client. Panicking within this method is
    ///discouraged, to allow the server to run smoothly.
    fn handle_request(&self, context: Context, response: Response);
}

impl<F: Fn(Context, Response) + Send + Sync + 'static> Handler for F {
    fn handle_request(&self, context: Context, response: Response) {
        self(context, response);
    }
}

impl<T: Handler> Handler for Arc<T> {
    fn handle_request(&self, context: Context, response: Response) {
        (**self).handle_request(context, response);
    }
}

impl Handler for Box<Handler> {
    fn handle_request(&self, context: Context, response: Response) {
        (**self).handle_request(context, response);
    }
}
<|endoftext|>


<commit_before>use error::{Error,Result};
use bookoption::BookOption;

use yaml_rust::{Yaml, YamlLoader};
use std::collections::HashMap;
use std::path::PathBuf;
use std::env;

static OPTIONS:&'static str = "
# Metadata
author:str:Anonymous                # The author of the book
title:str:Untitled                  # The title of the book
lang:str:en                         # The language of the book
subject:str                         # Subject of the book (used for EPUB metadata)
description:str                     # Description of the book (used for EPUB metadata)
cover:path                          # File name of the cover of the book
# Output options
output.epub:path                    # Output file name for EPUB rendering
output.html:path                    # Output file name for HTML rendering
output.tex:path                     # Output file name for LaTeX rendering
output.pdf:path                     # Output file name for PDF rendering
output.odt:path                     # Output file name for ODT rendering

# Misc options
base_path:path                      # By default, links and images are relative to the Markdown file. If this is set, it will be to this path.
base_path.links:path                # Set base path but only for links. Useless if base_path is set.
base_path.images:path               # Set base path but only for images. Useless if base_path is set.
enable_yaml_blocks:bool:false       # Enable inline YAML blocks to override options set in config file
zip.command:str:zip                 # Command to use to zip files (for EPUB/ODT)
numbering:int:1                     # The  maximum heading levels to number (0: no numbering, 1: only chapters, ..., 6: all)
display_toc:bool:false              # If true, display a table of content in the document
toc_name:str:Table of contents      # Name of the table of contents if toc is displayed in line
autoclean:bool:true                 # Toggles cleaning of input markdown (not used for LaTeX)
verbose:bool:false                  # If set to true, print warnings in Markdown processing
side_notes:bool:false               # Display footnotes as side notes in HTML/Epub
temp_dir:path:                      # Path where to create a temporary directory (default: uses result from Rust's std::env::temp_dir())
numbering_template:str:\"{{number}}. {{title}}\" # Format of numbered titles
nb_char:char:' '                    # The non-breaking character to use for autoclean when lang is set to fr

# HTML options
html.template:path                  # Path of an HTML template
html.css:path                       # Path of a stylesheet to use with HTML rendering

# EPUB options
epub.version:int:2                  # The EPUB version to generate
epub.css:path                       # Path of a stylesheet to use with EPUB rendering
epub.template:path                  # Path of an epub template for chapter

# LaTeX options
tex.short:bool:false                # If set to true, use article class instead of book and a the default \\maketitle
tex.links_as_footnotes:bool:true    # If set to true, will add foontotes to URL of links in LaTeX/PDF output
tex.command:str:pdflatex            # LaTeX flavour to use for generating PDF
tex.template:path                   # Path of a LaTeX template file
";

/// Contains the options of a book.
#[derive(Debug)]
pub struct BookOptions {
    options: HashMap<String, BookOption>,
    valid_bools: Vec<&'static str>,
    valid_chars: Vec<&'static str>,
    valid_strings: Vec<&'static str>,
    valid_paths: Vec<&'static str>,
    valid_ints: Vec<&'static str>,

    /// Root path of the book (unnecessary copy :/)
    pub root: PathBuf,
}

impl BookOptions {
    /// Creates a new BookOptions struct from the default compliled string
    pub fn new() -> BookOptions {
        let mut options = BookOptions {
            options: HashMap::new(),
            valid_bools:vec!(),
            valid_chars:vec!(),
            valid_ints:vec!(),
            valid_strings:vec!(),
            valid_paths:vec!(),
            root: PathBuf::new(),
        };

        for (_, key, option_type, default_value) in Self::options_to_vec() {
            if key.is_none() {
                continue;
            }
            let key = key.unwrap();
            match option_type.unwrap() {
                "str" => options.valid_strings.push(key),
                "bool" => options.valid_bools.push(key),
                "int" => options.valid_ints.push(key),
                "char" => options.valid_chars.push(key),
                "path" => options.valid_paths.push(key),
                _ => panic!(format!("Ill-formatted OPTIONS string: unrecognized type '{}'", option_type.unwrap())),
            }
            if key == "temp_dir" {
                // "temp_dir" has a special default value that depends on the environment
                options.set(key, &env::temp_dir().to_string_lossy()).unwrap();
                continue;
            }
            if let Some(value) = default_value {
                options.set(key, value).unwrap();
            }
        }
        options
    }

    /// Sets an option from a Yaml tuple
    ///
    /// # Arguments
    /// * `key`: the identifier of the option, must be Yaml::String(_)
    /// * `value`: the value of the option
    ///
    /// **Returns** an error if `key` is not a String, not a valid option, of if
    /// the value is not of the right type.
    pub fn set_yaml(&mut self, key: Yaml, value: Yaml) -> Result<Option<BookOption>> {
        let key = if let Yaml::String(key) = key {
            key
        } else {
            return Err(Error::BookOption(format!("Expected a String as a key, found {:?}", key)));
        };

        if self.valid_strings.contains(&key.as_ref()) {
            // value is a string
            if let Yaml::String(value) = value {
                return Ok(self.options.insert(key, BookOption::String(value)));
            } else {
                return Err(Error::BookOption(format!("Expected a string as value for key {}, found {:?}", &key, &value)));
            }
        } else if self.valid_paths.contains(&key.as_ref()) {
            // value is a path
            if let Yaml::String(value) = value {
                return Ok(self.options.insert(key, BookOption::Path(value)));
            } else {
                return Err(Error::BookOption(format!("Expected a string as value for key {}, found {:?}", &key, &value)));
            }
        } else if self.valid_chars.contains(&key.as_ref()) {
            // value is a char
            if let Yaml::String(value) = value {
                let chars: Vec<_> = value.chars().collect();
                if chars.len() != 1 {
                    return Err(Error::BookOption(format!("could not parse {} as a char: does not contain exactly one char", &value)));
                }
                return Ok(self.options.insert(key.to_owned(), BookOption::Char(chars[0])));
            } else {
                return Err(Error::BookOption(format!("Expected a string as value containing a char for key {}, found {:?}", &key, &value)));
            }
        } else if self.valid_bools.contains(&key.as_ref()) {
            // value is a bool
            if let Yaml::Boolean(value) = value {
                return Ok(self.options.insert(key, BookOption::Bool(value)));
            } else {
                return Err(Error::BookOption(format!("Expected a boolean as value for key {}, found {:?}", &key, &value)));
            }
        } else if self.valid_ints.contains(&key.as_ref()) {
            // value is an int
            if let Yaml::Integer(value) = value {
                return Ok(self.options.insert(key, BookOption::Int(value as i32)));
            } else {
                return Err(Error::BookOption(format!("Expected an integer as value for key {}, found {:?}", &key, &value)));
            }
        } else {
            // key not recognized
            return Err(Error::BookOption(format!("Unrecognized key: {}", &key)));
        }
    }

    /// Sets an option
    ///
    /// # Arguments
    /// * `key`: the identifier of the option, e.g.: "author"
    /// * `value`: the value of the option as a string
    ///
    /// **Returns** an error either if `key` is not a valid option or if the
    /// value is not of the right type. An option containing None if key was
    /// not set, and Some(previous_value) if key was already present.
    ///
    /// # Examples
    /// ```
    /// use crowbook::Book;
    /// let mut book = Book::new(&[]);
    /// book.options.set("author", "Joan Doe").unwrap(); // ok
    /// book.options.set("numbering", "2").unwrap(); // sets numbering to chapters and subsections
    /// let result = book.options.set("autor", "John Smith");
    /// assert!(result.is_err()); // error: "author" was mispelled "autor"
    ///
    /// let result = book.options.set("numbering", "foo");
    /// assert!(result.is_err()); // error: numbering must be an int
    /// ```
    pub fn set(&mut self, key: &str, value: &str) -> Result<Option<BookOption>> {
        let result = YamlLoader::load_from_str(value);
        if let Ok(yaml_docs) = result {
            if yaml_docs.len() == 1 {
                let yaml_value = yaml_docs.into_iter().next().unwrap();
                self.set_yaml(Yaml::String(key.to_owned()), yaml_value)
            } else {
                Err(Error::BookOption(format!("value {} for key {} does not contain one and only one YAML value", value, key)))
            }
        } else {
            Err(Error::BookOption(format!("could not parse {} as a valid YAML value", value)))
        }
    }

    /// get an option
    pub fn get(&self, key: &str) -> Result<&BookOption> {
        self.options.get(key).ok_or(Error::InvalidOption(format!("option {} is not persent", key)))
    }

    /// Gets a string option
    pub fn get_str(&self, key: &str) -> Result<&str> {
        try!(self.get(key)).as_str()
    }

    /// Get a path option
    ///
    /// Adds book's root path before it
    pub fn get_path(&self, key: &str) -> Result<String> {
        let path: &str = try!(try!(self.get(key)).as_path());
        let new_path:PathBuf = self.root.join(path);
        if let Some(path) = new_path.to_str() {
            Ok(path.to_owned())
        } else {
            Err(Error::BookOption(format!("'{}''s path contains invalid UTF-8 code", key)))
        }
    }

    /// Get a path option
    ///
    /// Don't add book's root path before it
    pub fn get_relative_path(&self, key: &str) -> Result<&str> {
        try!(self.get(key)).as_path()
    }

    /// gets a bool option
    pub fn get_bool(&self, key: &str) -> Result<bool> {
        try!(self.get(key)).as_bool()
    }

    /// gets a char option
    pub fn get_char(&self, key: &str) -> Result<char> {
        try!(self.get(key)).as_char()
    }

    /// gets an int  option
    pub fn get_i32(&self, key: &str) -> Result<i32> {
        try!(self.get(key)).as_i32()
    }

    /// Returns a description of all options valid to pass to a book.
    ///
    /// # arguments
    /// * `md`: whether the output should be formatted in Markdown
    pub fn description(md: bool) -> String {
        let mut out = String::new();
        let mut previous_is_comment = true;
        for (comment, key, o_type, default) in Self::options_to_vec() {
            if key.is_none() {
                if !previous_is_comment {
                    out.push_str("\n");
                    previous_is_comment = true;
                }
                out.push_str(&format!("### {} ###\n", comment));
                continue;
            }
            previous_is_comment = false;
            let o_type = match o_type.unwrap() {
                "bool" => "boolean",
                "int" => "integer",
                "char" => "char",
                "str" => "string",
                "path" => "path",
                _ => unreachable!()
            };
            let def = if let Some(value) = default {
                value
            } else {
                "not set"
            };
            if md {
                out.push_str(&format!("- **`{}`**
    - **type**: {}
    - **default value**: `{}`
    - {}\n", key.unwrap(), o_type, def, comment));
            } else {
                out.push_str(&format!("- {} (type: {}) (default: {}) {}\n", key.unwrap(), o_type, def,comment));
            }
        }
        out
    }

    /// OPTIONS to a vec of tuples (comment, key, type, default value)
    fn options_to_vec() -> Vec<(&'static str, Option<&'static str>,
                                Option<&'static str>, Option<&'static str>)> {
        let mut out = vec!();
        for line in OPTIONS.lines() {
            let line = line.trim();
            if line.is_empty() {
                continue;
            }
            if line.starts_with('#') {
                out.push((&line[1..], None, None, None));
                continue;
            }
            let v:Vec<_> = line.split('#').collect();
            let content = v[0];
            let comment = v[1];
            let v:Vec<_> = content.split(':').collect();
            let key = Some(v[0].trim());
            let option_type = Some(v[1].trim());
            let default_value = if v.len() > 2 {
                Some(v[2].trim())
            } else {
                None
            };
            out.push((comment, key, option_type, default_value));
        }
        out
    }
}
<commit_msg>Add resources.path and resources.files options<commit_after>use error::{Error,Result};
use bookoption::BookOption;

use yaml_rust::{Yaml, YamlLoader};
use std::collections::HashMap;
use std::path::PathBuf;
use std::env;

static OPTIONS:&'static str = "
# Metadata
author:str:Anonymous                # The author of the book
title:str:Untitled                  # The title of the book
lang:str:en                         # The language of the book
subject:str                         # Subject of the book (used for EPUB metadata)
description:str                     # Description of the book (used for EPUB metadata)
cover:path                          # File name of the cover of the book
# Output options
output.epub:path                    # Output file name for EPUB rendering
output.html:path                    # Output file name for HTML rendering
output.tex:path                     # Output file name for LaTeX rendering
output.pdf:path                     # Output file name for PDF rendering
output.odt:path                     # Output file name for ODT rendering

# Resources option
resources.path:path:data            # Paths where additional resources should be copied in the EPUB file or HTML directory
resources.files:str                 # Whitespace-separated list of files to embed in e.g. EPUB file

# Misc options
base_path:path                      # By default, links and images are relative to the Markdown file. If this is set, it will be to this path.
base_path.links:path                # Set base path but only for links. Useless if base_path is set.
base_path.images:path               # Set base path but only for images. Useless if base_path is set.
enable_yaml_blocks:bool:false       # Enable inline YAML blocks to override options set in config file
zip.command:str:zip                 # Command to use to zip files (for EPUB/ODT)
numbering:int:1                     # The  maximum heading levels to number (0: no numbering, 1: only chapters, ..., 6: all)
display_toc:bool:false              # If true, display a table of content in the document
toc_name:str:Table of contents      # Name of the table of contents if toc is displayed in line
autoclean:bool:true                 # Toggles cleaning of input markdown (not used for LaTeX)
verbose:bool:false                  # If set to true, print warnings in Markdown processing
side_notes:bool:false               # Display footnotes as side notes in HTML/Epub
temp_dir:path:                      # Path where to create a temporary directory (default: uses result from Rust's std::env::temp_dir())
numbering_template:str:\"{{number}}. {{title}}\" # Format of numbered titles
nb_char:char:' '                    # The non-breaking character to use for autoclean when lang is set to fr

# HTML options
html.template:path                  # Path of an HTML template
html.css:path                       # Path of a stylesheet to use with HTML rendering

# EPUB options
epub.version:int:2                  # The EPUB version to generate
epub.css:path                       # Path of a stylesheet to use with EPUB rendering
epub.template:path                  # Path of an epub template for chapter

# LaTeX options
tex.short:bool:false                # If set to true, use article class instead of book and a the default \\maketitle
tex.links_as_footnotes:bool:true    # If set to true, will add foontotes to URL of links in LaTeX/PDF output
tex.command:str:pdflatex            # LaTeX flavour to use for generating PDF
tex.template:path                   # Path of a LaTeX template file
";

/// Contains the options of a book.
#[derive(Debug)]
pub struct BookOptions {
    options: HashMap<String, BookOption>,
    valid_bools: Vec<&'static str>,
    valid_chars: Vec<&'static str>,
    valid_strings: Vec<&'static str>,
    valid_paths: Vec<&'static str>,
    valid_ints: Vec<&'static str>,

    /// Root path of the book (unnecessary copy :/)
    pub root: PathBuf,
}

impl BookOptions {
    /// Creates a new BookOptions struct from the default compliled string
    pub fn new() -> BookOptions {
        let mut options = BookOptions {
            options: HashMap::new(),
            valid_bools:vec!(),
            valid_chars:vec!(),
            valid_ints:vec!(),
            valid_strings:vec!(),
            valid_paths:vec!(),
            root: PathBuf::new(),
        };

        for (_, key, option_type, default_value) in Self::options_to_vec() {
            if key.is_none() {
                continue;
            }
            let key = key.unwrap();
            match option_type.unwrap() {
                "str" => options.valid_strings.push(key),
                "bool" => options.valid_bools.push(key),
                "int" => options.valid_ints.push(key),
                "char" => options.valid_chars.push(key),
                "path" => options.valid_paths.push(key),
                _ => panic!(format!("Ill-formatted OPTIONS string: unrecognized type '{}'", option_type.unwrap())),
            }
            if key == "temp_dir" {
                // "temp_dir" has a special default value that depends on the environment
                options.set(key, &env::temp_dir().to_string_lossy()).unwrap();
                continue;
            }
            if let Some(value) = default_value {
                options.set(key, value).unwrap();
            }
        }
        options
    }

    /// Sets an option from a Yaml tuple
    ///
    /// # Arguments
    /// * `key`: the identifier of the option, must be Yaml::String(_)
    /// * `value`: the value of the option
    ///
    /// **Returns** an error if `key` is not a String, not a valid option, of if
    /// the value is not of the right type.
    pub fn set_yaml(&mut self, key: Yaml, value: Yaml) -> Result<Option<BookOption>> {
        let key = if let Yaml::String(key) = key {
            key
        } else {
            return Err(Error::BookOption(format!("Expected a String as a key, found {:?}", key)));
        };

        if self.valid_strings.contains(&key.as_ref()) {
            // value is a string
            if let Yaml::String(value) = value {
                return Ok(self.options.insert(key, BookOption::String(value)));
            } else {
                return Err(Error::BookOption(format!("Expected a string as value for key {}, found {:?}", &key, &value)));
            }
        } else if self.valid_paths.contains(&key.as_ref()) {
            // value is a path
            if let Yaml::String(value) = value {
                return Ok(self.options.insert(key, BookOption::Path(value)));
            } else {
                return Err(Error::BookOption(format!("Expected a string as value for key {}, found {:?}", &key, &value)));
            }
        } else if self.valid_chars.contains(&key.as_ref()) {
            // value is a char
            if let Yaml::String(value) = value {
                let chars: Vec<_> = value.chars().collect();
                if chars.len() != 1 {
                    return Err(Error::BookOption(format!("could not parse {} as a char: does not contain exactly one char", &value)));
                }
                return Ok(self.options.insert(key.to_owned(), BookOption::Char(chars[0])));
            } else {
                return Err(Error::BookOption(format!("Expected a string as value containing a char for key {}, found {:?}", &key, &value)));
            }
        } else if self.valid_bools.contains(&key.as_ref()) {
            // value is a bool
            if let Yaml::Boolean(value) = value {
                return Ok(self.options.insert(key, BookOption::Bool(value)));
            } else {
                return Err(Error::BookOption(format!("Expected a boolean as value for key {}, found {:?}", &key, &value)));
            }
        } else if self.valid_ints.contains(&key.as_ref()) {
            // value is an int
            if let Yaml::Integer(value) = value {
                return Ok(self.options.insert(key, BookOption::Int(value as i32)));
            } else {
                return Err(Error::BookOption(format!("Expected an integer as value for key {}, found {:?}", &key, &value)));
            }
        } else {
            // key not recognized
            return Err(Error::BookOption(format!("Unrecognized key: {}", &key)));
        }
    }

    /// Sets an option
    ///
    /// # Arguments
    /// * `key`: the identifier of the option, e.g.: "author"
    /// * `value`: the value of the option as a string
    ///
    /// **Returns** an error either if `key` is not a valid option or if the
    /// value is not of the right type. An option containing None if key was
    /// not set, and Some(previous_value) if key was already present.
    ///
    /// # Examples
    /// ```
    /// use crowbook::Book;
    /// let mut book = Book::new(&[]);
    /// book.options.set("author", "Joan Doe").unwrap(); // ok
    /// book.options.set("numbering", "2").unwrap(); // sets numbering to chapters and subsections
    /// let result = book.options.set("autor", "John Smith");
    /// assert!(result.is_err()); // error: "author" was mispelled "autor"
    ///
    /// let result = book.options.set("numbering", "foo");
    /// assert!(result.is_err()); // error: numbering must be an int
    /// ```
    pub fn set(&mut self, key: &str, value: &str) -> Result<Option<BookOption>> {
        let result = YamlLoader::load_from_str(value);
        if let Ok(yaml_docs) = result {
            if yaml_docs.len() == 1 {
                let yaml_value = yaml_docs.into_iter().next().unwrap();
                self.set_yaml(Yaml::String(key.to_owned()), yaml_value)
            } else {
                Err(Error::BookOption(format!("value {} for key {} does not contain one and only one YAML value", value, key)))
            }
        } else {
            Err(Error::BookOption(format!("could not parse {} as a valid YAML value", value)))
        }
    }

    /// Gets an option
    pub fn get(&self, key: &str) -> Result<&BookOption> {
        self.options.get(key).ok_or(Error::InvalidOption(format!("option {} is not persent", key)))
    }

    /// Gets a list of path. Only used for resources.files.
    pub fn get_paths_list(&self, key: &str) -> Result<Vec<String>> {
        if key != "resources.files" {
            return Err(Error::BookOption(format!("Can't get {} as a list of files, only valid if key is resources.files", key)));
        }

        let list = try!(try!(self.get(key))
                        .as_str())
            .split_whitespace();
        let mut res = vec!();
        for s in list {
            if let Some(path) = self.root.join(s).to_str() {
                res.push(path.to_owned());
            } else {
                return Err(Error::BookOption(format!("{} contains invalid UTF-8", key)));
            }
        }
        Ok(res)
    }

    /// Gets a string option
    pub fn get_str(&self, key: &str) -> Result<&str> {
        try!(self.get(key)).as_str()
    }

    /// Get a path option
    ///
    /// Adds book's root path before it
    pub fn get_path(&self, key: &str) -> Result<String> {
        let path: &str = try!(try!(self.get(key)).as_path());
        let new_path:PathBuf = self.root.join(path);
        if let Some(path) = new_path.to_str() {
            Ok(path.to_owned())
        } else {
            Err(Error::BookOption(format!("'{}''s path contains invalid UTF-8 code", key)))
        }
    }

    /// Get a path option
    ///
    /// Don't add book's root path before it
    pub fn get_relative_path(&self, key: &str) -> Result<&str> {
        try!(self.get(key)).as_path()
    }

    /// gets a bool option
    pub fn get_bool(&self, key: &str) -> Result<bool> {
        try!(self.get(key)).as_bool()
    }

    /// gets a char option
    pub fn get_char(&self, key: &str) -> Result<char> {
        try!(self.get(key)).as_char()
    }

    /// gets an int  option
    pub fn get_i32(&self, key: &str) -> Result<i32> {
        try!(self.get(key)).as_i32()
    }

    /// Returns a description of all options valid to pass to a book.
    ///
    /// # arguments
    /// * `md`: whether the output should be formatted in Markdown
    pub fn description(md: bool) -> String {
        let mut out = String::new();
        let mut previous_is_comment = true;
        for (comment, key, o_type, default) in Self::options_to_vec() {
            if key.is_none() {
                if !previous_is_comment {
                    out.push_str("\n");
                    previous_is_comment = true;
                }
                out.push_str(&format!("### {} ###\n", comment));
                continue;
            }
            previous_is_comment = false;
            let o_type = match o_type.unwrap() {
                "bool" => "boolean",
                "int" => "integer",
                "char" => "char",
                "str" => "string",
                "path" => "path",
                _ => unreachable!()
            };
            let def = if let Some(value) = default {
                value
            } else {
                "not set"
            };
            if md {
                out.push_str(&format!("- **`{}`**
    - **type**: {}
    - **default value**: `{}`
    - {}\n", key.unwrap(), o_type, def, comment));
            } else {
                out.push_str(&format!("- {} (type: {}) (default: {}) {}\n", key.unwrap(), o_type, def,comment));
            }
        }
        out
    }

    /// OPTIONS to a vec of tuples (comment, key, type, default value)
    fn options_to_vec() -> Vec<(&'static str, Option<&'static str>,
                                Option<&'static str>, Option<&'static str>)> {
        let mut out = vec!();
        for line in OPTIONS.lines() {
            let line = line.trim();
            if line.is_empty() {
                continue;
            }
            if line.starts_with('#') {
                out.push((&line[1..], None, None, None));
                continue;
            }
            let v:Vec<_> = line.split('#').collect();
            let content = v[0];
            let comment = v[1];
            let v:Vec<_> = content.split(':').collect();
            let key = Some(v[0].trim());
            let option_type = Some(v[1].trim());
            let default_value = if v.len() > 2 {
                Some(v[2].trim())
            } else {
                None
            };
            out.push((comment, key, option_type, default_value));
        }
        out
    }
}
<|endoftext|>