<commit_before>#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    /*
     * The book doesn't have tokens for comments and
     * whitespaces. Introducing them the scanner can
     * deal with them uniformly and in a more
     * functional way.
     */
    Comment,
    Whitespace,
}

#[derive(Debug)]
pub struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn substring(&self, start: usize, end: usize) -> String {
        self.source.chars().skip(start).take(end - start).collect()
    }

    fn peek(&self) -> char() {
        if self.is_at_end() {
            '\0'
        } else {
            self.char_at(self.current)
        }
    }

    fn peek_next(&self) -> char() {
        if self.current + 1 >= self.source.len() {
            '\0'
        } else {
            self.char_at(self.current + 1)
        }
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.char_at(self.current - 1)
    }

    fn is_match(&mut self, expected: char) -> bool {
        if self.is_at_end() {
            return false;
        }
        if self.char_at(self.current) != expected {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.substring(self.start, self.current),
            line: self.line,
        }
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.line;
        while self.peek() != '"' && !self.is_at_end() {
            if self.peek() == '\n' {
                self.line += 1
            }
            self.advance();
        }
        if self.is_at_end() {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        self.advance();
        Ok(Token::StringLiteral(self.substring(self.start + 1, self.current - 1)))
    }

    fn number(&mut self) -> Token {
        while is_digit(self.peek()) {
            self.advance();
        }
        if self.peek() == '.' && is_digit(self.peek_next()) {
            self.advance(); // Consume the .
            while is_digit(self.peek()) {
                self.advance();
            }
        }
        let literal = self.substring(self.start, self.current);
        let value = literal.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while is_alphanumeric(self.peek()) {
            self.advance();
        }
        // TODO: take a ref in the first place
        match self.substring(self.start, self.current).as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.is_match('/') {
                    // Comments go on till the end of the line
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            ' ' => Token::Whitespace,
            '\r' => Token::Whitespace,
            '\t' => Token::Whitespace,
            '\n' => {
                self.line += 1;
                Token::Whitespace
            }
            '"' => try!(self.string()),
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, pos {}",
                                   c,
                                   self.line,
                                   self.current - 1));
            }
        };
        Ok(self.add_context(token))
    }
}

pub fn scan(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            _ => tokens.push(token_with_context),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

#[cfg(test)]
mod tests {
    use scanner::*;

    #[test]
    fn single_token() {
        let tokens = scan(&"+".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let tokens = scan(&"1+2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn expression_with_whitespaces() {
        let tokens = scan(&"1 + 2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn assignement_with_comment() {
        let tokens = scan(&"var a = 1.0; // A comment".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Eof);
    }
}<commit_msg>Test multiline text<commit_after>#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    /*
     * The book doesn't have tokens for comments and
     * whitespaces. Introducing them the scanner can
     * deal with them uniformly and in a more
     * functional way.
     */
    Comment,
    Whitespace,
}

#[derive(Debug)]
pub struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn substring(&self, start: usize, end: usize) -> String {
        self.source.chars().skip(start).take(end - start).collect()
    }

    fn peek(&self) -> char() {
        if self.is_at_end() {
            '\0'
        } else {
            self.char_at(self.current)
        }
    }

    fn peek_next(&self) -> char() {
        if self.current + 1 >= self.source.len() {
            '\0'
        } else {
            self.char_at(self.current + 1)
        }
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.char_at(self.current - 1)
    }

    fn is_match(&mut self, expected: char) -> bool {
        if self.is_at_end() {
            return false;
        }
        if self.char_at(self.current) != expected {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.substring(self.start, self.current),
            line: self.line + 1, // Converts from 0-indexed to 1-indexed
        }
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.line;
        while self.peek() != '"' && !self.is_at_end() {
            if self.peek() == '\n' {
                self.line += 1
            }
            self.advance();
        }
        if self.is_at_end() {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        self.advance();
        Ok(Token::StringLiteral(self.substring(self.start + 1, self.current - 1)))
    }

    fn number(&mut self) -> Token {
        while is_digit(self.peek()) {
            self.advance();
        }
        if self.peek() == '.' && is_digit(self.peek_next()) {
            self.advance(); // Consume the .
            while is_digit(self.peek()) {
                self.advance();
            }
        }
        let literal = self.substring(self.start, self.current);
        let value = literal.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while is_alphanumeric(self.peek()) {
            self.advance();
        }
        // TODO: take a ref in the first place
        match self.substring(self.start, self.current).as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.is_match('/') {
                    // Comments go on till the end of the line
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            ' ' => Token::Whitespace,
            '\r' => Token::Whitespace,
            '\t' => Token::Whitespace,
            '\n' => {
                self.line += 1;
                Token::Whitespace
            }
            '"' => try!(self.string()),
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, pos {}",
                                   c,
                                   self.line,
                                   self.current - 1));
            }
        };
        Ok(self.add_context(token))
    }
}

pub fn scan(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            _ => tokens.push(token_with_context),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

#[cfg(test)]
mod tests {
    use scanner::*;

    #[test]
    fn single_token() {
        let tokens = scan(&"+".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let tokens = scan(&"1+2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn expression_with_whitespaces() {
        let tokens = scan(&"1 + 2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn assignement_with_comment() {
        let tokens = scan(&"var a = 1.0; // A comment".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Eof);
    }

    #[test]
    fn multiline_statements(){
        let tokens = scan(&r#"var a = 1.0;
                              var b = "Hello";"#.into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[10].token, Token::Eof);
        assert_eq!(tokens[1].line, 1);
        assert_eq!(tokens[9].line, 2);
    }
}<|endoftext|>


<commit_before>
extern crate slp_shared;
extern crate slp_lang_htk;
extern crate slp_lang_hst;
extern crate slp_lang_hir;
extern crate slp_lang_cil;
extern crate slp_lang_cst;
extern crate slp_transform_preprocess;
extern crate slp_transform_lexer;
extern crate slp_transform_htk_to_hst;
extern crate slp_transform_hst_to_hir;
extern crate slp_transform_hir_to_cil;
extern crate slp_transform_cil_to_cst;
extern crate slp_transform_cst_printer;
extern crate slp_sequence_hlsl_to_cl;

#[cfg(test)]
pub mod tests;
<commit_msg>Re-export crates + add comments to meta slp crate<commit_after>
//! slp library for parsing shader languages
//!
//! This is a meta-crate that links all the individual crates together. The
//! crates are re-exported into modules.

extern crate slp_shared;
extern crate slp_lang_htk;
extern crate slp_lang_hst;
extern crate slp_lang_hir;
extern crate slp_lang_cil;
extern crate slp_lang_cst;
extern crate slp_transform_preprocess;
extern crate slp_transform_lexer;
extern crate slp_transform_htk_to_hst;
extern crate slp_transform_hst_to_hir;
extern crate slp_transform_hir_to_cil;
extern crate slp_transform_cil_to_cst;
extern crate slp_transform_cst_printer;
extern crate slp_sequence_hlsl_to_cl;

pub use slp_shared as shared;

/// The lang crates are intermediate language trees
pub mod lang {
    pub use slp_lang_htk as htk;
    pub use slp_lang_hst as hst;
    pub use slp_lang_hir as hir;
    pub use slp_lang_cil as cil;
    pub use slp_lang_cst as cst;
}

/// The transform crates are functions to transform from one language to another
pub mod transform {
    pub use slp_transform_preprocess as preprocess;
    pub use slp_transform_lexer as lexer;
    pub use slp_transform_hst_to_hir as hst_to_hir;
    pub use slp_transform_hir_to_cil as hir_to_cil;
    pub use slp_transform_cil_to_cst as cil_to_cst;
    pub use slp_transform_cst_printer as cst_printer;
}

/// The sequence crates are for combining sequences of transforms
pub mod sequence {
    pub use slp_sequence_hlsl_to_cl as hlsl_to_cl;
}

#[cfg(test)]
pub mod tests;
<|endoftext|>


<commit_before>//! What all the kool kidz are playing these days
//!
//! ## Features
//!
//! Easily navigable menus!
//!
//! ![Start menu screenshot](https://cdn.rawgit.com/nabijaczleweli/poke-a-mango/master/assets/screenshot_start_menu.png)
//!
//! Extensive leaderboard system!
//!
//! ![Leaderboard screenshot](https://cdn.rawgit.com/nabijaczleweli/poke-a-mango/master/assets/screenshot_leaderboard.png)
//!
//! A wide variety of difficulty modes!
//!
//! ![Difficulty selection
//! screenshot](https://cdn.rawgit.com/nabijaczleweli/poke-a-mango/master/assets/screenshot_difficulty_selection.png)
//!
//! Exciting gameplay that will have you glued to the screen for hours on end!
//!
//! ![Gameplay screenshot](https://cdn.rawgit.com/nabijaczleweli/poke-a-mango/master/assets/screenshot_gameplay.png)
//!
//! # Library doc
//!
//! This library is used by `poke-a-mango` itself for all its function and is therefore contains all necessary functions.
//!
//! ## Data flow
//!
//! #### With `conrod`
//!
//! ```plaintext
//! Options::parse()
//! |> create_window()
//! |> Widgets::new()
//! ```
//!
//! Then, each update event:
//!
//! ```plaintext
//! |> Widgets::update()
//! |> [check for states requiring usercode handling and act accordingly]
//! ```
//!
//! #### With a different UI
//!
//! ```plaintext
//! Options::parse()
//! |> state::*()
//! |> [check for states requiring usercode handling and act accordingly]
//! ```
//!
//! ### Prose explanation
//!
//! First, get an `Options` instance, be it via a struct-literal or `Options::parse()`;
//! or don't and just create the individual arguments manually.
//!
//! Then, use `create_window()` and a new `conrod::Ui`, follow up with creating a new `Widgets` instance.
//!
//! After that, among normally displaying and processing events with `conrod::Ui` call `Widgets::update()` each update event.
//! That sometimes produces `GameState`s that need action from you and you need to handle them manually.
//!
//! ### Example
//!
//! This is a semi-complete example, as it lacks event handling, rendering and UI setup which have been omitted for brevity.
//!
//! ```no_run
//! # extern crate conrod;
//! # extern crate window;
//! # extern crate poke_a_mango;
//! # use window::Window;
//! # use conrod::backend::piston::event::UpdateEvent;
//! # use conrod::backend::piston::window::{WindowEvents, EventWindow, Window as PistonWindow};
//! # use poke_a_mango::*;
//! # fn main() {
//! #   not_main();
//! # }
//! # fn not_main() -> Result<(), Error> {
//! let opts = Options::parse();
//!
//! let mut window: PistonWindow = try!(ops::create_window(opts.desktop_size));
//! let window_s = ops::window_size(opts.desktop_size);
//! let mut ui = conrod::UiBuilder::new([window_s[0] as f64, window_s[1] as f64]).build();
//! // Set up the UI like normal
//!
//! let mut game_state = ops::GameState::MainMenu;
//! let widgets = ops::Widgets::new(ui.widget_id_generator());
//!
//! let mut events = WindowEvents::new();
//! while let Some(event) = window.next(&mut events) {
//!     event.update(|_| {
//!         widgets.update(ui.set_widgets(), &mut game_state);
//!
//!         if game_state.should_exit() {
//!             window.set_should_close(true);
//!         } else if game_state.should_load_leaderboard() {
//!             game_state =
//!                 ops::GameState::DisplayLeaderboard(ops::Leader::read(&opts.config_dir.1.join("leaderboard.toml")).unwrap());
//!         } else if let ops::GameState::GameEnded { .. } = game_state {
//!             if let ops::GameState::GameEnded { ref name, score } = game_state {
//!                 ops::Leader::append(ops::Leader::now(name.clone(), score),
//!                                     &opts.config_dir.1.join("leaderboard.toml")).unwrap();
//!             }
//!             game_state = ops::GameState::MainMenu;
//!         }
//!     });
//!
//!     // Handle events and draw like you'd normally do
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Executable manpage
//!
//! Played with your friends or alone, this game provides great entertainment to
//! everyone all over the globe, requiring great eye-hand coordination and keeping
//! the atmosphere tense.
//!
//! Exit values and possible errors:
//!
//! ```text
//! 1 - Failed to parse a file
//! 2 - An I/O error occured
//! 3 - UI failed to cooperate
//! ```
//!
//! ## OPTIONS
//!
//! -c --config-dir <config_dir>
//!
//! ```text
//! Directory with the configuration.
//!
//! The configuration directory contains all of poke-a-mango's data.
//!
//! Default: $HOME/.poke-a-mango
//! ```
//!
//! -d --desktop-size <size>
//!
//! ```text
//! The target desktop's resolution.
//!
//! By default this is autodetected to match the primary monitor's resolution,
//! but can be overriden to scale the game window better.
//!
//! Format: NxM
//! ```
//!
//! ## EXAMPLES
//!
//! `poke-a-mango` `-d` *1280x720*
//!
//! ```text
//! Run the game as on an HD monitor.
//! ```
//!
//! `poke-a-mango` `-c` *pkmngo*
//!
//! ```text
//! Save the game data in "pkmngo" directory instead of the default one.
//! ```

extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;
extern crate chrono;
extern crate conrod;
extern crate glutin;
extern crate regex;
#[macro_use]
extern crate clap;
extern crate toml;
extern crate rand;

mod error;
mod options;

pub mod ops;
pub mod util;

pub use error::Error;
pub use options::Options;
<commit_msg>Fix example code wrapping<commit_after>//! What all the kool kidz are playing these days
//!
//! ## Features
//!
//! Easily navigable menus!
//!
//! ![Start menu screenshot](https://cdn.rawgit.com/nabijaczleweli/poke-a-mango/master/assets/screenshot_start_menu.png)
//!
//! Extensive leaderboard system!
//!
//! ![Leaderboard screenshot](https://cdn.rawgit.com/nabijaczleweli/poke-a-mango/master/assets/screenshot_leaderboard.png)
//!
//! A wide variety of difficulty modes!
//!
//! ![Difficulty selection
//! screenshot](https://cdn.rawgit.com/nabijaczleweli/poke-a-mango/master/assets/screenshot_difficulty_selection.png)
//!
//! Exciting gameplay that will have you glued to the screen for hours on end!
//!
//! ![Gameplay screenshot](https://cdn.rawgit.com/nabijaczleweli/poke-a-mango/master/assets/screenshot_gameplay.png)
//!
//! # Library doc
//!
//! This library is used by `poke-a-mango` itself for all its function and is therefore contains all necessary functions.
//!
//! ## Data flow
//!
//! #### With `conrod`
//!
//! ```plaintext
//! Options::parse()
//! |> create_window()
//! |> Widgets::new()
//! ```
//!
//! Then, each update event:
//!
//! ```plaintext
//! |> Widgets::update()
//! |> [check for states requiring usercode handling and act accordingly]
//! ```
//!
//! #### With a different UI
//!
//! ```plaintext
//! Options::parse()
//! |> state::*()
//! |> [check for states requiring usercode handling and act accordingly]
//! ```
//!
//! ### Prose explanation
//!
//! First, get an `Options` instance, be it via a struct-literal or `Options::parse()`;
//! or don't and just create the individual arguments manually.
//!
//! Then, use `create_window()` and a new `conrod::Ui`, follow up with creating a new `Widgets` instance.
//!
//! After that, among normally displaying and processing events with `conrod::Ui` call `Widgets::update()` each update event.
//! That sometimes produces `GameState`s that need action from you and you need to handle them manually.
//!
//! ### Example
//!
//! This is a semi-complete example, as it lacks event handling, rendering and UI setup which have been omitted for brevity.
//!
//! ```no_run
//! # extern crate conrod;
//! # extern crate window;
//! # extern crate poke_a_mango;
//! # use window::Window;
//! # use conrod::backend::piston::event::UpdateEvent;
//! # use conrod::backend::piston::window::{WindowEvents, EventWindow, Window as PistonWindow};
//! # use poke_a_mango::*;
//! # fn main() {
//! #   not_main();
//! # }
//! # fn not_main() -> Result<(), Error> {
//! let opts = Options::parse();
//!
//! let mut window: PistonWindow = try!(ops::create_window(opts.desktop_size));
//! let window_s = ops::window_size(opts.desktop_size);
//! let mut ui = conrod::UiBuilder::new([window_s[0] as f64, window_s[1] as f64]).build();
//! // Set up the UI like normal
//!
//! let mut game_state = ops::GameState::MainMenu;
//! let widgets = ops::Widgets::new(ui.widget_id_generator());
//!
//! let mut events = WindowEvents::new();
//! while let Some(event) = window.next(&mut events) {
//!     event.update(|_| {
//!         widgets.update(ui.set_widgets(), &mut game_state);
//!
//!         if game_state.should_exit() {
//!             window.set_should_close(true);
//!         } else if game_state.should_load_leaderboard() {
//!             game_state =
//!                 ops::GameState::DisplayLeaderboard(ops::Leader::read(
//!                     &opts.config_dir.1.join("leaderboard.toml")).unwrap());
//!         } else if let ops::GameState::GameEnded { .. } = game_state {
//!             if let ops::GameState::GameEnded { ref name, score } = game_state {
//!                 ops::Leader::append(ops::Leader::now(name.clone(), score),
//!                                     &opts.config_dir.1.join("leaderboard.toml")).unwrap();
//!             }
//!             game_state = ops::GameState::MainMenu;
//!         }
//!     });
//!
//!     // Handle events and draw like you'd normally do
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Executable manpage
//!
//! Played with your friends or alone, this game provides great entertainment to
//! everyone all over the globe, requiring great eye-hand coordination and keeping
//! the atmosphere tense.
//!
//! Exit values and possible errors:
//!
//! ```text
//! 1 - Failed to parse a file
//! 2 - An I/O error occured
//! 3 - UI failed to cooperate
//! ```
//!
//! ## OPTIONS
//!
//! -c --config-dir <config_dir>
//!
//! ```text
//! Directory with the configuration.
//!
//! The configuration directory contains all of poke-a-mango's data.
//!
//! Default: $HOME/.poke-a-mango
//! ```
//!
//! -d --desktop-size <size>
//!
//! ```text
//! The target desktop's resolution.
//!
//! By default this is autodetected to match the primary monitor's resolution,
//! but can be overriden to scale the game window better.
//!
//! Format: NxM
//! ```
//!
//! ## EXAMPLES
//!
//! `poke-a-mango` `-d` *1280x720*
//!
//! ```text
//! Run the game as on an HD monitor.
//! ```
//!
//! `poke-a-mango` `-c` *pkmngo*
//!
//! ```text
//! Save the game data in "pkmngo" directory instead of the default one.
//! ```

extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;
extern crate chrono;
extern crate conrod;
extern crate glutin;
extern crate regex;
#[macro_use]
extern crate clap;
extern crate toml;
extern crate rand;

mod error;
mod options;

pub mod ops;
pub mod util;

pub use error::Error;
pub use options::Options;
<|endoftext|>


<commit_before>#![doc(html_logo_url = "https://cdn.rawgit.com/urschrei/polylabel-rs/7a07336e85572eb5faaf0657c2383d7de5620cd8/ell.svg",
       html_root_url = "https://urschrei.github.io/polylabel-rs/")]
//! This crate provides a Rust implementation of the [Polylabel](https://github.com/mapbox/polylabel) algorithm
//! for finding the optimum position of a polygon label.
use std::cmp::Ordering;
use std::collections::BinaryHeap;

extern crate num_traits;
use self::num_traits::{Float, FromPrimitive, Signed};

extern crate geo;
use self::geo::{Point, Polygon};
use self::geo::algorithm::boundingbox::BoundingBox;
use self::geo::algorithm::distance::Distance;
use self::geo::algorithm::area::Area;
use self::geo::algorithm::centroid::Centroid;
use self::geo::algorithm::contains::Contains;

mod ffi;
pub use ffi::{polylabel_ffi, Array, Position, WrapperArray};

#[doc(hidden)]
#[allow(dead_code)]
pub extern "C" fn spare() {
    println!("");
}

/// Represention of a Quadtree cell
#[derive(Debug)]
struct Qcell<T>
where
    T: Float + Signed,
{
    centroid: Point<T>,
    // Half the cell size
    h: T,
    // Distance from centroid to polygon
    distance: T,
    // Maximum distance to polygon within a cell
    max_distance: T,
}

impl<T> Qcell<T>
where
    T: Float + Signed,
{
    fn new(x: T, y: T, h: T, distance: T, max_distance: T) -> Qcell<T> {
        Qcell {
            centroid: Point::new(x, y),
            h: h,
            distance: distance,
            max_distance: max_distance,
        }
    }
}

impl<T> Ord for Qcell<T>
where
    T: Float + Signed,
{
    fn cmp(&self, other: &Qcell<T>) -> std::cmp::Ordering {
        self.max_distance.partial_cmp(&other.max_distance).unwrap()
    }
}
impl<T> PartialOrd for Qcell<T>
where
    T: Float + Signed,
{
    fn partial_cmp(&self, other: &Qcell<T>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<T> Eq for Qcell<T>
where
    T: Float + Signed,
{
}
impl<T> PartialEq for Qcell<T>
where
    T: Float + Signed,
{
    fn eq(&self, other: &Qcell<T>) -> bool
    where
        T: Float,
    {
        self.max_distance == other.max_distance
    }
}

/// Signed distance from a Qcell's centroid to a Polygon's outline
/// Returned value is negative if the point is outside the polygon's exterior ring
fn signed_distance<T>(x: &T, y: &T, polygon: &Polygon<T>) -> T
where
    T: Float,
{
    let point = Point::new(*x, *y);
    let inside = polygon.contains(&point);
    // Use LineString distance, because Polygon distance returns 0.0 for inside
    let distance = point.distance(&polygon.exterior);
    if inside {
        distance
    } else {
        -distance
    }
}

/// Add a new Quadtree node made up of four `Qcell`s to the binary heap
fn add_quad<T>(
    mpq: &mut BinaryHeap<Qcell<T>>,
    cell: &Qcell<T>,
    new_height: &T,
    polygon: &Polygon<T>,
) where
    T: Float + Signed,
{
    let two = T::one() + T::one();
    let centroid_x = cell.centroid.x();
    let centroid_y = cell.centroid.y();
    for combo in &[
        (centroid_x - *new_height, centroid_y - *new_height),
        (centroid_x + *new_height, centroid_y - *new_height),
        (centroid_x - *new_height, centroid_y + *new_height),
        (centroid_x + *new_height, centroid_y + *new_height),
    ] {
        let mut new_dist = signed_distance(&combo.0, &combo.1, polygon);
        mpq.push(Qcell::new(
            combo.0,
            combo.1,
            *new_height,
            new_dist,
            new_dist + *new_height * two.sqrt(),
        ));
    }
}

/// Calculate a Polygon's ideal label position by calculating its ✨pole of inaccessibility✨
///
/// The calculation uses an [iterative grid-based algorithm](https://github.com/mapbox/polylabel#how-the-algorithm-works).
///
/// # Examples
///
/// ```
/// use polylabel::polylabel;
/// extern crate geo;
/// use self::geo::{Point, LineString, Polygon};
///
/// // An approximate `L` shape
/// let coords = vec![
///    (0.0, 0.0),
///    (4.0, 0.0),
///    (4.0, 1.0),
///    (1.0, 1.0),
///    (1.0, 4.0),
///    (0.0, 4.0),
///    (0.0, 0.0)];
///
/// let poly = Polygon::new(coords.into(), vec![]);
///
/// // Its centroid lies outside the polygon
/// assert_eq!(poly.centroid(), Point::new(1.3571428571428572, 1.3571428571428572));
///
/// let label_position = polylabel(&poly, &1.0);
/// // Optimum label position is inside the polygon
/// assert_eq!(label_position, Point::new(0.5625, 0.5625));
/// ```
///
pub fn polylabel<T>(polygon: &Polygon<T>, tolerance: &T) -> Point<T>
where
    T: Float + FromPrimitive + Signed,
{
    // special case for degenerate polygons
    if polygon.area() == T::zero() {
        // best_cell = Qcell {
        //     centroid = Point::new(polygon.exterior.0[0].x(), polygon.exterior.0[0].y())
        //     h: T::zero(),
        //     distance: distance,
        //     max_distance: max_distance
        // };
        return Point::new(T::zero(), T::zero());
    }
    let two = T::one() + T::one();
    // Initial best cell values
    let centroid = polygon.centroid().unwrap();
    let bbox = polygon.bbox().unwrap();
    let width = bbox.xmax - bbox.xmin;
    let height = bbox.ymax - bbox.ymin;
    let cell_size = width.min(height);
    // Special case for degenerate polygons
    if cell_size == T::zero() {
        return Point::new(bbox.xmin, bbox.ymin);
    }
    let mut h = cell_size / two;
    let distance = signed_distance(&centroid.x(), &centroid.y(), polygon);
    let max_distance = distance + T::zero() * two.sqrt();

    let mut best_cell = Qcell::new(
        centroid.x(),
        centroid.y(),
        T::zero(),
        distance,
        max_distance,
    );

    // special case for rectangular polygons
    let bbox_cell_dist = signed_distance(
        &(bbox.xmin + width / two),
        &(bbox.ymin + height / two),
        polygon,
    );
    let bbox_cell = Qcell {
        centroid: Point::new(bbox.xmin + width / two, bbox.ymin + height / two),
        h: T::zero(),
        distance: bbox_cell_dist,
        max_distance: bbox_cell_dist + T::zero() * two.sqrt(),
    };

    if bbox_cell.distance > best_cell.distance {
        best_cell = bbox_cell;
    }

    // Priority queue
    let mut cell_queue: BinaryHeap<Qcell<T>> = BinaryHeap::new();
    // Build an initial quadtree node, which covers the Polygon
    let mut x = bbox.xmin;
    let mut y;
    while x < bbox.xmax {
        y = bbox.ymin;
        while y < bbox.ymax {
            let latest_dist = signed_distance(&(x + h), &(y + h), polygon);
            cell_queue.push(Qcell {
                centroid: Point::new(x + h, y + h),
                h: h,
                distance: latest_dist,
                max_distance: latest_dist + h * two.sqrt(),
            });
            y = y + cell_size;
        }
        x = x + cell_size;
    }
    // Now try to find better solutions
    while !cell_queue.is_empty() {
        let cell = cell_queue.pop().unwrap();
        // Update the best cell if we find a cell with greater distance
        if cell.distance > best_cell.distance {
            best_cell.centroid = Point::new(cell.centroid.x(), cell.centroid.y());
            best_cell.h = cell.h;
            best_cell.distance = cell.distance;
            best_cell.max_distance = cell.max_distance;
        }
        // Bail out of this iteration if we can't find a better solution
        if cell.max_distance - best_cell.distance <= *tolerance {
            continue;
        }
        // Otherwise, add a new quadtree node and start again
        h = cell.h / two;
        add_quad(&mut cell_queue, &cell, &h, polygon);
    }
    // We've exhausted the queue, so return the best solution we've found
    Point::new(best_cell.centroid.x(), best_cell.centroid.y())
}

#[cfg(test)]
mod tests {
    use std::collections::BinaryHeap;
    use super::{polylabel, Qcell};
    use geo::{Point, Polygon};
    use geo::contains::Contains;
    #[test]
    // polygons are those used in Shapely's tests
    fn test_polylabel() {
        let coords = include!("poly1.rs");
        let poly = Polygon::new(coords.into(), vec![]);
        let res = polylabel(&poly, &10.000);
        assert_eq!(res, Point::new(59.35615556364569, 121.83919629746435));
    }
    #[test]
    // does a concave polygon contain the calculated point?
    // relevant because the centroid lies outside the polygon in this case
    fn test_concave() {
        let coords = include!("poly2.rs");
        let poly = Polygon::new(coords.into(), vec![]);
        let res = polylabel(&poly, &1.0);
        assert!(poly.contains(&res));
    }
    #[test]
    fn polygon_l_test() {
        // an L shape
        let coords = vec![
            (0.0, 0.0),
            (4.0, 0.0),
            (4.0, 1.0),
            (1.0, 1.0),
            (1.0, 4.0),
            (0.0, 4.0),
            (0.0, 0.0),
        ];
        let poly = Polygon::new(coords.into(), vec![]);
        let res = polylabel(&poly, &0.10);
        assert_eq!(res, Point::new(0.5625, 0.5625));
    }
    #[test]
    fn degenerate_polygon_test() {
        let a_coords = vec![(0.0, 0.0), (1.0, 0.0), (2.0, 0.0), (0.0, 0.0)];
        let a_poly = Polygon::new(a_coords.into(), vec![]);
        let a_res = polylabel(&a_poly, &1.0);
        assert_eq!(a_res, Point::new(0.0, 0.0));
    }
    #[test]
    fn degenerate_polygon_test_2() {
        let b_coords = vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)];
        let b_poly = Polygon::new(b_coords.into(), vec![]);
        let b_res = polylabel(&b_poly, &1.0);
        assert_eq!(b_res, Point::new(0.0, 0.0));
    }
    #[test]
    // Is our priority queue behaving as it should?
    fn test_queue() {
        let a = Qcell {
            centroid: Point::new(1.0, 2.0),
            h: 3.0,
            distance: 4.0,
            max_distance: 8.0,
        };
        let b = Qcell {
            centroid: Point::new(1.0, 2.0),
            h: 3.0,
            distance: 4.0,
            max_distance: 7.0,
        };
        let c = Qcell {
            centroid: Point::new(1.0, 2.0),
            h: 3.0,
            distance: 4.0,
            max_distance: 9.0,
        };
        let mut v = vec![];
        v.push(a);
        v.push(b);
        v.push(c);
        let mut q = BinaryHeap::from(v);
        assert_eq!(q.pop().unwrap().max_distance, 9.0);
        assert_eq!(q.pop().unwrap().max_distance, 8.0);
        assert_eq!(q.pop().unwrap().max_distance, 7.0);
    }
}
<commit_msg>Harrumph<commit_after>#![doc(html_logo_url = "https://cdn.rawgit.com/urschrei/polylabel-rs/7a07336e85572eb5faaf0657c2383d7de5620cd8/ell.svg",
       html_root_url = "https://urschrei.github.io/polylabel-rs/")]
//! This crate provides a Rust implementation of the [Polylabel](https://github.com/mapbox/polylabel) algorithm
//! for finding the optimum position of a polygon label.
use std::cmp::Ordering;
use std::collections::BinaryHeap;

extern crate num_traits;
use self::num_traits::{Float, FromPrimitive, Signed};

extern crate geo;
use self::geo::{Point, Polygon};
use self::geo::algorithm::boundingbox::BoundingBox;
use self::geo::algorithm::distance::Distance;
use self::geo::algorithm::area::Area;
use self::geo::algorithm::centroid::Centroid;
use self::geo::algorithm::contains::Contains;

mod ffi;
pub use ffi::{polylabel_ffi, Array, Position, WrapperArray};

#[doc(hidden)]
#[allow(dead_code)]
pub extern "C" fn spare() {
    println!("");
}

/// Represention of a Quadtree cell
#[derive(Debug)]
struct Qcell<T>
where
    T: Float + Signed,
{
    centroid: Point<T>,
    // Half the cell size
    h: T,
    // Distance from centroid to polygon
    distance: T,
    // Maximum distance to polygon within a cell
    max_distance: T,
}

impl<T> Qcell<T>
where
    T: Float + Signed,
{
    fn new(x: T, y: T, h: T, distance: T, max_distance: T) -> Qcell<T> {
        Qcell {
            centroid: Point::new(x, y),
            h: h,
            distance: distance,
            max_distance: max_distance,
        }
    }
}

impl<T> Ord for Qcell<T>
where
    T: Float + Signed,
{
    fn cmp(&self, other: &Qcell<T>) -> std::cmp::Ordering {
        self.max_distance.partial_cmp(&other.max_distance).unwrap()
    }
}
impl<T> PartialOrd for Qcell<T>
where
    T: Float + Signed,
{
    fn partial_cmp(&self, other: &Qcell<T>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<T> Eq for Qcell<T>
where
    T: Float + Signed,
{
}
impl<T> PartialEq for Qcell<T>
where
    T: Float + Signed,
{
    fn eq(&self, other: &Qcell<T>) -> bool
    where
        T: Float,
    {
        self.max_distance == other.max_distance
    }
}

/// Signed distance from a Qcell's centroid to a Polygon's outline
/// Returned value is negative if the point is outside the polygon's exterior ring
fn signed_distance<T>(x: &T, y: &T, polygon: &Polygon<T>) -> T
where
    T: Float,
{
    let point = Point::new(*x, *y);
    let inside = polygon.contains(&point);
    // Use LineString distance, because Polygon distance returns 0.0 for inside
    let distance = point.distance(&polygon.exterior);
    if inside {
        distance
    } else {
        -distance
    }
}

/// Add a new Quadtree node made up of four `Qcell`s to the binary heap
fn add_quad<T>(
    mpq: &mut BinaryHeap<Qcell<T>>,
    cell: &Qcell<T>,
    new_height: &T,
    polygon: &Polygon<T>,
) where
    T: Float + Signed,
{
    let two = T::one() + T::one();
    let centroid_x = cell.centroid.x();
    let centroid_y = cell.centroid.y();
    for combo in &[
        (centroid_x - *new_height, centroid_y - *new_height),
        (centroid_x + *new_height, centroid_y - *new_height),
        (centroid_x - *new_height, centroid_y + *new_height),
        (centroid_x + *new_height, centroid_y + *new_height),
    ] {
        let mut new_dist = signed_distance(&combo.0, &combo.1, polygon);
        mpq.push(Qcell::new(
            combo.0,
            combo.1,
            *new_height,
            new_dist,
            new_dist + *new_height * two.sqrt(),
        ));
    }
}

/// Calculate a Polygon's ideal label position by calculating its ✨pole of inaccessibility✨
///
/// The calculation uses an [iterative grid-based algorithm](https://github.com/mapbox/polylabel#how-the-algorithm-works).
///
/// # Examples
///
/// ```
/// use polylabel::polylabel;
/// extern crate geo;
/// use self::geo::{Point, LineString, Polygon};
///
/// // An approximate `L` shape
/// let coords = vec![
///    (0.0, 0.0),
///    (4.0, 0.0),
///    (4.0, 1.0),
///    (1.0, 1.0),
///    (1.0, 4.0),
///    (0.0, 4.0),
///    (0.0, 0.0)];
///
/// let poly = Polygon::new(coords.into(), vec![]);
///
/// // Its centroid lies outside the polygon
/// assert_eq!(poly.centroid(), Point::new(1.3571428571428572, 1.3571428571428572));
///
/// let label_position = polylabel(&poly, &1.0);
/// // Optimum label position is inside the polygon
/// assert_eq!(label_position, Point::new(0.5625, 0.5625));
/// ```
///
pub fn polylabel<T>(polygon: &Polygon<T>, tolerance: &T) -> Point<T>
where
    T: Float + FromPrimitive + Signed,
{
    // special case for degenerate polygons
    if polygon.area() == T::zero() {
        // best_cell = Qcell {
        //     centroid = Point::new(polygon.exterior.0[0].x(), polygon.exterior.0[0].y())
        //     h: T::zero(),
        //     distance: distance,
        //     max_distance: max_distance
        // };
        return Point::new(T::zero(), T::zero());
    }
    let two = T::one() + T::one();
    // Initial best cell values
    let centroid = polygon.centroid().unwrap();
    let bbox = polygon.bbox().unwrap();
    let width = bbox.xmax - bbox.xmin;
    let height = bbox.ymax - bbox.ymin;
    let cell_size = width.min(height);
    // Special case for degenerate polygons
    if cell_size == T::zero() {
        return Point::new(bbox.xmin, bbox.ymin);
    }
    let mut h = cell_size / two;
    let distance = signed_distance(&centroid.x(), &centroid.y(), polygon);
    let max_distance = distance + T::zero() * two.sqrt();

    let mut best_cell = Qcell::new(
        centroid.x(),
        centroid.y(),
        T::zero(),
        distance,
        max_distance,
    );

    // special case for rectangular polygons
    let bbox_cell_dist = signed_distance(
        &(bbox.xmin + width / two),
        &(bbox.ymin + height / two),
        polygon,
    );
    let bbox_cell = Qcell {
        centroid: Point::new(bbox.xmin + width / two, bbox.ymin + height / two),
        h: T::zero(),
        distance: bbox_cell_dist,
        max_distance: bbox_cell_dist + T::zero() * two.sqrt(),
    };

    if bbox_cell.distance > best_cell.distance {
        best_cell = bbox_cell;
    }

    // Priority queue
    let mut cell_queue: BinaryHeap<Qcell<T>> = BinaryHeap::new();
    // Build an initial quadtree node, which covers the Polygon
    let mut x = bbox.xmin;
    let mut y;
    while x < bbox.xmax {
        y = bbox.ymin;
        while y < bbox.ymax {
            let latest_dist = signed_distance(&(x + h), &(y + h), polygon);
            cell_queue.push(Qcell {
                centroid: Point::new(x + h, y + h),
                h: h,
                distance: latest_dist,
                max_distance: latest_dist + h * two.sqrt(),
            });
            y = y + cell_size;
        }
        x = x + cell_size;
    }
    // Now try to find better solutions
    while !cell_queue.is_empty() {
        let cell = cell_queue.pop().unwrap();
        // Update the best cell if we find a cell with greater distance
        if cell.distance > best_cell.distance {
            best_cell.centroid = Point::new(cell.centroid.x(), cell.centroid.y());
            best_cell.h = cell.h;
            best_cell.distance = cell.distance;
            best_cell.max_distance = cell.max_distance;
        }
        // Bail out of this iteration if we can't find a better solution
        if cell.max_distance - best_cell.distance <= *tolerance {
            continue;
        }
        // Otherwise, add a new quadtree node and start again
        h = cell.h / two;
        add_quad(&mut cell_queue, &cell, &h, polygon);
    }
    // We've exhausted the queue, so return the best solution we've found
    Point::new(best_cell.centroid.x(), best_cell.centroid.y())
}

#[cfg(test)]
mod tests {
    use std::collections::BinaryHeap;
    use super::{polylabel, Qcell};
    use geo::{Point, Polygon};
    use geo::contains::Contains;
    #[test]
    // polygons are those used in Shapely's tests
    fn test_polylabel() {
        let coords = include!("poly1.rs");
        let poly = Polygon::new(coords.into(), vec![]);
        let res = polylabel(&poly, &10.000);
        assert_eq!(res, Point::new(59.35615556364569, 121.83919629746435));
    }
    #[test]
    // does a concave polygon contain the calculated point?
    // relevant because the centroid lies outside the polygon in this case
    fn test_concave() {
        let coords = include!("poly2.rs");
        let poly = Polygon::new(coords.into(), vec![]);
        let res = polylabel(&poly, &1.0);
        assert!(poly.contains(&res));
    }
    #[test]
    fn polygon_l_test() {
        // an L shape
        let coords = vec![
            (0.0, 0.0),
            (4.0, 0.0),
            (4.0, 1.0),
            (1.0, 1.0),
            (1.0, 4.0),
            (0.0, 4.0),
            (0.0, 0.0),
        ];
        let poly = Polygon::new(coords.into(), vec![]);
        let res = polylabel(&poly, &0.10);
        assert_eq!(res, Point::new(0.5625, 0.5625));
    }
    #[test]
    fn degenerate_polygon_test() {
        let a_coords = vec![(0.0, 0.0), (1.0, 0.0), (2.0, 0.0), (0.0, 0.0)];
        let a_poly = Polygon::new(a_coords.into(), vec![]);
        let a_res = polylabel(&a_poly, &1.0);
        assert_eq!(a_res, Point::new(0.0, 0.0));
    }
    #[test]
    fn degenerate_polygon_test_2() {
        let b_coords = vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)];
        let b_poly = Polygon::new(b_coords.into(), vec![]);
        let b_res = polylabel(&b_poly, &1.0);
        assert_eq!(b_res, Point::new(0.0, 0.0));
    }
    #[test]
    // Is our priority queue behaving as it should?
    fn test_queue() {
        let a = Qcell {
            centroid: Point::new(1.0, 2.0),
            h: 3.0,
            distance: 4.0,
            max_distance: 8.0,
        };
        let b = Qcell {
            centroid: Point::new(1.0, 2.0),
            h: 3.0,
            distance: 4.0,
            max_distance: 7.0,
        };
        let c = Qcell {
            centroid: Point::new(1.0, 2.0),
            h: 3.0,
            distance: 4.0,
            max_distance: 9.0,
        };
        let mut v = vec![];
        v.push(a);
        v.push(b);
        v.push(c);
        let mut q = BinaryHeap::from(v);
        assert_eq!(q.pop().unwrap().max_distance, 9.0);
        assert_eq!(q.pop().unwrap().max_distance, 8.0);
        assert_eq!(q.pop().unwrap().max_distance, 7.0);
    }
}
<|endoftext|>


<commit_before><commit_msg>Added exported types.<commit_after>use super::bindgen::libffi as bg;

pub use bg::ffi_abi;
pub use bg::_ffi_type as ffi_type;
pub use bg::ffi_status;

pub use bg::ffi_cif;
pub use bg::ffi_closure;

pub use bg::ffi_type_void;
pub use bg::ffi_type_uint8;
pub use bg::ffi_type_sint8;
pub use bg::ffi_type_uint16;
pub use bg::ffi_type_sint16;
pub use bg::ffi_type_uint32;
pub use bg::ffi_type_sint32;
pub use bg::ffi_type_uint64;
pub use bg::ffi_type_sint64;
pub use bg::ffi_type_float;
pub use bg::ffi_type_double;
pub use bg::ffi_type_pointer;
pub use bg::ffi_type_longdouble;
pub use bg::ffi_type_complex_float;
pub use bg::ffi_type_complex_double;
pub use bg::ffi_type_complex_longdouble;
<|endoftext|>


<commit_before>use syntax::ast;
use syntax::codemap::{self, DUMMY_SP, Span, respan};
use syntax::ext::base::ExtCtxt;
use syntax::ext::expand;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::ParseSess;
use syntax::ptr::P;

use expr::ExprBuilder;
use invoke::{Invoke, Identity};

/// A Builder for macro invocations.
///
/// Note that there are no commas added between args, as otherwise
/// that macro invocations that could be expressed would be limited.
/// You will need to add all required symbols with `with_arg` or
/// `with_argss`.
pub struct MacBuilder<F=Identity> {
    callback: F,
    span: Span,
    tokens: Vec<ast::TokenTree>,
    path: Option<ast::Path>,
}

impl MacBuilder {
    pub fn new() -> Self {
        MacBuilder::new_with_callback(Identity)
    }
}

impl<F> MacBuilder<F>
    where F: Invoke<ast::Mac>
{
    pub fn new_with_callback(callback: F) -> Self {
        MacBuilder {
            callback: callback,
            span: DUMMY_SP,
            tokens: vec![],
            path: None,
        }
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn path(mut self, path: ast::Path) -> Self {
        self.path = Some(path);
        self
    }

    pub fn build(self) -> F::Result {
        let mac = ast::Mac_::MacInvocTT(
            self.path.expect("No path set for macro"), self.tokens, 0);
        self.callback.invoke(respan(self.span, mac))
    }

    pub fn with_args<I, T>(self, iter: I) -> Self
        where I: IntoIterator<Item=T>, T: ToTokens
    {
        iter.into_iter().fold(self, |self_, expr| self_.with_arg(expr))
    }

    pub fn with_arg<T>(mut self, expr: T) -> Self
        where T: ToTokens
    {
        let parse_sess = ParseSess::new();
        let cx = make_ext_ctxt(&parse_sess);
        let tokens = expr.to_tokens(&cx);
        assert!(tokens.len() == 1);
        self.tokens.push(tokens[0].clone());
        self
    }

    pub fn expr(self) -> ExprBuilder<Self> {
        ExprBuilder::new_with_callback(self)
    }

}

impl<F> Invoke<P<ast::Expr>> for MacBuilder<F>
    where F: Invoke<ast::Mac>,
{
    type Result = Self;

    fn invoke(self, expr: P<ast::Expr>) -> Self {
        self.with_arg(expr)
    }
}

fn make_ext_ctxt(sess: &ParseSess) -> ExtCtxt {
    let info = codemap::ExpnInfo {
        call_site: codemap::DUMMY_SP,
        callee: codemap::NameAndSpan {
            name: "test".to_string(),
            format: codemap::MacroAttribute,
            allow_internal_unstable: false,
            span: None
        }
    };

    let cfg = vec![];
    let ecfg = expand::ExpansionConfig::default(String::new());

    let mut cx = ExtCtxt::new(&sess, cfg, ecfg);
    cx.bt_push(info);

    cx
}
<commit_msg>Update to rustc 1.4.0-nightly (e35fd7481 2015-08-17)<commit_after>use syntax::ast;
use syntax::codemap::{self, DUMMY_SP, Span, respan};
use syntax::ext::base::ExtCtxt;
use syntax::ext::expand;
use syntax::ext::quote::rt::ToTokens;
use syntax::feature_gate::GatedCfg;
use syntax::parse::ParseSess;
use syntax::ptr::P;

use expr::ExprBuilder;
use invoke::{Invoke, Identity};

/// A Builder for macro invocations.
///
/// Note that there are no commas added between args, as otherwise
/// that macro invocations that could be expressed would be limited.
/// You will need to add all required symbols with `with_arg` or
/// `with_argss`.
pub struct MacBuilder<F=Identity> {
    callback: F,
    span: Span,
    tokens: Vec<ast::TokenTree>,
    path: Option<ast::Path>,
}

impl MacBuilder {
    pub fn new() -> Self {
        MacBuilder::new_with_callback(Identity)
    }
}

impl<F> MacBuilder<F>
    where F: Invoke<ast::Mac>
{
    pub fn new_with_callback(callback: F) -> Self {
        MacBuilder {
            callback: callback,
            span: DUMMY_SP,
            tokens: vec![],
            path: None,
        }
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn path(mut self, path: ast::Path) -> Self {
        self.path = Some(path);
        self
    }

    pub fn build(self) -> F::Result {
        let mac = ast::Mac_::MacInvocTT(
            self.path.expect("No path set for macro"), self.tokens, 0);
        self.callback.invoke(respan(self.span, mac))
    }

    pub fn with_args<I, T>(self, iter: I) -> Self
        where I: IntoIterator<Item=T>, T: ToTokens
    {
        iter.into_iter().fold(self, |self_, expr| self_.with_arg(expr))
    }

    pub fn with_arg<T>(mut self, expr: T) -> Self
        where T: ToTokens
    {
        let parse_sess = ParseSess::new();
        let mut feature_gated_cfgs = Vec::new();
        let cx = make_ext_ctxt(&parse_sess, &mut feature_gated_cfgs);
        let tokens = expr.to_tokens(&cx);
        assert!(tokens.len() == 1);
        self.tokens.push(tokens[0].clone());
        self
    }

    pub fn expr(self) -> ExprBuilder<Self> {
        ExprBuilder::new_with_callback(self)
    }

}

impl<F> Invoke<P<ast::Expr>> for MacBuilder<F>
    where F: Invoke<ast::Mac>,
{
    type Result = Self;

    fn invoke(self, expr: P<ast::Expr>) -> Self {
        self.with_arg(expr)
    }
}

fn make_ext_ctxt<'a>(sess: &'a ParseSess,
                     feature_gated_cfgs: &'a mut Vec<GatedCfg>) -> ExtCtxt<'a> {
    let info = codemap::ExpnInfo {
        call_site: codemap::DUMMY_SP,
        callee: codemap::NameAndSpan {
            name: "test".to_string(),
            format: codemap::MacroAttribute,
            allow_internal_unstable: false,
            span: None
        }
    };

    let cfg = vec![];
    let ecfg = expand::ExpansionConfig::default(String::new());

    let mut cx = ExtCtxt::new(&sess, cfg, ecfg, feature_gated_cfgs);
    cx.bt_push(info);

    cx
}
<|endoftext|>


<commit_before>use tokenizer::new;
use tokenizer::Tokenizer;
use tokenizer::StateFunction;
use token::Token;
use token::Category;

fn initial_state(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            if lexer.token_position == lexer.token_start {
                let remaining_data = lexer.data[lexer.token_position..].to_string();

                if remaining_data.starts_with("</") {
                    lexer.tokenize(Category::Identifier);
                    lexer.tokenize_next(2, Category::Text);
                    return Some(StateFunction(inside_tag))
                }
            }
            match c {
                '<' => {
                    lexer.tokenize_next(1, Category::Text);
                    return Some(StateFunction(start_of_tag));
                },
                ' ' | '\n' => {
                    lexer.tokenize(Category::Text);
                    lexer.advance();
                    lexer.states.push(StateFunction(initial_state));
                    return Some(StateFunction(whitespace));
                },
                _ => {
                    lexer.advance();
                }
            }

            Some(StateFunction(initial_state))
        }

        None => {
            lexer.tokenize(Category::Text);
            None
        }
    }
}

fn start_of_tag(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            match c {
                ' ' | '\n' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.states.push(StateFunction(inside_tag));
                    return Some(StateFunction(whitespace));
                },
                '>' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.tokenize_next(1, Category::Text);
                    Some(StateFunction(initial_state))
                }
                _ => {
                    lexer.advance();
                    Some(StateFunction(start_of_tag))
                }
            }
        }

        None => {
            lexer.tokenize(Category::Identifier);
            None
        }
    }
}

fn inside_tag(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            match c {
                '"' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.advance();
                    Some(StateFunction(inside_string))
                },
                ' ' | '\n' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.advance();
                    lexer.states.push(StateFunction(inside_tag));
                    return Some(StateFunction(whitespace));
                },
                '=' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.tokenize_next(1, Category::AssignmentOperator);
                    Some(StateFunction(inside_tag))
                }
                '>' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.tokenize_next(1, Category::Text);
                    Some(StateFunction(initial_state))
                }
                _ => {
                    if lexer.token_position == lexer.token_start {
                        let remaining_data = lexer.data[lexer.token_position..].to_string();

                        if remaining_data.starts_with("/>") {
                            lexer.tokenize(Category::Identifier);
                            lexer.tokenize_next(2, Category::Text);
                            return Some(StateFunction(initial_state))
                        }
                    }

                    lexer.advance();
                    Some(StateFunction(inside_tag))
                }
            }
        }

        None => {
            lexer.tokenize(Category::Identifier);
            None
        }
    }
}

fn inside_string(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            match c {
                '"' => {
                    lexer.advance();
                    lexer.tokenize(Category::String);
                    Some(StateFunction(inside_tag))
                },
                '\\' => {
                    lexer.advance();
                    lexer.advance();
                    Some(StateFunction(inside_string))
                }
                _ => {
                    lexer.advance();
                    Some(StateFunction(inside_string))
                }
            }
        }

        None => {
            lexer.tokenize(Category::String);
            None
        }
    }
}

fn whitespace(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            match c {
                ' ' | '\n' => {
                    lexer.advance();
                    Some(StateFunction(whitespace))
                },
                _ => {
                    lexer.tokenize(Category::Whitespace);
                    Some(lexer.states.pop().unwrap())
                }
            }
        }

        None => {
            lexer.tokenize(Category::Whitespace);
            None
        }
    }
}

pub fn lex(data: &str) -> Vec<Token> {
    let mut lexer = new(data);
    let mut state_function = StateFunction(initial_state);
    loop {
        let StateFunction(actual_function) = state_function;
        match actual_function(&mut lexer) {
            Some(f) => state_function = f,
            None => return lexer.tokens(),
        }
    }
}

mod tests {
    use super::lex;
    use token::Token;
    use token::Category;

    #[test]
    fn it_works() {
        let data = include_str!("../../test_data/data.xml");
        let tokens = lex(data);
        let expected_tokens = vec![
            Token{ lexeme: "<".to_string(), category: Category::Text },
            Token{ lexeme: "tag".to_string(), category: Category::Identifier },
            Token{ lexeme: ">".to_string(), category: Category::Text },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "<".to_string(), category: Category::Text },
            Token{ lexeme: "tag_with_attribute".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "attribute".to_string(), category: Category::Identifier },
            Token{ lexeme: "=".to_string(), category: Category::AssignmentOperator },
            Token{ lexeme: "\"value\"".to_string(), category: Category::String },
            Token{ lexeme: ">".to_string(), category: Category::Text },
            Token{ lexeme: "</".to_string(), category: Category::Text },
            Token{ lexeme: "tag_with_attribute".to_string(), category: Category::Identifier },
            Token{ lexeme: ">".to_string(), category: Category::Text },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "<".to_string(), category: Category::Text },
            Token{ lexeme: "self_closing_tag".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "/>".to_string(), category: Category::Text },
            Token{ lexeme: "\n".to_string(), category: Category::Whitespace },
            Token{ lexeme: "</".to_string(), category: Category::Text },
            Token{ lexeme: "tag".to_string(), category: Category::Identifier },
            Token{ lexeme: ">".to_string(), category: Category::Text },
            Token{ lexeme: "\n".to_string(), category: Category::Whitespace },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }

    #[test]
    fn it_can_handle_garbage() {
        let tokens = lex("} adwyx123&*_ ");
        let expected_tokens = vec![
            Token{ lexeme: "}".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "adwyx123&*_".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }

    #[test]
    fn it_can_handle_open_strings() {
        let tokens = lex("<tag \"open!>");
        let expected_tokens = vec![
            Token{ lexeme: "<".to_string(), category: Category::Text },
            Token{ lexeme: "tag".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "\"open!>".to_string(), category: Category::String },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }

    #[test]
    fn it_can_handle_utf8_data() {
        let tokens = lex("différent");
        let expected_tokens = vec![
            Token{ lexeme: "différent".to_string(), category: Category::Text },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }
}
<commit_msg>Add test config attribute to xml tests module.<commit_after>use tokenizer::new;
use tokenizer::Tokenizer;
use tokenizer::StateFunction;
use token::Token;
use token::Category;

fn initial_state(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            if lexer.token_position == lexer.token_start {
                let remaining_data = lexer.data[lexer.token_position..].to_string();

                if remaining_data.starts_with("</") {
                    lexer.tokenize(Category::Identifier);
                    lexer.tokenize_next(2, Category::Text);
                    return Some(StateFunction(inside_tag))
                }
            }
            match c {
                '<' => {
                    lexer.tokenize_next(1, Category::Text);
                    return Some(StateFunction(start_of_tag));
                },
                ' ' | '\n' => {
                    lexer.tokenize(Category::Text);
                    lexer.advance();
                    lexer.states.push(StateFunction(initial_state));
                    return Some(StateFunction(whitespace));
                },
                _ => {
                    lexer.advance();
                }
            }

            Some(StateFunction(initial_state))
        }

        None => {
            lexer.tokenize(Category::Text);
            None
        }
    }
}

fn start_of_tag(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            match c {
                ' ' | '\n' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.states.push(StateFunction(inside_tag));
                    return Some(StateFunction(whitespace));
                },
                '>' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.tokenize_next(1, Category::Text);
                    Some(StateFunction(initial_state))
                }
                _ => {
                    lexer.advance();
                    Some(StateFunction(start_of_tag))
                }
            }
        }

        None => {
            lexer.tokenize(Category::Identifier);
            None
        }
    }
}

fn inside_tag(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            match c {
                '"' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.advance();
                    Some(StateFunction(inside_string))
                },
                ' ' | '\n' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.advance();
                    lexer.states.push(StateFunction(inside_tag));
                    return Some(StateFunction(whitespace));
                },
                '=' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.tokenize_next(1, Category::AssignmentOperator);
                    Some(StateFunction(inside_tag))
                }
                '>' => {
                    lexer.tokenize(Category::Identifier);
                    lexer.tokenize_next(1, Category::Text);
                    Some(StateFunction(initial_state))
                }
                _ => {
                    if lexer.token_position == lexer.token_start {
                        let remaining_data = lexer.data[lexer.token_position..].to_string();

                        if remaining_data.starts_with("/>") {
                            lexer.tokenize(Category::Identifier);
                            lexer.tokenize_next(2, Category::Text);
                            return Some(StateFunction(initial_state))
                        }
                    }

                    lexer.advance();
                    Some(StateFunction(inside_tag))
                }
            }
        }

        None => {
            lexer.tokenize(Category::Identifier);
            None
        }
    }
}

fn inside_string(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            match c {
                '"' => {
                    lexer.advance();
                    lexer.tokenize(Category::String);
                    Some(StateFunction(inside_tag))
                },
                '\\' => {
                    lexer.advance();
                    lexer.advance();
                    Some(StateFunction(inside_string))
                }
                _ => {
                    lexer.advance();
                    Some(StateFunction(inside_string))
                }
            }
        }

        None => {
            lexer.tokenize(Category::String);
            None
        }
    }
}

fn whitespace(lexer: &mut Tokenizer) -> Option<StateFunction> {
    match lexer.current_char() {
        Some(c) => {
            match c {
                ' ' | '\n' => {
                    lexer.advance();
                    Some(StateFunction(whitespace))
                },
                _ => {
                    lexer.tokenize(Category::Whitespace);
                    Some(lexer.states.pop().unwrap())
                }
            }
        }

        None => {
            lexer.tokenize(Category::Whitespace);
            None
        }
    }
}

pub fn lex(data: &str) -> Vec<Token> {
    let mut lexer = new(data);
    let mut state_function = StateFunction(initial_state);
    loop {
        let StateFunction(actual_function) = state_function;
        match actual_function(&mut lexer) {
            Some(f) => state_function = f,
            None => return lexer.tokens(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::lex;
    use token::Token;
    use token::Category;

    #[test]
    fn it_works() {
        let data = include_str!("../../test_data/data.xml");
        let tokens = lex(data);
        let expected_tokens = vec![
            Token{ lexeme: "<".to_string(), category: Category::Text },
            Token{ lexeme: "tag".to_string(), category: Category::Identifier },
            Token{ lexeme: ">".to_string(), category: Category::Text },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "<".to_string(), category: Category::Text },
            Token{ lexeme: "tag_with_attribute".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "attribute".to_string(), category: Category::Identifier },
            Token{ lexeme: "=".to_string(), category: Category::AssignmentOperator },
            Token{ lexeme: "\"value\"".to_string(), category: Category::String },
            Token{ lexeme: ">".to_string(), category: Category::Text },
            Token{ lexeme: "</".to_string(), category: Category::Text },
            Token{ lexeme: "tag_with_attribute".to_string(), category: Category::Identifier },
            Token{ lexeme: ">".to_string(), category: Category::Text },
            Token{ lexeme: "\n  ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "<".to_string(), category: Category::Text },
            Token{ lexeme: "self_closing_tag".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "/>".to_string(), category: Category::Text },
            Token{ lexeme: "\n".to_string(), category: Category::Whitespace },
            Token{ lexeme: "</".to_string(), category: Category::Text },
            Token{ lexeme: "tag".to_string(), category: Category::Identifier },
            Token{ lexeme: ">".to_string(), category: Category::Text },
            Token{ lexeme: "\n".to_string(), category: Category::Whitespace },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }

    #[test]
    fn it_can_handle_garbage() {
        let tokens = lex("} adwyx123&*_ ");
        let expected_tokens = vec![
            Token{ lexeme: "}".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "adwyx123&*_".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }

    #[test]
    fn it_can_handle_open_strings() {
        let tokens = lex("<tag \"open!>");
        let expected_tokens = vec![
            Token{ lexeme: "<".to_string(), category: Category::Text },
            Token{ lexeme: "tag".to_string(), category: Category::Identifier },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "\"open!>".to_string(), category: Category::String },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }

    #[test]
    fn it_can_handle_utf8_data() {
        let tokens = lex("différent");
        let expected_tokens = vec![
            Token{ lexeme: "différent".to_string(), category: Category::Text },
        ];

        for (index, token) in tokens.iter().enumerate() {
            assert_eq!(*token, expected_tokens[index]);
        }
    }
}
<|endoftext|>


<commit_before>//Copyright 2016 secret-service-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

// key exchange and crypto for session:
// 1. Before session negotiation (openSession), set private key and public key using DH method.
// 2. In session negotiation, send public key.
// 3. As result of session negotiation, get object path for session, which (I think
//      it means that it uses the same server public key to create an aes key which is used
//      to decode the encoded secret using the aes seed that's sent with the secret).
// 4. As result of session negotition, get server public key.
// 5. Use server public key, my private key, to set an aes key using HKDF.
// 6. Format Secret: aes iv is random seed, in secret struct it's the parameter (Array(Byte))
// 7. Format Secret: encode the secret value for the value field in secret struct.
//      This encoding uses the aes_key from the associated Session.

use proxy::service::ServiceInterfaceProxy;
use ss::ALGORITHM_PLAIN;

use sha2::Sha256;
use hkdf::Hkdf;
use num::bigint::BigUint;
use num::traits::{One, Zero};
use num::integer::Integer;
use num::FromPrimitive;
use rand::{Rng, rngs::OsRng};
use std::convert::TryInto;
use zvariant::OwnedObjectPath;

use std::ops::{Mul, Rem, Shr};

// for key exchange
lazy_static! {
    pub static ref DH_GENERATOR: BigUint = BigUint::from_u64(0x2).unwrap();
    pub static ref DH_PRIME: BigUint = BigUint::from_bytes_be(&[
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
    ]);
}

#[derive(Debug, PartialEq)]
pub enum EncryptionType {
    Plain,
    Dh,
}

#[derive(Debug)]
pub struct Session {
    pub object_path: OwnedObjectPath,
    encrypted: bool,
    server_public_key: Option<Vec<u8>>,
    aes_key: Option<Vec<u8>>,
    my_private_key: Option<Vec<u8>>,
    my_public_key: Option<Vec<u8>>,
}

impl Session {
    pub fn new(service_interface: &ServiceInterfaceProxy, encryption: EncryptionType) -> ::Result<Self> {
        match encryption {
            EncryptionType::Plain => {
                let session= service_interface.open_session(
                    ALGORITHM_PLAIN,
                    "".into(),
                )?;
                let session_path = session.result;

                Ok(Session {
                    object_path: session_path,
                    encrypted: false,
                    server_public_key: None,
                    aes_key: None,
                    my_private_key: None,
                    my_public_key: None,
                })
            },
            EncryptionType::Dh => {
                // crypto: create private and public key, send public key
                // requires some finagling to get pow() for bigints
                let mut rng = OsRng {};
                let mut private_key_bytes = [0;128];
                rng.fill(&mut private_key_bytes);

                let private_key = BigUint::from_bytes_be(&private_key_bytes);
                let public_key = powm(&DH_GENERATOR, &private_key, &DH_PRIME);

                let public_key_bytes = public_key.to_bytes_be();

                let session= service_interface.open_session(
                    ALGORITHM_PLAIN,
                    public_key_bytes.as_slice().into(),
                )?;
                let server_public_key: Vec<_> = session.output.try_into()?;
                let session_path = session.result;

                // Set aes key from server key
                // TODO: Don't store keys except for aes?
                let server_public_key = BigUint::from_bytes_be(&server_public_key);
                let server_public_key_bytes = server_public_key.to_bytes_be();
                let common_secret = powm(&server_public_key, &private_key, &DH_PRIME);

                let mut common_secret_bytes = common_secret.to_bytes_be();
                let mut common_secret_padded = vec![0; 128 - common_secret_bytes.len()];
                //inefficient, but ok for now
                common_secret_padded.append(&mut common_secret_bytes);

                // hkdf

                // input_keying_material
                let ikm = common_secret_padded;
                let salt = None;
                let info = [];

                // output keying material
                let mut okm = [0;16];

                let (_, hk) = Hkdf::<Sha256>::extract(salt, &ikm);
                hk.expand(&info, &mut okm).expect("hkdf expand should never fail");

                let aes_key = okm.to_vec();

                Ok(Session {
                    object_path: session_path,
                    encrypted: true,
                    server_public_key: Some(server_public_key_bytes),
                    aes_key: Some(aes_key),
                    my_private_key: Some(private_key_bytes.to_vec()),
                    my_public_key: Some(public_key_bytes.to_vec()),
                })
            },
        }

    }

    pub fn is_encrypted(&self) -> bool {
        self.encrypted
    }

    pub fn get_aes_key(&self) -> Vec<u8> {
        self.aes_key.clone().unwrap()
    }
}

/// from https://github.com/plietar/librespot/blob/master/core/src/util/mod.rs#L53
fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
    let mut base = base.clone();
    let mut exp = exp.clone();
    let mut result: BigUint = One::one();

    while !exp.is_zero() {
        if exp.is_odd() {
            result = result.mul(&base).rem(modulus);
        }
        exp = exp.shr(1);
        base = (&base).mul(&base).rem(modulus);
    }

    result
}

#[cfg(test)]
mod test {
    use super::*;
    use zbus;

    #[test]
    fn should_create_plain_session() {
        let conn = zbus::Connection::new_session().unwrap();
        let service_interface = ServiceInterfaceProxy::new(&conn).unwrap();
        let session = Session::new(&service_interface, EncryptionType::Plain).unwrap();
        assert!(!session.is_encrypted());
    }

    #[test]
    fn should_create_encrypted_session() {
        let conn = zbus::Connection::new_session().unwrap();
        let service_interface = ServiceInterfaceProxy::new(&conn).unwrap();
        let session = Session::new(&service_interface, EncryptionType::Dh).unwrap();
        assert!(session.is_encrypted());
    }
}
<commit_msg>fix encryption type constant typo<commit_after>//Copyright 2016 secret-service-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

// key exchange and crypto for session:
// 1. Before session negotiation (openSession), set private key and public key using DH method.
// 2. In session negotiation, send public key.
// 3. As result of session negotiation, get object path for session, which (I think
//      it means that it uses the same server public key to create an aes key which is used
//      to decode the encoded secret using the aes seed that's sent with the secret).
// 4. As result of session negotition, get server public key.
// 5. Use server public key, my private key, to set an aes key using HKDF.
// 6. Format Secret: aes iv is random seed, in secret struct it's the parameter (Array(Byte))
// 7. Format Secret: encode the secret value for the value field in secret struct.
//      This encoding uses the aes_key from the associated Session.

use proxy::service::ServiceInterfaceProxy;
use ss::{ALGORITHM_DH, ALGORITHM_PLAIN};

use sha2::Sha256;
use hkdf::Hkdf;
use num::bigint::BigUint;
use num::traits::{One, Zero};
use num::integer::Integer;
use num::FromPrimitive;
use rand::{Rng, rngs::OsRng};
use std::convert::TryInto;
use zvariant::OwnedObjectPath;

use std::ops::{Mul, Rem, Shr};

// for key exchange
lazy_static! {
    pub static ref DH_GENERATOR: BigUint = BigUint::from_u64(0x2).unwrap();
    pub static ref DH_PRIME: BigUint = BigUint::from_bytes_be(&[
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
    ]);
}

#[derive(Debug, PartialEq)]
pub enum EncryptionType {
    Plain,
    Dh,
}

#[derive(Debug)]
pub struct Session {
    pub object_path: OwnedObjectPath,
    encrypted: bool,
    server_public_key: Option<Vec<u8>>,
    aes_key: Option<Vec<u8>>,
    my_private_key: Option<Vec<u8>>,
    my_public_key: Option<Vec<u8>>,
}

impl Session {
    pub fn new(service_interface: &ServiceInterfaceProxy, encryption: EncryptionType) -> ::Result<Self> {
        match encryption {
            EncryptionType::Plain => {
                let session= service_interface.open_session(
                    ALGORITHM_PLAIN,
                    "".into(),
                )?;
                let session_path = session.result;

                Ok(Session {
                    object_path: session_path,
                    encrypted: false,
                    server_public_key: None,
                    aes_key: None,
                    my_private_key: None,
                    my_public_key: None,
                })
            },
            EncryptionType::Dh => {
                // crypto: create private and public key, send public key
                // requires some finagling to get pow() for bigints
                let mut rng = OsRng {};
                let mut private_key_bytes = [0;128];
                rng.fill(&mut private_key_bytes);

                let private_key = BigUint::from_bytes_be(&private_key_bytes);
                let public_key = powm(&DH_GENERATOR, &private_key, &DH_PRIME);

                let public_key_bytes = public_key.to_bytes_be();

                let session= service_interface.open_session(
                    ALGORITHM_DH,
                    public_key_bytes.as_slice().into(),
                )?;
                let server_public_key: Vec<_> = session.output.try_into()?;
                let session_path = session.result;

                // Set aes key from server key
                // TODO: Don't store keys except for aes?
                let server_public_key = BigUint::from_bytes_be(&server_public_key);
                let server_public_key_bytes = server_public_key.to_bytes_be();
                let common_secret = powm(&server_public_key, &private_key, &DH_PRIME);

                let mut common_secret_bytes = common_secret.to_bytes_be();
                let mut common_secret_padded = vec![0; 128 - common_secret_bytes.len()];
                //inefficient, but ok for now
                common_secret_padded.append(&mut common_secret_bytes);

                // hkdf

                // input_keying_material
                let ikm = common_secret_padded;
                let salt = None;
                let info = [];

                // output keying material
                let mut okm = [0;16];

                let (_, hk) = Hkdf::<Sha256>::extract(salt, &ikm);
                hk.expand(&info, &mut okm).expect("hkdf expand should never fail");

                let aes_key = okm.to_vec();

                Ok(Session {
                    object_path: session_path,
                    encrypted: true,
                    server_public_key: Some(server_public_key_bytes),
                    aes_key: Some(aes_key),
                    my_private_key: Some(private_key_bytes.to_vec()),
                    my_public_key: Some(public_key_bytes.to_vec()),
                })
            },
        }

    }

    pub fn is_encrypted(&self) -> bool {
        self.encrypted
    }

    pub fn get_aes_key(&self) -> Vec<u8> {
        self.aes_key.clone().unwrap()
    }
}

/// from https://github.com/plietar/librespot/blob/master/core/src/util/mod.rs#L53
fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
    let mut base = base.clone();
    let mut exp = exp.clone();
    let mut result: BigUint = One::one();

    while !exp.is_zero() {
        if exp.is_odd() {
            result = result.mul(&base).rem(modulus);
        }
        exp = exp.shr(1);
        base = (&base).mul(&base).rem(modulus);
    }

    result
}

#[cfg(test)]
mod test {
    use super::*;
    use zbus;

    #[test]
    fn should_create_plain_session() {
        let conn = zbus::Connection::new_session().unwrap();
        let service_interface = ServiceInterfaceProxy::new(&conn).unwrap();
        let session = Session::new(&service_interface, EncryptionType::Plain).unwrap();
        assert!(!session.is_encrypted());
    }

    #[test]
    fn should_create_encrypted_session() {
        let conn = zbus::Connection::new_session().unwrap();
        let service_interface = ServiceInterfaceProxy::new(&conn).unwrap();
        let session = Session::new(&service_interface, EncryptionType::Dh).unwrap();
        assert!(session.is_encrypted());
    }
}
<|endoftext|>