<commit_before>//! The `ast` module contains a lot of useful functionality
//! to create and walk through the ast (abstract syntaxtree)

use frontend::lexer::Token;
use backend::zcode::zfile;
use backend::zcode::zfile::{FormattingState, ZOP};
use std::collections::HashMap;

//==============================
// ast

pub struct AST {
    passages: Vec<ASTNode>
}

 /// add zcode based on tokens
fn gen_zcode<'a>(node: &'a ASTNode, mut out: &mut zfile::Zfile, mut manager: &mut CodeGenManager<'a>) -> Vec<ZOP> {
    let mut state_copy = manager.format_state.clone();
    let mut set_formatting = false;

    match node {
        &ASTNode::Passage(ref node) => {
            let mut code: Vec<ZOP> = vec![];
            match &node.category {
                &Token::TokPassageName(ref name) => {
                    code.push(ZOP::Routine{name: name.to_string(), count_variables: 0});
                },
                _ => {
                    debug!("no match 1");
                }
            };

            for child in &node.childs {
                for instr in gen_zcode(child, out, manager) {
                    code.push(instr);
                }

            }

            code.push(ZOP::Newline);
            code.push(ZOP::Call{jump_to_label: "system_check_links".to_string()});
            code
        },
        &ASTNode::Default(ref t) => {
            let mut code: Vec<ZOP> = match &t.category {
                &Token::TokText(ref s) => {
                    vec![ZOP::PrintOps{text: s.to_string()}]
                },
                &Token::TokNewLine => {
                    vec![ZOP::Newline]
                },
                &Token::TokFormatBoldStart => {
                    state_copy.bold = true;
                    set_formatting = true;
                    vec![ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}]
                },
                &Token::TokFormatItalicStart => {
                    state_copy.italic = true;
                    set_formatting = true;
                    vec![ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}]
                },
                &Token::TokPassageLink (ref name, ref link) => {
                    set_formatting = true;
                    vec![
                    ZOP::CallWithAddress{jump_to_label: "system_add_link".to_string(), address: link.to_string()},
                    ZOP::SetTextStyle{bold: state_copy.bold, reverse: true, monospace: state_copy.mono, italic: state_copy.italic},
                    ZOP::Print{text: format!("{}[", name)},
                    ZOP::PrintNumVar{variable: 16},
                    ZOP::Print{text: "]".to_string()},
                    ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}
                    ]
                },
                &Token::TokAssign(ref var, ref operator) => {
                    if operator == "=" || operator == "to" {
                        if !manager.symbol_table.is_known_symbol(var) {
                            manager.symbol_table.insert_new_symbol(&var);
                        }
                        let symbol_id = manager.symbol_table.get_symbol_id(var);
                        if t.childs.len() == 1 {
                            match t.childs[0].as_default().category {
                                Token::TokInt(value) => {
                                    vec![ZOP::StoreU16{variable: symbol_id, value: value as u16}]
                                },
                                Token::TokBoolean(ref bool_val) => {
                                    vec![ZOP::StoreU8{variable: symbol_id, value: boolstr_to_u8(&*bool_val)}]
                                }
                                _ => { vec![] }
                            }
                        } else {
                            debug!("Assign Expression currently not supported.");
                            vec![]
                        }
                    } else { vec![] }
                },
                &Token::TokIf => {
                    if t.childs.len() < 2 {
                        panic!("Unsupported if-expression!");
                    }

                    let mut compare: u8 = 1;

                    // check if the first node is a pseudonode
                    let pseudo_node = match t.childs[0].as_default().category {
                        Token::TokPseudo => t.childs[0].as_default(),
                        _ =>  panic!("Unsupported if-expression!")
                    };

                    // Check if first token is variable
                    let var_name = match pseudo_node.childs[0].as_default().category {
                        Token::TokVariable(ref var) => var,
                        _ =>  panic!("Unsupported if-expression!")
                    };

                    if pseudo_node.childs.len() > 1 {
                        // Check if second token is compare operator
                        match pseudo_node.childs[1].as_default().category {
                            Token::TokCompOp(ref op) => {
                                match &*(*op) {
                                    "==" | "is" => {} ,
                                    _ => panic!("Unsupported Compare Operator!")
                                }
                            }, _ =>  panic!("Unsupported if-expression!")
                        }

                        // Check if third token is number
                        compare = match pseudo_node.childs[2].as_default().category {
                            Token::TokInt(ref value) => {
                                *value as u8
                            },
                            Token::TokBoolean(ref bool_val) => {
                                boolstr_to_u8(&*bool_val)
                            }, _ => panic!("Unsupported assign value!")
                        };
                    }

                    let symbol_id = manager.symbol_table.get_symbol_id(&*var_name);
                    let if_id = manager.ids_if.start_next();

                    let if_label = format!("if_{}", if_id);
                    let after_if_label = format!("after_if_{}", if_id);
                    let after_else_label = format!("after_else_{}", if_id);
                    let mut code: Vec<ZOP> = vec![
                        ZOP::JE{local_var_id: symbol_id, equal_to_const: compare, jump_to_label: if_label.to_string()},
                        ZOP::Jump{jump_to_label: after_if_label.to_string()},
                        ZOP::Label{name: if_label.to_string()}
                    ];

                    for i in 1..t.childs.len() {
                        for instr in gen_zcode(&t.childs[i], out, manager) {
                            code.push(instr);
                        }
                    }

                    code.push(ZOP::Jump{jump_to_label: after_else_label});
                    code.push(ZOP::Label{name: after_if_label});
                    code
                },
                &Token::TokElse => {
                    let mut code: Vec<ZOP> = vec![];
                    for child in &t.childs {
                        for instr in gen_zcode(child, out, manager) {
                            code.push(instr);
                        }
                    }
                    code
                },
                &Token::TokEndIf => {
                    let after_else_label = format!("after_else_{}", manager.ids_if.pop_id());
                    vec![ZOP::Label{name: after_else_label}]
                },
                _ => {
                    debug!("no match 2");
                    vec![]
                }
            };
            if set_formatting {
                for child in &t.childs {
                    for instr in gen_zcode(child, out, manager) {
                        code.push(instr);
                    }
                }
                code.push(ZOP::SetTextStyle{bold: false, reverse: false, monospace: false, italic: false});
                let state = manager.format_state;
                code.push(ZOP::SetTextStyle{bold: state.bold, reverse: state.inverted, monospace: state.mono, italic: state.italic});
            }
            code
        }
    }

}

fn boolstr_to_u8(string: &str) -> u8 {
    match string {
        "true" => 1 as u8,
        _ => 0 as u8
    }
}

impl AST {
    /// convert ast to zcode
    pub fn to_zcode(& self, out: &mut zfile::Zfile) {
        let mut manager = CodeGenManager::new();
        let mut code: Vec<ZOP> = vec![];
        for child in &self.passages {
            for instr in gen_zcode(child, out, &mut manager) {
                code.push(instr);
            }
        }
        debug!("emit zcode:");
        for instr in &code {
            debug!("{:?}", instr);
        }
        out.emit(code);
    }

    pub fn new() -> AST {
        AST {
            passages: Vec::new()
        }
    }

    /// prints the tree
    pub fn print(&self) {
        debug!("Abstract Syntax Tree: ");
        for child in &self.passages {
            child.print(0);
        }
        debug!("");
    }

    /// adds a passage to the path in the ast
    pub fn add_passage(&mut self, token: Token) {
        let node = ASTNode::Passage(NodePassage { category: token, childs: Vec::new() });
        self.passages.push(node);
    }

    /// adds a child to the path in the ast
    pub fn add_child(&mut self, path: &Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].add_child(new_path, token)
        } else {
            self.passages.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() }));
        }
    }

    /// counts the childs of the path in the asts
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].count_childs(new_path)
        } else {
            self.passages.len()
        }
    }
}

// ================================
// node types
enum ASTNode {
    Default (NodeDefault),
    Passage (NodePassage)
}

struct NodePassage {
    category: Token,
    pub childs: Vec<ASTNode>,
    /*tags: Vec<ASTNode>*/
}

struct NodeDefault {
    category: Token,
    childs: Vec<ASTNode>
}

struct CodeGenManager<'a> {
    ids_if: IdentifierProvider,
    symbol_table: SymbolTable<'a>,
    format_state: FormattingState
}

struct IdentifierProvider {
    current_id: u32,
    id_stack: Vec<u32>
}

struct SymbolTable<'a> {
    current_id: u8,
    symbol_map: HashMap<&'a str, u8>
}

impl <'a> CodeGenManager<'a> {
    pub fn new() -> CodeGenManager<'a> {
        CodeGenManager {
            ids_if: IdentifierProvider::new(),
            symbol_table: SymbolTable::new(),
            format_state: FormattingState {bold: false, italic: false, mono: false, inverted: false}
        }
    }
}

impl IdentifierProvider {
    pub fn new() -> IdentifierProvider {
        IdentifierProvider {
            current_id: 0,
            id_stack: Vec::new()
        }
    }

    // Returns a new id and pushes it onto the stack
    pub fn start_next(&mut self) -> u32 {
        let id = self.current_id;
        self.current_id += 1;
        self.id_stack.push(id);
        id
    }

    // Pops the last id from the stack
    pub fn pop_id(&mut self) -> u32 {
        self.id_stack.pop().unwrap()
    }
}

impl <'a> SymbolTable<'a> {
    pub fn new() -> SymbolTable<'a> {
        SymbolTable {
            current_id: 25,
            symbol_map: HashMap::<&str, u8>::new()
        }
    }

    // Inserts a symbol into the table, assigning a new id
    pub fn insert_new_symbol(&mut self, symbol: &'a str) {
        debug!("Assigned id {} to variable {}", self.current_id, symbol);
        self.symbol_map.insert(symbol, self.current_id);
        self.current_id += 1;
    }

    // Checks if the symbol is already existent in the table
    pub fn is_known_symbol(&self, symbol: &str) -> bool {
        self.symbol_map.contains_key(symbol)
    }

    // Returns the id for a given symbol
    // (check if is_known_symbol, otherwise panics)
    pub fn get_symbol_id(&self, symbol: &str) -> u8 {
        *self.symbol_map.get(symbol).unwrap()
    }
}

impl ASTNode {
    /// adds an child to the path in the ast
    pub fn add_child(&mut self, path: Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &mut ASTNode::Default(ref mut node) => node.childs[*index].add_child(new_path, token),
                &mut ASTNode::Passage(ref mut node) => node.childs[*index].add_child(new_path, token),
            }
        } else {
            match self {
                &mut ASTNode::Default(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
                &mut ASTNode::Passage(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
            }
        }
    }

    /// counts the childs of the current path in the ast
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &ASTNode::Default(ref node) => node.childs[*index].count_childs(new_path),
                &ASTNode::Passage(ref node) => node.childs[*index].count_childs(new_path),
            }
        } else {
            match self {
                &ASTNode::Default(ref node) => node.childs.len(),
                &ASTNode::Passage(ref node) => node.childs.len(),
            }
        }
    }

    /// prints an node of an ast
    pub fn print(&self, indent: usize) {
        let mut spaces = "".to_string();
        for _ in 0..indent {
            spaces.push_str(" ");
        }

        match self {
            &ASTNode::Passage(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            },
            &ASTNode::Default(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            }
        }
    }

    pub fn as_default(&self) -> &NodeDefault {
        match self {
            &ASTNode::Default(ref def) => def,
            _ => panic!("Node cannot be unwrapped as NodeDefault!")
        }
    }
}
<commit_msg>first print for ints. still testing state<commit_after>//! The `ast` module contains a lot of useful functionality
//! to create and walk through the ast (abstract syntaxtree)

use frontend::lexer::Token;
use backend::zcode::zfile;
use backend::zcode::zfile::{FormattingState, ZOP};
use std::collections::HashMap;

//==============================
// ast

pub struct AST {
    passages: Vec<ASTNode>
}

 /// add zcode based on tokens
fn gen_zcode<'a>(node: &'a ASTNode, mut out: &mut zfile::Zfile, mut manager: &mut CodeGenManager<'a>) -> Vec<ZOP> {
    let mut state_copy = manager.format_state.clone();
    let mut set_formatting = false;

    match node {
        &ASTNode::Passage(ref node) => {
            let mut code: Vec<ZOP> = vec![];
            match &node.category {
                &Token::TokPassageName(ref name) => {
                    code.push(ZOP::Routine{name: name.to_string(), count_variables: 0});
                },
                _ => {
                    debug!("no match 1");
                }
            };

            for child in &node.childs {
                for instr in gen_zcode(child, out, manager) {
                    code.push(instr);
                }

            }

            code.push(ZOP::Newline);
            code.push(ZOP::Call{jump_to_label: "system_check_links".to_string()});
            code
        },
        &ASTNode::Default(ref t) => {
            let mut code: Vec<ZOP> = match &t.category {
                &Token::TokText(ref s) => {
                    vec![ZOP::PrintOps{text: s.to_string()}]
                },
                &Token::TokNewLine => {
                    vec![ZOP::Newline]
                },
                &Token::TokFormatBoldStart => {
                    state_copy.bold = true;
                    set_formatting = true;
                    vec![ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}]
                },
                &Token::TokFormatItalicStart => {
                    state_copy.italic = true;
                    set_formatting = true;
                    vec![ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}]
                },
                &Token::TokPassageLink (ref name, ref link) => {
                    set_formatting = true;
                    vec![
                    ZOP::CallWithAddress{jump_to_label: "system_add_link".to_string(), address: link.to_string()},
                    ZOP::SetTextStyle{bold: state_copy.bold, reverse: true, monospace: state_copy.mono, italic: state_copy.italic},
                    ZOP::Print{text: format!("{}[", name)},
                    ZOP::PrintNumVar{variable: 16},
                    ZOP::Print{text: "]".to_string()},
                    ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}
                    ]
                },
                &Token::TokAssign(ref var, ref operator) => {
                    if operator == "=" || operator == "to" {
                        if !manager.symbol_table.is_known_symbol(var) {
                            manager.symbol_table.insert_new_symbol(&var);
                        }
                        let symbol_id = manager.symbol_table.get_symbol_id(var);
                        if t.childs.len() == 1 {
                            match t.childs[0].as_default().category {
                                Token::TokInt(value) => {
                                    vec![ZOP::StoreU16{variable: symbol_id, value: value as u16}]
                                },
                                Token::TokBoolean(ref bool_val) => {
                                    vec![ZOP::StoreU8{variable: symbol_id, value: boolstr_to_u8(&*bool_val)}]
                                }
                                _ => { vec![] }
                            }
                        } else {
                            debug!("Assign Expression currently not supported.");
                            vec![]
                        }
                    } else { vec![] }
                },
                &Token::TokIf => {
                    if t.childs.len() < 2 {
                        panic!("Unsupported if-expression!");
                    }

                    let mut compare: u8 = 1;

                    // check if the first node is a pseudonode
                    let pseudo_node = match t.childs[0].as_default().category {
                        Token::TokPseudo => t.childs[0].as_default(),
                        _ =>  panic!("Unsupported if-expression!")
                    };

                    // Check if first token is variable
                    let var_name = match pseudo_node.childs[0].as_default().category {
                        Token::TokVariable(ref var) => var,
                        _ =>  panic!("Unsupported if-expression!")
                    };

                    if pseudo_node.childs.len() > 1 {
                        // Check if second token is compare operator
                        match pseudo_node.childs[1].as_default().category {
                            Token::TokCompOp(ref op) => {
                                match &*(*op) {
                                    "==" | "is" => {} ,
                                    _ => panic!("Unsupported Compare Operator!")
                                }
                            }, _ =>  panic!("Unsupported if-expression!")
                        }

                        // Check if third token is number
                        compare = match pseudo_node.childs[2].as_default().category {
                            Token::TokInt(ref value) => {
                                *value as u8
                            },
                            Token::TokBoolean(ref bool_val) => {
                                boolstr_to_u8(&*bool_val)
                            }, _ => panic!("Unsupported assign value!")
                        };
                    }

                    let symbol_id = manager.symbol_table.get_symbol_id(&*var_name);
                    let if_id = manager.ids_if.start_next();

                    let if_label = format!("if_{}", if_id);
                    let after_if_label = format!("after_if_{}", if_id);
                    let after_else_label = format!("after_else_{}", if_id);
                    let mut code: Vec<ZOP> = vec![
                        ZOP::JE{local_var_id: symbol_id, equal_to_const: compare, jump_to_label: if_label.to_string()},
                        ZOP::Jump{jump_to_label: after_if_label.to_string()},
                        ZOP::Label{name: if_label.to_string()}
                    ];

                    for i in 1..t.childs.len() {
                        for instr in gen_zcode(&t.childs[i], out, manager) {
                            code.push(instr);
                        }
                    }

                    code.push(ZOP::Jump{jump_to_label: after_else_label});
                    code.push(ZOP::Label{name: after_if_label});
                    code
                },
                &Token::TokElse => {
                    let mut code: Vec<ZOP> = vec![];
                    for child in &t.childs {
                        for instr in gen_zcode(child, out, manager) {
                            code.push(instr);
                        }
                    }
                    code
                },
                &Token::TokEndIf => {
                    let after_else_label = format!("after_else_{}", manager.ids_if.pop_id());
                    vec![ZOP::Label{name: after_else_label}]
                },
                &Token::TokMakroVar(ref name) => {
                    let var_id = manager.symbol_table.get_symbol_id(&*name);
                    println!("{:?}", var_id);
                    vec![ZOP::PrintNumVar{variable: var_id}]
                },
                _ => {
                    debug!("no match if");
                    vec![]
                }
            };
            if set_formatting {
                for child in &t.childs {
                    for instr in gen_zcode(child, out, manager) {
                        code.push(instr);
                    }
                }
                code.push(ZOP::SetTextStyle{bold: false, reverse: false, monospace: false, italic: false});
                let state = manager.format_state;
                code.push(ZOP::SetTextStyle{bold: state.bold, reverse: state.inverted, monospace: state.mono, italic: state.italic});
            }
            code
        }
    }

}

fn boolstr_to_u8(string: &str) -> u8 {
    match string {
        "true" => 1 as u8,
        _ => 0 as u8
    }
}

impl AST {
    /// convert ast to zcode
    pub fn to_zcode(& self, out: &mut zfile::Zfile) {
        let mut manager = CodeGenManager::new();
        let mut code: Vec<ZOP> = vec![];
        for child in &self.passages {
            for instr in gen_zcode(child, out, &mut manager) {
                code.push(instr);
            }
        }
        debug!("emit zcode:");
        for instr in &code {
            debug!("{:?}", instr);
        }
        out.emit(code);
    }

    pub fn new() -> AST {
        AST {
            passages: Vec::new()
        }
    }

    /// prints the tree
    pub fn print(&self) {
        debug!("Abstract Syntax Tree: ");
        for child in &self.passages {
            child.print(0);
        }
        debug!("");
    }

    /// adds a passage to the path in the ast
    pub fn add_passage(&mut self, token: Token) {
        let node = ASTNode::Passage(NodePassage { category: token, childs: Vec::new() });
        self.passages.push(node);
    }

    /// adds a child to the path in the ast
    pub fn add_child(&mut self, path: &Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].add_child(new_path, token)
        } else {
            self.passages.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() }));
        }
    }

    /// counts the childs of the path in the asts
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].count_childs(new_path)
        } else {
            self.passages.len()
        }
    }
}

// ================================
// node types
enum ASTNode {
    Default (NodeDefault),
    Passage (NodePassage)
}

struct NodePassage {
    category: Token,
    pub childs: Vec<ASTNode>,
    /*tags: Vec<ASTNode>*/
}

struct NodeDefault {
    category: Token,
    childs: Vec<ASTNode>
}

struct CodeGenManager<'a> {
    ids_if: IdentifierProvider,
    symbol_table: SymbolTable<'a>,
    format_state: FormattingState
}

struct IdentifierProvider {
    current_id: u32,
    id_stack: Vec<u32>
}

struct SymbolTable<'a> {
    current_id: u8,
    symbol_map: HashMap<&'a str, u8>
}

impl <'a> CodeGenManager<'a> {
    pub fn new() -> CodeGenManager<'a> {
        CodeGenManager {
            ids_if: IdentifierProvider::new(),
            symbol_table: SymbolTable::new(),
            format_state: FormattingState {bold: false, italic: false, mono: false, inverted: false}
        }
    }
}

impl IdentifierProvider {
    pub fn new() -> IdentifierProvider {
        IdentifierProvider {
            current_id: 0,
            id_stack: Vec::new()
        }
    }

    // Returns a new id and pushes it onto the stack
    pub fn start_next(&mut self) -> u32 {
        let id = self.current_id;
        self.current_id += 1;
        self.id_stack.push(id);
        id
    }

    // Pops the last id from the stack
    pub fn pop_id(&mut self) -> u32 {
        self.id_stack.pop().unwrap()
    }
}

impl <'a> SymbolTable<'a> {
    pub fn new() -> SymbolTable<'a> {
        SymbolTable {
            current_id: 25,
            symbol_map: HashMap::<&str, u8>::new()
        }
    }

    // Inserts a symbol into the table, assigning a new id
    pub fn insert_new_symbol(&mut self, symbol: &'a str) {
        debug!("Assigned id {} to variable {}", self.current_id, symbol);
        self.symbol_map.insert(symbol, self.current_id);
        self.current_id += 1;
    }

    // Checks if the symbol is already existent in the table
    pub fn is_known_symbol(&self, symbol: &str) -> bool {
        self.symbol_map.contains_key(symbol)
    }

    // Returns the id for a given symbol
    // (check if is_known_symbol, otherwise panics)
    pub fn get_symbol_id(&self, symbol: &str) -> u8 {
        *self.symbol_map.get(symbol).unwrap()
    }
}

impl ASTNode {
    /// adds an child to the path in the ast
    pub fn add_child(&mut self, path: Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &mut ASTNode::Default(ref mut node) => node.childs[*index].add_child(new_path, token),
                &mut ASTNode::Passage(ref mut node) => node.childs[*index].add_child(new_path, token),
            }
        } else {
            match self {
                &mut ASTNode::Default(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
                &mut ASTNode::Passage(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
            }
        }
    }

    /// counts the childs of the current path in the ast
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &ASTNode::Default(ref node) => node.childs[*index].count_childs(new_path),
                &ASTNode::Passage(ref node) => node.childs[*index].count_childs(new_path),
            }
        } else {
            match self {
                &ASTNode::Default(ref node) => node.childs.len(),
                &ASTNode::Passage(ref node) => node.childs.len(),
            }
        }
    }

    /// prints an node of an ast
    pub fn print(&self, indent: usize) {
        let mut spaces = "".to_string();
        for _ in 0..indent {
            spaces.push_str(" ");
        }

        match self {
            &ASTNode::Passage(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            },
            &ASTNode::Default(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            }
        }
    }

    pub fn as_default(&self) -> &NodeDefault {
        match self {
            &ASTNode::Default(ref def) => def,
            _ => panic!("Node cannot be unwrapped as NodeDefault!")
        }
    }
}
<|endoftext|>


<commit_before>use read_write::*;
use service::*;
use libc::*;
use std::mem::transmute;

pub static BACKEND_API_VERSION: &'static [u8] = b"ProDBG Backend 1\0";

pub trait Backend {
    fn new(service: &Service) -> Self;
    fn update(&mut self, action: i32, reader: &mut Reader, writer: &mut Writer);
}

#[repr(C)]
pub struct CBackendCallbacks {
    pub name: *const c_uchar,
    pub create_instance: Option<fn(service_func: extern "C" fn(service: *const c_uchar)
                                                               -> *mut c_void)
                                   -> *mut c_void>,
    pub destroy_instance: Option<fn(*mut c_void)>,
    pub register_menu: Option<fn() -> *mut c_void>,
    pub update: Option<fn(ptr: *mut c_void,
                          a: c_int,
                          ra: *mut c_void,
                          wa: *mut c_void)>,
}

unsafe impl Sync for CBackendCallbacks {}

pub fn create_backend_instance<T: Backend>(service_func: extern "C" fn(service: *const c_uchar)
                                                                       -> *mut c_void)
                                           -> *mut c_void {
    let service = Service { service_func: service_func };
    let instance = unsafe { transmute(Box::new(T::new(&service))) };
    println!("Lets create instance!");
    instance
}

pub fn destroy_backend_instance<T: Backend>(ptr: *mut c_void) {
    println!("rust: backend: destroy");
    let _: Box<T> = unsafe { transmute(ptr) };
    // implicitly dropped
}

pub fn update_backend_instance<T: Backend>(ptr: *mut c_void,
                                           action: *mut c_int,
                                           reader_api: *mut c_void,
                                           writer_api: *mut c_void) {
    let backend: &mut T = unsafe { &mut *(ptr as *mut T) };
    let c_reader: &mut CPDReaderAPI = unsafe { &mut *(reader_api as *mut CPDReaderAPI) };
    let c_writer: &mut CPDWriterAPI = unsafe { &mut *(writer_api as *mut CPDWriterAPI) };
    let mut reader = Reader {
        api: c_reader,
        it: 0,
    };

    let mut writer = Writer { api: c_writer };

    backend.update(action as i32, &mut reader, &mut writer);
}

#[macro_export]
macro_rules! define_backend_plugin {
    ($p_name:ident, $name:expr, $x:ty) => {
        static $p_name: CBackendCallbacks = CBackendCallbacks {
            name: $name as *const u8,
            create_instance: Some(prodbg::backend::create_backend_instance::<$x>),
            destroy_instance: Some(prodbg::backend::destroy_backend_instance::<$x>),
            register_menu: None,
            update: Some(prodbg::backend::update_backend_instance::<$x>)
        };
    }
}
<commit_msg>Backend fixes for the Rust wrapper<commit_after>use read_write::*;
use service::*;
use libc::*;
use std::mem::transmute;

pub static BACKEND_API_VERSION: &'static [u8] = b"ProDBG Backend 1\0";

pub trait Backend {
    fn new(service: &Service) -> Self;
    fn update(&mut self, action: i32, reader: &mut Reader, writer: &mut Writer);
}

#[repr(C)]
pub struct CBackendCallbacks {
    pub name: *const c_uchar,
    pub create_instance: Option<fn(service_func: extern "C" fn(service: *const c_uchar)
                                                               -> *mut c_void)
                                   -> *mut c_void>,
    pub destroy_instance: Option<fn(*mut c_void)>,
    pub register_menu: Option<fn() -> *mut c_void>,
    pub update: Option<fn(ptr: *mut c_void,
                          a: c_int,
                          ra: *mut c_void,
                          wa: *mut c_void)>,
}

unsafe impl Sync for CBackendCallbacks {}

pub fn create_backend_instance<T: Backend>(service_func: extern "C" fn(service: *const c_uchar)
                                                                       -> *mut c_void)
                                           -> *mut c_void {
    let service = Service { service_func: service_func };
    let instance = unsafe { transmute(Box::new(T::new(&service))) };
    println!("Lets create instance!");
    instance
}

pub fn destroy_backend_instance<T: Backend>(ptr: *mut c_void) {
    println!("rust: backend: destroy");
    let _: Box<T> = unsafe { transmute(ptr) };
    // implicitly dropped
}

pub fn update_backend_instance<T: Backend>(ptr: *mut c_void,
                                           action: c_int,
                                           reader_api: *mut c_void,
                                           writer_api: *mut c_void) {
    let backend: &mut T = unsafe { &mut *(ptr as *mut T) };
    let c_reader: &mut CPDReaderAPI = unsafe { &mut *(reader_api as *mut CPDReaderAPI) };
    let c_writer: &mut CPDWriterAPI = unsafe { &mut *(writer_api as *mut CPDWriterAPI) };
    let mut reader = Reader {
        api: c_reader,
        it: 0,
    };

    let mut writer = Writer { api: c_writer };

    backend.update(action as i32, &mut reader, &mut writer);
}

#[macro_export]
macro_rules! define_backend_plugin {
    ($p_name:ident, $name:expr, $x:ty) => {
        static $p_name: CBackendCallbacks = CBackendCallbacks {
            name: $name as *const u8,
            create_instance: Some(prodbg_api::backend::create_backend_instance::<$x>),
            destroy_instance: Some(prodbg_api::backend::destroy_backend_instance::<$x>),
            register_menu: None,
            update: Some(prodbg_api::backend::update_backend_instance::<$x>)
        };
    }
}
<|endoftext|>


<commit_before>//pub const FINGERPRINT_SIZE: usize = 1;
pub const BUCKET_SIZE: usize = 4;

// Fingerprint Size is 1 byte so lets remove the Vec
#[derive(PartialEq, Copy, Clone)]
pub struct Fingerprint (pub u8);

pub struct Bucket (pub Vec<Fingerprint>);

impl Bucket {

    pub fn new() -> Bucket {
        return Bucket(vec![]);
    }

    pub fn insert(&mut self, fp: Fingerprint) -> bool {
        if self.0.len() < BUCKET_SIZE {
            self.0.push(fp);
            return true;
        }
        return false;
    }

    pub fn delete(&mut self, fp: Fingerprint) -> bool {
        for i in 0..self.0.len() {
            if self.0[i] == fp {
                self.0.remove(i);
                return true;
            }
        }
        return false;
    }

    pub fn get_fingerprint_index(&mut self, fp: Fingerprint) -> usize {
        for i in 0..self.0.len() {
            if self.0[i] == fp {
                return i;
            }
        }
        return BUCKET_SIZE + 1;
    }
}<commit_msg>cleanup: Remove many `return` keywords<commit_after>//pub const FINGERPRINT_SIZE: usize = 1;
pub const BUCKET_SIZE: usize = 4;

// Fingerprint Size is 1 byte so lets remove the Vec
#[derive(PartialEq, Copy, Clone)]
pub struct Fingerprint (pub u8);

pub struct Bucket (pub Vec<Fingerprint>);

impl Bucket {

    pub fn new() -> Bucket {
        Bucket(vec![])
    }

    pub fn insert(&mut self, fp: Fingerprint) -> bool {
        if self.0.len() < BUCKET_SIZE {
            self.0.push(fp);
            true
        } else {
            false
        }
    }

    pub fn delete(&mut self, fp: Fingerprint) -> bool {
        for i in 0..self.0.len() {
            if self.0[i] == fp {
                self.0.remove(i);
                return true;
            }
        }
        false
    }

    pub fn get_fingerprint_index(&mut self, fp: Fingerprint) -> usize {
        for i in 0..self.0.len() {
            if self.0[i] == fp {
                return i;
            }
        }
        BUCKET_SIZE + 1
    }
}
<|endoftext|>


<commit_before>use std::io::{self, BufRead, BufStream, Write};
use std::net::{TcpStream, ToSocketAddrs};

use message::{Message, OwnedMessage, ParseError};

pub struct Client {
    stream: TcpStream,
    buf: BufStream<TcpStream>,
}

impl Client {

    pub fn new<A: ToSocketAddrs>(addr: A) -> io::Result<Client> {
        let stream = try!(TcpStream::connect(addr));
        let buf = BufStream::new(try!(stream.try_clone()));
        Ok(Client{
            stream: stream,
            buf: buf,
        })
    }

    pub fn send_raw<A: AsRef<str>>(&mut self, line: A) -> io::Result<()> {
        try!(write!(self.buf, "{}\r\n", line.as_ref()));
        self.buf.flush()
    }

    pub fn user(&mut self, username: &str, realname: &str) -> io::Result<()> {
        self.send_raw(format!("USER {} 8 * :{}", username, realname))
    }

    pub fn nick(&mut self, nickname: &str) -> io::Result<()> {
        self.send_raw(format!("NICK {}", nickname))
    }

    pub fn join(&mut self, channel: &str) -> io::Result<()> {
        self.send_raw(format!("JOIN {}", channel))
    }

    pub fn read(&mut self) -> Result<OwnedMessage, ReadError> {
        let mut line = String::new();
        try!(self.buf.read_line(&mut line));
        let msg = try!(Message::parse(&line[..]));
        Ok(msg.to_owned())
    }

    pub fn try_clone(&self) -> io::Result<Client> {
        let stream = try!(self.stream.try_clone());
        let buf = BufStream::new(try!(self.stream.try_clone()));
        Ok(Client{
            stream: stream,
            buf: buf,
        })
    }

}

#[derive(Debug)]
pub enum ReadError {
    IoError(io::Error),
    ParseError(ParseError),
}

impl From<io::Error> for ReadError {
    fn from(err: io::Error) -> ReadError {
        ReadError::IoError(err)
    }
}

impl From<ParseError> for ReadError {
    fn from(err: ParseError) -> ReadError {
        ReadError::ParseError(err)
    }
}
<commit_msg>Add PRIVMSG method<commit_after>use std::io::{self, BufRead, BufStream, Write};
use std::net::{TcpStream, ToSocketAddrs};

use message::{Message, OwnedMessage, ParseError};

pub struct Client {
    stream: TcpStream,
    buf: BufStream<TcpStream>,
}

impl Client {

    pub fn new<A: ToSocketAddrs>(addr: A) -> io::Result<Client> {
        let stream = try!(TcpStream::connect(addr));
        let buf = BufStream::new(try!(stream.try_clone()));
        Ok(Client{
            stream: stream,
            buf: buf,
        })
    }

    pub fn send_raw<A: AsRef<str>>(&mut self, line: A) -> io::Result<()> {
        try!(write!(self.buf, "{}\r\n", line.as_ref()));
        self.buf.flush()
    }

    pub fn user(&mut self, username: &str, realname: &str) -> io::Result<()> {
        self.send_raw(format!("USER {} 8 * :{}", username, realname))
    }

    pub fn nick(&mut self, nickname: &str) -> io::Result<()> {
        self.send_raw(format!("NICK {}", nickname))
    }

    pub fn join(&mut self, channel: &str) -> io::Result<()> {
        self.send_raw(format!("JOIN {}", channel))
    }

    pub fn priv_msg(&mut self, channel: &str, msg: &str) -> io::Result<()> {
        self.send_raw(format!("PRIVMSG {} :{}", channel, msg))
    }

    pub fn read(&mut self) -> Result<OwnedMessage, ReadError> {
        let mut line = String::new();
        try!(self.buf.read_line(&mut line));
        let msg = try!(Message::parse(&line[..]));
        Ok(msg.to_owned())
    }

    pub fn try_clone(&self) -> io::Result<Client> {
        let stream = try!(self.stream.try_clone());
        let buf = BufStream::new(try!(self.stream.try_clone()));
        Ok(Client{
            stream: stream,
            buf: buf,
        })
    }

}

#[derive(Debug)]
pub enum ReadError {
    IoError(io::Error),
    ParseError(ParseError),
}

impl From<io::Error> for ReadError {
    fn from(err: io::Error) -> ReadError {
        ReadError::IoError(err)
    }
}

impl From<ParseError> for ReadError {
    fn from(err: ParseError) -> ReadError {
        ReadError::ParseError(err)
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::collections::HashMap;
use std::io::Write;
use std::path::{PathBuf, Path};
use liquid;
use rss;
use jsonfeed::Feed;
use jsonfeed;

use cobalt_model::{Config, SortOrder};
use cobalt_model::files;
use cobalt_model::Collection;
use cobalt_model;
use document::Document;
use error::*;

/// The primary build function that transforms a directory into a site
pub fn build(config: &Config) -> Result<()> {
    trace!("Build configuration: {:?}", config);

    let source = config.source.as_path();
    let dest = config.destination.as_path();

    let parser = &config.liquid;
    let layouts = &config.layouts_dir;
    let mut layouts_cache = HashMap::new();
    let posts_path = source.join(&config.posts.dir);

    debug!("Layouts directory: {:?}", layouts);
    debug!("Posts directory: {:?}", posts_path);

    let post_files = find_post_files(source, &config.posts)?;
    let mut posts = parse_pages(&post_files, &config.posts, source)?;
    process_included_drafts(&config.posts, source, &mut posts)?;

    let page_files = find_page_files(source, config)?;
    let documents = parse_pages(&page_files, &config.pages, source)?;

    sort_pages(&mut posts, &config.posts)?;
    generate_posts(&mut posts,
                   config,
                   &parser,
                   dest,
                   &layouts,
                   &mut layouts_cache)?;

    // check if we should create an RSS file and create it!
    if let Some(ref path) = config.posts.rss {
        create_rss(path, dest, &config.posts, &posts)?;
    }
    // check if we should create an jsonfeed file and create it!
    if let Some(ref path) = config.posts.jsonfeed {
        create_jsonfeed(path, dest, &config.posts, &posts)?;
    }

    generate_pages(posts,
                   documents,
                   config,
                   &parser,
                   dest,
                   &layouts,
                   &mut layouts_cache)?;

    // copy all remaining files in the source to the destination
    // compile SASS along the way
    {
        debug!("Copying remaining assets");
        config.assets.populate(dest)?;
    }

    Ok(())
}

fn generate_doc(dest: &Path,
                layouts: &Path,
                mut layouts_cache: &mut HashMap<String, String>,
                parser: &cobalt_model::Liquid,
                posts_data: &[liquid::Value],
                doc: &mut Document,
                config: &Config)
                -> Result<()> {
    for dump in config.dump.iter().filter(|d| d.is_doc()) {
        trace!("Dumping {:?}", dump);
        let (content, ext) = doc.render_dump(*dump)?;
        let mut file_path = doc.file_path.clone();
        let file_name = file_path
            .file_stem()
            .and_then(|p| p.to_str())
            .expect("page must have file name")
            .to_owned();
        let file_name = format!("_{}.{}.{}", file_name, dump, ext);
        file_path.set_file_name(file_name);
        trace!("Generating {:?}", file_path);
        files::write_document_file(content, dest.join(file_path))?;
    }

    // Everything done with `globals` is terrible for performance.  liquid#95 allows us to
    // improve this.
    let mut posts_variable = config.posts.attributes.clone();
    posts_variable.insert("pages".to_owned(),
                          liquid::Value::Array(posts_data.to_vec()));
    let global_collection: liquid::Object = vec![(config.posts.slug.clone(),
                                                  liquid::Value::Object(posts_variable))]
        .into_iter()
        .collect();
    let mut globals: liquid::Object =
        vec![("site".to_owned(), liquid::Value::Object(config.site.clone())),
             ("collections".to_owned(), liquid::Value::Object(global_collection))]
            .into_iter()
            .collect();
    globals.insert("page".to_owned(),
                   liquid::Value::Object(doc.attributes.clone()));

    doc.render_excerpt(&globals, parser, &config.markdown)
        .chain_err(|| format!("Failed to render excerpt for {:?}", doc.file_path))?;
    doc.render_content(&globals, parser, &config.markdown)
        .chain_err(|| format!("Failed to render content for {:?}", doc.file_path))?;

    // Refresh `page` with the `excerpt` / `content` attribute
    globals.insert("page".to_owned(),
                   liquid::Value::Object(doc.attributes.clone()));
    let doc_html = doc.render(&globals, parser, layouts, &mut layouts_cache)
        .chain_err(|| format!("Failed to render for {:?}", doc.file_path))?;
    files::write_document_file(doc_html, dest.join(&doc.file_path))?;
    Ok(())
}

fn generate_pages(posts: Vec<Document>,
                  documents: Vec<Document>,
                  config: &Config,
                  parser: &cobalt_model::Liquid,
                  dest: &Path,
                  layouts: &Path,
                  mut layouts_cache: &mut HashMap<String, String>)
                  -> Result<()> {
    // during post rendering additional attributes such as content were
    // added to posts. collect them so that non-post documents can access them
    let posts_data: Vec<liquid::Value> = posts
        .into_iter()
        .map(|x| liquid::Value::Object(x.attributes))
        .collect();

    trace!("Generating other documents");
    for mut doc in documents {
        trace!("Generating {}", doc.url_path);
        generate_doc(dest,
                     layouts,
                     &mut layouts_cache,
                     parser,
                     &posts_data,
                     &mut doc,
                     config)?;
    }

    Ok(())
}

fn generate_posts(posts: &mut Vec<Document>,
                  config: &Config,
                  parser: &cobalt_model::Liquid,
                  dest: &Path,
                  layouts: &Path,
                  mut layouts_cache: &mut HashMap<String, String>)
                  -> Result<()> {
    // collect all posts attributes to pass them to other posts for rendering
    let simple_posts_data: Vec<liquid::Value> = posts
        .iter()
        .map(|x| liquid::Value::Object(x.attributes.clone()))
        .collect();

    trace!("Generating posts");
    for (i, post) in &mut posts.iter_mut().enumerate() {
        trace!("Generating {}", post.url_path);

        // posts are in reverse date order, so previous post is the next in the list (+1)
        let previous = simple_posts_data
            .get(i + 1)
            .cloned()
            .unwrap_or(liquid::Value::Nil);
        post.attributes.insert("previous".to_owned(), previous);

        let next = if i >= 1 {
            simple_posts_data.get(i - 1)
        } else {
            None
        }.cloned()
            .unwrap_or(liquid::Value::Nil);
        post.attributes.insert("next".to_owned(), next);

        generate_doc(dest,
                     layouts,
                     &mut layouts_cache,
                     parser,
                     &simple_posts_data,
                     post,
                     config)?;
    }

    Ok(())
}

fn sort_pages(posts: &mut Vec<Document>, collection: &Collection) -> Result<()> {
    // January 1, 1970 0:00:00 UTC, the beginning of time
    let default_date = cobalt_model::DateTime::default();

    // sort documents by date, if there's no date (none was provided or it couldn't be read) then
    // fall back to the default date
    posts.sort_by(|a, b| {
                      b.front
                          .published_date
                          .unwrap_or(default_date)
                          .cmp(&a.front.published_date.unwrap_or(default_date))
                  });

    match collection.order {
        SortOrder::Asc => posts.reverse(),
        SortOrder::Desc => (),
        SortOrder::None => (),
    }

    Ok(())
}

fn parse_drafts(drafts_root: &PathBuf,
                draft_files: &files::Files,
                documents: &mut Vec<Document>,
                collection: &Collection)
                -> Result<()> {
    for file_path in draft_files.files() {
        // Provide a fake path as if it was not a draft
        let rel_src = file_path
            .strip_prefix(&drafts_root)
            .expect("file was found under the root");
        let new_path = Path::new(&collection.dir).join(rel_src);

        let default_front = collection.default.clone().set_draft(true);

        let doc = Document::parse(&file_path, &new_path, default_front)
            .chain_err(|| format!("Failed to parse {:?}", rel_src))?;
        documents.push(doc);
    }
    Ok(())
}

fn process_included_drafts(collection: &Collection,
                           source: &Path,
                           documents: &mut Vec<Document>)
                           -> Result<()> {
    if collection.include_drafts {
        if let Some(ref drafts_dir) = collection.drafts_dir {
            debug!("Draft directory: {:?}", drafts_dir);
            let drafts_root = source.join(&drafts_dir);
            let draft_files = find_post_draft_files(&drafts_root, collection)?;

            parse_drafts(&drafts_root, &draft_files, documents, collection)?;
        }
    }
    Ok(())
}

fn find_post_files(source: &Path, collection: &Collection) -> Result<files::Files> {
    let mut page_files = files::FilesBuilder::new(source)?;
    page_files
        .add_ignore(&format!("!/{}", collection.dir))?
        .add_ignore(&format!("!/{}/**", collection.dir))?
        .add_ignore(&format!("/{}/**/_*", collection.dir))?
        .add_ignore(&format!("/{}/**/_*/**", collection.dir))?;
    for line in &collection.ignore {
        page_files.add_ignore(line.as_str())?;
    }
    for ext in collection.template_extensions.iter() {
        page_files.add_extension(ext)?;
    }
    page_files.limit(PathBuf::from(&collection.dir))?;
    page_files.build()
}

fn find_post_draft_files(drafts_root: &Path, collection: &Collection) -> Result<files::Files> {
    let mut page_files = files::FilesBuilder::new(drafts_root)?;
    for line in &collection.ignore {
        page_files.add_ignore(line.as_str())?;
    }
    for ext in collection.template_extensions.iter() {
        page_files.add_extension(ext)?;
    }
    page_files.build()
}

fn find_page_files(source: &Path, config: &Config) -> Result<files::Files> {
    let mut page_files = files::FilesBuilder::new(source)?;
    for line in &config.pages.ignore {
        page_files.add_ignore(line.as_str())?;
    }
    for ext in config.pages.template_extensions.iter() {
        page_files.add_extension(ext)?;
    }
    page_files.add_ignore(&format!("/{}", config.posts.dir))?;
    if let Some(ref drafts_dir) = config.posts.drafts_dir {
        page_files.add_ignore(&format!("/{}", drafts_dir))?;
    }
    page_files.build()
}

fn parse_pages(page_files: &files::Files,
               collection: &Collection,
               source: &Path)
               -> Result<Vec<Document>> {
    let mut documents = vec![];
    for file_path in page_files.files() {
        let rel_src = file_path
            .strip_prefix(source)
            .expect("file was found under the root");

        let default_front = collection.default.clone();

        let doc = Document::parse(&file_path, rel_src, default_front)
            .chain_err(|| format!("Failed to parse {:?}", rel_src))?;
        if !doc.front.is_draft || collection.include_drafts {
            documents.push(doc);
        }
    }
    Ok(documents)
}

// creates a new RSS file with the contents of the site blog
fn create_rss(path: &str,
              dest: &Path,
              collection: &Collection,
              documents: &[Document])
              -> Result<()> {
    let rss_path = dest.join(path);
    debug!("Creating RSS file at {}", rss_path.display());

    let title = &collection.title;
    let description = collection
        .description
        .as_ref()
        .map(|s| s.as_str())
        .unwrap_or("");
    let link = collection
        .base_url
        .as_ref()
        .ok_or(ErrorKind::ConfigFileMissingFields)?;

    let items: Result<Vec<rss::Item>> = documents.iter().map(|doc| doc.to_rss(link)).collect();
    let items = items?;

    let channel = rss::ChannelBuilder::default()
        .title(title.to_owned())
        .link(link.to_owned())
        .description(description.to_owned())
        .items(items)
        .build()?;

    let rss_string = channel.to_string();
    trace!("RSS data: {}", rss_string);

    // create target directories if any exist
    if let Some(parent) = rss_path.parent() {
        fs::create_dir_all(parent)
            .map_err(|e| format!("Could not create {:?}: {}", parent, e))?;
    }

    let mut rss_file = fs::File::create(&rss_path)?;
    rss_file
        .write_all(br#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
    rss_file.write_all(&rss_string.into_bytes())?;
    rss_file.write_all(b"\n")?;

    Ok(())
}

// creates a new jsonfeed file with the contents of the site blog
fn create_jsonfeed(path: &str,
                   dest: &Path,
                   collection: &Collection,
                   documents: &[Document])
                   -> Result<()> {
    let jsonfeed_path = dest.join(path);
    debug!("Creating jsonfeed file at {}", jsonfeed_path.display());

    let title = &collection.title;
    let description = collection
        .description
        .as_ref()
        .map(|s| s.as_str())
        .unwrap_or("");
    let link = collection
        .base_url
        .as_ref()
        .ok_or(ErrorKind::ConfigFileMissingFields)?;

    let jsonitems = documents.iter().map(|doc| doc.to_jsonfeed(link)).collect();

    let feed = Feed {
        title: title.to_string(),
        items: jsonitems,
        home_page_url: Some(link.to_string()),
        description: Some(description.to_string()),
        ..Default::default()
    };

    let jsonfeed_string = jsonfeed::to_string(&feed).unwrap();
    files::write_document_file(jsonfeed_string, jsonfeed_path)?;

    Ok(())
}
<commit_msg>refactor: Remove dead code<commit_after>use std::fs;
use std::collections::HashMap;
use std::io::Write;
use std::path::{PathBuf, Path};
use liquid;
use rss;
use jsonfeed::Feed;
use jsonfeed;

use cobalt_model::{Config, SortOrder};
use cobalt_model::files;
use cobalt_model::Collection;
use cobalt_model;
use document::Document;
use error::*;

/// The primary build function that transforms a directory into a site
pub fn build(config: &Config) -> Result<()> {
    trace!("Build configuration: {:?}", config);

    let source = config.source.as_path();
    let dest = config.destination.as_path();

    let parser = &config.liquid;
    let layouts = &config.layouts_dir;
    let mut layouts_cache = HashMap::new();

    debug!("Layouts directory: {:?}", layouts);

    let post_files = find_post_files(source, &config.posts)?;
    let mut posts = parse_pages(&post_files, &config.posts, source)?;
    process_included_drafts(&config.posts, source, &mut posts)?;

    let page_files = find_page_files(source, config)?;
    let documents = parse_pages(&page_files, &config.pages, source)?;

    sort_pages(&mut posts, &config.posts)?;
    generate_posts(&mut posts,
                   config,
                   &parser,
                   dest,
                   &layouts,
                   &mut layouts_cache)?;

    // check if we should create an RSS file and create it!
    if let Some(ref path) = config.posts.rss {
        create_rss(path, dest, &config.posts, &posts)?;
    }
    // check if we should create an jsonfeed file and create it!
    if let Some(ref path) = config.posts.jsonfeed {
        create_jsonfeed(path, dest, &config.posts, &posts)?;
    }

    generate_pages(posts,
                   documents,
                   config,
                   &parser,
                   dest,
                   &layouts,
                   &mut layouts_cache)?;

    // copy all remaining files in the source to the destination
    // compile SASS along the way
    {
        debug!("Copying remaining assets");
        config.assets.populate(dest)?;
    }

    Ok(())
}

fn generate_doc(dest: &Path,
                layouts: &Path,
                mut layouts_cache: &mut HashMap<String, String>,
                parser: &cobalt_model::Liquid,
                posts_data: &[liquid::Value],
                doc: &mut Document,
                config: &Config)
                -> Result<()> {
    for dump in config.dump.iter().filter(|d| d.is_doc()) {
        trace!("Dumping {:?}", dump);
        let (content, ext) = doc.render_dump(*dump)?;
        let mut file_path = doc.file_path.clone();
        let file_name = file_path
            .file_stem()
            .and_then(|p| p.to_str())
            .expect("page must have file name")
            .to_owned();
        let file_name = format!("_{}.{}.{}", file_name, dump, ext);
        file_path.set_file_name(file_name);
        trace!("Generating {:?}", file_path);
        files::write_document_file(content, dest.join(file_path))?;
    }

    // Everything done with `globals` is terrible for performance.  liquid#95 allows us to
    // improve this.
    let mut posts_variable = config.posts.attributes.clone();
    posts_variable.insert("pages".to_owned(),
                          liquid::Value::Array(posts_data.to_vec()));
    let global_collection: liquid::Object = vec![(config.posts.slug.clone(),
                                                  liquid::Value::Object(posts_variable))]
        .into_iter()
        .collect();
    let mut globals: liquid::Object =
        vec![("site".to_owned(), liquid::Value::Object(config.site.clone())),
             ("collections".to_owned(), liquid::Value::Object(global_collection))]
            .into_iter()
            .collect();
    globals.insert("page".to_owned(),
                   liquid::Value::Object(doc.attributes.clone()));

    doc.render_excerpt(&globals, parser, &config.markdown)
        .chain_err(|| format!("Failed to render excerpt for {:?}", doc.file_path))?;
    doc.render_content(&globals, parser, &config.markdown)
        .chain_err(|| format!("Failed to render content for {:?}", doc.file_path))?;

    // Refresh `page` with the `excerpt` / `content` attribute
    globals.insert("page".to_owned(),
                   liquid::Value::Object(doc.attributes.clone()));
    let doc_html = doc.render(&globals, parser, layouts, &mut layouts_cache)
        .chain_err(|| format!("Failed to render for {:?}", doc.file_path))?;
    files::write_document_file(doc_html, dest.join(&doc.file_path))?;
    Ok(())
}

fn generate_pages(posts: Vec<Document>,
                  documents: Vec<Document>,
                  config: &Config,
                  parser: &cobalt_model::Liquid,
                  dest: &Path,
                  layouts: &Path,
                  mut layouts_cache: &mut HashMap<String, String>)
                  -> Result<()> {
    // during post rendering additional attributes such as content were
    // added to posts. collect them so that non-post documents can access them
    let posts_data: Vec<liquid::Value> = posts
        .into_iter()
        .map(|x| liquid::Value::Object(x.attributes))
        .collect();

    trace!("Generating other documents");
    for mut doc in documents {
        trace!("Generating {}", doc.url_path);
        generate_doc(dest,
                     layouts,
                     &mut layouts_cache,
                     parser,
                     &posts_data,
                     &mut doc,
                     config)?;
    }

    Ok(())
}

fn generate_posts(posts: &mut Vec<Document>,
                  config: &Config,
                  parser: &cobalt_model::Liquid,
                  dest: &Path,
                  layouts: &Path,
                  mut layouts_cache: &mut HashMap<String, String>)
                  -> Result<()> {
    // collect all posts attributes to pass them to other posts for rendering
    let simple_posts_data: Vec<liquid::Value> = posts
        .iter()
        .map(|x| liquid::Value::Object(x.attributes.clone()))
        .collect();

    trace!("Generating posts");
    for (i, post) in &mut posts.iter_mut().enumerate() {
        trace!("Generating {}", post.url_path);

        // posts are in reverse date order, so previous post is the next in the list (+1)
        let previous = simple_posts_data
            .get(i + 1)
            .cloned()
            .unwrap_or(liquid::Value::Nil);
        post.attributes.insert("previous".to_owned(), previous);

        let next = if i >= 1 {
            simple_posts_data.get(i - 1)
        } else {
            None
        }.cloned()
            .unwrap_or(liquid::Value::Nil);
        post.attributes.insert("next".to_owned(), next);

        generate_doc(dest,
                     layouts,
                     &mut layouts_cache,
                     parser,
                     &simple_posts_data,
                     post,
                     config)?;
    }

    Ok(())
}

fn sort_pages(posts: &mut Vec<Document>, collection: &Collection) -> Result<()> {
    // January 1, 1970 0:00:00 UTC, the beginning of time
    let default_date = cobalt_model::DateTime::default();

    // sort documents by date, if there's no date (none was provided or it couldn't be read) then
    // fall back to the default date
    posts.sort_by(|a, b| {
                      b.front
                          .published_date
                          .unwrap_or(default_date)
                          .cmp(&a.front.published_date.unwrap_or(default_date))
                  });

    match collection.order {
        SortOrder::Asc => posts.reverse(),
        SortOrder::Desc => (),
        SortOrder::None => (),
    }

    Ok(())
}

fn parse_drafts(drafts_root: &PathBuf,
                draft_files: &files::Files,
                documents: &mut Vec<Document>,
                collection: &Collection)
                -> Result<()> {
    for file_path in draft_files.files() {
        // Provide a fake path as if it was not a draft
        let rel_src = file_path
            .strip_prefix(&drafts_root)
            .expect("file was found under the root");
        let new_path = Path::new(&collection.dir).join(rel_src);

        let default_front = collection.default.clone().set_draft(true);

        let doc = Document::parse(&file_path, &new_path, default_front)
            .chain_err(|| format!("Failed to parse {:?}", rel_src))?;
        documents.push(doc);
    }
    Ok(())
}

fn process_included_drafts(collection: &Collection,
                           source: &Path,
                           documents: &mut Vec<Document>)
                           -> Result<()> {
    if collection.include_drafts {
        if let Some(ref drafts_dir) = collection.drafts_dir {
            debug!("Draft directory: {:?}", drafts_dir);
            let drafts_root = source.join(&drafts_dir);
            let draft_files = find_post_draft_files(&drafts_root, collection)?;

            parse_drafts(&drafts_root, &draft_files, documents, collection)?;
        }
    }
    Ok(())
}

fn find_post_files(source: &Path, collection: &Collection) -> Result<files::Files> {
    let mut page_files = files::FilesBuilder::new(source)?;
    page_files
        .add_ignore(&format!("!/{}", collection.dir))?
        .add_ignore(&format!("!/{}/**", collection.dir))?
        .add_ignore(&format!("/{}/**/_*", collection.dir))?
        .add_ignore(&format!("/{}/**/_*/**", collection.dir))?;
    for line in &collection.ignore {
        page_files.add_ignore(line.as_str())?;
    }
    for ext in collection.template_extensions.iter() {
        page_files.add_extension(ext)?;
    }
    page_files.limit(PathBuf::from(&collection.dir))?;
    page_files.build()
}

fn find_post_draft_files(drafts_root: &Path, collection: &Collection) -> Result<files::Files> {
    let mut page_files = files::FilesBuilder::new(drafts_root)?;
    for line in &collection.ignore {
        page_files.add_ignore(line.as_str())?;
    }
    for ext in collection.template_extensions.iter() {
        page_files.add_extension(ext)?;
    }
    page_files.build()
}

fn find_page_files(source: &Path, config: &Config) -> Result<files::Files> {
    let mut page_files = files::FilesBuilder::new(source)?;
    for line in &config.pages.ignore {
        page_files.add_ignore(line.as_str())?;
    }
    for ext in config.pages.template_extensions.iter() {
        page_files.add_extension(ext)?;
    }
    page_files.add_ignore(&format!("/{}", config.posts.dir))?;
    if let Some(ref drafts_dir) = config.posts.drafts_dir {
        page_files.add_ignore(&format!("/{}", drafts_dir))?;
    }
    page_files.build()
}

fn parse_pages(page_files: &files::Files,
               collection: &Collection,
               source: &Path)
               -> Result<Vec<Document>> {
    let mut documents = vec![];
    for file_path in page_files.files() {
        let rel_src = file_path
            .strip_prefix(source)
            .expect("file was found under the root");

        let default_front = collection.default.clone();

        let doc = Document::parse(&file_path, rel_src, default_front)
            .chain_err(|| format!("Failed to parse {:?}", rel_src))?;
        if !doc.front.is_draft || collection.include_drafts {
            documents.push(doc);
        }
    }
    Ok(documents)
}

// creates a new RSS file with the contents of the site blog
fn create_rss(path: &str,
              dest: &Path,
              collection: &Collection,
              documents: &[Document])
              -> Result<()> {
    let rss_path = dest.join(path);
    debug!("Creating RSS file at {}", rss_path.display());

    let title = &collection.title;
    let description = collection
        .description
        .as_ref()
        .map(|s| s.as_str())
        .unwrap_or("");
    let link = collection
        .base_url
        .as_ref()
        .ok_or(ErrorKind::ConfigFileMissingFields)?;

    let items: Result<Vec<rss::Item>> = documents.iter().map(|doc| doc.to_rss(link)).collect();
    let items = items?;

    let channel = rss::ChannelBuilder::default()
        .title(title.to_owned())
        .link(link.to_owned())
        .description(description.to_owned())
        .items(items)
        .build()?;

    let rss_string = channel.to_string();
    trace!("RSS data: {}", rss_string);

    // create target directories if any exist
    if let Some(parent) = rss_path.parent() {
        fs::create_dir_all(parent)
            .map_err(|e| format!("Could not create {:?}: {}", parent, e))?;
    }

    let mut rss_file = fs::File::create(&rss_path)?;
    rss_file
        .write_all(br#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
    rss_file.write_all(&rss_string.into_bytes())?;
    rss_file.write_all(b"\n")?;

    Ok(())
}

// creates a new jsonfeed file with the contents of the site blog
fn create_jsonfeed(path: &str,
                   dest: &Path,
                   collection: &Collection,
                   documents: &[Document])
                   -> Result<()> {
    let jsonfeed_path = dest.join(path);
    debug!("Creating jsonfeed file at {}", jsonfeed_path.display());

    let title = &collection.title;
    let description = collection
        .description
        .as_ref()
        .map(|s| s.as_str())
        .unwrap_or("");
    let link = collection
        .base_url
        .as_ref()
        .ok_or(ErrorKind::ConfigFileMissingFields)?;

    let jsonitems = documents.iter().map(|doc| doc.to_jsonfeed(link)).collect();

    let feed = Feed {
        title: title.to_string(),
        items: jsonitems,
        home_page_url: Some(link.to_string()),
        description: Some(description.to_string()),
        ..Default::default()
    };

    let jsonfeed_string = jsonfeed::to_string(&feed).unwrap();
    files::write_document_file(jsonfeed_string, jsonfeed_path)?;

    Ok(())
}
<|endoftext|>


<commit_before>import to_str::to_str;
import ptr::addr_of;
import comm::{port, chan};
import result::{result, ok, err};
import std::net::ip::{
    get_addr, format_addr, ipv4, ipv6, ip_addr,
    ip_get_addr_err
};
import std::net::tcp::{connect, tcp_socket};
import std::net::url;
import std::net::url::url;
import std::uv_global_loop;
import connection::{
    Connection, ConnectionFactory, UvConnectionFactory,
    MockConnection, MockConnectionFactory
};
import parser::{Parser, ParserCallbacks};
import request::build_request;

const timeout: uint = 2000;

/// HTTP status codes
enum StatusCode {
    StatusOk = 200,
    StatusFound = 302,
    StatusUnknown
}

/// HTTP request error conditions
enum RequestError {
    ErrorDnsResolution,
    ErrorConnect,
    ErrorMisc
}

/// Request
enum RequestEvent {
    Status(StatusCode),
    Payload(~mut option<~[u8]>),
    Error(RequestError)
}

type DnsResolver = fn@(host: ~str) -> result<~[ip_addr], ip_get_addr_err>;

fn uv_dns_resolver() -> DnsResolver {
    |host| {
        let iotask = uv_global_loop::get();
        get_addr(host, iotask)
    }
}

fn uv_http_request(+url: url) -> HttpRequest<tcp_socket, UvConnectionFactory> {
    HttpRequest(uv_dns_resolver(), UvConnectionFactory, url)
}

#[allow(non_implicitly_copyable_typarams)]
struct HttpRequest<C: Connection, CF: ConnectionFactory<C>> {

    let resolve_ip_addr: DnsResolver;
    let connection_factory: CF;
    let url: url;
    let parser: Parser;
    let mut cb: fn@(+RequestEvent);

    new(resolver: DnsResolver, +connection_factory: CF, +url: url) {
        self.resolve_ip_addr = resolver;
        self.connection_factory = connection_factory;
        self.url = url;
        self.parser = Parser();
        self.cb = |_event| { };
    }

    fn begin(cb: fn@(+RequestEvent)) {
        #debug("http_client: looking up url %?", self.url.to_str());
        let ip_addr = match self.get_ip() {
          ok(ip_addr) => { copy ip_addr }
          err(e) => { cb(Error(e)); return }
        };

        #debug("http_client: using IP %? for %?", format_addr(ip_addr), self.url.to_str());

        let socket = {
            #debug("http_client: connecting to %?", ip_addr);
            let socket = self.connection_factory.connect(copy ip_addr, 80);
            if socket.is_ok() {
                result::unwrap(socket)
            } else {
                #debug("http_client: unable to connect to %?: %?", ip_addr, socket);
                cb(Error(ErrorConnect));
                return;
            }
        };

        #debug("http_client: got socket for %?", ip_addr);

        let request_header = build_request(self.url);
        #debug("http_client: writing request header: %?", request_header);
        let request_header_bytes = str::bytes(request_header);
        match socket.write_(request_header_bytes) {
          result::ok(*) => { }
          result::err(e) => {
            // FIXME: Need test
            cb(Error(ErrorMisc));
            return;
          }
        }

        let read_port = {
            let read_port = socket.read_start_();
            if read_port.is_ok() {
                result::unwrap(read_port)
            } else {
                cb(Error(ErrorMisc));
                return;
            }
        };

        // This unsafety is unfortunate but we can't capture self
        // into shared closures
        let unsafe_self = addr_of(self);
        let callbacks: ParserCallbacks = unsafe {{
            on_message_begin: || (*unsafe_self).on_message_begin(),
            on_url: |data| (*unsafe_self).on_url(data),
            on_header_field: |data| (*unsafe_self).on_header_field(data),
            on_header_value: |data| (*unsafe_self).on_header_value(data),
            on_headers_complete: || (*unsafe_self).on_headers_complete(),
            on_body: |data| (*unsafe_self).on_body(data),
            on_message_complete: || (*unsafe_self).on_message_complete()
        }};

        // Set the callback used by the parser event handlers
        self.cb = cb;

        loop {
            let next_data = read_port.recv();

            if next_data.is_ok() {
                let next_data = next_data.get();
                #debug("data: %?", next_data);
                let bytes_parsed = self.parser.execute(next_data, &callbacks);
                if bytes_parsed != next_data.len() {
                    // FIXME: Need tests
                    fail ~"http parse failure";
                }
            } else {
                #debug("http_client: read error: %?", next_data);

                // This method of detecting EOF is lame
                match next_data {
                  result::err({err_name: ~"EOF", _}) => {
                    self.parser.execute(~[], &callbacks);
                    break;
                  }
                  _ => {
                    // FIXME: Need tests and error handling
                    socket.read_stop_(read_port);
                    cb(Error(ErrorMisc));
                    return;
                  }
                }
            }
        }
        socket.read_stop_(read_port);
    }

    fn get_ip() -> result<ip_addr, RequestError> {
        let ip_addrs = self.resolve_ip_addr(self.url.host);
        if ip_addrs.is_ok() {
            let ip_addrs = result::unwrap(ip_addrs);
            // FIXME: This log crashes
            //#debug("http_client: got IP addresses for %?: %?", self.url, ip_addrs);
            if ip_addrs.is_not_empty() {
                // FIXME: Which address should we really pick?
                let best_ip = do ip_addrs.find |ip| {
                    match ip {
                      ipv4(*) => { true }
                      ipv6(*) => { false }
                    }
                };

                if best_ip.is_some() {
                    return ok(option::unwrap(best_ip));
                } else {
                    // FIXME: Need test
                    return err(ErrorMisc);
                }
            } else {
                #debug("http_client: got no IP addresses for %?", self.url);
                // FIXME: Need test
                return err(ErrorMisc);
            }
        } else {
            #debug("http_client: DNS lookup failure: %?", ip_addrs.get_err());
            return err(ErrorDnsResolution);
        }
    }

    fn on_message_begin() -> bool {
        #debug("on_message_begin");
        true
    }

    fn on_url(+_data: ~[u8]) -> bool {
        #debug("on_url");
        true
    }

    fn on_header_field(+data: ~[u8]) -> bool {
        let header_field = str::from_bytes(data);
        #debug("on_header_field: %?", header_field);
        true
    }

    fn on_header_value(+data: ~[u8]) -> bool {
        let header_value = str::from_bytes(data);
        #debug("on_header_value: %?", header_value);
        true
    }

    fn on_headers_complete() -> bool {
        #debug("on_headers_complete");
        true
    }

    fn on_body(+data: ~[u8]) -> bool {
        #debug("on_body");
        let the_payload = Payload(~mut some(data));
        self.cb(the_payload);
        true
    }

    fn on_message_complete() -> bool {
        #debug("on_message_complete");
        true
    }
}

#[allow(non_implicitly_copyable_typarams)]
fn sequence<C: Connection, CF: ConnectionFactory<C>>(request: HttpRequest<C, CF>) ->
    ~[RequestEvent] {

    let events = @mut ~[];
    do request.begin |event| {
        vec::push(*events, event)
    }
    return *events;
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_resolve_error() {
    let url = url::from_str(~"http://example.com_not_real/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorDnsResolution),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_connect_error() {
    // This address is invalid because the first octet
    // of a class A address cannot be 0
    let url = url::from_str(~"http://0.42.42.42/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorConnect),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_connect_success() {
    let url = url::from_str(~"http://example.com/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    for events.each |ev| {
        match ev {
          Error(*) => { fail }
          _ => { }
        }
    }
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_simple_body() {
    let url = url::from_str(~"http://www.iana.org/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    let mut found = false;

    for events.each |ev| {
        match ev {
          Payload(value) => {
            if str::from_bytes(value.get()).contains(~"DOCTYPE html") {
                found = true
            }
          }
          _ => { }
        }
    }

    assert found;
}

#[test]
#[ignore(reason = "ICE")]
#[allow(non_implicitly_copyable_typarams)]
fn test_simple_response() {
    let _url = url::from_str(~"http://whatever/").get();
    let _mock_connection: MockConnection = {
        write_fn: |_data| { ok(()) },
        read_start_fn: || {
            let port = port();
            let chan = port.chan();

            let response = ~"HTTP/1.0 200 OK\
                            \
                            Test";
            chan.send(ok(str::bytes(response)));

            ok(port)
        },
        read_stop_fn: |_port| { ok(()) }
    };

    let _mock_connection_factory: MockConnectionFactory = {
        connect_fn: |_ip, _port| {

            // FIXME this doesn't work
            fail;//ok(mock_connection)
        }
    };
}
<commit_msg>Fix for Rust language changes<commit_after>import to_str::to_str;
import ptr::addr_of;
import comm::{port, chan};
import result::{result, ok, err};
import std::net::ip::{
    get_addr, format_addr, ipv4, ipv6, ip_addr,
    ip_get_addr_err
};
import std::net::tcp::{connect, tcp_socket};
import std::net::url;
import std::net::url::url;
import std::uv_global_loop;
import connection::{
    Connection, ConnectionFactory, UvConnectionFactory,
    MockConnection, MockConnectionFactory
};
import parser::{Parser, ParserCallbacks};
import request::build_request;

const timeout: uint = 2000;

/// HTTP status codes
enum StatusCode {
    StatusOk = 200,
    StatusFound = 302,
    StatusUnknown
}

/// HTTP request error conditions
enum RequestError {
    ErrorDnsResolution,
    ErrorConnect,
    ErrorMisc
}

/// Request
enum RequestEvent {
    Status(StatusCode),
    Payload(~mut option<~[u8]>),
    Error(RequestError)
}

type DnsResolver = fn@(host: ~str) -> result<~[ip_addr], ip_get_addr_err>;

fn uv_dns_resolver() -> DnsResolver {
    |host| {
        let iotask = uv_global_loop::get();
        get_addr(host, iotask)
    }
}

fn uv_http_request(+url: url) -> HttpRequest<tcp_socket, UvConnectionFactory> {
    HttpRequest(uv_dns_resolver(), UvConnectionFactory, url)
}

#[allow(non_implicitly_copyable_typarams)]
struct HttpRequest<C: Connection, CF: ConnectionFactory<C>> {

    let resolve_ip_addr: DnsResolver;
    let connection_factory: CF;
    let url: url;
    let parser: Parser;
    let mut cb: fn@(+RequestEvent);

    new(resolver: DnsResolver, +connection_factory: CF, +url: url) {
        self.resolve_ip_addr = resolver;
        self.connection_factory = connection_factory;
        self.url = url;
        self.parser = Parser();
        self.cb = |_event| { };
    }

    fn begin(cb: fn@(+RequestEvent)) {
        #debug("http_client: looking up url %?", self.url.to_str());
        let ip_addr = match self.get_ip() {
          ok(ip_addr) => { copy ip_addr }
          err(e) => { cb(Error(e)); return }
        };

        #debug("http_client: using IP %? for %?", format_addr(ip_addr), self.url.to_str());

        let socket = {
            #debug("http_client: connecting to %?", ip_addr);
            let socket = self.connection_factory.connect(copy ip_addr, 80);
            if socket.is_ok() {
                result::unwrap(socket)
            } else {
                #debug("http_client: unable to connect to %?: %?", ip_addr, socket);
                cb(Error(ErrorConnect));
                return;
            }
        };

        #debug("http_client: got socket for %?", ip_addr);

        let request_header = build_request(self.url);
        #debug("http_client: writing request header: %?", request_header);
        let request_header_bytes = str::to_bytes(request_header);
        match socket.write_(request_header_bytes) {
          result::ok(*) => { }
          result::err(e) => {
            // FIXME: Need test
            cb(Error(ErrorMisc));
            return;
          }
        }

        let read_port = {
            let read_port = socket.read_start_();
            if read_port.is_ok() {
                result::unwrap(read_port)
            } else {
                cb(Error(ErrorMisc));
                return;
            }
        };

        // This unsafety is unfortunate but we can't capture self
        // into shared closures
        let unsafe_self = addr_of(self);
        let callbacks: ParserCallbacks = unsafe {{
            on_message_begin: || (*unsafe_self).on_message_begin(),
            on_url: |data| (*unsafe_self).on_url(data),
            on_header_field: |data| (*unsafe_self).on_header_field(data),
            on_header_value: |data| (*unsafe_self).on_header_value(data),
            on_headers_complete: || (*unsafe_self).on_headers_complete(),
            on_body: |data| (*unsafe_self).on_body(data),
            on_message_complete: || (*unsafe_self).on_message_complete()
        }};

        // Set the callback used by the parser event handlers
        self.cb = cb;

        loop {
            let next_data = read_port.recv();

            if next_data.is_ok() {
                let next_data = next_data.get();
                #debug("data: %?", next_data);
                let bytes_parsed = self.parser.execute(next_data, &callbacks);
                if bytes_parsed != next_data.len() {
                    // FIXME: Need tests
                    fail ~"http parse failure";
                }
            } else {
                #debug("http_client: read error: %?", next_data);

                // This method of detecting EOF is lame
                match next_data {
                  result::err({err_name: ~"EOF", _}) => {
                    self.parser.execute(~[], &callbacks);
                    break;
                  }
                  _ => {
                    // FIXME: Need tests and error handling
                    socket.read_stop_(read_port);
                    cb(Error(ErrorMisc));
                    return;
                  }
                }
            }
        }
        socket.read_stop_(read_port);
    }

    fn get_ip() -> result<ip_addr, RequestError> {
        let ip_addrs = self.resolve_ip_addr(self.url.host);
        if ip_addrs.is_ok() {
            let ip_addrs = result::unwrap(ip_addrs);
            // FIXME: This log crashes
            //#debug("http_client: got IP addresses for %?: %?", self.url, ip_addrs);
            if ip_addrs.is_not_empty() {
                // FIXME: Which address should we really pick?
                let best_ip = do ip_addrs.find |ip| {
                    match ip {
                      ipv4(*) => { true }
                      ipv6(*) => { false }
                    }
                };

                if best_ip.is_some() {
                    return ok(option::unwrap(best_ip));
                } else {
                    // FIXME: Need test
                    return err(ErrorMisc);
                }
            } else {
                #debug("http_client: got no IP addresses for %?", self.url);
                // FIXME: Need test
                return err(ErrorMisc);
            }
        } else {
            #debug("http_client: DNS lookup failure: %?", ip_addrs.get_err());
            return err(ErrorDnsResolution);
        }
    }

    fn on_message_begin() -> bool {
        #debug("on_message_begin");
        true
    }

    fn on_url(+_data: ~[u8]) -> bool {
        #debug("on_url");
        true
    }

    fn on_header_field(+data: ~[u8]) -> bool {
        let header_field = str::from_bytes(data);
        #debug("on_header_field: %?", header_field);
        true
    }

    fn on_header_value(+data: ~[u8]) -> bool {
        let header_value = str::from_bytes(data);
        #debug("on_header_value: %?", header_value);
        true
    }

    fn on_headers_complete() -> bool {
        #debug("on_headers_complete");
        true
    }

    fn on_body(+data: ~[u8]) -> bool {
        #debug("on_body");
        let the_payload = Payload(~mut some(data));
        self.cb(the_payload);
        true
    }

    fn on_message_complete() -> bool {
        #debug("on_message_complete");
        true
    }
}

#[allow(non_implicitly_copyable_typarams)]
fn sequence<C: Connection, CF: ConnectionFactory<C>>(request: HttpRequest<C, CF>) ->
    ~[RequestEvent] {

    let events = @mut ~[];
    do request.begin |event| {
        vec::push(*events, event)
    }
    return *events;
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_resolve_error() {
    let url = url::from_str(~"http://example.com_not_real/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorDnsResolution),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_connect_error() {
    // This address is invalid because the first octet
    // of a class A address cannot be 0
    let url = url::from_str(~"http://0.42.42.42/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorConnect),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_connect_success() {
    let url = url::from_str(~"http://example.com/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    for events.each |ev| {
        match ev {
          Error(*) => { fail }
          _ => { }
        }
    }
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_simple_body() {
    let url = url::from_str(~"http://www.iana.org/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    let mut found = false;

    for events.each |ev| {
        match ev {
          Payload(value) => {
            if str::from_bytes(value.get()).contains(~"DOCTYPE html") {
                found = true
            }
          }
          _ => { }
        }
    }

    assert found;
}

#[test]
#[ignore(reason = "ICE")]
#[allow(non_implicitly_copyable_typarams)]
fn test_simple_response() {
    let _url = url::from_str(~"http://whatever/").get();
    let _mock_connection: MockConnection = {
        write_fn: |_data| { ok(()) },
        read_start_fn: || {
            let port = port();
            let chan = port.chan();

            let response = ~"HTTP/1.0 200 OK\
                            \
                            Test";
            chan.send(ok(str::to_bytes(response)));

            ok(port)
        },
        read_stop_fn: |_port| { ok(()) }
    };

    let _mock_connection_factory: MockConnectionFactory = {
        connect_fn: |_ip, _port| {

            // FIXME this doesn't work
            fail;//ok(mock_connection)
        }
    };
}
<|endoftext|>


<commit_before>extern crate grade_school as school;

fn stringvec_to_strvec(v: &Vec<String>) -> Vec<&str> {
    v.iter().map(|s| s.as_ref()).collect()
}

#[test]
fn test_add_student() {
    let mut s = school::School::new();
    s.add(2, "Aimee");
    assert_eq!(s.grades(), vec!(2));
    assert_eq!(s.grade(2).map(|v| stringvec_to_strvec(v)),
                Some(vec!("Aimee")))
}

#[test]
#[ignore]
fn test_add_more_students_in_same_class() {
    let mut s = school::School::new();
    s.add(2, "James");
    s.add(2, "Blair");
    s.add(2, "Paul");
    assert_eq!(s.grades(), vec!(2));
    assert_eq!(s.grade(2).map(|v| stringvec_to_strvec(v)),
               Some(vec!("Blair", "James", "Paul")));
}

#[test]
#[ignore]
fn test_add_students_to_different_grades() {
    let mut s = school::School::new();
    s.add(3, "Chelsea");
    s.add(7, "Logan");
    assert_eq!(s.grades(), vec!(3, 7));
    assert_eq!(s.grade(3).map(|v| stringvec_to_strvec(v)),
               Some(vec!("Chelsea")));
    assert_eq!(s.grade(7).map(|v| stringvec_to_strvec(v)),
               Some(vec!("Logan")));
}

#[test]
#[ignore]
fn test_get_students_in_a_non_existent_grade() {
    let s = school::School::new();
    assert_eq!(s.grade(1), None);
}
<commit_msg>Improve tests in grade school.<commit_after>extern crate grade_school as school;

fn stringvec_to_strvec(v: &Vec<String>) -> Vec<&str> {
    v.iter().map(|s| s.as_ref()).collect()
}

#[test]
fn test_grades_for_empty_school() {
    let s = school::School::new();
    assert_eq!(s.grades(), vec![]);
}

#[test]
#[ignore]
fn test_grades_for_one_student() {
    let mut s = school::School::new();
    s.add(2, "Aimee");
    assert_eq!(s.grades(), vec!(2));
}

#[test]
#[ignore]
fn test_grades_for_several_students_are_sorted() {
    let mut s = school::School::new();
    s.add(2, "Aimee");
    s.add(7, "Logan");
    s.add(4, "Blair");
    assert_eq!(s.grades(), vec!(2, 4, 7));
}

#[test]
#[ignore]
fn test_grades_when_several_students_have_the_same_grade() {
    let mut s = school::School::new();
    s.add(2, "Aimee");
    s.add(2, "Logan");
    s.add(2, "Blair");
    assert_eq!(s.grades(), vec!(2));
}

#[test]
#[ignore]
fn test_grade_for_empty_school() {
    let s = school::School::new();
    assert_eq!(s.grade(1), None);
}

#[test]
#[ignore]
fn test_grade_when_no_students_have_that_grade() {
    let mut s = school::School::new();
    s.add(7, "Logan");
    assert_eq!(s.grade(1), None);
}

#[test]
#[ignore]
fn test_grade_for_one_student() {
    let mut s = school::School::new();
    s.add(2, "Aimee");
    assert_eq!(s.grade(2).map(|v| stringvec_to_strvec(v)),
                Some(vec!("Aimee")))
}

#[test]
#[ignore]
fn test_grade_returns_students_sorted_by_name() {
    let mut s = school::School::new();
    s.add(2, "James");
    s.add(2, "Blair");
    s.add(2, "Paul");
    assert_eq!(s.grade(2).map(|v| stringvec_to_strvec(v)),
               Some(vec!("Blair", "James", "Paul")));
}

#[test]
#[ignore]
fn test_add_students_to_different_grades() {
    let mut s = school::School::new();
    s.add(3, "Chelsea");
    s.add(7, "Logan");
    assert_eq!(s.grades(), vec!(3, 7));
    assert_eq!(s.grade(3).map(|v| stringvec_to_strvec(v)),
               Some(vec!("Chelsea")));
    assert_eq!(s.grade(7).map(|v| stringvec_to_strvec(v)),
               Some(vec!("Logan")));
}
<|endoftext|>