<commit_before>use lexer::{Lexer, TokenType, Token};
use nodes::{Node, SpecificNode};

#[derive(Debug)]
pub struct Parser {
    name: String,
    text: String,
    lexer: Lexer,
    root: Node,
    current_token: usize, // where we are in the parsing of the tokens
}

impl Parser {
    pub fn new(name: &str, text: &str) -> Parser {
        let mut lexer = Lexer::new(name, text);
        lexer.run();

        Parser {
            name: name.to_owned(),
            text: text.to_owned(),
            root: Node::new(0, SpecificNode::List(vec![])),
            lexer: lexer,
            current_token: 0
        }
    }

    // Main loop of the parser, stops when there are no token left
    pub fn parse(&mut self) {
        loop {
            let node = match self.parse_next() {
                Some(n) => n,
                None => break
            };

            self.root.push(node);
        }
    }

    // Look at the next token
    fn peek(&self) -> Token {
        self.lexer.tokens.get(self.current_token).unwrap().clone()
    }

    // Look at the next token that isn't space
    fn peek_non_space(&mut self) -> Token {
        let mut token = self.next();
        loop {
            if token.kind != TokenType::Space {
                break;
            }
            token = self.next();
        }
        // Only rewind once (see once i have tests)
        self.current_token -= 1;

        token
    }

    // Get the next token
    fn next(&mut self) -> Token {
        let token = self.peek();
        self.current_token += 1;

        token
    }

    // Get the next token that isn't space
    fn next_non_space(&mut self) -> Token {
        let mut token = self.next();
        loop {
            if token.kind != TokenType::Space {
                break;
            }
            token = self.next();
        }

        token
    }

    // Panics if the expected token isn't found
    fn expect(&mut self, kind: TokenType) -> Token {
        let token = self.peek_non_space();
        if token.kind != kind {
            panic!("Unexpected token: {:?}", token);
        }

        self.next_non_space()
    }

    // All the different "states" the parser can be in: either in a block, in text
    // or in a tag
    fn parse_next(&mut self) -> Option<Box<Node>> {
        loop {
            match self.peek().kind {
                TokenType::TagStart => (),  // TODO
                TokenType::VariableStart => return self.parse_variable_block(),
                TokenType::Text => return self.parse_text(),
                _ => break
            };
        }

        None
    }

    // Parse some html text
    fn parse_text(&mut self) -> Option<Box<Node>> {
        let token = self.next();
        Some(Box::new(Node::new(token.position, SpecificNode::Text(token.value))))
    }

    // Parse the content of a  {{ }} block
    fn parse_variable_block(&mut self) -> Option<Box<Node>> {
        let token = self.expect(TokenType::VariableStart);
        let contained = self.parse_whole_expression(None, TokenType::VariableEnd);
        let node = Node::new(token.position, SpecificNode::VariableBlock(contained.unwrap()));
        self.expect(TokenType::VariableEnd);

        Some(Box::new(node))
    }

    // Parse a block/tag until we get to the terminator
    // Also handles all the precedence
    fn parse_whole_expression(&mut self, stack: Option<Node>, terminator: TokenType) -> Option<Box<Node>> {
        let token = self.peek_non_space();

        let mut node_stack = stack.unwrap_or(Node::new(token.position, SpecificNode::List(vec![])));
        let next = self.parse_single_expression(&node_stack, &terminator).unwrap();
        node_stack.push(next);

        loop {
            let token = self.peek_non_space();
            if token.kind == terminator {
                if node_stack.len() == 0 {
                    panic!("Unexpected terminator");
                }
                return Some(node_stack.pop());
            }

            match token.kind {
                TokenType::Add | TokenType::Substract => {
                    // consume it
                    self.next_non_space();
                    if node_stack.len() == 0 {
                        continue;
                    }

                    let rhs = self.parse_whole_expression(Some(node_stack.clone()), terminator.clone()).unwrap();

                    // Now for + - we need to know if the next token has a higher
                    // precedence (ie * or /)
                    let next_token = self.peek_non_space();
                    if next_token.precedence() > token.precedence() {
                        node_stack.push(rhs);
                        return self.parse_whole_expression(Some(node_stack.clone()), terminator.clone());
                    } else {
                        // Or the next thing has lower precedence and we just
                        // add the node to the stack
                        let lhs = node_stack.pop();
                        let node = if token.kind == TokenType::Add {
                            Node::new(lhs.position, SpecificNode::Addition{lhs: lhs, rhs: rhs})
                        } else {
                            Node::new(lhs.position, SpecificNode::Substraction{lhs: lhs, rhs: rhs})
                        };
                        node_stack.push(Box::new(node));

                    }
                },
                TokenType::Divide | TokenType::Multiply => {
                    // consume the operator
                    self.next_non_space();
                    if node_stack.len() == 0 {
                        panic!("Unexpected division or multiplication");
                    }

                    // * and / have the highest precedence so no need to check
                    // the following operators precedences
                    let rhs = self.parse_single_expression(&node_stack, &terminator).unwrap();
                    let lhs = node_stack.pop();
                    let node = if token.kind == TokenType::Multiply {
                        Node::new(lhs.position, SpecificNode::Multiplication{lhs: lhs, rhs: rhs})
                    } else {
                        Node::new(lhs.position, SpecificNode::Division{lhs: lhs, rhs: rhs})
                    };
                    node_stack.push(Box::new(node));
                },
                _ => panic!("Unexpected token") // TODO: not panic
            }
        }
    }

    // Parses the next non-space token as a simple expression
    // Used when parsing inside a block/tag and we want to get the next value
    fn parse_single_expression(&mut self, stack: &Node, terminator: &TokenType) -> Option<Box<Node>> {
        let token = self.peek_non_space();

        if token.kind == *terminator {
            panic!("Unexpected terminator");
        }

        match token.kind {
            TokenType::Identifier => return self.parse_identifier(),
            TokenType::Float | TokenType::Int | TokenType::Bool => return self.parse_literal(),
            TokenType::Add | TokenType::Substract => {
                panic!("wololo");
            }
            _ => panic!("unexpected")
        }

        None
    }

    // Parse an identifier (variable name)
    fn parse_identifier(&mut self) -> Option<Box<Node>> {
        let ident = self.next_non_space();
        Some(Box::new(Node::new(ident.position, SpecificNode::Identifier(ident.value))))
    }

    // Parse a bool/int/float
    fn parse_literal(&mut self) -> Option<Box<Node>> {
        let literal = self.next_non_space();

        match literal.kind {
            TokenType::Int => {
                let value = literal.value.parse::<i32>().unwrap();
                return Some(Box::new(Node::new(literal.position, SpecificNode::Int(value))));
            },
            TokenType::Float => {
                let value = literal.value.parse::<f32>().unwrap();
                return Some(Box::new(Node::new(literal.position, SpecificNode::Float(value))));
            },
            TokenType::Bool => {
                let value = if literal.value == "false" { false } else { true };
                return Some(Box::new(Node::new(literal.position, SpecificNode::Bool(value))));
            },
            _ => panic!("unexpected type when parsing literal")
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Parser};
    use nodes::{Node, SpecificNode};

    fn compared_expected(expected: Vec<SpecificNode>, got: Vec<Box<Node>>) {
        if expected.len() != got.len() {
            assert!(false);
        }

        for (i, node) in got.iter().enumerate() {
            let expected_node = expected.get(i).unwrap().clone();
            println!("Expected: {:#?}", expected_node);
            println!("Got: {:#?}", node.specific);
            assert_eq!(expected_node, node.specific);
        }
    }

    fn test_parser(input: &str, expected: Vec<SpecificNode>) {
        let mut parser = Parser::new("dummy", input);
        parser.parse();
        let children = parser.root.get_children();
        compared_expected(expected, children)
    }

    #[test]
    fn test_empty() {
        let mut parser = Parser::new("empty", "");
        parser.parse();
        assert_eq!(0, parser.root.len());
    }

    #[test]
    fn test_plain_string() {
        test_parser(
            "Hello world",
            vec![SpecificNode::Text("Hello world".to_owned())]
        );
    }

    #[test]
    fn test_variable_block_and_text() {
        test_parser(
            "{{ greeting }} 世界",
            vec![
                SpecificNode::VariableBlock(
                    Box::new(Node::new(3, SpecificNode::Identifier("greeting".to_owned())))
                ),
                SpecificNode::Text(" 世界".to_owned()),
            ]
        );
    }

    #[test]
    fn test_basic_math() {
        test_parser(
            "{{1+3.14}}{{1-42}}{{1*42}}{{1/42}}{{test+1}}",
            vec![
                SpecificNode::VariableBlock(
                    Box::new(Node::new(2, SpecificNode::Addition {
                        lhs: Box::new(Node::new(2, SpecificNode::Int(1))),
                        rhs: Box::new(Node::new(4, SpecificNode::Float(3.14))),
                    }))
                ),
                SpecificNode::VariableBlock(
                    Box::new(Node::new(12, SpecificNode::Substraction {
                        lhs: Box::new(Node::new(12, SpecificNode::Int(1))),
                        rhs: Box::new(Node::new(14, SpecificNode::Int(42))),
                    }))
                ),
                SpecificNode::VariableBlock(
                    Box::new(Node::new(20, SpecificNode::Multiplication {
                        lhs: Box::new(Node::new(20, SpecificNode::Int(1))),
                        rhs: Box::new(Node::new(22, SpecificNode::Int(42))),
                    }))
                ),
                SpecificNode::VariableBlock(
                    Box::new(Node::new(28, SpecificNode::Division {
                        lhs: Box::new(Node::new(28, SpecificNode::Int(1))),
                        rhs: Box::new(Node::new(30, SpecificNode::Int(42))),
                    }))
                ),
                SpecificNode::VariableBlock(
                    Box::new(Node::new(36, SpecificNode::Addition {
                        lhs: Box::new(Node::new(36, SpecificNode::Identifier("test".to_owned()))),
                        rhs: Box::new(Node::new(41, SpecificNode::Int(1))),
                    }))
                ),
            ]
        );
    }

    #[test]
    fn test_math_precedence_simple() {
        test_parser(
            "{{ 1 / 2 + 1 }}",
            vec![
                SpecificNode::VariableBlock(
                    Box::new(Node::new(3, SpecificNode::Addition {
                        lhs: Box::new(Node::new(3, SpecificNode::Division {
                            lhs: Box::new(Node::new(3, SpecificNode::Int(1))),
                            rhs: Box::new(Node::new(7, SpecificNode::Int(2))),
                        })),
                        rhs: Box::new(Node::new(11, SpecificNode::Int(1)))
                    }))
                ),
            ]
        );
    }

    #[test]
    fn test_math_precedence_complex() {
        test_parser(
            "{{ 1 / 2 + 3 * 2 + 42 }}", // should be  ((1/2) + (3*2)) + 42
            vec![
                SpecificNode::VariableBlock(
                    Box::new(Node::new(3, SpecificNode::Addition {
                        lhs: Box::new(Node::new(3, SpecificNode::Division {
                            lhs: Box::new(Node::new(3, SpecificNode::Int(1))),
                            rhs: Box::new(Node::new(7, SpecificNode::Int(2))),
                        })),
                        rhs: Box::new(Node::new(11, SpecificNode::Addition {
                            lhs: Box::new(Node::new(11, SpecificNode::Multiplication {
                                lhs: Box::new(Node::new(11, SpecificNode::Int(3))),
                                rhs: Box::new(Node::new(15, SpecificNode::Int(2)))
                            })),
                            rhs: Box::new(Node::new(19, SpecificNode::Int(42)))
                        }))
                    }))
                ),
            ]
        );
    }
}
<commit_msg>No need to pass the stack around<commit_after>use lexer::{Lexer, TokenType, Token};
use nodes::{Node, SpecificNode};

#[derive(Debug)]
pub struct Parser {
    name: String,
    text: String,
    lexer: Lexer,
    root: Node,
    current_token: usize, // where we are in the parsing of the tokens
}

impl Parser {
    pub fn new(name: &str, text: &str) -> Parser {
        let mut lexer = Lexer::new(name, text);
        lexer.run();

        Parser {
            name: name.to_owned(),
            text: text.to_owned(),
            root: Node::new(0, SpecificNode::List(vec![])),
            lexer: lexer,
            current_token: 0
        }
    }

    // Main loop of the parser, stops when there are no token left
    pub fn parse(&mut self) {
        loop {
            let node = match self.parse_next() {
                Some(n) => n,
                None => break
            };

            self.root.push(node);
        }
    }

    // Look at the next token
    fn peek(&self) -> Token {
        self.lexer.tokens.get(self.current_token).unwrap().clone()
    }

    // Look at the next token that isn't space
    fn peek_non_space(&mut self) -> Token {
        let mut token = self.next();
        loop {
            if token.kind != TokenType::Space {
                break;
            }
            token = self.next();
        }
        // Only rewind once (see once i have tests)
        self.current_token -= 1;

        token
    }

    // Get the next token
    fn next(&mut self) -> Token {
        let token = self.peek();
        self.current_token += 1;

        token
    }

    // Get the next token that isn't space
    fn next_non_space(&mut self) -> Token {
        let mut token = self.next();
        loop {
            if token.kind != TokenType::Space {
                break;
            }
            token = self.next();
        }

        token
    }

    // Panics if the expected token isn't found
    fn expect(&mut self, kind: TokenType) -> Token {
        let token = self.peek_non_space();
        if token.kind != kind {
            panic!("Unexpected token: {:?}", token);
        }

        self.next_non_space()
    }

    // All the different "states" the parser can be in: either in a block, in text
    // or in a tag
    fn parse_next(&mut self) -> Option<Box<Node>> {
        loop {
            match self.peek().kind {
                TokenType::TagStart => (),  // TODO
                TokenType::VariableStart => return self.parse_variable_block(),
                TokenType::Text => return self.parse_text(),
                _ => break
            };
        }

        None
    }

    // Parse some html text
    fn parse_text(&mut self) -> Option<Box<Node>> {
        let token = self.next();
        Some(Box::new(Node::new(token.position, SpecificNode::Text(token.value))))
    }

    // Parse the content of a  {{ }} block
    fn parse_variable_block(&mut self) -> Option<Box<Node>> {
        let token = self.expect(TokenType::VariableStart);
        let contained = self.parse_whole_expression(None, TokenType::VariableEnd);
        let node = Node::new(token.position, SpecificNode::VariableBlock(contained.unwrap()));
        self.expect(TokenType::VariableEnd);

        Some(Box::new(node))
    }

    // Parse a block/tag until we get to the terminator
    // Also handles all the precedence
    fn parse_whole_expression(&mut self, stack: Option<Node>, terminator: TokenType) -> Option<Box<Node>> {
        let token = self.peek_non_space();

        let mut node_stack = stack.unwrap_or(Node::new(token.position, SpecificNode::List(vec![])));
        let next = self.parse_single_expression(&terminator).unwrap();
        node_stack.push(next);

        loop {
            let token = self.peek_non_space();
            if token.kind == terminator {
                if node_stack.len() == 0 {
                    panic!("Unexpected terminator");
                }
                return Some(node_stack.pop());
            }

            match token.kind {
                TokenType::Add | TokenType::Substract => {
                    // consume it
                    self.next_non_space();
                    if node_stack.len() == 0 {
                        continue;
                    }

                    let rhs = self.parse_whole_expression(Some(node_stack.clone()), terminator.clone()).unwrap();

                    // Now for + - we need to know if the next token has a higher
                    // precedence (ie * or /)
                    let next_token = self.peek_non_space();
                    if next_token.precedence() > token.precedence() {
                        node_stack.push(rhs);
                        return self.parse_whole_expression(Some(node_stack.clone()), terminator.clone());
                    } else {
                        // Or the next thing has lower precedence and we just
                        // add the node to the stack
                        let lhs = node_stack.pop();
                        let node = if token.kind == TokenType::Add {
                            Node::new(lhs.position, SpecificNode::Addition{lhs: lhs, rhs: rhs})
                        } else {
                            Node::new(lhs.position, SpecificNode::Substraction{lhs: lhs, rhs: rhs})
                        };
                        node_stack.push(Box::new(node));

                    }
                },
                TokenType::Divide | TokenType::Multiply => {
                    // consume the operator
                    self.next_non_space();
                    if node_stack.len() == 0 {
                        panic!("Unexpected division or multiplication");
                    }

                    // * and / have the highest precedence so no need to check
                    // the following operators precedences
                    let rhs = self.parse_single_expression(&terminator).unwrap();
                    let lhs = node_stack.pop();
                    let node = if token.kind == TokenType::Multiply {
                        Node::new(lhs.position, SpecificNode::Multiplication{lhs: lhs, rhs: rhs})
                    } else {
                        Node::new(lhs.position, SpecificNode::Division{lhs: lhs, rhs: rhs})
                    };
                    node_stack.push(Box::new(node));
                },
                _ => panic!("Unexpected token") // TODO: not panic
            }
        }
    }

    // Parses the next non-space token as a simple expression
    // Used when parsing inside a block/tag and we want to get the next value
    fn parse_single_expression(&mut self, terminator: &TokenType) -> Option<Box<Node>> {
        let token = self.peek_non_space();

        if token.kind == *terminator {
            panic!("Unexpected terminator");
        }

        match token.kind {
            TokenType::Identifier => return self.parse_identifier(),
            TokenType::Float | TokenType::Int | TokenType::Bool => return self.parse_literal(),
            TokenType::Add | TokenType::Substract => {
                panic!("wololo");
            }
            _ => panic!("unexpected")
        }

        None
    }

    // Parse an identifier (variable name)
    fn parse_identifier(&mut self) -> Option<Box<Node>> {
        let ident = self.next_non_space();
        Some(Box::new(Node::new(ident.position, SpecificNode::Identifier(ident.value))))
    }

    // Parse a bool/int/float
    fn parse_literal(&mut self) -> Option<Box<Node>> {
        let literal = self.next_non_space();

        match literal.kind {
            TokenType::Int => {
                let value = literal.value.parse::<i32>().unwrap();
                return Some(Box::new(Node::new(literal.position, SpecificNode::Int(value))));
            },
            TokenType::Float => {
                let value = literal.value.parse::<f32>().unwrap();
                return Some(Box::new(Node::new(literal.position, SpecificNode::Float(value))));
            },
            TokenType::Bool => {
                let value = if literal.value == "false" { false } else { true };
                return Some(Box::new(Node::new(literal.position, SpecificNode::Bool(value))));
            },
            _ => panic!("unexpected type when parsing literal")
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Parser};
    use nodes::{Node, SpecificNode};

    fn compared_expected(expected: Vec<SpecificNode>, got: Vec<Box<Node>>) {
        if expected.len() != got.len() {
            assert!(false);
        }

        for (i, node) in got.iter().enumerate() {
            let expected_node = expected.get(i).unwrap().clone();
            println!("Expected: {:#?}", expected_node);
            println!("Got: {:#?}", node.specific);
            assert_eq!(expected_node, node.specific);
        }
    }

    fn test_parser(input: &str, expected: Vec<SpecificNode>) {
        let mut parser = Parser::new("dummy", input);
        parser.parse();
        let children = parser.root.get_children();
        compared_expected(expected, children)
    }

    #[test]
    fn test_empty() {
        let mut parser = Parser::new("empty", "");
        parser.parse();
        assert_eq!(0, parser.root.len());
    }

    #[test]
    fn test_plain_string() {
        test_parser(
            "Hello world",
            vec![SpecificNode::Text("Hello world".to_owned())]
        );
    }

    #[test]
    fn test_variable_block_and_text() {
        test_parser(
            "{{ greeting }} 世界",
            vec![
                SpecificNode::VariableBlock(
                    Box::new(Node::new(3, SpecificNode::Identifier("greeting".to_owned())))
                ),
                SpecificNode::Text(" 世界".to_owned()),
            ]
        );
    }

    #[test]
    fn test_basic_math() {
        test_parser(
            "{{1+3.14}}{{1-42}}{{1*42}}{{1/42}}{{test+1}}",
            vec![
                SpecificNode::VariableBlock(
                    Box::new(Node::new(2, SpecificNode::Addition {
                        lhs: Box::new(Node::new(2, SpecificNode::Int(1))),
                        rhs: Box::new(Node::new(4, SpecificNode::Float(3.14))),
                    }))
                ),
                SpecificNode::VariableBlock(
                    Box::new(Node::new(12, SpecificNode::Substraction {
                        lhs: Box::new(Node::new(12, SpecificNode::Int(1))),
                        rhs: Box::new(Node::new(14, SpecificNode::Int(42))),
                    }))
                ),
                SpecificNode::VariableBlock(
                    Box::new(Node::new(20, SpecificNode::Multiplication {
                        lhs: Box::new(Node::new(20, SpecificNode::Int(1))),
                        rhs: Box::new(Node::new(22, SpecificNode::Int(42))),
                    }))
                ),
                SpecificNode::VariableBlock(
                    Box::new(Node::new(28, SpecificNode::Division {
                        lhs: Box::new(Node::new(28, SpecificNode::Int(1))),
                        rhs: Box::new(Node::new(30, SpecificNode::Int(42))),
                    }))
                ),
                SpecificNode::VariableBlock(
                    Box::new(Node::new(36, SpecificNode::Addition {
                        lhs: Box::new(Node::new(36, SpecificNode::Identifier("test".to_owned()))),
                        rhs: Box::new(Node::new(41, SpecificNode::Int(1))),
                    }))
                ),
            ]
        );
    }

    #[test]
    fn test_math_precedence_simple() {
        test_parser(
            "{{ 1 / 2 + 1 }}",
            vec![
                SpecificNode::VariableBlock(
                    Box::new(Node::new(3, SpecificNode::Addition {
                        lhs: Box::new(Node::new(3, SpecificNode::Division {
                            lhs: Box::new(Node::new(3, SpecificNode::Int(1))),
                            rhs: Box::new(Node::new(7, SpecificNode::Int(2))),
                        })),
                        rhs: Box::new(Node::new(11, SpecificNode::Int(1)))
                    }))
                ),
            ]
        );
    }

    #[test]
    fn test_math_precedence_complex() {
        test_parser(
            "{{ 1 / 2 + 3 * 2 + 42 }}", // should be  ((1/2) + (3*2)) + 42
            vec![
                SpecificNode::VariableBlock(
                    Box::new(Node::new(3, SpecificNode::Addition {
                        lhs: Box::new(Node::new(3, SpecificNode::Division {
                            lhs: Box::new(Node::new(3, SpecificNode::Int(1))),
                            rhs: Box::new(Node::new(7, SpecificNode::Int(2))),
                        })),
                        rhs: Box::new(Node::new(11, SpecificNode::Addition {
                            lhs: Box::new(Node::new(11, SpecificNode::Multiplication {
                                lhs: Box::new(Node::new(11, SpecificNode::Int(3))),
                                rhs: Box::new(Node::new(15, SpecificNode::Int(2)))
                            })),
                            rhs: Box::new(Node::new(19, SpecificNode::Int(42)))
                        }))
                    }))
                ),
            ]
        );
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;

use std::error;
use std::fmt;
use std::io;
use std::str;

use self::byteorder::{ByteOrder, BigEndian, LittleEndian};

use super::{Catalog, Message};

/// Represents an error encountered while parsing an MO file.
#[derive(Debug)]
pub enum Error {
    /// An incorrect magic number has been encountered
    BadMagic,
    /// An invalid byte sequence for the given encoding has been encountered
    DecodingError,
    /// An unexpected EOF occured
    Eof,
    /// An I/O error occured
    Io(io::Error),
}
// Can not use use `Error::*` as per this issue:
// (https://github.com/rust-lang/rust/issues/4865)
use Error::{BadMagic, DecodingError, Eof, Io};

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic => "bad magic number",
            DecodingError => "invalid byte sequence in a string",
            Eof => "unxpected end of file",
            Io(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let self_err: &error::Error = self;
        write!(fmt, "{}", self_err.description())
    }
}

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

/// According to the given magic number of a MO file,
/// returns the function which reads a `u32` in the relevant endianness.
fn get_read_u32_fn(magic: &[u8]) -> Option<fn(&[u8]) -> u32> {
    if magic == [0xde, 0x12, 0x04, 0x95] {
        Some(LittleEndian::read_u32)
    } else if magic == [0x95, 0x04, 0x12, 0xde] {
        Some(BigEndian::read_u32)
    } else {
        None
    }
}

fn from_utf8(bytes: &[u8]) -> Result<&str, Error> {
    str::from_utf8(bytes).map_err(|_| DecodingError)
}

pub fn parse_catalog<R: io::Read>(mut file: R) -> Result<Catalog, Error> {
    let mut contents = vec![];
    let n = try!(file.read_to_end(&mut contents));
    if n < 28 {
        return Err(Eof);
    }

    let read_u32 = match get_read_u32_fn(&contents[0..4]) {
        Some(f) => f,
        None => return Err(BadMagic),
    };

    // ignore hashing tables (bytes at 20..28)
    let num_strings = read_u32(&contents[8..12]) as usize;
    let mut off_otable = read_u32(&contents[12..16]) as usize;
    let mut off_ttable = read_u32(&contents[16..20]) as usize;
    if n < off_otable || n < off_ttable {
        return Err(Eof);
    }

    let mut catalog = Catalog::new();
    for _ in 0..num_strings {
        let id;
        let context;
        let translated: Vec<&str>;
        // Parse the original string
        {
            if n < off_otable + 8 {
                return Err(Eof);
            }
            let len = read_u32(&contents[off_otable..off_otable + 4]) as usize;
            let off = read_u32(&contents[off_otable + 4..off_otable + 8]) as usize;
            // +1 compensates for the ending NUL byte which is not included in length
            if n < off + len + 1 {
                return Err(Eof);
            }
            let mut original = &contents[off..off + len + 1];
            // check for context
            context = match original.iter().position(|x| *x == 4) {
                Some(idx) => {
                    let ctx = &original[..idx];
                    original = &original[idx + 1..];
                    Some(try!(from_utf8(ctx)))
                }
                None => None,
            };
            // extract msg_id singular, ignoring the plural
            id = match original.iter().position(|x| *x == 0).map(|i| &original[..i]) {
                Some(b) => try!(from_utf8(b)),
                None => return Err(Eof),
            };
        }

        // Parse the translation strings
        {
            if n < off_ttable + 8 {
                return Err(Eof);
            }
            let len = read_u32(&contents[off_ttable..off_ttable + 4]) as usize;
            let off = read_u32(&contents[off_ttable + 4..off_ttable + 8]) as usize;
            // +1 compensates for the ending NUL byte which is not included in length
            if n < off + len + 1 {
                return Err(Eof);
            }
            let decode_results = (&contents[off..off + len + 1])
                                     .split(|x| *x == 0)
                                     .map(from_utf8)
                                     .collect::<Vec<_>>();
            if decode_results.iter().any(|r| r.is_err()) {
                return Err(DecodingError);
            }
            translated = decode_results.into_iter().map(|r| r.unwrap()).collect();
        }

        catalog.insert(Message::new(id, context, translated));

        off_otable += 8;
        off_ttable += 8;
    }

    Ok(catalog)
}

#[test]
fn test_get_read_u32_fn() {
    use std::mem;

    assert!(get_read_u32_fn(&[]).is_none());
    assert!(get_read_u32_fn(&[0xde, 0x12, 0x04, 0x95, 0x00]).is_none());

    {
        let le_ptr: *const ();
        let ret_ptr;
        unsafe {
            le_ptr = mem::transmute(LittleEndian::read_u32);
            ret_ptr = mem::transmute(get_read_u32_fn(&[0xde, 0x12, 0x04, 0x95]).unwrap());
        }
        assert_eq!(le_ptr, ret_ptr);
    }

    {
        let be_ptr: *const ();
        let ret_ptr;
        unsafe {
            be_ptr = mem::transmute(BigEndian::read_u32);
            ret_ptr = mem::transmute(get_read_u32_fn(&[0x95, 0x04, 0x12, 0xde]).unwrap());
        }
        assert_eq!(be_ptr, ret_ptr);
    }
}

#[test]
fn test_parse_catalog() {
    macro_rules! assert_variant {
        ($value:expr, $variant:path) => {
            match $value {
                $variant => (),
                _ => panic!("Expected {:?}, got {:?}", $variant, $value),
            }
        };
    }

    let fluff = [0; 24]; // zeros to pad our magic test cases to satisfy the length requirements

    {
        let mut reader = vec![1u8, 2, 3];
        reader.extend(fluff.iter().cloned());
        let err = parse_catalog(&reader[..]).unwrap_err();
        assert_variant!(err, Eof);
    }

    {
        let mut reader = vec![1u8, 2, 3, 4];
        reader.extend(fluff.iter().cloned());
        let err = parse_catalog(&reader[..]).unwrap_err();
        assert_variant!(err, BadMagic);
    }

    {
        let mut reader = vec![0x95, 0x04, 0x12, 0xde];
        reader.extend(fluff.iter().cloned());
        assert!(parse_catalog(&reader[..]).is_ok());
    }

    {
        let mut reader = vec![0xde, 0x12, 0x04, 0x95];
        reader.extend(fluff.iter().cloned());
        assert!(parse_catalog(&reader[..]).is_ok());
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/1.mo");
        let catalog = parse_catalog(reader).unwrap();
        assert_eq!(catalog.strings.len(), 1);
        assert_eq!(catalog.strings["this is context\x04Text"],
                   Message::new("Text", Some("this is context"), vec!["Tekstas", "Tekstai"]));
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/2.mo");
        let catalog = parse_catalog(reader).unwrap();
        assert_eq!(catalog.strings.len(), 2);
        assert_eq!(catalog.strings["Image"],
                   Message::new("Image", None, vec!["Nuotrauka", "Nuotraukos"]));
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/invalid_utf8.mo");
        let err = parse_catalog(reader).unwrap_err();
        assert_variant!(err, DecodingError);
    }
}
<commit_msg>Fix the splitting of translation strings<commit_after>extern crate byteorder;

use std::error;
use std::fmt;
use std::io;
use std::str;

use self::byteorder::{ByteOrder, BigEndian, LittleEndian};

use super::{Catalog, Message};

/// Represents an error encountered while parsing an MO file.
#[derive(Debug)]
pub enum Error {
    /// An incorrect magic number has been encountered
    BadMagic,
    /// An invalid byte sequence for the given encoding has been encountered
    DecodingError,
    /// An unexpected EOF occured
    Eof,
    /// An I/O error occured
    Io(io::Error),
}
// Can not use use `Error::*` as per this issue:
// (https://github.com/rust-lang/rust/issues/4865)
use Error::{BadMagic, DecodingError, Eof, Io};

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic => "bad magic number",
            DecodingError => "invalid byte sequence in a string",
            Eof => "unxpected end of file",
            Io(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let self_err: &error::Error = self;
        write!(fmt, "{}", self_err.description())
    }
}

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

/// According to the given magic number of a MO file,
/// returns the function which reads a `u32` in the relevant endianness.
fn get_read_u32_fn(magic: &[u8]) -> Option<fn(&[u8]) -> u32> {
    if magic == [0xde, 0x12, 0x04, 0x95] {
        Some(LittleEndian::read_u32)
    } else if magic == [0x95, 0x04, 0x12, 0xde] {
        Some(BigEndian::read_u32)
    } else {
        None
    }
}

fn from_utf8(bytes: &[u8]) -> Result<&str, Error> {
    str::from_utf8(bytes).map_err(|_| DecodingError)
}

pub fn parse_catalog<R: io::Read>(mut file: R) -> Result<Catalog, Error> {
    let mut contents = vec![];
    let n = try!(file.read_to_end(&mut contents));
    if n < 28 {
        return Err(Eof);
    }

    let read_u32 = match get_read_u32_fn(&contents[0..4]) {
        Some(f) => f,
        None => return Err(BadMagic),
    };

    // ignore hashing tables (bytes at 20..28)
    let num_strings = read_u32(&contents[8..12]) as usize;
    let mut off_otable = read_u32(&contents[12..16]) as usize;
    let mut off_ttable = read_u32(&contents[16..20]) as usize;
    if n < off_otable || n < off_ttable {
        return Err(Eof);
    }

    let mut catalog = Catalog::new();
    for _ in 0..num_strings {
        let id;
        let context;
        let translated: Vec<&str>;
        // Parse the original string
        {
            if n < off_otable + 8 {
                return Err(Eof);
            }
            let len = read_u32(&contents[off_otable..off_otable + 4]) as usize;
            let off = read_u32(&contents[off_otable + 4..off_otable + 8]) as usize;
            // +1 compensates for the ending NUL byte which is not included in length
            if n < off + len + 1 {
                return Err(Eof);
            }
            let mut original = &contents[off..off + len + 1];
            // check for context
            context = match original.iter().position(|x| *x == 4) {
                Some(idx) => {
                    let ctx = &original[..idx];
                    original = &original[idx + 1..];
                    Some(try!(from_utf8(ctx)))
                }
                None => None,
            };
            // extract msg_id singular, ignoring the plural
            id = match original.iter().position(|x| *x == 0).map(|i| &original[..i]) {
                Some(b) => try!(from_utf8(b)),
                None => return Err(Eof),
            };
        }

        // Parse the translation strings
        {
            if n < off_ttable + 8 {
                return Err(Eof);
            }
            let len = read_u32(&contents[off_ttable..off_ttable + 4]) as usize;
            let off = read_u32(&contents[off_ttable + 4..off_ttable + 8]) as usize;
            // +1 compensates for the ending NUL byte which is not included in length
            if n < off + len + 1 {
                return Err(Eof);
            }
            let decode_results = (&contents[off..off + len])
                                     .split(|x| *x == 0)
                                     .map(from_utf8)
                                     .collect::<Vec<_>>();
            if decode_results.iter().any(|r| r.is_err()) {
                return Err(DecodingError);
            }
            translated = decode_results.into_iter().map(|r| r.unwrap()).collect();
        }

        catalog.insert(Message::new(id, context, translated));

        off_otable += 8;
        off_ttable += 8;
    }

    Ok(catalog)
}

#[test]
fn test_get_read_u32_fn() {
    use std::mem;

    assert!(get_read_u32_fn(&[]).is_none());
    assert!(get_read_u32_fn(&[0xde, 0x12, 0x04, 0x95, 0x00]).is_none());

    {
        let le_ptr: *const ();
        let ret_ptr;
        unsafe {
            le_ptr = mem::transmute(LittleEndian::read_u32);
            ret_ptr = mem::transmute(get_read_u32_fn(&[0xde, 0x12, 0x04, 0x95]).unwrap());
        }
        assert_eq!(le_ptr, ret_ptr);
    }

    {
        let be_ptr: *const ();
        let ret_ptr;
        unsafe {
            be_ptr = mem::transmute(BigEndian::read_u32);
            ret_ptr = mem::transmute(get_read_u32_fn(&[0x95, 0x04, 0x12, 0xde]).unwrap());
        }
        assert_eq!(be_ptr, ret_ptr);
    }
}

#[test]
fn test_parse_catalog() {
    macro_rules! assert_variant {
        ($value:expr, $variant:path) => {
            match $value {
                $variant => (),
                _ => panic!("Expected {:?}, got {:?}", $variant, $value),
            }
        };
    }

    let fluff = [0; 24]; // zeros to pad our magic test cases to satisfy the length requirements

    {
        let mut reader = vec![1u8, 2, 3];
        reader.extend(fluff.iter().cloned());
        let err = parse_catalog(&reader[..]).unwrap_err();
        assert_variant!(err, Eof);
    }

    {
        let mut reader = vec![1u8, 2, 3, 4];
        reader.extend(fluff.iter().cloned());
        let err = parse_catalog(&reader[..]).unwrap_err();
        assert_variant!(err, BadMagic);
    }

    {
        let mut reader = vec![0x95, 0x04, 0x12, 0xde];
        reader.extend(fluff.iter().cloned());
        assert!(parse_catalog(&reader[..]).is_ok());
    }

    {
        let mut reader = vec![0xde, 0x12, 0x04, 0x95];
        reader.extend(fluff.iter().cloned());
        assert!(parse_catalog(&reader[..]).is_ok());
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/1.mo");
        let catalog = parse_catalog(reader).unwrap();
        assert_eq!(catalog.strings.len(), 1);
        assert_eq!(catalog.strings["this is context\x04Text"],
                   Message::new("Text", Some("this is context"), vec!["Tekstas", "Tekstai"]));
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/2.mo");
        let catalog = parse_catalog(reader).unwrap();
        assert_eq!(catalog.strings.len(), 2);
        assert_eq!(catalog.strings["Image"],
                   Message::new("Image", None, vec!["Nuotrauka", "Nuotraukos"]));
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/invalid_utf8.mo");
        let err = parse_catalog(reader).unwrap_err();
        assert_variant!(err, DecodingError);
    }
}
<|endoftext|>


<commit_before>use std::str;
use nom;
use nom::IResult;

pub fn try_parse(i: &[u8]) -> Result<super::Version, ()> {
    match version(i) {
        IResult::Done(_, d) => Ok(d),
        _ => Err(()),
    }
}

fn number(i: &[u8]) -> IResult<&[u8], u32> {
    map_res!(i,
             nom::digit,
             |d| str::FromStr::from_str(str::from_utf8(d).unwrap()))
}

named!(dot_number<&[u8], u32>, chain!(
        tag!(".") ~
        i: number, || { i }
));

named!(version<&[u8], super::Version>, chain!(
        major: number ~
        rest: opt!(complete!(chain!(
                minor: dot_number ~
                patch: opt!(complete!(dot_number)),
                || { (minor, patch.unwrap_or(0)) }
        ))),
        || {
            let (minor, patch) = rest.unwrap_or((0, 0));
            super::Version { major: major, minor: minor, patch: patch }
        }
));

#[cfg(test)]
mod tests {
    use super::number;
    use super::version;
    use nom::IResult::Done;
    use Version;

    #[test]
    fn parse_number() {
        let v = "10";

        assert_eq!(number(v.as_bytes()), Done(&[][..], 10));
    }

    #[test]
    fn parse_major() {
        let v1 = "10";
        let v2 = Version {
            major: 10,
            minor: 0,
            patch: 0,
        };

        assert_eq!(version(v1.as_bytes()), Done(&[][..], v2));
    }

    #[test]
    fn parse_minor() {
        let v1 = "10.11";
        let v2 = Version {
            major: 10,
            minor: 11,
            patch: 0,
        };

        assert_eq!(version(v1.as_bytes()), Done(&[][..], v2));
    }

    #[test]
    fn parse_version() {
        let v1 = "10.11.12";
        let v2 = Version {
            major: 10,
            minor: 11,
            patch: 12,
        };

        assert_eq!(version(v1.as_bytes()), Done(&[][..], v2));
    }
}
<commit_msg>Add test for dot_number<commit_after>use std::str;
use nom;
use nom::IResult;

pub fn try_parse(i: &[u8]) -> Result<super::Version, ()> {
    match version(i) {
        IResult::Done(_, d) => Ok(d),
        _ => Err(()),
    }
}

fn number(i: &[u8]) -> IResult<&[u8], u32> {
    map_res!(i,
             nom::digit,
             |d| str::FromStr::from_str(str::from_utf8(d).unwrap()))
}

named!(dot_number<&[u8], u32>, chain!(
        tag!(".") ~
        i: number, || { i }
));

named!(version<&[u8], super::Version>, chain!(
        major: number ~
        rest: opt!(complete!(chain!(
                minor: dot_number ~
                patch: opt!(complete!(dot_number)),
                || { (minor, patch.unwrap_or(0)) }
        ))),
        || {
            let (minor, patch) = rest.unwrap_or((0, 0));
            super::Version { major: major, minor: minor, patch: patch }
        }
));

#[cfg(test)]
mod tests {
    use super::number;
    use super::dot_number;
    use super::version;
    use nom::IResult::Done;
    use Version;

    #[test]
    fn parse_number() {
        let v = "10";

        assert_eq!(number(v.as_bytes()), Done(&[][..], 10));
    }

    #[test]
    fn parse_dot_number() {
        let v = ".10";

        assert_eq!(dot_number(v.as_bytes()), Done(&[][..], 10));
    }

    #[test]
    fn parse_major() {
        let v1 = "10";
        let v2 = Version {
            major: 10,
            minor: 0,
            patch: 0,
        };

        assert_eq!(version(v1.as_bytes()), Done(&[][..], v2));
    }

    #[test]
    fn parse_minor() {
        let v1 = "10.11";
        let v2 = Version {
            major: 10,
            minor: 11,
            patch: 0,
        };

        assert_eq!(version(v1.as_bytes()), Done(&[][..], v2));
    }

    #[test]
    fn parse_version() {
        let v1 = "10.11.12";
        let v2 = Version {
            major: 10,
            minor: 11,
            patch: 12,
        };

        assert_eq!(version(v1.as_bytes()), Done(&[][..], v2));
    }
}
<|endoftext|>


<commit_before>use regex::{Captures, Regex};
use term::{self, color, Terminal};

/// Finds all matches for the given pattern on the given subject and prints out
/// the subject with matches highlighted.
pub fn print_matches(pattern: &str, subject: &str, matches_only: bool) {
    // Attempt to compile the given regex pattern.
    let regex = match Regex::new(&pattern) {
        Ok(result) => { result },
        Err(_) => {
            println!("Invalid regular expression pattern '{}'.", pattern);
            return;
        }
    };

    // Loop over each match in the subject and pretty-print the match as well as
    // any trailing, non-matching text preceding the match.
    let mut last_index = 0;
    let mut i = 0;
    for captures in regex.captures_iter(subject) {
        let positions = captures.pos(0).unwrap();

        if !matches_only {
            print!("{}", &subject[last_index .. positions.0]);
        } else {
            print!("#{}: ", i);
        }
        print_captures(captures);
        if matches_only {
            println!("");
            i += 1;
        }

        last_index = positions.1;
    }

    // Print trailing unmatched text if there is any.
    if !matches_only && last_index < subject.len() {
        print!("{}", &subject[last_index ..]);
    }
    println!("");
}

/// Prints out a capture group using color formatting.
fn print_captures(captures: Captures) {
    let mut terminal = term::stdout().unwrap();
    let color_cycle = [color::BLUE, color::GREEN, color::MAGENTA, color::YELLOW];
    let mut color_index = 1;

    // Get the string of the entire match and the relative offset in the subject.
    let string = captures.at(0).unwrap();
    let offsets = captures.pos(0).unwrap();

    // To highlight sub-matches, divide the match string into a series of
    // resizeable regions that store their range and their color. As we loop
    // over each capture, find the parent region the current capture group fits
    // into, and split the region in two, with a new region for the current
    // capture between it. Worst-case time is O(3n^2), where n is the number of
    // subgroups in the regular expression.
    let mut regions: Vec<(usize, usize, u16)> = Vec::new();
    regions.push((offsets.0, offsets.1, color::BLUE));

    for i in 1..captures.len() {
        let pos = captures.pos(i).unwrap();

        for j in 0..regions.len() {
            if pos.0 >= regions[j].0 && pos.1 <= regions[j].1 {
                // Define two new regions that overlap the old one.
                let middle = (pos.0, pos.1, color_cycle[color_index]);
                let right = (pos.1, regions[j].1, regions[j].2);
                // Shrink the old region to be the leftmost one.
                regions[j].1 = pos.0;

                // Insert the new regions.
                regions.insert(j + 1, middle);
                regions.insert(j + 2, right);

                // Choose next color next time.
                color_index = (color_index + 1) % 4;
                break;
            }
        }
    }

    // Now print out the prepared regions; this part is pretty easy, since the
    // region parser already did all the work.
    for i in 0..regions.len() {
        terminal.bg(regions[i].2).unwrap();
        print!("{}", &string[regions[i].0 - offsets.0 .. regions[i].1 - offsets.0]);
    }

    // Reset coloring to normal.
    terminal.reset().unwrap();
}
<commit_msg>Pass regex syntax error on to the user.<commit_after>use regex::{Captures, Regex};
use term::{self, color, Terminal};

/// Finds all matches for the given pattern on the given subject and prints out
/// the subject with matches highlighted.
pub fn print_matches(pattern: &str, subject: &str, matches_only: bool) {
    // Attempt to compile the given regex pattern.
    let regex = match Regex::new(&pattern) {
        Ok(result) => { result },
        Err(err) => {
            println!("Invalid regular expression pattern '{}': {}",
                     pattern, err);
            return;
        }
    };

    // Loop over each match in the subject and pretty-print the match as well as
    // any trailing, non-matching text preceding the match.
    let mut last_index = 0;
    let mut i = 0;
    for captures in regex.captures_iter(subject) {
        let positions = captures.pos(0).unwrap();

        if !matches_only {
            print!("{}", &subject[last_index .. positions.0]);
        } else {
            print!("#{}: ", i);
        }
        print_captures(captures);
        if matches_only {
            println!("");
            i += 1;
        }

        last_index = positions.1;
    }

    // Print trailing unmatched text if there is any.
    if !matches_only && last_index < subject.len() {
        print!("{}", &subject[last_index ..]);
    }
    println!("");
}

/// Prints out a capture group using color formatting.
fn print_captures(captures: Captures) {
    let mut terminal = term::stdout().unwrap();
    let color_cycle = [color::BLUE, color::GREEN, color::MAGENTA, color::YELLOW];
    let mut color_index = 1;

    // Get the string of the entire match and the relative offset in the subject.
    let string = captures.at(0).unwrap();
    let offsets = captures.pos(0).unwrap();

    // To highlight sub-matches, divide the match string into a series of
    // resizeable regions that store their range and their color. As we loop
    // over each capture, find the parent region the current capture group fits
    // into, and split the region in two, with a new region for the current
    // capture between it. Worst-case time is O(3n^2), where n is the number of
    // subgroups in the regular expression.
    let mut regions: Vec<(usize, usize, u16)> = Vec::new();
    regions.push((offsets.0, offsets.1, color::BLUE));

    for i in 1..captures.len() {
        let pos = captures.pos(i).unwrap();

        for j in 0..regions.len() {
            if pos.0 >= regions[j].0 && pos.1 <= regions[j].1 {
                // Define two new regions that overlap the old one.
                let middle = (pos.0, pos.1, color_cycle[color_index]);
                let right = (pos.1, regions[j].1, regions[j].2);
                // Shrink the old region to be the leftmost one.
                regions[j].1 = pos.0;

                // Insert the new regions.
                regions.insert(j + 1, middle);
                regions.insert(j + 2, right);

                // Choose next color next time.
                color_index = (color_index + 1) % 4;
                break;
            }
        }
    }

    // Now print out the prepared regions; this part is pretty easy, since the
    // region parser already did all the work.
    for i in 0..regions.len() {
        terminal.bg(regions[i].2).unwrap();
        print!("{}", &string[regions[i].0 - offsets.0 .. regions[i].1 - offsets.0]);
    }

    // Reset coloring to normal.
    terminal.reset().unwrap();
}
<|endoftext|>


<commit_before>//! Contains the routing logic for the website.

use std::error::Error;
use std::fs;
use std::path::Path;

use chrono::NaiveDate;
use mount::Mount;
use hbs::{self, DirectorySource, HandlebarsEngine, Template};
use iron::prelude::*;
use iron::{status, AfterMiddleware, Handler};
use persistent::{self, Read};
use router::{Router, NoRoute};
use staticfile::Static;

use blog;
use config;
use helpers;
use projects::Project;
use persistence::{self, Config, Projects};

/// The number of blog post summaries that should be displayed.
const NUM_SUMMARIES: usize = 3;

fn resume(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let data = btreemap!{
        "resume_link" => req.get::<Read<Config>>().unwrap().resume_link.to_owned(),
    };
    res.set_mut(Template::new("resume", data)).set_mut(status::Ok);
    Ok(res)
}

fn projects(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let projects = req.get::<Read<Projects>>().unwrap();
    let data = btreemap!{
        "projects" => projects,
    };
    res.set_mut(Template::new("projects", data)).set_mut(status::Ok);
    Ok(res)
}

fn blog_post(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let year = req.extensions.get::<Router>().unwrap().find("year").and_then(|y| y.parse().ok());
    let month = req.extensions.get::<Router>().unwrap().find("month").and_then(|m| m.parse().ok());
    let day = req.extensions.get::<Router>().unwrap().find("day").and_then(|d| d.parse().ok());
    let title = req.extensions.get::<Router>().unwrap().find("title");

    let date = match (year, month, day) {
        (Some(year), Some(month), Some(day)) => NaiveDate::from_ymd(year, month, day),
        _ => return Err(IronError::new(NoRoute, status::NotFound)),
    };

    let title = match title {
        Some(title) => title,
        None => return Err(IronError::new(NoRoute, status::NotFound)),
    };

    match blog::get_post(&date, title) {
        Ok(ref post) => {
            res.set_mut(Template::new("blog_post", post)).set_mut(status::Ok);
            Ok(res)
        }
        Err(err) => {
            error!("Error retrieving blog post: {}", err);
            Err(IronError::new(NoRoute, status::NotFound))
        }
    }
}

fn blog(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let connection = persistence::get_db_connection();
    let data = btreemap!{
        "posts" => blog::get_summaries(&connection).unwrap(),
    };
    res.set_mut(Template::new("blog", data)).set_mut(status::Ok);
    Ok(res)
}

fn about(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let images = Path::new("static/images/slideshow");
    let image_urls = fs::read_dir(images)
        .unwrap()
        .into_iter()
        .map(|path| path.unwrap().path().to_str().unwrap().to_owned())
        .collect::<Vec<_>>();

    let data = btreemap! {
        "image_urls" => image_urls,
    };
    res.set_mut(Template::new("about", data)).set_mut(status::Ok);
    Ok(res)
}

fn index(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let connection = persistence::get_db_connection();
    let posts = blog::get_summaries(&connection);

    let data = btreemap!{
        "posts" => posts.unwrap().into_iter().take(NUM_SUMMARIES).collect::<Vec<_>>(),
    };
    res.set_mut(Template::new("index", data)).set_mut(status::Ok);
    Ok(res)
}

/// Returns the router for the server.
fn get_router() -> Router {
    router!(
        get "/" => index,
        get "/about" => about,
        get "/blog" => blog,
        get "/blog/:year/:month/:day/:title" => blog_post,
        get "/projects" => projects,
        get "/resume" => resume,

        get "/favicon.ico" => Static::new(Path::new("static/images")),
        get "/robots.txt" => Static::new(Path::new("static")),
    )
}

fn initialize_templates(folder: &str,
                        extension: &str)
                        -> Result<HandlebarsEngine, hbs::SourceError> {
    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(DirectorySource::new(folder, extension)));
    try!(hbse.reload());

    {
        let mut reg = hbse.registry.write().unwrap();
        reg.register_helper("join", Box::new(helpers::join));
    }

    Ok(hbse)
}

fn mount(chain: Chain) -> Mount {
    let mut mount = Mount::new();
    mount.mount("/", chain);
    mount.mount("/static", Static::new(Path::new("static")));
    mount
}

/// Returns the main route handler for the website.
///
/// This handler contains a number of middleware:
///   - Routing for all page requests
///   - Routing for static files
///   - Persistence for the website configuration
///   - Rendering handlebars templates
///   - Error reporting
///   - Error handling
pub fn handler(config: config::Config, projects: Vec<Project>) -> Box<Handler> {
    let mut chain = Chain::new(get_router());

    chain.link_before(persistent::Read::<Config>::one(config));
    chain.link_before(persistent::Read::<Projects>::one(projects));

    chain.link_after(initialize_templates("./templates/", ".hbs").unwrap());
    chain.link_after(ErrorReporter);
    chain.link_after(ErrorHandler);

    let mount = mount(chain);
    Box::new(mount)
}

struct ErrorReporter;

impl AfterMiddleware for ErrorReporter {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        error!("{}", err.description());
        Err(err)
    }
}

struct ErrorHandler;

impl AfterMiddleware for ErrorHandler {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        let mut res = Response::new();

        if let Some(_) = err.error.downcast::<NoRoute>() {
            res.set_mut(Template::new("not_found", ())).set_mut(status::NotFound);
            Ok(res)
        } else {
            Err(err)
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate iron;
    extern crate iron_test;
    extern crate url;

    use std::fs::File;
    use std::io::prelude::*;

    use self::iron::{Handler, Headers};
    use self::iron_test::request;

    use persistence;

    fn create_handler() -> Box<Handler> {
        use self::url::Url;

        use config::Config;

        super::handler(Config { resume_link: Url::parse("http://google.com").unwrap() },
                       vec![])
    }

    #[test]
    fn index() {
        // Create the schema.
        //
        // FIXME: This should be handled by a server object, to avoid duplication between code and
        // tests..
        let connection = persistence::get_db_connection();
        let schema = {
            let mut schema_file = File::open("schema.sql").unwrap();
            let mut schema = String::new();
            schema_file.read_to_string(&mut schema).unwrap();
            schema
        };

        connection.execute_batch(&schema).unwrap();

        let handler = create_handler();
        let response = request::get("http://localhost:3000/", Headers::new(), &handler).unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn static_files() {
        let handler = create_handler();
        let response = request::get("http://localhost:3000/favicon.ico",
                                    Headers::new(),
                                    &handler)
            .unwrap();
        assert!(response.status.unwrap().is_success());

        let response = request::get("http://localhost:3000/robots.txt", Headers::new(), &handler)
            .unwrap();
        assert!(response.status.unwrap().is_success());
    }
}
<commit_msg>fix(routes): display 404 page on Not Found<commit_after>//! Contains the routing logic for the website.

use std::error::Error;
use std::fs;
use std::path::Path;

use chrono::NaiveDate;
use mount::Mount;
use hbs::{self, DirectorySource, HandlebarsEngine, Template};
use iron::prelude::*;
use iron::{status, AfterMiddleware, Handler};
use persistent::{self, Read};
use router::{Router, NoRoute};
use staticfile::Static;

use blog;
use config;
use helpers;
use projects::Project;
use persistence::{self, Config, Projects};

/// The number of blog post summaries that should be displayed.
const NUM_SUMMARIES: usize = 3;

fn resume(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let data = btreemap!{
        "resume_link" => req.get::<Read<Config>>().unwrap().resume_link.to_owned(),
    };
    res.set_mut(Template::new("resume", data)).set_mut(status::Ok);
    Ok(res)
}

fn projects(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let projects = req.get::<Read<Projects>>().unwrap();
    let data = btreemap!{
        "projects" => projects,
    };
    res.set_mut(Template::new("projects", data)).set_mut(status::Ok);
    Ok(res)
}

fn blog_post(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let year = req.extensions.get::<Router>().unwrap().find("year").and_then(|y| y.parse().ok());
    let month = req.extensions.get::<Router>().unwrap().find("month").and_then(|m| m.parse().ok());
    let day = req.extensions.get::<Router>().unwrap().find("day").and_then(|d| d.parse().ok());
    let title = req.extensions.get::<Router>().unwrap().find("title");

    let date = match (year, month, day) {
        (Some(year), Some(month), Some(day)) => NaiveDate::from_ymd(year, month, day),
        _ => return Err(IronError::new(NoRoute, status::NotFound)),
    };

    let title = match title {
        Some(title) => title,
        None => return Err(IronError::new(NoRoute, status::NotFound)),
    };

    match blog::get_post(&date, title) {
        Ok(ref post) => {
            res.set_mut(Template::new("blog_post", post)).set_mut(status::Ok);
            Ok(res)
        }
        Err(err) => {
            error!("Error retrieving blog post: {}", err);
            Err(IronError::new(NoRoute, status::NotFound))
        }
    }
}

fn blog(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let connection = persistence::get_db_connection();
    let data = btreemap!{
        "posts" => blog::get_summaries(&connection).unwrap(),
    };
    res.set_mut(Template::new("blog", data)).set_mut(status::Ok);
    Ok(res)
}

fn about(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let images = Path::new("static/images/slideshow");
    let image_urls = fs::read_dir(images)
        .unwrap()
        .into_iter()
        .map(|path| path.unwrap().path().to_str().unwrap().to_owned())
        .collect::<Vec<_>>();

    let data = btreemap! {
        "image_urls" => image_urls,
    };
    res.set_mut(Template::new("about", data)).set_mut(status::Ok);
    Ok(res)
}

fn index(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let connection = persistence::get_db_connection();
    let posts = blog::get_summaries(&connection);

    let data = btreemap!{
        "posts" => posts.unwrap().into_iter().take(NUM_SUMMARIES).collect::<Vec<_>>(),
    };
    res.set_mut(Template::new("index", data)).set_mut(status::Ok);
    Ok(res)
}

/// Returns the router for the server.
fn get_router() -> Router {
    router!(
        get "/" => index,
        get "/about" => about,
        get "/blog" => blog,
        get "/blog/:year/:month/:day/:title" => blog_post,
        get "/projects" => projects,
        get "/resume" => resume,

        get "/favicon.ico" => Static::new(Path::new("static/images")),
        get "/robots.txt" => Static::new(Path::new("static")),
    )
}

fn initialize_templates(folder: &str,
                        extension: &str)
                        -> Result<HandlebarsEngine, hbs::SourceError> {
    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(DirectorySource::new(folder, extension)));
    try!(hbse.reload());

    {
        let mut reg = hbse.registry.write().unwrap();
        reg.register_helper("join", Box::new(helpers::join));
    }

    Ok(hbse)
}

fn mount(chain: Chain) -> Mount {
    let mut mount = Mount::new();
    mount.mount("/", chain);
    mount.mount("/static", Static::new(Path::new("static")));
    mount
}

/// Returns the main route handler for the website.
///
/// This handler contains a number of middleware:
///   - Routing for all page requests
///   - Routing for static files
///   - Persistence for the website configuration
///   - Rendering handlebars templates
///   - Error reporting
///   - Error handling
pub fn handler(config: config::Config, projects: Vec<Project>) -> Box<Handler> {
    let mut chain = Chain::new(get_router());

    chain.link_before(persistent::Read::<Config>::one(config));
    chain.link_before(persistent::Read::<Projects>::one(projects));

    chain.link_after(ErrorHandler);
    chain.link_after(initialize_templates("./templates/", ".hbs").unwrap());
    chain.link_after(ErrorReporter);

    let mount = mount(chain);
    Box::new(mount)
}

struct ErrorReporter;

impl AfterMiddleware for ErrorReporter {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        error!("{}", err.description());
        Err(err)
    }
}

struct ErrorHandler;

impl AfterMiddleware for ErrorHandler {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        if let Some(_) = err.error.downcast::<NoRoute>() {
            Ok(Response::with((status::NotFound, Template::new("not_found", ()))))
        } else {
            Err(err)
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate iron;
    extern crate iron_test;
    extern crate url;

    use std::fs::File;
    use std::io::prelude::*;

    use self::iron::{Handler, Headers};
    use self::iron_test::{request, response};

    use persistence;

    fn create_handler() -> Box<Handler> {
        use self::url::Url;

        use config::Config;

        super::handler(Config { resume_link: Url::parse("http://google.com").unwrap() },
                       vec![])
    }

    #[test]
    fn index() {
        // Create the schema.
        //
        // FIXME: This should be handled by a server object, to avoid duplication between code and
        // tests..
        let connection = persistence::get_db_connection();
        let schema = {
            let mut schema_file = File::open("schema.sql").unwrap();
            let mut schema = String::new();
            schema_file.read_to_string(&mut schema).unwrap();
            schema
        };

        connection.execute_batch(&schema).unwrap();

        let handler = create_handler();
        let response = request::get("http://localhost:3000/", Headers::new(), &handler).unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn static_files() {
        let handler = create_handler();
        let response = request::get("http://localhost:3000/favicon.ico",
                                    Headers::new(),
                                    &handler)
            .unwrap();
        assert!(response.status.unwrap().is_success());

        let response = request::get("http://localhost:3000/robots.txt", Headers::new(), &handler)
            .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn not_found() {
        let handler = create_handler();
        let response = request::get("http://localhost:3000/this/path/does/not/exist",
                                    Headers::new(),
                                    &handler)
            .unwrap();
        let body = response::extract_body_to_string(response);
        assert!(body.contains("Page Not Found"));
    }
}
<|endoftext|>


<commit_before>//! Structs and methods for searching for tweets.
//!
//! Since there are several optional parameters for searches, egg-mode handles it with a builder
//! pattern. To begin, call `search` with your requested search term. Additional parameters can be
//! added onto the `SearchBuilder` struct that is returned. When you're ready to load the first
//! page of results, hand your tokens to `call`.
//!
//! ```rust,no_run
//! # let con_token = egg_mode::Token::new("", "");
//! # let access_token = egg_mode::Token::new("", "");
//! use egg_mode::search::{self, ResultType};
//!
//! let search = search::search("rustlang")
//!                     .result_type(ResultType::Recent)
//!                     .call(&con_token, &access_token)
//!                     .unwrap();
//!
//! for tweet in &search.response.statuses {
//!     println!("(@{}) {}", tweet.user.screen_name, tweet.text);
//! }
//! ```

use std::collections::HashMap;
use std::fmt;

use rustc_serialize::json;

use auth;
use error;
use error::Error::{InvalidResponse, MissingValue};
use links;
use tweet::Tweet;
use common::*;

///Begin setting up a tweet search with the given query.
pub fn search<'a>(query: &'a str) -> SearchBuilder<'a> {
    SearchBuilder {
        query: query,
        lang: None,
        result_type: None,
        count: None,
        until: None,
        geocode: None,
    }
}

///Represents what kind of tweets should be included in search results.
#[derive(Debug, Copy, Clone)]
pub enum ResultType {
    ///Return only the most recent tweets in the response.
    Recent,
    ///Return only the most popular tweets in the response.
    Popular,
    ///Include both popular and real-time results in the response.
    Mixed,
}

///Display impl that turns the variants into strings that can be used as search parameters.
impl fmt::Display for ResultType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ResultType::Recent => write!(f, "recent"),
            ResultType::Popular => write!(f, "popular"),
            ResultType::Mixed => write!(f, "mixed"),
        }
    }
}

///Represents a radius around a given location to return search results for.
pub enum Distance {
    ///A radius given in miles.
    Miles(u32),
    ///A radius given in kilometers.
    Kilometers(u32),
}

///Represents a tweet search query before being sent.
#[must_use = "SearchBuilder is lazy and won't do anything unless `call`ed"]
pub struct SearchBuilder<'a> {
    ///The text to search for.
    query: &'a str,
    lang: Option<&'a str>,
    result_type: Option<ResultType>,
    count: Option<u32>,
    until: Option<(u32, u32, u32)>,
    geocode: Option<(f32, f32, Distance)>
}

impl<'a> SearchBuilder<'a> {
    ///Restrict search results to those that have been machine-parsed as the given two-letter
    ///language code.
    pub fn lang(self, lang: &'a str) -> Self {
        SearchBuilder {
            lang: Some(lang),
            ..self
        }
    }

    ///Specify the type of search results to include. The default is `Recent`.
    pub fn result_type(self, result_type: ResultType) -> Self {
        SearchBuilder {
            result_type: Some(result_type),
            ..self
        }
    }

    ///Set the number of tweets to return per-page, up to a maximum of 100. The default is 15.
    pub fn count(self, count: u32) -> Self {
        SearchBuilder {
            count: Some(count),
            ..self
        }
    }

    ///Returns tweets created before the given date. Keep in mind that search is limited to the
    ///last 7 days of results, so giving a date here that's older than a week will return no
    ///results.
    pub fn until(self, year: u32, month: u32, day: u32) -> Self {
        SearchBuilder {
            until: Some((year, month, day)),
            ..self
        }
    }

    ///Restricts results to users located within the given radius of the given coordinate. This is
    ///preferably populated from location-tagged tweets, but can be filled in from the user's
    ///profile as a fallback.
    pub fn geocode(self, latitude: f32, longitude: f32, radius: Distance) -> Self {
        SearchBuilder {
            geocode: Some((latitude, longitude, radius)),
            ..self
        }
    }

    ///Finalize the search terms and return the first page of responses.
    pub fn call(self, con_token: &auth::Token, access_token: &auth::Token) -> WebResponse<SearchResult<'a>> {
        let mut params = HashMap::new();

        add_param(&mut params, "q", self.query);

        if let Some(lang) = self.lang {
            add_param(&mut params, "lang", lang);
        }

        if let Some(result_type) = self.result_type {
            add_param(&mut params, "result_type", result_type.to_string());
        }

        if let Some(count) = self.count {
            add_param(&mut params, "count", count.to_string());
        }

        if let Some((year, month, day)) = self.until {
            add_param(&mut params, "until", format!("{}-{}-{}", year, month, day));
        }

        if let Some((lat, lon, radius)) = self.geocode {
            match radius {
                Distance::Miles(r) => add_param(&mut params, "geocode", format!("{:.6},{:.6},{}mi", lat, lon, r)),
                Distance::Kilometers(r) => add_param(&mut params, "geocode", format!("{:.6},{:.6},{}km", lat, lon, r)),
            };
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, con_token, access_token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.response.params = Some(params);
        Ok(ret)
    }
}

///Represents a page of search results, along with metadata to request the next or previous page.
#[derive(Debug)]
pub struct SearchResult<'a> {
    ///The list of statuses in this page of results.
    pub statuses: Vec<Tweet>,
    ///The query used to generate this page of results. Note that changing this will not affect the
    ///`next_page` method.
    pub query: String,
    max_id: i64,
    since_id: i64,
    params: Option<ParamList<'a>>,
}

impl<'a> FromJson for SearchResult<'a> {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("SearchResult received json that wasn't an object", Some(input.to_string())));
        }

        let metadata = try!(input.find("search_metadata").ok_or(MissingValue("search_metadata")));

        Ok(SearchResult {
            statuses: try!(field(input, "statuses")),
            query: try!(field(metadata, "query")),
            max_id: try!(field(metadata, "max_id")),
            since_id: try!(field(metadata, "since_id")),
            params: None,
        })
    }
}

impl<'a> SearchResult<'a> {
    ///Load the next page of search results for the same query.
    pub fn older(&self, con_token: &auth::Token, access_token: &auth::Token) -> WebResponse<SearchResult> {
        let mut params = self.params.as_ref().cloned().unwrap_or_default();
        params.remove("since_id");

        if let Some(min_id) = self.statuses.iter().map(|t| t.id).min() {
            add_param(&mut params, "max_id", (min_id - 1).to_string());
        }
        else {
            params.remove("max_id");
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, con_token, access_token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.response.params = Some(params);
        Ok(ret)
    }

    ///Load the previous page of search results for the same query.
    pub fn newer(&self, con_token: &auth::Token, access_token: &auth::Token) -> WebResponse<SearchResult> {
        let mut params = self.params.as_ref().cloned().unwrap_or_default();
        params.remove("max_id");

        if let Some(max_id) = self.statuses.iter().map(|t| t.id).max() {
            add_param(&mut params, "since_id", max_id.to_string());
        }
        else {
            params.remove("since_id");
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, con_token, access_token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.response.params = Some(params);
        Ok(ret)
    }
}
<commit_msg>add docs blurb to search about older/newer<commit_after>//! Structs and methods for searching for tweets.
//!
//! Since there are several optional parameters for searches, egg-mode handles it with a builder
//! pattern. To begin, call `search` with your requested search term. Additional parameters can be
//! added onto the `SearchBuilder` struct that is returned. When you're ready to load the first
//! page of results, hand your tokens to `call`.
//!
//! ```rust,no_run
//! # let con_token = egg_mode::Token::new("", "");
//! # let access_token = egg_mode::Token::new("", "");
//! use egg_mode::search::{self, ResultType};
//!
//! let search = search::search("rustlang")
//!                     .result_type(ResultType::Recent)
//!                     .call(&con_token, &access_token)
//!                     .unwrap();
//!
//! for tweet in &search.response.statuses {
//!     println!("(@{}) {}", tweet.user.screen_name, tweet.text);
//! }
//! ```
//!
//! Once you have your `SearchResult`, you can navigate the search results by calling `older` and
//! `newer` to get the next and previous pages, respsectively. In addition, you can see your
//! original query in the search result struct as well, so you can categorize multiple searches by
//! their query. While this is given as a regular field, note that modifying `query` will not
//! change what is searched for when you call `older` or `newer`; the `SearchResult` keeps its
//! search arguments in a separate private field.

use std::collections::HashMap;
use std::fmt;

use rustc_serialize::json;

use auth;
use error;
use error::Error::{InvalidResponse, MissingValue};
use links;
use tweet::Tweet;
use common::*;

///Begin setting up a tweet search with the given query.
pub fn search<'a>(query: &'a str) -> SearchBuilder<'a> {
    SearchBuilder {
        query: query,
        lang: None,
        result_type: None,
        count: None,
        until: None,
        geocode: None,
    }
}

///Represents what kind of tweets should be included in search results.
#[derive(Debug, Copy, Clone)]
pub enum ResultType {
    ///Return only the most recent tweets in the response.
    Recent,
    ///Return only the most popular tweets in the response.
    Popular,
    ///Include both popular and real-time results in the response.
    Mixed,
}

///Display impl that turns the variants into strings that can be used as search parameters.
impl fmt::Display for ResultType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ResultType::Recent => write!(f, "recent"),
            ResultType::Popular => write!(f, "popular"),
            ResultType::Mixed => write!(f, "mixed"),
        }
    }
}

///Represents a radius around a given location to return search results for.
pub enum Distance {
    ///A radius given in miles.
    Miles(u32),
    ///A radius given in kilometers.
    Kilometers(u32),
}

///Represents a tweet search query before being sent.
#[must_use = "SearchBuilder is lazy and won't do anything unless `call`ed"]
pub struct SearchBuilder<'a> {
    ///The text to search for.
    query: &'a str,
    lang: Option<&'a str>,
    result_type: Option<ResultType>,
    count: Option<u32>,
    until: Option<(u32, u32, u32)>,
    geocode: Option<(f32, f32, Distance)>
}

impl<'a> SearchBuilder<'a> {
    ///Restrict search results to those that have been machine-parsed as the given two-letter
    ///language code.
    pub fn lang(self, lang: &'a str) -> Self {
        SearchBuilder {
            lang: Some(lang),
            ..self
        }
    }

    ///Specify the type of search results to include. The default is `Recent`.
    pub fn result_type(self, result_type: ResultType) -> Self {
        SearchBuilder {
            result_type: Some(result_type),
            ..self
        }
    }

    ///Set the number of tweets to return per-page, up to a maximum of 100. The default is 15.
    pub fn count(self, count: u32) -> Self {
        SearchBuilder {
            count: Some(count),
            ..self
        }
    }

    ///Returns tweets created before the given date. Keep in mind that search is limited to the
    ///last 7 days of results, so giving a date here that's older than a week will return no
    ///results.
    pub fn until(self, year: u32, month: u32, day: u32) -> Self {
        SearchBuilder {
            until: Some((year, month, day)),
            ..self
        }
    }

    ///Restricts results to users located within the given radius of the given coordinate. This is
    ///preferably populated from location-tagged tweets, but can be filled in from the user's
    ///profile as a fallback.
    pub fn geocode(self, latitude: f32, longitude: f32, radius: Distance) -> Self {
        SearchBuilder {
            geocode: Some((latitude, longitude, radius)),
            ..self
        }
    }

    ///Finalize the search terms and return the first page of responses.
    pub fn call(self, con_token: &auth::Token, access_token: &auth::Token) -> WebResponse<SearchResult<'a>> {
        let mut params = HashMap::new();

        add_param(&mut params, "q", self.query);

        if let Some(lang) = self.lang {
            add_param(&mut params, "lang", lang);
        }

        if let Some(result_type) = self.result_type {
            add_param(&mut params, "result_type", result_type.to_string());
        }

        if let Some(count) = self.count {
            add_param(&mut params, "count", count.to_string());
        }

        if let Some((year, month, day)) = self.until {
            add_param(&mut params, "until", format!("{}-{}-{}", year, month, day));
        }

        if let Some((lat, lon, radius)) = self.geocode {
            match radius {
                Distance::Miles(r) => add_param(&mut params, "geocode", format!("{:.6},{:.6},{}mi", lat, lon, r)),
                Distance::Kilometers(r) => add_param(&mut params, "geocode", format!("{:.6},{:.6},{}km", lat, lon, r)),
            };
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, con_token, access_token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.response.params = Some(params);
        Ok(ret)
    }
}

///Represents a page of search results, along with metadata to request the next or previous page.
#[derive(Debug)]
pub struct SearchResult<'a> {
    ///The list of statuses in this page of results.
    pub statuses: Vec<Tweet>,
    ///The query used to generate this page of results. Note that changing this will not affect the
    ///`next_page` method.
    pub query: String,
    max_id: i64,
    since_id: i64,
    params: Option<ParamList<'a>>,
}

impl<'a> FromJson for SearchResult<'a> {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("SearchResult received json that wasn't an object", Some(input.to_string())));
        }

        let metadata = try!(input.find("search_metadata").ok_or(MissingValue("search_metadata")));

        Ok(SearchResult {
            statuses: try!(field(input, "statuses")),
            query: try!(field(metadata, "query")),
            max_id: try!(field(metadata, "max_id")),
            since_id: try!(field(metadata, "since_id")),
            params: None,
        })
    }
}

impl<'a> SearchResult<'a> {
    ///Load the next page of search results for the same query.
    pub fn older(&self, con_token: &auth::Token, access_token: &auth::Token) -> WebResponse<SearchResult> {
        let mut params = self.params.as_ref().cloned().unwrap_or_default();
        params.remove("since_id");

        if let Some(min_id) = self.statuses.iter().map(|t| t.id).min() {
            add_param(&mut params, "max_id", (min_id - 1).to_string());
        }
        else {
            params.remove("max_id");
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, con_token, access_token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.response.params = Some(params);
        Ok(ret)
    }

    ///Load the previous page of search results for the same query.
    pub fn newer(&self, con_token: &auth::Token, access_token: &auth::Token) -> WebResponse<SearchResult> {
        let mut params = self.params.as_ref().cloned().unwrap_or_default();
        params.remove("max_id");

        if let Some(max_id) = self.statuses.iter().map(|t| t.id).max() {
            add_param(&mut params, "since_id", max_id.to_string());
        }
        else {
            params.remove("since_id");
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, con_token, access_token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.response.params = Some(params);
        Ok(ret)
    }
}
<|endoftext|>


<commit_before>use std::iter::Iterator;
use std::collections::HashSet;
use std::marker::PhantomData;
use std::hash::{Hash, Hasher};
use fnv::FnvHasher;

struct Visited<T> {
    hash_set: HashSet<u64>,
    phantom: PhantomData<T>,
}

impl<T> Visited<T> where T: Hash {
    fn new() -> Visited<T> {
        Visited {
            hash_set: HashSet::new(),
            phantom: PhantomData,
        }
    }

    fn insert(&mut self, value: &T) -> bool {
        let mut hasher = FnvHasher::default();
        value.hash(&mut hasher);
        self.hash_set.insert(hasher.finish())
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        *self == *state
    }
}

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: Self::State, goal: G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(&start) {
            return Some(vec![]);
        }

        let mut path = Vec::new();
        let mut visited = Visited::new();
        let mut frontier = vec![self.expand(&start)];

        loop {
            let result = match frontier.last_mut() {
                None => {
                    return None
                },
                Some(&mut ref mut iter) => {
                    match iter.next() {
                        None => {
                            path.pop();
                            None
                        },
                        Some((action, state)) => {
                            if !visited.insert(&state) {
                                continue;
                            }
                            path.push(action);
                            if goal.is_goal(&state) {
                                return Some(path);
                            }
                            Some(self.expand(&state))
                        }
                    }
                }
            };
            match result {
                None => {
                    frontier.pop();
                }
                Some(iter) => {
                    frontier.push(iter);
                }
            };
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::SearchSpace;

    #[test]
    pub fn test_dfs() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Dir::Left, 1), (Dir::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Dir::Left, 3), (Dir::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Dir::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(0, 0).unwrap(), vec![]);
        assert_eq!(ts.dfs(0, 1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(0, 2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(0, 3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(0, 4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(2, 2).unwrap(), vec![]);
        assert!(ts.dfs(2, 0).is_none());
        assert!(ts.dfs(5, 0).is_none());
    }
}
<commit_msg>Avoid hash collision but require Clone + Eq for State<commit_after>use std::iter::Iterator;
use std::collections::HashSet;
use std::hash::Hash;

struct Visited<T> {
    hash_set: HashSet<T>
}

impl<T> Visited<T> where T: Hash + Clone + Eq {
    fn new() -> Visited<T> {
        Visited {
            hash_set: HashSet::new()
        }
    }

    fn insert(&mut self, value: &T) -> bool {
        self.hash_set.insert(value.clone())
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        *self == *state
    }
}

pub trait SearchSpace {
    type State: Hash + Clone + Eq;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: Self::State, goal: G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(&start) {
            return Some(vec![]);
        }

        let mut path = Vec::new();
        let mut visited = Visited::new();
        let mut frontier = vec![self.expand(&start)];

        loop {
            let result = match frontier.last_mut() {
                None => {
                    return None
                },
                Some(&mut ref mut iter) => {
                    match iter.next() {
                        None => {
                            path.pop();
                            None
                        },
                        Some((action, state)) => {
                            if !visited.insert(&state) {
                                continue;
                            }
                            path.push(action);
                            if goal.is_goal(&state) {
                                return Some(path);
                            }
                            Some(self.expand(&state))
                        }
                    }
                }
            };
            match result {
                None => {
                    frontier.pop();
                }
                Some(iter) => {
                    frontier.push(iter);
                }
            };
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::SearchSpace;

    #[test]
    pub fn test_dfs() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Dir::Left, 1), (Dir::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Dir::Left, 3), (Dir::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Dir::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(0, 0).unwrap(), vec![]);
        assert_eq!(ts.dfs(0, 1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(0, 2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(0, 3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(0, 4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(2, 2).unwrap(), vec![]);
        assert!(ts.dfs(2, 0).is_none());
        assert!(ts.dfs(5, 0).is_none());
    }
}
<|endoftext|>