<commit_before>use super::*;
use redox::*;

impl Editor {
    /// Execute a instruction
    pub fn exec(&mut self, Inst(para, cmd): Inst) {
        use super::Key::*;
        use super::Mode::*;
        use super::PrimitiveMode::*;
        use super::CommandMode::*;

        let n = para.d();
        match cmd {
            Ctrl(b) => self.key_state.ctrl = b,
            Alt(b) => self.key_state.alt = b,
            Shift(b) => self.key_state.shift = b,
            _ => {},
        }

        if cmd == Char(' ') && self.key_state.shift {
            self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
        } else if self.key_state.alt {
            let new_pos = self.to_motion(Inst(para, cmd));
            self.goto(new_pos);
        } else {
            match self.cursor().mode {
                Command(Normal) => match cmd {
                    Char('i') => {
                        self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(
                            InsertOptions {
                                mode: InsertMode::Insert,
                            }));

                    },
                    Char('o') => {
                        // TODO: Autoindent (keep the same indentation level)
                        let y = self.y();
                        let ind = self.get_indent(y);
                        let last = ind.len();
                        self.text.insert(y + 1, ind);
                        self.goto((last, y + 1));
                        self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(
                            InsertOptions {
                                mode: InsertMode::Insert,
                            }));
                    }
                    Char('h') => self.goto_left(n),
                    Char('j') => self.goto_down(n),
                    Char('k') => self.goto_up(n),
                    Char('l') => self.goto_right(n),
                    Char('J') => self.goto_down(15),
                    Char('K') => self.goto_up(15),
                    Char('x') => self.delete(),
                    Char('X') => {
                        self.goto_previous();
                        self.delete();
                    },
                    Char('L') => self.goto_ln_end(),
                    Char('H') => self.cursor_mut().x = 0,
                    Char('r') => {
                        loop {
                            if let EventOption::Key(k) = self.window.poll()
                                                         .unwrap_or(Event::new())
                                                         .to_option() {
                                if k.pressed {
                                    let x = self.x();
                                    let y = self.y();
                                    self.text[y][x] = k.character;
                                    break;
                                }
                            }
                        }
                    },
                    Char('R') => {
                        self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(
                            InsertOptions {
                                mode: InsertMode::Replace,
                            }));
                    },
                    Char('d') => {
                        let ins = self.next_inst();
                        let motion = self.to_motion(ins);
                        self.remove_rb(motion);
                    },
                    Char('G') => {
                        let last = self.text.len() - 1;
                        self.goto((0, last));
                    },
                    Char('g') => {
                        if let Parameter::Int(n) = para {
                            self.goto((0, n - 1));
                        } else {
                            let inst = self.next_inst();
                            let new = self.to_motion(inst);
                            self.cursor_mut().x = new.0;
                            self.cursor_mut().y = new.1;
                        }

                    },
//                    ????
//                    Char('K') => {
//                        self.goto((0, 0));
//                    },
//                    Char('J') => {
//                        self.goto((0, self.text.len() - 1));
//                    },
                    Char(' ') => self.goto_next(),
                    _ => {},
                },
                Primitive(Insert(opt)) => {
                    self.insert(cmd, opt);
                },
            }
        }
    }
}
<commit_msg>Add cursor branching<commit_after>use super::*;
use redox::*;

impl Editor {
    /// Execute a instruction
    pub fn exec(&mut self, Inst(para, cmd): Inst) {
        use super::Key::*;
        use super::Mode::*;
        use super::PrimitiveMode::*;
        use super::CommandMode::*;

        let n = para.d();
        match cmd {
            Ctrl(b) => self.key_state.ctrl = b,
            Alt(b) => self.key_state.alt = b,
            Shift(b) => self.key_state.shift = b,
            _ => {},
        }

        if cmd == Char(' ') && self.key_state.shift {
            self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
        } else if self.key_state.alt && cmd == Key::Char(' ') {
            self.next_cursor();
        } else if self.key_state.alt {
            let new_pos = self.to_motion(Inst(para, cmd));
            self.goto(new_pos);
        } else {
            match self.cursor().mode {
                Command(Normal) => match cmd {
                    Char('i') => {
                        self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(
                            InsertOptions {
                                mode: InsertMode::Insert,
                            }));

                    },
                    Char('o') => {
                        // TODO: Autoindent (keep the same indentation level)
                        let y = self.y();
                        let ind = self.get_indent(y);
                        let last = ind.len();
                        self.text.insert(y + 1, ind);
                        self.goto((last, y + 1));
                        self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(
                            InsertOptions {
                                mode: InsertMode::Insert,
                            }));
                    }
                    Char('h') => self.goto_left(n),
                    Char('j') => self.goto_down(n),
                    Char('k') => self.goto_up(n),
                    Char('l') => self.goto_right(n),
                    Char('J') => self.goto_down(15),
                    Char('K') => self.goto_up(15),
                    Char('x') => self.delete(),
                    Char('X') => {
                        self.goto_previous();
                        self.delete();
                    },
                    Char('L') => self.goto_ln_end(),
                    Char('H') => self.cursor_mut().x = 0,
                    Char('r') => {
                        loop {
                            if let EventOption::Key(k) = self.window.poll()
                                                         .unwrap_or(Event::new())
                                                         .to_option() {
                                if k.pressed {
                                    let x = self.x();
                                    let y = self.y();
                                    self.text[y][x] = k.character;
                                    break;
                                }
                            }
                        }
                    },
                    Char('R') => {
                        self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Insert(
                            InsertOptions {
                                mode: InsertMode::Replace,
                            }));
                    },
                    Char('d') => {
                        let ins = self.next_inst();
                        let motion = self.to_motion(ins);
                        self.remove_rb(motion);
                    },
                    Char('G') => {
                        let last = self.text.len() - 1;
                        self.goto((0, last));
                    },
                    Char('g') => {
                        if let Parameter::Int(n) = para {
                            self.goto((0, n - 1));
                        } else {
                            let inst = self.next_inst();
                            let new = self.to_motion(inst);
                            self.cursor_mut().x = new.0;
                            self.cursor_mut().y = new.1;
                        }

                    },
                    Char('b') => {
                        // Branch cursor
                        let cursor = self.cursor().clone();
                        self.cursors.push(cursor);
                    },
//                    ????
//                    Char('K') => {
//                        self.goto((0, 0));
//                    },
//                    Char('J') => {
//                        self.goto((0, self.text.len() - 1));
//                    },
                    Char(' ') => self.goto_next(),
                    _ => {},
                },
                Primitive(Insert(opt)) => {
                    self.insert(cmd, opt);
                },
            }
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use ast::*;
use visit::{Visitor};
use std::cell::RefCell;
use ptr::*;

pub type OptionalBinding = Option<B<Binding>>;
/*impl<T> Visitor<T> for CodeGenVisitor where T: std::fmt::Display {
    fn visit(&self, t: T) {
        println!("{}", t);
    }
}*/

/*struct CodeGenVisitor;
struct ExpVisitor{
    result : i32
}

impl<'a> Visitor<&'a NumExpression> for ExpVisitor {
    fn visit(&mut self, n: &'a NumExpression) {
        self.result = n.value;
    }
}

impl<'a> Visitor<&'a AddExpression> for ExpVisitor {
    fn visit(&mut self, add: &'a AddExpression) {
        let b1 = *(add.e1);
        b1.accept(self);
        let r1 = self.result;
        add.e2.accept(self);
        let r2 = self.result;
        self.result = r1 + r2;
    }
}*/

struct ExpEvaluator{
    value : i32
}

impl ExpEvaluator{
    fn get_value(&mut self, expr : & Expr)->i32{
        match expr{
            &Expr::NumExpr(value) => value,
            _ => 1
        }
    }
}

impl<'a> Visitor<'a> for ExpEvaluator{
    fn visit_expr(&mut self, expr: &'a Expr){
        match expr{
            &Expr::AddExpr(ref left, ref right) => {
                let v1 = self.get_value(left);
                let v2 = self.get_value(right);
                self.value = v1 + v2;
            },
            _ => {}
        }
    }
}

pub struct TypeChecker{
    //block_stack : Vec<RefCell<&'a  Block>>,
    pub sym_tab : Vec<(String, OptionalBinding)>,
    //decl_cnt : u32,
    //decl_cnt_stack : Vec<u32>,
    pub ty : TType
}

impl TypeChecker{
    pub fn new()->Self{
        TypeChecker {sym_tab : Vec::new(), ty : TType::TNil}
    }

    fn get_type_for(&self){//}->&TType{
        //self.block_stack
    }
}

impl<'a> Visitor<'a> for TypeChecker{
    fn visit_expr(&mut self, expr: &'a Expr){
        match expr{
            //FIXME remove NilExpr; this is only for unit testing
            &Expr::NilExpr => self.ty = TType::TString,
            &Expr::NumExpr(_) => self.ty = TType::TInt32,
            &Expr::StringExpr(_) => self.ty = TType::TString,
            &Expr::IdExpr(ref id) =>{
                //search in the symtab for id's existence and get the type
                let mut found = false;
                for &(ref _id, ref _binding) in &self.sym_tab{ //iterator returns a ref to tuple while iterating; so &(_,_) has to be used
                    if *_id == *id{
                        found = true;
                        self.ty = match **_binding.as_ref().unwrap(){
                            Binding::TypeBinding(ref ty) |
                            Binding::VarBinding(ref ty) |
                            Binding::FuncBinding(ref ty) => ty.clone()
                        };
                        break;
                    }
                }
                if !found{
                    panic!("{} not found", id);
                }
            },
            &Expr::AddExpr(ref left, ref right) => {
                self.visit_expr(left);
                let left_ty = self.ty.clone();
                if left_ty != TType::TInt32{
                    panic!("Expected left operand of int type");
                }
                self.visit_expr(right);
                if self.ty != TType::TInt32{
                    panic!("Expected right operand of int type")
                }
            },
            &Expr::SeqExpr(ref opt_expr_list) => {
                for b_expr in opt_expr_list.as_ref().unwrap() {
                    self.visit_expr(&*b_expr);
                }
            },
            &Expr::LetExpr(ref decls, ref opt_expr) => {
                self.sym_tab.push(("<marker>".to_string(), None));

                for dec in decls{ //decls is a &
                    self.visit_decl(dec);
                }

                match opt_expr {
                    &Some(ref b_expr) => {
                        self.visit_expr(&*expr);
                    },
                    _ => {}
                }
                //pop till marker and then pop marker
                while self.sym_tab.last().unwrap().0 != "<marker>".to_string(){
                    self.sym_tab.pop();
                }
                self.sym_tab.pop();
            },

            _ => {}
        }
    }

    fn visit_decl(&mut self, decl : &'a Decl){
        macro_rules! store_into_sym_tab {
            ($self_ : ident, $i : ident, $p : path) => {
                $self_.sym_tab.push(($i.clone(), Some(B($p($self_.ty.clone())))));
            }
        }
        match decl{
            &Decl::VarDec(ref id, ref ty, ref expr) => {
                self.visit_expr(expr);
                if *ty != self.ty{
                    panic!("Types mismatch");
                }
                store_into_sym_tab!(self, id, Binding::VarBinding);
            },
            &Decl::FunDec(ref id, ref params, ref ret_type, ref body) => {
                self.visit_expr(&body);
                if self.ty != *ret_type{
                    panic!("Return type doesn't match with the type of the last expression.");
                }
                store_into_sym_tab!(self, id, Binding::FuncBinding);
            },
            &Decl::TypeDec(ref id, ref ty) => {
                store_into_sym_tab!(self, id, Binding::TypeBinding);
            }
        }
    }
}

struct PrettyPrintVisitor;

impl<'a> Visitor<'a> for PrettyPrintVisitor{
    fn visit_expr(&mut self, expr:&'a Expr){
        match expr{
            &Expr::LetExpr(ref v, _) => {
                println!("(let");
                for d in v{
                    println!("\t(");

                }
                println!(")");
            },
            &Expr::AddExpr(ref left, ref right) => {
                self.visit_expr(left);
                println!(" Plus ");
                self.visit_expr(right);
            },
            &Expr::NumExpr(value) => {
                println!(" Num({}) ", value);
            },
            &Expr::IdExpr(ref value) => {
                println!(" Ident({}) ", value);
            },
            _ => {}
        }
    }

    fn visit_block(&mut self, block: &'a Block){
        for s in &block.statements{
            self.visit_stmt(&*s);
        }
    }

    fn visit_stmt(&mut self, stmt : &'a Stmt){
        match stmt{
            &Stmt::VarDeclStmt(ref local) => {
                println!("(var {} type {} init ", local.ident, local.ty);
                self.visit_expr(&*local.expr);
                println!(")");
            },
            &Stmt::ExprStmt(ref expr) => {
                self.visit_expr(expr);
            }
        }
    }
}

//FIXME remove this struct
struct SymbolTableBuilder<'a>{
    block_stack : Vec<RefCell<&'a  Block>>
}

impl<'a> SymbolTableBuilder<'a>{
    fn new()->Self{
        SymbolTableBuilder {block_stack : Vec::new()}
    }
}

impl<'a> Visitor<'a> for SymbolTableBuilder<'a>{
    fn visit_block(&mut self, block : &'a Block){
        self.block_stack.push(RefCell::new(block));
        for s in &block.statements{
            self.visit_stmt(s);
        }
        self.block_stack.pop();
    }

    fn visit_stmt(&mut self, stmt : &'a Stmt){
        match stmt{
            &Stmt::VarDeclStmt(ref local) => {
                    //FIXME deduce the correct type
                    let mut block = self.block_stack.last_mut().unwrap().borrow_mut();
                    block.sym_tab.borrow_mut().insert(local.ident.clone(), TType::TInt32);
                },
            &Stmt::ExprStmt(ref expr) => {
                let b =  &**expr; //*expr is deref B which is Box<T>; **expr is deref Box<T> which is T; &**expr is therefore &T
                match b {
                    &Expr::BlockExpr(ref block) => {
                        self.visit_block(block);
                    },
                    _ => {}
                }
            }
        }
    }
}

#[test]
fn test_pp_visit_add_expr(){
    let mut p = PrettyPrintVisitor;
    p.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(2))));
}

#[test]
fn test_pp_visit_block(){
    let mut p = PrettyPrintVisitor;
    let mut b = Block::new();
    // let l = Local::new("a".to_string(), LuaType::LNil, B(Expr::NumExpr(1)));
    // b.statements.push(B(Stmt::VarDeclStmt(l)));
    p.visit_block(&b);
}

//#[test]
fn test_pp_visit_add(){
    let mut p = PrettyPrintVisitor;
    let mut b = Block::new();
    p.visit_block(&b);
}

#[test]
fn test_ty_set_for_num() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::NumExpr(23));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_ty_set_for_int_id() {
    let mut v = TypeChecker::new();
    v.sym_tab.push(("a".to_string(), Some(B(Binding::VarBinding(TType::TInt32)))));
    v.visit_expr(&Expr::IdExpr("a".to_string()));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_type_match_int_for_var_dec() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TInt32, B(Expr::NumExpr(4))));
    assert_eq!(TType::TInt32, v.ty);
    assert_eq!(v.sym_tab.len(), 1);
    assert_eq!(v.sym_tab[0].0, "a".to_string());
    //assert_eq!(v.sym_tab[0].1, TType::TInt32);
}

#[test]
fn test_type_match_string_for_var_dec() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TString, B(Expr::NilExpr)));
    assert_eq!(TType::TString, v.ty);
    assert_eq!(v.sym_tab.len(), 1);
    assert_eq!(v.sym_tab[0].0, "a".to_string());
    //assert_eq!(v.sym_tab[0].1, TType::TString);
}

#[test]
#[should_panic]
fn test_type_check_for_var_dec_type_mismatch() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TInt32, B(Expr::NilExpr)));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_correct_types_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(4)), B(Expr::NumExpr(4))));
    assert_eq!(v.ty, TType::TInt32);
}

#[test]
#[should_panic(expected="Expected left operand of int type")]
fn test_left_type_invalid_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NilExpr), B(Expr::NumExpr(4))));
}

#[test]
#[should_panic(expected="Expected right operand of int type")]
fn test_right_type_invalid_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(4)), B(Expr::NilExpr)));
}

#[test]
fn test_var_hiding() {
    //let mut v =
}

#[test]
fn test_func_decl_correct_return_type() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::FunDec(String::from("foo"), None, TType::TInt32, B(Expr::NumExpr(4))));
}

#[test]
#[should_panic(expected="Return type doesn't match with the type of the last expression.")]
fn test_func_decl_incorrect_return_type() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::FunDec(String::from("foo"), None, TType::TString, B(Expr::NumExpr(4))));
}
<commit_msg>Add preliminary type-checking for if-expr<commit_after>#![allow(dead_code)]

use ast::*;
use visit::{Visitor};
use std::cell::RefCell;
use ptr::*;

pub type OptionalBinding = Option<B<Binding>>;

struct ExpEvaluator{
    value : i32
}

impl ExpEvaluator{
    fn get_value(&mut self, expr : & Expr)->i32{
        match expr{
            &Expr::NumExpr(value) => value,
            _ => 1
        }
    }
}

impl<'a> Visitor<'a> for ExpEvaluator{
    fn visit_expr(&mut self, expr: &'a Expr){
        match expr{
            &Expr::AddExpr(ref left, ref right) => {
                let v1 = self.get_value(left);
                let v2 = self.get_value(right);
                self.value = v1 + v2;
            },
            _ => {}
        }
    }
}

pub struct TypeChecker{
    //block_stack : Vec<RefCell<&'a  Block>>,
    pub sym_tab : Vec<(String, OptionalBinding)>,
    //decl_cnt : u32,
    //decl_cnt_stack : Vec<u32>,
    pub ty : TType
}

impl TypeChecker{
    pub fn new()->Self{
        TypeChecker {sym_tab : Vec::new(), ty : TType::TNil}
    }

    fn get_type_for(&self){//}->&TType{
        //self.block_stack
    }
}

impl<'a> Visitor<'a> for TypeChecker{
    fn visit_expr(&mut self, expr: &'a Expr){
        match expr{
            //FIXME remove NilExpr; this is only for unit testing
            &Expr::NilExpr => self.ty = TType::TString,
            &Expr::NumExpr(_) => self.ty = TType::TInt32,
            &Expr::StringExpr(_) => self.ty = TType::TString,
            &Expr::IdExpr(ref id) =>{
                //search in the symtab for id's existence and get the type
                let mut found = false;
                for &(ref _id, ref _binding) in &self.sym_tab{ //iterator returns a ref to tuple while iterating; so &(_,_) has to be used
                    if *_id == *id{
                        found = true;
                        self.ty = match **_binding.as_ref().unwrap(){
                            Binding::TypeBinding(ref ty) |
                            Binding::VarBinding(ref ty) |
                            Binding::FuncBinding(ref ty) => ty.clone()
                        };
                        break;
                    }
                }
                if !found{
                    panic!("{} not found", id);
                }
            },
            &Expr::AddExpr(ref left, ref right) => {
                self.visit_expr(left);
                let left_ty = self.ty.clone();
                if left_ty != TType::TInt32{
                    panic!("Expected left operand of int type");
                }
                self.visit_expr(right);
                if self.ty != TType::TInt32{
                    panic!("Expected right operand of int type")
                }
            },
            &Expr::SeqExpr(ref opt_expr_list) => {
                for b_expr in opt_expr_list.as_ref().unwrap() {
                    self.visit_expr(&*b_expr);
                }
            },
            &Expr::IfThenExpr(ref conditional_expr, _) => {
                self.visit_expr(conditional_expr);
                if self.ty != TType::TInt32{
                    panic!("Expected conditional expression of int type");
                }
            },
            &Expr::LetExpr(ref decls, ref opt_expr) => {
                self.sym_tab.push(("<marker>".to_string(), None));

                for dec in decls{ //decls is a &
                    self.visit_decl(dec);
                }

                match opt_expr {
                    &Some(ref b_expr) => {
                        self.visit_expr(&*expr);
                    },
                    _ => {}
                }
                //pop till marker and then pop marker
                while self.sym_tab.last().unwrap().0 != "<marker>".to_string(){
                    self.sym_tab.pop();
                }
                self.sym_tab.pop();
            },

            _ => {}
        }
    }

    fn visit_decl(&mut self, decl : &'a Decl){
        macro_rules! store_into_sym_tab {
            ($self_ : ident, $i : ident, $p : path) => {
                $self_.sym_tab.push(($i.clone(), Some(B($p($self_.ty.clone())))));
            }
        }
        match decl{
            &Decl::VarDec(ref id, ref ty, ref expr) => {
                self.visit_expr(expr);
                if *ty != self.ty{
                    panic!("Types mismatch");
                }
                store_into_sym_tab!(self, id, Binding::VarBinding);
            },
            &Decl::FunDec(ref id, ref params, ref ret_type, ref body) => {
                self.visit_expr(&body);
                if self.ty != *ret_type{
                    panic!("Return type doesn't match with the type of the last expression.");
                }
                store_into_sym_tab!(self, id, Binding::FuncBinding);
            },
            &Decl::TypeDec(ref id, ref ty) => {
                store_into_sym_tab!(self, id, Binding::TypeBinding);
            }
        }
    }
}

struct PrettyPrintVisitor;

impl<'a> Visitor<'a> for PrettyPrintVisitor{
    fn visit_expr(&mut self, expr:&'a Expr){
        match expr{
            &Expr::LetExpr(ref v, _) => {
                println!("(let");
                for d in v{
                    println!("\t(");

                }
                println!(")");
            },
            &Expr::AddExpr(ref left, ref right) => {
                self.visit_expr(left);
                println!(" Plus ");
                self.visit_expr(right);
            },
            &Expr::NumExpr(value) => {
                println!(" Num({}) ", value);
            },
            &Expr::IdExpr(ref value) => {
                println!(" Ident({}) ", value);
            },
            _ => {}
        }
    }

    fn visit_block(&mut self, block: &'a Block){
        for s in &block.statements{
            self.visit_stmt(&*s);
        }
    }

    fn visit_stmt(&mut self, stmt : &'a Stmt){
        match stmt{
            &Stmt::VarDeclStmt(ref local) => {
                println!("(var {} type {} init ", local.ident, local.ty);
                self.visit_expr(&*local.expr);
                println!(")");
            },
            &Stmt::ExprStmt(ref expr) => {
                self.visit_expr(expr);
            }
        }
    }
}

//FIXME remove this struct
struct SymbolTableBuilder<'a>{
    block_stack : Vec<RefCell<&'a  Block>>
}

impl<'a> SymbolTableBuilder<'a>{
    fn new()->Self{
        SymbolTableBuilder {block_stack : Vec::new()}
    }
}

impl<'a> Visitor<'a> for SymbolTableBuilder<'a>{
    fn visit_block(&mut self, block : &'a Block){
        self.block_stack.push(RefCell::new(block));
        for s in &block.statements{
            self.visit_stmt(s);
        }
        self.block_stack.pop();
    }

    fn visit_stmt(&mut self, stmt : &'a Stmt){
        match stmt{
            &Stmt::VarDeclStmt(ref local) => {
                    //FIXME deduce the correct type
                    let mut block = self.block_stack.last_mut().unwrap().borrow_mut();
                    block.sym_tab.borrow_mut().insert(local.ident.clone(), TType::TInt32);
                },
            &Stmt::ExprStmt(ref expr) => {
                let b =  &**expr; //*expr is deref B which is Box<T>; **expr is deref Box<T> which is T; &**expr is therefore &T
                match b {
                    &Expr::BlockExpr(ref block) => {
                        self.visit_block(block);
                    },
                    _ => {}
                }
            }
        }
    }
}

#[test]
fn test_pp_visit_add_expr(){
    let mut p = PrettyPrintVisitor;
    p.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(1)), B(Expr::NumExpr(2))));
}

#[test]
fn test_pp_visit_block(){
    let mut p = PrettyPrintVisitor;
    let mut b = Block::new();
    // let l = Local::new("a".to_string(), LuaType::LNil, B(Expr::NumExpr(1)));
    // b.statements.push(B(Stmt::VarDeclStmt(l)));
    p.visit_block(&b);
}

//#[test]
fn test_pp_visit_add(){
    let mut p = PrettyPrintVisitor;
    let mut b = Block::new();
    p.visit_block(&b);
}

#[test]
fn test_ty_set_for_num() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::NumExpr(23));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_ty_set_for_int_id() {
    let mut v = TypeChecker::new();
    v.sym_tab.push(("a".to_string(), Some(B(Binding::VarBinding(TType::TInt32)))));
    v.visit_expr(&Expr::IdExpr("a".to_string()));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_type_match_int_for_var_dec() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TInt32, B(Expr::NumExpr(4))));
    assert_eq!(TType::TInt32, v.ty);
    assert_eq!(v.sym_tab.len(), 1);
    assert_eq!(v.sym_tab[0].0, "a".to_string());
    //assert_eq!(v.sym_tab[0].1, TType::TInt32);
}

#[test]
fn test_type_match_string_for_var_dec() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TString, B(Expr::NilExpr)));
    assert_eq!(TType::TString, v.ty);
    assert_eq!(v.sym_tab.len(), 1);
    assert_eq!(v.sym_tab[0].0, "a".to_string());
    //assert_eq!(v.sym_tab[0].1, TType::TString);
}

#[test]
#[should_panic]
fn test_type_check_for_var_dec_type_mismatch() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::VarDec("a".to_string(), TType::TInt32, B(Expr::NilExpr)));
    assert_eq!(TType::TInt32, v.ty);
}

#[test]
fn test_correct_types_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(4)), B(Expr::NumExpr(4))));
    assert_eq!(v.ty, TType::TInt32);
}

#[test]
#[should_panic(expected="Expected left operand of int type")]
fn test_left_type_invalid_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NilExpr), B(Expr::NumExpr(4))));
}

#[test]
#[should_panic(expected="Expected right operand of int type")]
fn test_right_type_invalid_for_add_expr() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::AddExpr(B(Expr::NumExpr(4)), B(Expr::NilExpr)));
}

#[test]
fn test_var_hiding() {
    //let mut v =
}

#[test]
fn test_func_decl_correct_return_type() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::FunDec(String::from("foo"), None, TType::TInt32, B(Expr::NumExpr(4))));
}

#[test]
#[should_panic(expected="Return type doesn't match with the type of the last expression.")]
fn test_func_decl_incorrect_return_type() {
    let mut v = TypeChecker::new();
    v.visit_decl(&Decl::FunDec(String::from("foo"), None, TType::TString, B(Expr::NumExpr(4))));
}

#[test]
#[should_panic(expected="Expected conditional expression of int type")]
fn test_if_expr_with_incorrect_conditional_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::IfThenExpr(B(Expr::StringExpr(String::from("a"))), B(Expr::StringExpr(String::from("a")))));
}

#[test]
fn test_if_expr_with_int_type() {
    let mut v = TypeChecker::new();
    v.visit_expr(&Expr::IfThenExpr(B(Expr::NumExpr(1)), B(Expr::StringExpr(String::from("a")))));
    assert_eq!(v.ty, TType::TInt32);
}
<|endoftext|>


<commit_before>//! Functions to support processing request/response bodies

use pact_matching::models::matchingrules::{Category, MatchingRule, RuleLogic};
use pact_matching::models::generators::{Generators, Generator, GeneratorCategory};
use serde_json::{Value, Map};
use pact_matching::models::json_utils::json_to_string;
use pact_matching::models::{Request, OptionalBody, Response};
use maplit::*;
use hyper::header::Headers;
use formdata::{FormData, FilePart};
use std::path::Path;
use log::*;

const CONTENT_TYPE_HEADER: &str = "Content-Type";

fn process_array(array: &[Value], matching_rules: &mut Category, generators: &mut Generators, path: &str, type_matcher: bool) -> Value {
  Value::Array(array.iter().enumerate().map(|(index, val)| {
    let updated_path = if type_matcher {
      path.to_owned() + "[*]"
    } else {
      path.to_owned() + "[" + &index.to_string() + "]"
    };
    match val {
      Value::Object(ref map) => process_object(map, matching_rules, generators, &updated_path, false),
      Value::Array(ref array) => process_array(array, matching_rules, generators, &updated_path, false),
      _ => val.clone()
    }
  }).collect())
}

fn process_object(obj: &Map<String, Value>, matching_rules: &mut Category, generators: &mut Generators, path: &str, type_matcher: bool) -> Value {
  if obj.contains_key("pact:matcher:type") {
    if let Some(rule) = MatchingRule::from_integration_json(obj) {
      matching_rules.add_rule(&path.to_string(), rule, &RuleLogic::And);
    }
    if let Some(gen) = obj.get("pact:generator:type") {
      if let Some(generator) = Generator::from_map(&json_to_string(gen), obj) {
        generators.add_generator_with_subcategory(&GeneratorCategory::BODY, path, generator);
      }
    }
    match obj.get("value") {
      Some(val) => match val {
        Value::Object(ref map) => process_object(map, matching_rules, generators, path, true),
        Value::Array(array) => process_array(array, matching_rules, generators, path, true),
        _ => val.clone()
      },
      None => Value::Null
    }
  } else {
    Value::Object(obj.iter().map(|(key, val)| {
      let updated_path = if type_matcher {
        path.to_owned() + ".*"
      } else {
        path.to_owned() + "." + key
      };
      (key.clone(), match val {
        Value::Object(ref map) => process_object(map, matching_rules, generators, &updated_path, false),
        Value::Array(ref array) => process_array(array, matching_rules, generators, &updated_path, false),
        _ => val.clone()
      })
    }).collect())
  }
}

/// Process a JSON body with embedded matching rules and generators
pub fn process_json(body: String, matching_rules: &mut Category, generators: &mut Generators) -> String {
  match serde_json::from_str(&body) {
    Ok(json) => match json {
      Value::Object(ref map) => process_object(map, matching_rules, generators, &"$".to_string(), false).to_string(),
      Value::Array(ref array) => process_array(array, matching_rules, generators, &"$".to_string(), false).to_string(),
      _ => body
    },
    Err(_) => body
  }
}

/// Setup the request as a multipart form upload
pub fn request_multipart(request: &mut Request, boundary: &str, body: OptionalBody, content_type: &str, part_name: &str) {
  request.body = body;
  match request.headers {
    Some(ref mut headers) => {
      headers.insert(CONTENT_TYPE_HEADER.to_string(), vec![format!("multipart/form-data; boundary={}", boundary)]);
    },
    None => {
      request.headers = Some(hashmap! {
        CONTENT_TYPE_HEADER.to_string() => vec![format!("multipart/form-data; boundary={}", boundary)]
      });
    }
  };
  request.matching_rules.add_category("body")
    .add_rule(format!("$['{}']", part_name), MatchingRule::ContentType(content_type.into()), &RuleLogic::And);
  request.matching_rules.add_category("header")
    .add_rule("Content-Type", MatchingRule::Regex(r"multipart/form-data;(\s*charset=[^;]*;)?\s*boundary=.*".into()), &RuleLogic::And);
}

/// Setup the response as a multipart form upload
pub fn response_multipart(response: &mut Response, boundary: &str, body: OptionalBody, content_type: &str, part_name: &str) {
  response.body = body;
  match response.headers {
    Some(ref mut headers) => {
      headers.insert(CONTENT_TYPE_HEADER.to_string(), vec![format!("multipart/form-data; boundary={}", boundary)]);
    },
    None => {
      response.headers = Some(hashmap! {
        CONTENT_TYPE_HEADER.to_string() => vec![format!("multipart/form-data; boundary={}", boundary)]
      });
    }
  }
  response.matching_rules.add_category("body")
    .add_rule(format!("$['{}']", part_name), MatchingRule::ContentType(content_type.into()), &RuleLogic::And);
  response.matching_rules.add_category("header")
    .add_rule("Content-Type", MatchingRule::Regex(r"multipart/form-data;(\s*charset=[^;]*;)?\s*boundary=.*".into()), &RuleLogic::And);
}

/// Loads an example file as a MIME Multipart body
pub fn file_as_multipart_body(file: &str, part_name: &str, boundary: &str) -> Result<OptionalBody, String> {
  let headers = Headers::new();
  let formdata = FormData {
    fields: vec![],
    files: vec![(part_name.to_string(), FilePart::new(headers, Path::new(file)))]
  };
  let mut buffer: Vec<u8> = vec![];
  match formdata::write_formdata(&mut buffer, &boundary.as_bytes().to_vec(), &formdata) {
    Ok(_) => Ok(OptionalBody::Present(buffer.clone())),
    Err(err) => {
      warn!("convert_ptr_to_mime_part_body: Failed to generate multipart body: {}", err);
      Err(format!("convert_ptr_to_mime_part_body: Failed to generate multipart body: {}", err))
    }
  }
}
<commit_msg>feat: make body processing functions public so other language impl can use them<commit_after>//! Functions to support processing request/response bodies

use pact_matching::models::matchingrules::{Category, MatchingRule, RuleLogic};
use pact_matching::models::generators::{Generators, Generator, GeneratorCategory};
use serde_json::{Value, Map};
use pact_matching::models::json_utils::json_to_string;
use pact_matching::models::{Request, OptionalBody, Response};
use maplit::*;
use hyper::header::Headers;
use formdata::{FormData, FilePart};
use std::path::Path;
use log::*;

const CONTENT_TYPE_HEADER: &str = "Content-Type";

/// Process an array with embedded matching rules and generators
pub fn process_array(array: &[Value], matching_rules: &mut Category, generators: &mut Generators, path: &str, type_matcher: bool) -> Value {
  Value::Array(array.iter().enumerate().map(|(index, val)| {
    let updated_path = if type_matcher {
      path.to_owned() + "[*]"
    } else {
      path.to_owned() + "[" + &index.to_string() + "]"
    };
    match val {
      Value::Object(ref map) => process_object(map, matching_rules, generators, &updated_path, false),
      Value::Array(ref array) => process_array(array, matching_rules, generators, &updated_path, false),
      _ => val.clone()
    }
  }).collect())
}

/// Process an object (map) with embedded matching rules and generators
pub fn process_object(obj: &Map<String, Value>, matching_rules: &mut Category, generators: &mut Generators, path: &str, type_matcher: bool) -> Value {
  if obj.contains_key("pact:matcher:type") {
    if let Some(rule) = MatchingRule::from_integration_json(obj) {
      matching_rules.add_rule(&path.to_string(), rule, &RuleLogic::And);
    }
    if let Some(gen) = obj.get("pact:generator:type") {
      if let Some(generator) = Generator::from_map(&json_to_string(gen), obj) {
        generators.add_generator_with_subcategory(&GeneratorCategory::BODY, path, generator);
      }
    }
    match obj.get("value") {
      Some(val) => match val {
        Value::Object(ref map) => process_object(map, matching_rules, generators, path, true),
        Value::Array(array) => process_array(array, matching_rules, generators, path, true),
        _ => val.clone()
      },
      None => Value::Null
    }
  } else {
    Value::Object(obj.iter().map(|(key, val)| {
      let updated_path = if type_matcher {
        path.to_owned() + ".*"
      } else {
        path.to_owned() + "." + key
      };
      (key.clone(), match val {
        Value::Object(ref map) => process_object(map, matching_rules, generators, &updated_path, false),
        Value::Array(ref array) => process_array(array, matching_rules, generators, &updated_path, false),
        _ => val.clone()
      })
    }).collect())
  }
}

/// Process a JSON body with embedded matching rules and generators
pub fn process_json(body: String, matching_rules: &mut Category, generators: &mut Generators) -> String {
  match serde_json::from_str(&body) {
    Ok(json) => match json {
      Value::Object(ref map) => process_object(map, matching_rules, generators, &"$".to_string(), false).to_string(),
      Value::Array(ref array) => process_array(array, matching_rules, generators, &"$".to_string(), false).to_string(),
      _ => body
    },
    Err(_) => body
  }
}

/// Process a JSON body with embedded matching rules and generators
pub fn process_json_value(body: &Value, matching_rules: &mut Category, generators: &mut Generators) -> String {
  match body {
    Value::Object(ref map) => process_object(map, matching_rules, generators, &"$".to_string(), false).to_string(),
    Value::Array(ref array) => process_array(array, matching_rules, generators, &"$".to_string(), false).to_string(),
    _ => body.to_string()
  }
}

/// Setup the request as a multipart form upload
pub fn request_multipart(request: &mut Request, boundary: &str, body: OptionalBody, content_type: &str, part_name: &str) {
  request.body = body;
  match request.headers {
    Some(ref mut headers) => {
      headers.insert(CONTENT_TYPE_HEADER.to_string(), vec![format!("multipart/form-data; boundary={}", boundary)]);
    },
    None => {
      request.headers = Some(hashmap! {
        CONTENT_TYPE_HEADER.to_string() => vec![format!("multipart/form-data; boundary={}", boundary)]
      });
    }
  };
  request.matching_rules.add_category("body")
    .add_rule(format!("$['{}']", part_name), MatchingRule::ContentType(content_type.into()), &RuleLogic::And);
  request.matching_rules.add_category("header")
    .add_rule("Content-Type", MatchingRule::Regex(r"multipart/form-data;(\s*charset=[^;]*;)?\s*boundary=.*".into()), &RuleLogic::And);
}

/// Setup the response as a multipart form upload
pub fn response_multipart(response: &mut Response, boundary: &str, body: OptionalBody, content_type: &str, part_name: &str) {
  response.body = body;
  match response.headers {
    Some(ref mut headers) => {
      headers.insert(CONTENT_TYPE_HEADER.to_string(), vec![format!("multipart/form-data; boundary={}", boundary)]);
    },
    None => {
      response.headers = Some(hashmap! {
        CONTENT_TYPE_HEADER.to_string() => vec![format!("multipart/form-data; boundary={}", boundary)]
      });
    }
  }
  response.matching_rules.add_category("body")
    .add_rule(format!("$['{}']", part_name), MatchingRule::ContentType(content_type.into()), &RuleLogic::And);
  response.matching_rules.add_category("header")
    .add_rule("Content-Type", MatchingRule::Regex(r"multipart/form-data;(\s*charset=[^;]*;)?\s*boundary=.*".into()), &RuleLogic::And);
}

/// Loads an example file as a MIME Multipart body
pub fn file_as_multipart_body(file: &str, part_name: &str, boundary: &str) -> Result<OptionalBody, String> {
  let headers = Headers::new();
  let formdata = FormData {
    fields: vec![],
    files: vec![(part_name.to_string(), FilePart::new(headers, Path::new(file)))]
  };
  let mut buffer: Vec<u8> = vec![];
  match formdata::write_formdata(&mut buffer, &boundary.as_bytes().to_vec(), &formdata) {
    Ok(_) => Ok(OptionalBody::Present(buffer.clone())),
    Err(err) => {
      warn!("convert_ptr_to_mime_part_body: Failed to generate multipart body: {}", err);
      Err(format!("convert_ptr_to_mime_part_body: Failed to generate multipart body: {}", err))
    }
  }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Background", inherited=False) %>

${helpers.predefined_type(
    "background-color",
    "Color",
    "computed_value::T::transparent()",
    initial_specified_value="SpecifiedValue::transparent()",
    spec="https://drafts.csswg.org/css-backgrounds/#background-color",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    allow_quirks=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
)}

${helpers.predefined_type("background-image", "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
    vector="True",
    animation_value_type="discrete",
    ignored_when_colors_disabled="True",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}

% for (axis, direction, initial) in [("x", "Horizontal", "left"), ("y", "Vertical", "top")]:
    ${helpers.predefined_type(
        "background-position-" + axis,
        "position::" + direction + "Position",
        initial_value="computed::LengthOrPercentage::zero()",
        initial_specified_value="SpecifiedValue::initial_specified_value()",
        spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
        animation_value_type="ComputedValue",
        vector=True,
        flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    )}
% endfor

${helpers.predefined_type(
    "background-repeat",
    "BackgroundRepeat",
    "computed::BackgroundRepeat::repeat()",
    initial_specified_value="specified::BackgroundRepeat::repeat()",
    animation_value_type="discrete",
    vector=True,
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
)}

${helpers.single_keyword("background-attachment",
                         "scroll fixed" + (" local" if product == "gecko" else ""),
                         vector=True,
                         gecko_constant_prefix="NS_STYLE_IMAGELAYER_ATTACHMENT",
                         spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment",
                         animation_value_type="discrete",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}

${helpers.single_keyword("background-clip",
                         "border-box padding-box content-box",
                         extra_gecko_values="text",
                         vector=True, extra_prefixes="webkit",
                         gecko_enum_prefix="StyleGeometryBox",
                         spec="https://drafts.csswg.org/css-backgrounds/#the-background-clip",
                         animation_value_type="discrete",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}

${helpers.single_keyword("background-origin",
                         "padding-box border-box content-box",
                         vector=True, extra_prefixes="webkit",
                         gecko_enum_prefix="StyleGeometryBox",
                         spec="https://drafts.csswg.org/css-backgrounds/#the-background-origin",
                         animation_value_type="discrete",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}

${helpers.predefined_type("background-size", "BackgroundSize",
    initial_value="computed::BackgroundSize::auto()",
    initial_specified_value="specified::BackgroundSize::auto()",
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
    vector=True,
    animation_value_type="BackgroundSizeList",
    need_animatable=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    extra_prefixes="webkit")}

// https://drafts.fxtf.org/compositing/#background-blend-mode
${helpers.single_keyword("background-blend-mode",
                         """normal multiply screen overlay darken lighten color-dodge
                            color-burn hard-light soft-light difference exclusion hue
                            saturation color luminosity""",
                         gecko_constant_prefix="NS_STYLE_BLEND",
                         gecko_pref="layout.css.background-blend-mode.enabled",
                         vector=True, products="gecko", animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/compositing/#background-blend-mode",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
<commit_msg>style: Convert NS_STYLE_IMAGELAYER_ATTACHMENT_* to enum class.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Background", inherited=False) %>

${helpers.predefined_type(
    "background-color",
    "Color",
    "computed_value::T::transparent()",
    initial_specified_value="SpecifiedValue::transparent()",
    spec="https://drafts.csswg.org/css-backgrounds/#background-color",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    allow_quirks=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
)}

${helpers.predefined_type("background-image", "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
    vector="True",
    animation_value_type="discrete",
    ignored_when_colors_disabled="True",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}

% for (axis, direction, initial) in [("x", "Horizontal", "left"), ("y", "Vertical", "top")]:
    ${helpers.predefined_type(
        "background-position-" + axis,
        "position::" + direction + "Position",
        initial_value="computed::LengthOrPercentage::zero()",
        initial_specified_value="SpecifiedValue::initial_specified_value()",
        spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
        animation_value_type="ComputedValue",
        vector=True,
        flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    )}
% endfor

${helpers.predefined_type(
    "background-repeat",
    "BackgroundRepeat",
    "computed::BackgroundRepeat::repeat()",
    initial_specified_value="specified::BackgroundRepeat::repeat()",
    animation_value_type="discrete",
    vector=True,
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat",
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
)}

${helpers.single_keyword("background-attachment",
                         "scroll fixed" + (" local" if product == "gecko" else ""),
                         vector=True,
                         gecko_enum_prefix="StyleImageLayerAttachment",
                         spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment",
                         animation_value_type="discrete",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}

${helpers.single_keyword("background-clip",
                         "border-box padding-box content-box",
                         extra_gecko_values="text",
                         vector=True, extra_prefixes="webkit",
                         gecko_enum_prefix="StyleGeometryBox",
                         spec="https://drafts.csswg.org/css-backgrounds/#the-background-clip",
                         animation_value_type="discrete",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}

${helpers.single_keyword("background-origin",
                         "padding-box border-box content-box",
                         vector=True, extra_prefixes="webkit",
                         gecko_enum_prefix="StyleGeometryBox",
                         spec="https://drafts.csswg.org/css-backgrounds/#the-background-origin",
                         animation_value_type="discrete",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}

${helpers.predefined_type("background-size", "BackgroundSize",
    initial_value="computed::BackgroundSize::auto()",
    initial_specified_value="specified::BackgroundSize::auto()",
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
    vector=True,
    animation_value_type="BackgroundSizeList",
    need_animatable=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    extra_prefixes="webkit")}

// https://drafts.fxtf.org/compositing/#background-blend-mode
${helpers.single_keyword("background-blend-mode",
                         """normal multiply screen overlay darken lighten color-dodge
                            color-burn hard-light soft-light difference exclusion hue
                            saturation color luminosity""",
                         gecko_constant_prefix="NS_STYLE_BLEND",
                         gecko_pref="layout.css.background-blend-mode.enabled",
                         vector=True, products="gecko", animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/compositing/#background-blend-mode",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
<|endoftext|>


<commit_before>use error::{Error,Result, Source};
use html::HtmlRenderer;
use book::Book;
use token::Token;
use templates::{html};
use resource_handler::ResourceHandler;
use renderer::Renderer;

use mustache;

use std::io::{Read,Write};
use std::fs;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::borrow::Cow;

/// Multiple files HTML renderer
///
/// Renders HTML in a given directory.
pub struct HtmlDirRenderer<'a> {
    html: HtmlRenderer<'a>,
}

impl<'a> HtmlDirRenderer<'a> {
    /// Creates a new HtmlDirRenderer
    pub fn new(book: &'a Book) -> HtmlDirRenderer<'a> {
        let mut html = HtmlRenderer::new(book);
        html.handler.set_images_mapping(true);
        html.handler.set_base64(false);
        HtmlDirRenderer {
            html: html,
        }
    }

    /// Render a book
    pub fn render_book(&mut self) -> Result<()> {
        // Add internal files to resource handler
        for (i, filename) in self.html.book.filenames.iter().enumerate() {
            self.html.handler.add_link(filename.clone(), filenamer(i));
        }

        // Create the directory
        let dest_path = try!(self.html.book.options.get_path("output.html_dir"));
        match fs::metadata(&dest_path) {
            Ok(metadata) => if metadata.is_file() {
                return Err(Error::Render(format!("{} already exists and is not a directory", &dest_path)));
            } else if metadata.is_dir() {
                self.html.book.logger.warning(format!("{} already exists, deleting it", &dest_path));
                try!(fs::remove_dir_all(&dest_path)
                     .map_err(|e| Error::Render(format!("error deleting directory {}: {}", &dest_path, e))));
            },
            Err(_) => (),
        }
        try!(fs::DirBuilder::new()
             .recursive(true)
             .create(&dest_path)
             .map_err(|e| Error::Render(format!("could not create HTML directory {}:{}", &dest_path, e))));

        // Write CSS
        try!(self.write_css());
        // Write print.css
        try!(self.write_file("print.css",
                             &self.html.book.get_template("html.print_css").unwrap().as_bytes()));
        // Write index.html and chapter_xxx.html
        try!(self.write_html());
        // Write menu.svg
        try!(self.write_file("menu.svg", html::MENU_SVG));

        // Write highlight files if they are needed
        if self.html.book.options.get_bool("html.highlight_code") == Ok(true) {
            try!(self.write_file("highlight.js", self.html.book.get_template("html.highlight.js").unwrap().as_bytes()));
            try!(self.write_file("highlight.css", self.html.book.get_template("html.highlight.css").unwrap().as_bytes()));
        }

        // Write all images (including cover)
        let images_path = PathBuf::from(&self.html.book.options.get_path("resources.base_path.images").unwrap());
        for (source, dest) in self.html.handler.images_mapping() {
            let mut f = try!(File::open(images_path.join(source)).map_err(|_| Error::FileNotFound(self.html.book.source.clone(),
                                                                                "image or cover".to_owned(),
                                                                                source.to_owned())));
            let mut content = vec!();
            try!(f.read_to_end(&mut content).map_err(|e| Error::Render(format!("error while reading image file {}: {}", source, e))));
            try!(self.write_file(dest, &content));
        }

        // Write additional files
        if let Ok(list) = self.html.book.options.get_paths_list("resources.files") {
            let files_path = self.html.book.options.get_path("resources.base_path.files").unwrap();
            let data_path = Path::new(self.html.book.options.get_relative_path("resources.out_path").unwrap());
            let list = try!(ResourceHandler::get_files(list, &files_path));
            for path in list {
                let abs_path = Path::new(&files_path).join(&path);
                let mut f = try!(File::open(&abs_path)
                                 .map_err(|_| Error::FileNotFound(self.html.book.source.clone(),
                                                                  "additional resource from resources.files".to_owned(),
                                                                  abs_path.to_string_lossy().into_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::Render(format!("error while reading resource file: {}", e))));
                try!(self.write_file(data_path.join(&path).to_str().unwrap(), &content));
            }
        }

        Ok(())
    }

    // Render each chapter and write them, and index.html too
    fn write_html(&mut self) -> Result<()> {
        let mut chapters = vec!();
        let mut titles = vec!();
        for (i, &(n, ref v)) in self.html.book.chapters.iter().enumerate() {
            self.html.chapter_config(i, n, filenamer(i));
            let mut title = String::new();
            for token in v {
                match *token {
                    Token::Header(1, ref vec) => {
                        if self.html.current_hide || self.html.current_numbering == 0 {
                            title = try!(self.html.render_vec(vec));
                        } else {
                            title = try!(self.html.book.get_header(self.html.current_chapter[0] + 1,
                                                              &try!(self.html.render_vec(vec))));
                        }
                        break;
                    },
                    _ => {
                        continue;
                    }
                }
            }
            titles.push(title);

            let chapter = self.html.render_html(v);
            chapters.push(chapter);
        }
        self.html.source = Source::empty();
        let toc = self.html.toc.render();

        for (i, content) in chapters.into_iter().enumerate() {
            let prev_chapter = if i > 0 {
                format!("

    « {}

",
                        filenamer(i-1),
                        titles[i-1])
            } else {
                String::new()
            };

            let next_chapter = if i < titles.len() - 1 {
                format!("

    {} »

",
                        filenamer(i+1),
                        titles[i+1])
            } else {
                String::new()
            };

            // Render each HTML document
            let mut mapbuilder = self.html.book.get_mapbuilder("none")
                .insert_str("content", try!(content))
                .insert_str("chapter_title", format!("{} – {}",
                                             self.html.book.options.get_str("title").unwrap(),
                                             titles[i]))
                .insert_str("toc", toc.clone())
                .insert_str("prev_chapter", prev_chapter)
                .insert_str("next_chapter", next_chapter)
                .insert_str("footer", self.html.get_footer())
                .insert_str("top", self.html.get_top())
                .insert_str("script", self.html.book.get_template("html_dir.script").unwrap())
                .insert_bool(self.html.book.options.get_str("lang").unwrap(), true);

            if self.html.book.options.get_bool("html.highlight_code").unwrap() == true {
                mapbuilder = mapbuilder.insert_bool("highlight_code", true);
            }
            let data = mapbuilder.build();
            let template = mustache::compile_str(try!(self.html.book.get_template("html_dir.chapter.html")).as_ref());
            let mut res = vec!();
            template.render_data(&mut res, &data);
            try!(self.write_file(&filenamer(i), &res));
        }

        let mut content = if let Ok(cover) = self.html.book.options.get_path("cover") {
            // checks first that cover exists
            if fs::metadata(&cover).is_err() {
                return Err(Error::FileNotFound(self.html.book.source.clone(),
                                               "cover".to_owned(),
                                               cover));

            }
            format!("

",
                    self.html.book.options.get_str("title").unwrap(),
                    try!(self.html.handler.map_image(&self.html.book.source,
                                                     Cow::Owned(cover))).as_ref())
        } else {
            String::new()
        };
        if titles.len() > 1 {
            content.push_str(&format!("

    {} »

",
                        filenamer(0),
                        titles[0]));
        }
        // Render index.html and write it too
        let mut mapbuilder = self.html.book.get_mapbuilder("none")
            .insert_str("content", content)
            .insert_str("top", self.html.get_top())
            .insert_str("footer", self.html.get_footer())
            .insert_str("toc", toc.clone())
            .insert_str("script", self.html.book.get_template("html_dir.script").unwrap())
            .insert_bool(self.html.book.options.get_str("lang").unwrap(), true);
        if self.html.book.options.get_bool("html.highlight_code").unwrap() == true {
            mapbuilder = mapbuilder.insert_bool("highlight_code", true);
        }
        let data = mapbuilder.build();
        let template = mustache::compile_str(try!(self.html.book.get_template("html_dir.index.html")).as_ref());
        let mut res = vec!();
        template.render_data(&mut res, &data);
        try!(self.write_file("index.html", &res));

        Ok(())
    }

    // Render the CSS file and write it
    fn write_css(&self) -> Result<()> {
        // Render the CSS
        let template_css = mustache::compile_str(try!(self.html.book.get_template("html_dir.css")).as_ref());
        let data = self.html.book.get_mapbuilder("none")
            .insert_bool(self.html.book.options.get_str("lang").unwrap(), true)
            .build();
        let mut res:Vec<u8> = vec!();
        template_css.render_data(&mut res, &data);
        let css = String::from_utf8_lossy(&res);

        // Write it
        self.write_file("stylesheet.css", css.as_bytes())
    }

    // Write content to a file
    fn write_file(&self, file: &str, content: &[u8]) -> Result<()> {
        let dest_path = PathBuf::from(&self.html.book.options.get_path("output.html_dir").unwrap());
        if dest_path.starts_with("..") {
            panic!("html dir is asked to create a file outside of its directory, no way!");
        }
        let dest_file = dest_path.join(file);
        let dest_dir = dest_file.parent().unwrap();
        if !fs::metadata(dest_dir).is_ok() { // dir does not exist, create it
            try!(fs::DirBuilder::new()
                 .recursive(true)
                 .create(&dest_dir)
                 .map_err(|e| Error::Render(format!("could not create directory in {}:{}", dest_dir.display(), e))));
        }
        let mut f = try!(File::create(&dest_file)
                         .map_err(|e| Error::Render(format!("could not create file {}:{}", dest_file.display(), e))));
        f.write_all(content)
            .map_err(|e| Error::Render(format!("could not write to file {}:{}", dest_file.display(), e)))
    }
}

/// Generate a file name given an int
fn filenamer(i: usize) -> String {
    format!("chapter_{:03}.html", i)
}
<commit_msg>Add licence information<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use error::{Error,Result, Source};
use html::HtmlRenderer;
use book::Book;
use token::Token;
use templates::{html};
use resource_handler::ResourceHandler;
use renderer::Renderer;

use mustache;

use std::io::{Read,Write};
use std::fs;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::borrow::Cow;

/// Multiple files HTML renderer
///
/// Renders HTML in a given directory.
pub struct HtmlDirRenderer<'a> {
    html: HtmlRenderer<'a>,
}

impl<'a> HtmlDirRenderer<'a> {
    /// Creates a new HtmlDirRenderer
    pub fn new(book: &'a Book) -> HtmlDirRenderer<'a> {
        let mut html = HtmlRenderer::new(book);
        html.handler.set_images_mapping(true);
        html.handler.set_base64(false);
        HtmlDirRenderer {
            html: html,
        }
    }

    /// Render a book
    pub fn render_book(&mut self) -> Result<()> {
        // Add internal files to resource handler
        for (i, filename) in self.html.book.filenames.iter().enumerate() {
            self.html.handler.add_link(filename.clone(), filenamer(i));
        }

        // Create the directory
        let dest_path = try!(self.html.book.options.get_path("output.html_dir"));
        match fs::metadata(&dest_path) {
            Ok(metadata) => if metadata.is_file() {
                return Err(Error::Render(format!("{} already exists and is not a directory", &dest_path)));
            } else if metadata.is_dir() {
                self.html.book.logger.warning(format!("{} already exists, deleting it", &dest_path));
                try!(fs::remove_dir_all(&dest_path)
                     .map_err(|e| Error::Render(format!("error deleting directory {}: {}", &dest_path, e))));
            },
            Err(_) => (),
        }
        try!(fs::DirBuilder::new()
             .recursive(true)
             .create(&dest_path)
             .map_err(|e| Error::Render(format!("could not create HTML directory {}:{}", &dest_path, e))));

        // Write CSS
        try!(self.write_css());
        // Write print.css
        try!(self.write_file("print.css",
                             &self.html.book.get_template("html.print_css").unwrap().as_bytes()));
        // Write index.html and chapter_xxx.html
        try!(self.write_html());
        // Write menu.svg
        try!(self.write_file("menu.svg", html::MENU_SVG));

        // Write highlight files if they are needed
        if self.html.book.options.get_bool("html.highlight_code") == Ok(true) {
            try!(self.write_file("highlight.js", self.html.book.get_template("html.highlight.js").unwrap().as_bytes()));
            try!(self.write_file("highlight.css", self.html.book.get_template("html.highlight.css").unwrap().as_bytes()));
        }

        // Write all images (including cover)
        let images_path = PathBuf::from(&self.html.book.options.get_path("resources.base_path.images").unwrap());
        for (source, dest) in self.html.handler.images_mapping() {
            let mut f = try!(File::open(images_path.join(source)).map_err(|_| Error::FileNotFound(self.html.book.source.clone(),
                                                                                "image or cover".to_owned(),
                                                                                source.to_owned())));
            let mut content = vec!();
            try!(f.read_to_end(&mut content).map_err(|e| Error::Render(format!("error while reading image file {}: {}", source, e))));
            try!(self.write_file(dest, &content));
        }

        // Write additional files
        if let Ok(list) = self.html.book.options.get_paths_list("resources.files") {
            let files_path = self.html.book.options.get_path("resources.base_path.files").unwrap();
            let data_path = Path::new(self.html.book.options.get_relative_path("resources.out_path").unwrap());
            let list = try!(ResourceHandler::get_files(list, &files_path));
            for path in list {
                let abs_path = Path::new(&files_path).join(&path);
                let mut f = try!(File::open(&abs_path)
                                 .map_err(|_| Error::FileNotFound(self.html.book.source.clone(),
                                                                  "additional resource from resources.files".to_owned(),
                                                                  abs_path.to_string_lossy().into_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::Render(format!("error while reading resource file: {}", e))));
                try!(self.write_file(data_path.join(&path).to_str().unwrap(), &content));
            }
        }

        Ok(())
    }

    // Render each chapter and write them, and index.html too
    fn write_html(&mut self) -> Result<()> {
        let mut chapters = vec!();
        let mut titles = vec!();
        for (i, &(n, ref v)) in self.html.book.chapters.iter().enumerate() {
            self.html.chapter_config(i, n, filenamer(i));
            let mut title = String::new();
            for token in v {
                match *token {
                    Token::Header(1, ref vec) => {
                        if self.html.current_hide || self.html.current_numbering == 0 {
                            title = try!(self.html.render_vec(vec));
                        } else {
                            title = try!(self.html.book.get_header(self.html.current_chapter[0] + 1,
                                                              &try!(self.html.render_vec(vec))));
                        }
                        break;
                    },
                    _ => {
                        continue;
                    }
                }
            }
            titles.push(title);

            let chapter = self.html.render_html(v);
            chapters.push(chapter);
        }
        self.html.source = Source::empty();
        let toc = self.html.toc.render();

        for (i, content) in chapters.into_iter().enumerate() {
            let prev_chapter = if i > 0 {
                format!("

    « {}

",
                        filenamer(i-1),
                        titles[i-1])
            } else {
                String::new()
            };

            let next_chapter = if i < titles.len() - 1 {
                format!("

    {} »

",
                        filenamer(i+1),
                        titles[i+1])
            } else {
                String::new()
            };

            // Render each HTML document
            let mut mapbuilder = self.html.book.get_mapbuilder("none")
                .insert_str("content", try!(content))
                .insert_str("chapter_title", format!("{} – {}",
                                             self.html.book.options.get_str("title").unwrap(),
                                             titles[i]))
                .insert_str("toc", toc.clone())
                .insert_str("prev_chapter", prev_chapter)
                .insert_str("next_chapter", next_chapter)
                .insert_str("footer", self.html.get_footer())
                .insert_str("top", self.html.get_top())
                .insert_str("script", self.html.book.get_template("html_dir.script").unwrap())
                .insert_bool(self.html.book.options.get_str("lang").unwrap(), true);

            if self.html.book.options.get_bool("html.highlight_code").unwrap() == true {
                mapbuilder = mapbuilder.insert_bool("highlight_code", true);
            }
            let data = mapbuilder.build();
            let template = mustache::compile_str(try!(self.html.book.get_template("html_dir.chapter.html")).as_ref());
            let mut res = vec!();
            template.render_data(&mut res, &data);
            try!(self.write_file(&filenamer(i), &res));
        }

        let mut content = if let Ok(cover) = self.html.book.options.get_path("cover") {
            // checks first that cover exists
            if fs::metadata(&cover).is_err() {
                return Err(Error::FileNotFound(self.html.book.source.clone(),
                                               "cover".to_owned(),
                                               cover));

            }
            format!("

",
                    self.html.book.options.get_str("title").unwrap(),
                    try!(self.html.handler.map_image(&self.html.book.source,
                                                     Cow::Owned(cover))).as_ref())
        } else {
            String::new()
        };
        if titles.len() > 1 {
            content.push_str(&format!("

    {} »

",
                        filenamer(0),
                        titles[0]));
        }
        // Render index.html and write it too
        let mut mapbuilder = self.html.book.get_mapbuilder("none")
            .insert_str("content", content)
            .insert_str("top", self.html.get_top())
            .insert_str("footer", self.html.get_footer())
            .insert_str("toc", toc.clone())
            .insert_str("script", self.html.book.get_template("html_dir.script").unwrap())
            .insert_bool(self.html.book.options.get_str("lang").unwrap(), true);
        if self.html.book.options.get_bool("html.highlight_code").unwrap() == true {
            mapbuilder = mapbuilder.insert_bool("highlight_code", true);
        }
        let data = mapbuilder.build();
        let template = mustache::compile_str(try!(self.html.book.get_template("html_dir.index.html")).as_ref());
        let mut res = vec!();
        template.render_data(&mut res, &data);
        try!(self.write_file("index.html", &res));

        Ok(())
    }

    // Render the CSS file and write it
    fn write_css(&self) -> Result<()> {
        // Render the CSS
        let template_css = mustache::compile_str(try!(self.html.book.get_template("html_dir.css")).as_ref());
        let data = self.html.book.get_mapbuilder("none")
            .insert_bool(self.html.book.options.get_str("lang").unwrap(), true)
            .build();
        let mut res:Vec<u8> = vec!();
        template_css.render_data(&mut res, &data);
        let css = String::from_utf8_lossy(&res);

        // Write it
        self.write_file("stylesheet.css", css.as_bytes())
    }

    // Write content to a file
    fn write_file(&self, file: &str, content: &[u8]) -> Result<()> {
        let dest_path = PathBuf::from(&self.html.book.options.get_path("output.html_dir").unwrap());
        if dest_path.starts_with("..") {
            panic!("html dir is asked to create a file outside of its directory, no way!");
        }
        let dest_file = dest_path.join(file);
        let dest_dir = dest_file.parent().unwrap();
        if !fs::metadata(dest_dir).is_ok() { // dir does not exist, create it
            try!(fs::DirBuilder::new()
                 .recursive(true)
                 .create(&dest_dir)
                 .map_err(|e| Error::Render(format!("could not create directory in {}:{}", dest_dir.display(), e))));
        }
        let mut f = try!(File::create(&dest_file)
                         .map_err(|e| Error::Render(format!("could not create file {}:{}", dest_file.display(), e))));
        f.write_all(content)
            .map_err(|e| Error::Render(format!("could not write to file {}:{}", dest_file.display(), e)))
    }
}

/// Generate a file name given an int
fn filenamer(i: usize) -> String {
    format!("chapter_{:03}.html", i)
}
<|endoftext|>