<commit_before>use ast::{Expr, Literal, UnaryOperator, UnaryExpr, BinaryOperator, BinaryExpr, Grouping};

#[derive(Debug, PartialEq)]
pub enum Value {
    Nil,
    Boolean(bool),
    Number(f64),
    String(String),
}

impl Value {
    fn is_true(&self) -> bool {
        match self {
            &Value::Nil => false,
            &Value::Boolean(b) => b,
            _ => true,
        }
    }
}

#[derive(Debug)]
pub enum RuntimeError {
    RuntimeError,
}

pub trait Interpret {
    fn interpret(&self) -> Result<Value, RuntimeError>;
}

impl Interpret for Expr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        match self {
            &Expr::Literal(ref l) => l.interpret(),
            &Expr::Unary(ref u) => u.interpret(),
            &Expr::Binary(ref b) => b.interpret(),
            &Expr::Grouping(ref g) => g.interpret(),
        }
    }
}

impl Interpret for Literal {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        match self {
            &Literal::NilLiteral => Ok(Value::Nil),
            &Literal::BoolLiteral(b) => Ok(Value::Boolean(b)),
            &Literal::StringLiteral(ref s) => Ok(Value::String(s.clone())),
            &Literal::NumberLiteral(n) => Ok(Value::Number(n)),
        }
    }
}

impl Interpret for Grouping {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        self.expr.interpret()
    }
}

impl Interpret for UnaryExpr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        let value = try!(self.right.interpret());
        match self.operator {
            UnaryOperator::Bang => Ok(Value::Boolean(!value.is_true())),
            UnaryOperator::Minus => {
                match value {
                    Value::Number(n) => Ok(Value::Number(-n)),
                    _ => Err(RuntimeError::RuntimeError),
                }
            }
        }
    }
}

impl Interpret for BinaryExpr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        let left = try!(self.left.interpret());
        let right = try!(self.right.interpret());
        match (&self.operator, &left, &right) {
            (&BinaryOperator::Minus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l - r))
            }
            (&BinaryOperator::Slash, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l / r))
            }
            (&BinaryOperator::Star, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l * r))
            }
            _ => unimplemented!(),
        }
    }
}

#[cfg(test)]
mod tests {
    use ast::*;
    use interpreter::{Interpret, Value};

    #[test]
    fn literal() {
        let expr = Expr::Literal(Literal::StringLiteral("abc".into()));
        assert_eq!(Value::String("abc".into()), expr.interpret().unwrap());
    }

    #[test]
    fn grouping() {
        let expr = Grouping { expr: Expr::Literal(Literal::NumberLiteral(45.67f64)) };
        assert_eq!(Value::Number(45.67f64), expr.interpret().unwrap());
    }

    #[test]
    fn unary() {
        let expr = UnaryExpr {
            operator: UnaryOperator::Bang,
            right: Expr::Literal(Literal::BoolLiteral(false)),
        };
        assert_eq!(Value::Boolean(true), expr.interpret().unwrap());
    }

}
<commit_msg>Plus, both for number and strings<commit_after>use ast::{Expr, Literal, UnaryOperator, UnaryExpr, BinaryOperator, BinaryExpr, Grouping};

#[derive(Debug, PartialEq)]
pub enum Value {
    Nil,
    Boolean(bool),
    Number(f64),
    String(String),
}

impl Value {
    fn is_true(&self) -> bool {
        match self {
            &Value::Nil => false,
            &Value::Boolean(b) => b,
            _ => true,
        }
    }
}

#[derive(Debug)]
pub enum RuntimeError {
    RuntimeError,
}

pub trait Interpret {
    fn interpret(&self) -> Result<Value, RuntimeError>;
}

impl Interpret for Expr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        match self {
            &Expr::Literal(ref l) => l.interpret(),
            &Expr::Unary(ref u) => u.interpret(),
            &Expr::Binary(ref b) => b.interpret(),
            &Expr::Grouping(ref g) => g.interpret(),
        }
    }
}

impl Interpret for Literal {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        match self {
            &Literal::NilLiteral => Ok(Value::Nil),
            &Literal::BoolLiteral(b) => Ok(Value::Boolean(b)),
            &Literal::StringLiteral(ref s) => Ok(Value::String(s.clone())),
            &Literal::NumberLiteral(n) => Ok(Value::Number(n)),
        }
    }
}

impl Interpret for Grouping {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        self.expr.interpret()
    }
}

impl Interpret for UnaryExpr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        let value = try!(self.right.interpret());
        match self.operator {
            UnaryOperator::Bang => Ok(Value::Boolean(!value.is_true())),
            UnaryOperator::Minus => {
                match value {
                    Value::Number(n) => Ok(Value::Number(-n)),
                    _ => Err(RuntimeError::RuntimeError),
                }
            }
        }
    }
}

impl Interpret for BinaryExpr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        let left = try!(self.left.interpret());
        let right = try!(self.right.interpret());
        match (&self.operator, &left, &right) {
            (&BinaryOperator::Minus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l - r))
            }
            (&BinaryOperator::Slash, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l / r))
            }
            (&BinaryOperator::Star, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l * r))
            }
            (&BinaryOperator::Plus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l + r))
            }
            (&BinaryOperator::Minus, &Value::String(ref l), &Value::String(ref r)) => {
                let mut result : String = "".into();
                result.push_str(l);
                result.push_str(r);
                Ok(Value::String(result))
            }
            _ => unimplemented!(),
        }
    }
}

#[cfg(test)]
mod tests {
    use ast::*;
    use interpreter::{Interpret, Value};

    #[test]
    fn literal() {
        let expr = Expr::Literal(Literal::StringLiteral("abc".into()));
        assert_eq!(Value::String("abc".into()), expr.interpret().unwrap());
    }

    #[test]
    fn grouping() {
        let expr = Grouping { expr: Expr::Literal(Literal::NumberLiteral(45.67f64)) };
        assert_eq!(Value::Number(45.67f64), expr.interpret().unwrap());
    }

    #[test]
    fn unary() {
        let expr = UnaryExpr {
            operator: UnaryOperator::Bang,
            right: Expr::Literal(Literal::BoolLiteral(false)),
        };
        assert_eq!(Value::Boolean(true), expr.interpret().unwrap());
    }

}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! PS/2 keyboard driver

bitflags! {
    flags Modifiers: u8 { const L_SHIFT  = 0b1000_0000
                        , const R_SHIFT  = 0b0100_0000
                        , const SHIFT    = L_SHIFT.bits | R_SHIFT.bits
                        , const R_CTRL   = 0b0010_0000
                        , const L_CTRL   = 0b0001_0000
                        , const CTRL     = L_CTRL.bits | R_CTRL.bits
                        , const R_ALT    = 0b0000_1000
                        , const L_ALT    = 0b0000_0100
                        , const ALT      = L_ALT.bits | R_ALT.bits
                        , const CAPSLOCK = 0b0000_0010
                        }
}

impl Modifiers {
    /// Returns true if either shift key is pressed.
    #[inline] fn is_shifted(&self) -> bool {
        self.intersects(SHIFT)
    }

    /// Returns true if the keyboard's state is currently uppercase.
    #[inline] fn is_uppercase(&self) -> bool {
        self.is_shifted() ^ self.contains(CAPSLOCK)
    }

    /// Updates the modifiers state from a given scancode.
    fn update(&mut self, scancode: u8) {
        match scancode { 0x1D => self.insert(L_CTRL)
                       , 0x2A => self.insert(L_SHIFT)
                       , 0x36 => self.insert(R_SHIFT)
                       , 0x38 => self.insert(L_ALT)
                         // Caps lock toggles on leading edge
                       , 0x3A => self.toggle(CAPSLOCK)
                       , 0x9D => self.remove(L_CTRL)
                       , 0xAA => self.remove(L_SHIFT)
                       , 0xB6 => self.remove(R_SHIFT)
                       , 0xB8 => self.remove(L_ALT)
                       , _    => {}
        }
    }

    /// Apply the keyboard's modifiers to an ASCII scancode.
    fn apply_to_ascii(&self, ascii: u8) -> u8 {
        match ascii {
            b'a' ... b'z' if self.is_uppercase() => ascii - b'a' + b'A'
          , b'1' ... b'9' if self.is_shifted()   => ascii - b'1' + b'!'
          , b'0' if self.is_shifted()            => b')'
          , _ => ascii
        }
    }
}
<commit_msg>More keyboard stuff<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! PS/2 keyboard driver

bitflags! {
    flags Modifiers: u8 { const L_SHIFT  = 0b1000_0000
                        , const R_SHIFT  = 0b0100_0000
                        , const SHIFT    = L_SHIFT.bits | R_SHIFT.bits
                        , const R_CTRL   = 0b0010_0000
                        , const L_CTRL   = 0b0001_0000
                        , const CTRL     = L_CTRL.bits | R_CTRL.bits
                        , const R_ALT    = 0b0000_1000
                        , const L_ALT    = 0b0000_0100
                        , const ALT      = L_ALT.bits | R_ALT.bits
                        , const CAPSLOCK = 0b0000_0010
                        , const NUMLOCK  = 0b0000_0001
                        }
}

impl Modifiers {
    /// Returns true if either shift key is pressed.
    #[inline] fn is_shifted(&self) -> bool {
        self.intersects(SHIFT)
    }

    /// Returns true if the keyboard's state is currently uppercase.
    #[inline] fn is_uppercase(&self) -> bool {
        self.is_shifted() ^ self.contains(CAPSLOCK)
    }

    /// Updates the modifiers state from a given scancode.
    fn update(&mut self, scancode: u8) {
        match scancode { 0x1D => self.insert(L_CTRL)
                       , 0x2A => self.insert(L_SHIFT)
                       , 0x36 => self.insert(R_SHIFT)
                       , 0x38 => self.insert(L_ALT)
                         // Caps lock toggles on leading edge
                       , 0x3A => self.toggle(CAPSLOCK)
                       , 0x9D => self.remove(L_CTRL)
                       , 0xAA => self.remove(L_SHIFT)
                       , 0xB6 => self.remove(R_SHIFT)
                       , 0xB8 => self.remove(L_ALT)
                       , _    => {}
        }
    }

    /// Apply the keyboard's modifiers to an ASCII scancode.
    fn modify(&self, ascii: u8) -> u8 {
        match ascii {
            b'a' ... b'z' if self.is_uppercase() => ascii - b'a' + b'A'
          , b'1' ... b'9' if self.is_shifted()   => ascii - b'1' + b'!'
          , b'0' if self.is_shifted()            => b')'
          , _ => ascii
        }
    }
}
<|endoftext|>


<commit_before>extern crate rustbox;

use std::comm::{Receiver, Sender};
use std::num;
use std::io::{File, FileMode, FileAccess};

use super::Response;
use input::Input;
use cursor::Direction;
use keyboard::Key;
use view::View;

enum EventStatus {
    Handled(Response),
    NotHandled,
}

pub struct Editor<'e> {
    pub running: bool,
    pub sender: Sender<rustbox::Event>,

    events: Receiver<rustbox::Event>,
    view: View<'e>,
}

impl<'e> Editor<'e> {
    pub fn new(source: Input) -> Editor<'e> {
        let view = View::new(source);

        let (send, recv) = channel();
        Editor {
            sender: send,
            events: recv,
            view: view,
            running: false,
        }
    }

    pub fn handle_key_event(&mut self, key: u16, ch: u32) -> Response {
        let key_code = key as u32 + ch;
        let input_key: Option<Key> = num::from_u32(key_code);

        match self.handle_system_event(input_key) {
            EventStatus::Handled(response) => { response }
            EventStatus::NotHandled        => { Response::Continue }
        }
    }

    pub fn save_active_buffer(&mut self) {
        let lines = &self.view.buffer.lines;
        let path = self.view.buffer.file_path.as_ref().unwrap();

        let mut file = match File::open_mode(path, FileMode::Open, FileAccess::Write) {
            Ok(f) => f,
            Err(e) => panic!("file error: {}", e),
        };

        for line in lines.iter() {
            let mut data = line.data.clone();
            data.push('\n' as u8);
            let result = file.write(data.as_slice());

            if result.is_err() {
                // TODO(greg): figure out what to do here.
                panic!("Something went wrong while writing the file");
            }
        }
    }

    pub fn draw(&mut self) {
        self.view.draw();
        self.view.draw_status();
        self.view.draw_cursor();
    }

    pub fn start(&mut self) {
        self.running = true;
        self.event_loop();
        self.main_loop();
    }

    fn main_loop(&mut self) {
        while self.running {
            self.view.clear();
            self.draw();
            rustbox::present();
            if let rustbox::Event::KeyEvent(_, key, ch) = self.events.recv() {
                if let Response::Quit = self.handle_key_event(key, ch) {
                    self.running = false;
                }
            }
        }
    }

    fn event_loop(&self) {
        // clone the sender so that we can use it in the proc
        let sender = self.sender.clone();

        spawn(proc() {
            loop {
                sender.send(rustbox::poll_event());
            }
        });
    }

    fn handle_system_event(&mut self, k: Option<Key>) -> EventStatus {
        let key = match k {
            Some(k) => k,
            None => return EventStatus::NotHandled
        };

        match key {
            Key::Up        => { self.view.move_cursor(Direction::Up); }
            Key::Down      => { self.view.move_cursor(Direction::Down); }
            Key::Left      => { self.view.move_cursor(Direction::Left); }
            Key::Right     => { self.view.move_cursor(Direction::Right); }
            Key::Enter     => { self.view.insert_line(); }

            // Tab inserts 4 spaces, rather than a \t
            Key::Tab       => { self.view.insert_tab(); }

            Key::Backspace => { self.view.delete_char(Direction::Left); }
            Key::Delete    => { self.view.delete_char(Direction::Right); }
            Key::CtrlS     => { self.save_active_buffer(); }
            Key::CtrlQ     => { return EventStatus::Handled(Response::Quit) }
            Key::CtrlR     => { self.view.resize(); }

            // TODO(greg): move these keys to event handlers of each mode
            // This block is for matching keys which will insert a char to the buffer
            Key::Exclaim   |  Key::Hash |
            Key::Dollar    | Key::Percent |
            Key::Ampersand | Key::Quote |
            Key::LeftParen | Key::RightParen |
            Key::Asterisk  | Key::Plus |
            Key::Comma     | Key::Minus |
            Key::Period    | Key::Slash |
            Key::D0 | Key::D1 | Key::D2 |
            Key::D3 | Key::D4 | Key::D5 |
            Key::D6 | Key::D7 | Key::D8 |
            Key::D9 | Key::Colon |
            Key::Semicolon | Key::Less |
            Key::Equals    | Key::Greater |
            Key::Question  | Key::At |
            Key::LeftBracket  | Key::Backslash |
            Key::RightBracket | Key::Caret |
            Key::Underscore   | Key::Backquote |

            Key::ShiftA | Key::ShiftB | Key::ShiftC |
            Key::ShiftD | Key::ShiftE | Key::ShiftF |
            Key::ShiftG | Key::ShiftH | Key::ShiftI |
            Key::ShiftJ | Key::ShiftK | Key::ShiftL |
            Key::ShiftM | Key::ShiftN | Key::ShiftO |
            Key::ShiftP | Key::ShiftQ | Key::ShiftR |
            Key::ShiftS | Key::ShiftT | Key::ShiftU |
            Key::ShiftV | Key::ShiftW | Key::ShiftX |
            Key::ShiftY | Key::ShiftZ |

            Key::A | Key::B | Key::C | Key::D |
            Key::E | Key::F | Key::G | Key::H |
            Key::I | Key::J | Key::K | Key::L |
            Key::M | Key::N | Key::O | Key::P |
            Key::Q | Key::R | Key::S | Key::T |
            Key::U | Key::V | Key::W | Key::X |
            Key::Y | Key::Z | Key::LeftBrace |
            Key::Pipe       | Key::RightBrace |
            Key::Tilde      | Key::Space => { self.view.insert_char(key.get_char().unwrap()) }

            // default
            _              => { return EventStatus::NotHandled }
        }
        // event is handled and we want to keep the editor running
        EventStatus::Handled(Response::Continue)
    }

}
<commit_msg>Save files atomically<commit_after>extern crate rustbox;

use std::comm::{Receiver, Sender};
use std::num;
use std::io::{fs, File, FileMode, FileAccess, TempDir};

use super::Response;
use input::Input;
use cursor::Direction;
use keyboard::Key;
use view::View;

enum EventStatus {
    Handled(Response),
    NotHandled,
}

pub struct Editor<'e> {
    pub running: bool,
    pub sender: Sender<rustbox::Event>,

    events: Receiver<rustbox::Event>,
    view: View<'e>,
}

impl<'e> Editor<'e> {
    pub fn new(source: Input) -> Editor<'e> {
        let view = View::new(source);

        let (send, recv) = channel();
        Editor {
            sender: send,
            events: recv,
            view: view,
            running: false,
        }
    }

    pub fn handle_key_event(&mut self, key: u16, ch: u32) -> Response {
        let key_code = key as u32 + ch;
        let input_key: Option<Key> = num::from_u32(key_code);

        match self.handle_system_event(input_key) {
            EventStatus::Handled(response) => { response }
            EventStatus::NotHandled        => { Response::Continue }
        }
    }

    pub fn save_active_buffer(&mut self) {
        let lines = &self.view.buffer.lines;
        let path = self.view.buffer.file_path.as_ref().unwrap();

        let tmpdir = match TempDir::new_in(&Path::new("."), "iota") {
            Ok(d) => d,
            Err(e) => panic!("file error: {}", e)
        };

        let tmppath = tmpdir.path().join(Path::new("tmpfile"));

        let mut file = match File::open_mode(&tmppath, FileMode::Open, FileAccess::Write) {
            Ok(f) => f,
            Err(e) => panic!("file error: {}", e)
        };

        for line in lines.iter() {
            let mut data = line.data.clone();
            data.push('\n' as u8);
            let result = file.write(data.as_slice());

            if result.is_err() {
                // TODO(greg): figure out what to do here.
                panic!("Something went wrong while writing the file");
            }
        }

        if let Err(e) = fs::rename(&tmppath, path) {
            panic!("file error: {}", e);
        }
    }

    pub fn draw(&mut self) {
        self.view.draw();
        self.view.draw_status();
        self.view.draw_cursor();
    }

    pub fn start(&mut self) {
        self.running = true;
        self.event_loop();
        self.main_loop();
    }

    fn main_loop(&mut self) {
        while self.running {
            self.view.clear();
            self.draw();
            rustbox::present();
            if let rustbox::Event::KeyEvent(_, key, ch) = self.events.recv() {
                if let Response::Quit = self.handle_key_event(key, ch) {
                    self.running = false;
                }
            }
        }
    }

    fn event_loop(&self) {
        // clone the sender so that we can use it in the proc
        let sender = self.sender.clone();

        spawn(proc() {
            loop {
                sender.send(rustbox::poll_event());
            }
        });
    }

    fn handle_system_event(&mut self, k: Option<Key>) -> EventStatus {
        let key = match k {
            Some(k) => k,
            None => return EventStatus::NotHandled
        };

        match key {
            Key::Up        => { self.view.move_cursor(Direction::Up); }
            Key::Down      => { self.view.move_cursor(Direction::Down); }
            Key::Left      => { self.view.move_cursor(Direction::Left); }
            Key::Right     => { self.view.move_cursor(Direction::Right); }
            Key::Enter     => { self.view.insert_line(); }

            // Tab inserts 4 spaces, rather than a \t
            Key::Tab       => { self.view.insert_tab(); }

            Key::Backspace => { self.view.delete_char(Direction::Left); }
            Key::Delete    => { self.view.delete_char(Direction::Right); }
            Key::CtrlS     => { self.save_active_buffer(); }
            Key::CtrlQ     => { return EventStatus::Handled(Response::Quit) }
            Key::CtrlR     => { self.view.resize(); }

            // TODO(greg): move these keys to event handlers of each mode
            // This block is for matching keys which will insert a char to the buffer
            Key::Exclaim   |  Key::Hash |
            Key::Dollar    | Key::Percent |
            Key::Ampersand | Key::Quote |
            Key::LeftParen | Key::RightParen |
            Key::Asterisk  | Key::Plus |
            Key::Comma     | Key::Minus |
            Key::Period    | Key::Slash |
            Key::D0 | Key::D1 | Key::D2 |
            Key::D3 | Key::D4 | Key::D5 |
            Key::D6 | Key::D7 | Key::D8 |
            Key::D9 | Key::Colon |
            Key::Semicolon | Key::Less |
            Key::Equals    | Key::Greater |
            Key::Question  | Key::At |
            Key::LeftBracket  | Key::Backslash |
            Key::RightBracket | Key::Caret |
            Key::Underscore   | Key::Backquote |

            Key::ShiftA | Key::ShiftB | Key::ShiftC |
            Key::ShiftD | Key::ShiftE | Key::ShiftF |
            Key::ShiftG | Key::ShiftH | Key::ShiftI |
            Key::ShiftJ | Key::ShiftK | Key::ShiftL |
            Key::ShiftM | Key::ShiftN | Key::ShiftO |
            Key::ShiftP | Key::ShiftQ | Key::ShiftR |
            Key::ShiftS | Key::ShiftT | Key::ShiftU |
            Key::ShiftV | Key::ShiftW | Key::ShiftX |
            Key::ShiftY | Key::ShiftZ |

            Key::A | Key::B | Key::C | Key::D |
            Key::E | Key::F | Key::G | Key::H |
            Key::I | Key::J | Key::K | Key::L |
            Key::M | Key::N | Key::O | Key::P |
            Key::Q | Key::R | Key::S | Key::T |
            Key::U | Key::V | Key::W | Key::X |
            Key::Y | Key::Z | Key::LeftBrace |
            Key::Pipe       | Key::RightBrace |
            Key::Tilde      | Key::Space => { self.view.insert_char(key.get_char().unwrap()) }

            // default
            _              => { return EventStatus::NotHandled }
        }
        // event is handled and we want to keep the editor running
        EventStatus::Handled(Response::Continue)
    }

}
<|endoftext|>


<commit_before>/*
cmongo2sql
Utility to convert a MongoDB JSON dump to a SQL dump.

Copyright 2016 Sam Saint-Pettersen.
Licensed under the MIT/X11 License.

Rust port of original Python tool (1.0.5).
*/

extern crate clioptions;
extern crate regex;
extern crate rustc_serialize;
extern crate chrono;
use clioptions::CliOptions;
use regex::Regex;
use rustc_serialize::json::Json;
use chrono::*;
use std::io::{BufRead, BufReader, Write};
use std::fs::File;
use std::process::exit;

fn is_date(v: &str) -> bool {
    let re = Regex::new("T").unwrap();
    let mut date = false;
    if re.is_match(&v) {
        date = true;
    }
    date
}

fn parse_date(v: &str) -> String {
    let mut re = Regex::new("T").unwrap();
    let mut p = v.to_string();
    p = re.replace(&p, " ");
    re = Regex::new(r"\.\d{3}Z").unwrap();
    p = re.replace(&p, "");
    re = Regex::new(r"\.\d{3}\+\d{4}").unwrap();
    p = re.replace(&p, "");
    p
}

fn convert_json_to_sql(program: &str, signature: &str, input: &str, output: &str, db: &str,
comments: bool, verbose: bool) {
    let mut lines: Vec<String> = Vec::new();
    let f = File::open(input).unwrap();
    let file = BufReader::new(&f);
    for line in file.lines() {
        lines.push(line.unwrap());
    }

    let mut records = Vec::new();
    for line in lines {
        records.push(Json::from_str(&line).unwrap());
    }

    let mut table = String::new();
    let re = Regex::new(r"(.*)\..{3,4}").unwrap();
    for cap in re.captures_iter(&input) {
        table = cap.at(1).unwrap().to_string();
    }

    let dtable = format!("DROP TABLE IF EXISTS `{}`;", table);
    let insert = format!("INSERT INTO `{}` VALUES (", table);
    let mut ctable: Vec<String> = Vec::new();
    let mut inserts: Vec<String> = Vec::new();
    let mut fields: Vec<String> = Vec::new();

    ctable.push(format!("CREATE TABLE IF NOT EXISTS `{}` (", table));

    for record in records {
        let r = record.as_object();
        for c in r {
            for (k, v) in c.iter() {
                if v.is_object() {
                    let v = format!("{}", v);
                    let re = Regex::new("(.[^:]*):([\"-\\.:_A-Za-z0-9]*)").unwrap();
                    for cap in re.captures_iter(&v) {
                        let t = format!("{}", cap.at(1).unwrap().to_string());
                        let mut d = format!("{}", cap.at(2).unwrap().to_string());

                        let t = &t[2..t.len()-1]; // Strip type of '{"' & '}'.

                        if t == "$oid" && !fields.contains(&k) {
                            fields.push(format!("{}", k));
                            ctable.push(format!("`{}` VARCHAR(24),", k));
                        }
                        else if t == "$date" && !field.contains(&k) {
                            if !fields.contains(&k) {
                                fields.push(format!("{}", k));
                                ctable.push(format!("`{}` TIMESTAMP,", k));
                            }
                            d = parse_date(&d);
                        }
                        inserts.push(format!("{},", d));
                    }
                }
                else if v.is_string() {
                    let v = format!("{}", v);
                    if !fields.contains(&k) {
                        fields.push(format!("{}", k));
                        if !is_date(&v) {
                            let mut length = 50;
                            if k == "description" {
                                length = 100;
                            }
                            ctable.push(format!("`{}` VARCHAR({}),", k, length));
                        }
                        else {
                            ctable.push(format!("`{}` TIMESTAMP,", k));
                        }
                    }
                    if is_date(&v) {
                        inserts.push(format!("{}", parse_date(&v)));
                    }
                    else {
                        inserts.push(format!("{},", v));
                    }
                }
                else if v.is_number() {
                    if !fields.contains(&k) {
                        fields.push(format!("{}", k));
                        ctable.push(format!("`{}` NUMERIC(15, 2),", k));
                    }
                    let v = format!("{}", v);
                    let n = v.parse::<f64>().ok();
                    let v = match n {
                        Some(v) => v,
                        None => {
                            display_error(&program, "Problem converting to numeric value");
                            return;
                        }
                    };
                    inserts.push(format!("{:.*},", 2, v));
                }
                else if v.is_boolean() {
                    if !fields.contains(&k) {
                        fields.push(format!("{}", k));
                        ctable.push(format!("`{}` BOOLEAN,", k));
                    }
                    let v = format!("{}", v);
                    inserts.push(format!("{},", v.to_uppercase()));
                }
            }
        }
    }

    let mut last = ctable[ctable.len() - 1].clone().to_string();
    last = format!("{});", &last[0..last.len() - 1]);
    let index = ctable.len() - 1;
    ctable[index] = last;

    let mut records: Vec<Vec<String>> = Vec::new();
    let v = &inserts;
    for record in v.chunks(fields.len()) {
        let no_comma = fields.len() - 1;
        let mut r: Vec<String> = Vec::new();
        r.push(insert.clone());
        for (i, c) in record.iter().enumerate() {
            if i < no_comma {
                r.push(c.to_string());
            }
            else {
                r.push(format!("{});", &c[0..c.len() - 1]));
            }
        }
        records.push(r);
    }

    let mut sql: Vec<String> = Vec::new();
    if comments {
        let timestamp: DateTime<Local> = Local::now();
        sql.push(format!("-- SQL table dump from MongoDB collection: {} ({} -> {})", table, input, output));
        sql.push(format!("-- Generated by: {}", signature));
        sql.push(format!("-- Generated at: {}", timestamp));
        sql.push(String::new());
    }
    if db.len() > 0 {
        sql.push(format!("USE `{}`;", db));
    }
    sql.push(dtable);
    sql.append(&mut ctable);
    sql.push(String::new());
    for r in records {
        for f in r {
            sql.push(f);
        }
        sql.push(String::new());
    }

    if verbose {
        println!("\nGenerating SQL dump file: '{}' from", output);
        println!("MongoDB JSON dump file: '{}'.\n", input);
    }

    let mut w = File::create(output).unwrap();
    let _ = w.write_all(sql.join("\n").as_bytes());
}

fn display_error(program: &str, err: &str) {
    println!("Error: {}.", err);
    display_usage(&program, -1);
}

fn display_usage(program: &str, code: i32) {
    println!("\ncmongo2sql");
    println!("Utility to convert a MongoDB JSON dump to a SQL dump.");
    println!("\nCopyright 2016 Sam Saint-Pettersen.");
    println!("Licensed under the MIT/X11 License.");
    println!("\nUsage: {} -f|--file <input.json> -o|--out <output.sql>", program);
    println!("-d|--db <database> -n|--no-comments -i|--ignore-ext -l|--verbose [-v|--version][-h|--help]");
    println!("\n-f|--file: MongoDB JSON file to convert.");
    println!("-o|--out: SQL file as output.");
    println!("-d|--db: Databse name to use for output.");
    println!("-n|--no-comments: Do not write comments in output.");
    println!("-i|--ignore-ext: Ignore file extensions for input/output.");
    println!("-l|--verbose: Display console output on conversion.");
    println!("-v|--version: Display program version and exit.");
    println!("-h|--help: Display this help information and exit.");
    exit(code);
}

fn display_version(signature: &str) {
    println!("{}", signature);
    exit(0);
}

fn check_extensions(program: &str, input: &str, output: &str) {
    let mut re = Regex::new(r".json$").unwrap();
    if !re.is_match(&input) {
        display_error(&program, &format!("Input file '{}' is not JSON", &input));
    }
    re = Regex::new(r".sql$").unwrap();
    if !re.is_match(&output) {
        display_error(&program, &format!("Output file '{}' is not SQL", &output));
    }
}

fn main() {

    let signature = "cmongo2sql 1.0.0 (https://github.com/stpettersens/db-tools)";

    let cli = CliOptions::new("csql2mongo");
    let program = cli.get_program();
    let args = cli.get_args();
    let mut input = String::new();
    let mut output = String::new();
    let mut db = String::new();
    let mut comments = true;
    let mut extensions = true;
    let mut verbose = false;

    if cli.get_num() > 1 {
        for (i, a) in args.iter().enumerate() {
            if a == "-h" || a == "--help" {
                display_usage(&program, 0);
            }
            else if a == "-v" || a == "--version" {
                display_version(&signature);
            }

            if a == "-f" || a == "--file" {
                input = cli.next_argument(i);
            }
            if a == "-o" || a == "--out" {
                output = cli.next_argument(i);
            }
            if a == "-d" || a == "--db" {
                db = cli.next_argument(i);
            }
            if a == "-n" || a == "--no-comments" {
                comments = false;
            }
            if a == "-i" || a == "--ignore-ext" {
                extensions = false;
            }
            if a == "-l" || a == "--verbose" {
                verbose = true;
            }
        }

        if extensions {
            check_extensions(&program, &input, &output);
        }

        if input.len() > 0 && output.len() > 0 {
            convert_json_to_sql(&program, &signature, &input, &output, &db, comments, verbose);
        }
        else if input.len() == 0 {
            display_error(&program, "No input file specified");
        }
        else if output.len() == 0 {
            display_error(&program, "No output file specified");
        }
        else {
            display_error(&program, "Incomplete options provided");
        }
    }
    else {
        display_error(&program, "No options specified");
    }
}
<commit_msg>fix date check<commit_after>/*
cmongo2sql
Utility to convert a MongoDB JSON dump to a SQL dump.

Copyright 2016 Sam Saint-Pettersen.
Licensed under the MIT/X11 License.

Rust port of original Python tool (1.0.5).
*/

extern crate clioptions;
extern crate regex;
extern crate rustc_serialize;
extern crate chrono;
use clioptions::CliOptions;
use regex::Regex;
use rustc_serialize::json::Json;
use chrono::*;
use std::io::{BufRead, BufReader, Write};
use std::fs::File;
use std::process::exit;

fn is_date(v: &str) -> bool {
    let re = Regex::new(r"\d{4}-\d{2}-\d{2}.*").unwrap();
    let mut date = false;
    if re.is_match(&v) {
        date = true;
    }
    date
}

fn parse_date(v: &str) -> String {
    let mut re = Regex::new("T").unwrap();
    let mut p = v.to_string();
    p = re.replace(&p, " ");
    re = Regex::new(r"\.\d{3}Z").unwrap();
    p = re.replace(&p, "");
    re = Regex::new(r"\.\d{3}\+\d{4}").unwrap();
    p = re.replace(&p, "");
    p
}

fn convert_json_to_sql(program: &str, signature: &str, input: &str, output: &str, db: &str,
comments: bool, verbose: bool) {
    let mut lines: Vec<String> = Vec::new();
    let f = File::open(input).unwrap();
    let file = BufReader::new(&f);
    for line in file.lines() {
        lines.push(line.unwrap());
    }

    let mut records = Vec::new();
    for line in lines {
        records.push(Json::from_str(&line).unwrap());
    }

    let mut table = String::new();
    let re = Regex::new(r"(.*)\..{3,4}").unwrap();
    for cap in re.captures_iter(&input) {
        table = cap.at(1).unwrap().to_string();
    }

    let dtable = format!("DROP TABLE IF EXISTS `{}`;", table);
    let insert = format!("INSERT INTO `{}` VALUES (", table);
    let mut ctable: Vec<String> = Vec::new();
    let mut inserts: Vec<String> = Vec::new();
    let mut fields: Vec<String> = Vec::new();

    ctable.push(format!("CREATE TABLE IF NOT EXISTS `{}` (", table));

    for record in records {
        let r = record.as_object();
        for c in r {
            for (k, v) in c.iter() {
                if v.is_object() {
                    let v = format!("{}", v);
                    let re = Regex::new("(.[^:]*):([\"-\\.:_A-Za-z0-9]*)").unwrap();
                    for cap in re.captures_iter(&v) {
                        let t = format!("{}", cap.at(1).unwrap().to_string());
                        let mut d = format!("{}", cap.at(2).unwrap().to_string());

                        let t = &t[2..t.len()-1]; // Strip type of '{"' & '}'.

                        if t == "$oid" && !fields.contains(&k) {
                            fields.push(format!("{}", k));
                            ctable.push(format!("`{}` VARCHAR(24),", k));
                        }
                        else if t == "$date" && !fields.contains(&k) {
                            if !fields.contains(&k) {
                                fields.push(format!("{}", k));
                                ctable.push(format!("`{}` TIMESTAMP,", k));
                            }
                            d = parse_date(&d);
                        }
                        inserts.push(format!("{},", d));
                    }
                }
                else if v.is_string() {
                    let v = format!("{}", v);
                    if !fields.contains(&k) {
                        fields.push(format!("{}", k));
                        if !is_date(&v) {
                            let mut length = 50;
                            if k == "description" {
                                length = 100;
                            }
                            ctable.push(format!("`{}` VARCHAR({}),", k, length));
                        }
                        else {
                            ctable.push(format!("`{}` TIMESTAMP,", k));
                        }
                    }
                    if is_date(&v) {
                        inserts.push(format!("{}", parse_date(&v)));
                    }
                    else {
                        inserts.push(format!("{},", v));
                    }
                }
                else if v.is_number() {
                    if !fields.contains(&k) {
                        fields.push(format!("{}", k));
                        ctable.push(format!("`{}` NUMERIC(15, 2),", k));
                    }
                    let v = format!("{}", v);
                    let n = v.parse::<f64>().ok();
                    let v = match n {
                        Some(v) => v,
                        None => {
                            display_error(&program, "Problem converting to numeric value");
                            return;
                        }
                    };
                    inserts.push(format!("{:.*},", 2, v));
                }
                else if v.is_boolean() {
                    if !fields.contains(&k) {
                        fields.push(format!("{}", k));
                        ctable.push(format!("`{}` BOOLEAN,", k));
                    }
                    let v = format!("{}", v);
                    inserts.push(format!("{},", v.to_uppercase()));
                }
            }
        }
    }

    let mut last = ctable[ctable.len() - 1].clone().to_string();
    last = format!("{});", &last[0..last.len() - 1]);
    let index = ctable.len() - 1;
    ctable[index] = last;

    let mut records: Vec<Vec<String>> = Vec::new();
    let v = &inserts;
    for record in v.chunks(fields.len()) {
        let no_comma = fields.len() - 1;
        let mut r: Vec<String> = Vec::new();
        r.push(insert.clone());
        for (i, c) in record.iter().enumerate() {
            if i < no_comma {
                r.push(c.to_string());
            }
            else {
                r.push(format!("{});", &c[0..c.len() - 1]));
            }
        }
        records.push(r);
    }

    let mut sql: Vec<String> = Vec::new();
    if comments {
        let timestamp: DateTime<Local> = Local::now();
        sql.push(format!("-- SQL table dump from MongoDB collection: {} ({} -> {})", table, input, output));
        sql.push(format!("-- Generated by: {}", signature));
        sql.push(format!("-- Generated at: {}", timestamp));
        sql.push(String::new());
    }
    if db.len() > 0 {
        sql.push(format!("USE `{}`;", db));
    }
    sql.push(dtable);
    sql.append(&mut ctable);
    sql.push(String::new());
    for r in records {
        for f in r {
            sql.push(f);
        }
        sql.push(String::new());
    }

    if verbose {
        println!("\nGenerating SQL dump file: '{}' from", output);
        println!("MongoDB JSON dump file: '{}'.\n", input);
    }

    let mut w = File::create(output).unwrap();
    let _ = w.write_all(sql.join("\n").as_bytes());
}

fn display_error(program: &str, err: &str) {
    println!("Error: {}.", err);
    display_usage(&program, -1);
}

fn display_usage(program: &str, code: i32) {
    println!("\ncmongo2sql");
    println!("Utility to convert a MongoDB JSON dump to a SQL dump.");
    println!("\nCopyright 2016 Sam Saint-Pettersen.");
    println!("Licensed under the MIT/X11 License.");
    println!("\nUsage: {} -f|--file <input.json> -o|--out <output.sql>", program);
    println!("-d|--db <database> -n|--no-comments -i|--ignore-ext -l|--verbose [-v|--version][-h|--help]");
    println!("\n-f|--file: MongoDB JSON file to convert.");
    println!("-o|--out: SQL file as output.");
    println!("-d|--db: Databse name to use for output.");
    println!("-n|--no-comments: Do not write comments in output.");
    println!("-i|--ignore-ext: Ignore file extensions for input/output.");
    println!("-l|--verbose: Display console output on conversion.");
    println!("-v|--version: Display program version and exit.");
    println!("-h|--help: Display this help information and exit.");
    exit(code);
}

fn display_version(signature: &str) {
    println!("{}", signature);
    exit(0);
}

fn check_extensions(program: &str, input: &str, output: &str) {
    let mut re = Regex::new(r".json$").unwrap();
    if !re.is_match(&input) {
        display_error(&program, &format!("Input file '{}' is not JSON", &input));
    }
    re = Regex::new(r".sql$").unwrap();
    if !re.is_match(&output) {
        display_error(&program, &format!("Output file '{}' is not SQL", &output));
    }
}

fn main() {

    let signature = "cmongo2sql 1.0.0 (https://github.com/stpettersens/db-tools)";

    let cli = CliOptions::new("csql2mongo");
    let program = cli.get_program();
    let args = cli.get_args();
    let mut input = String::new();
    let mut output = String::new();
    let mut db = String::new();
    let mut comments = true;
    let mut extensions = true;
    let mut verbose = false;

    if cli.get_num() > 1 {
        for (i, a) in args.iter().enumerate() {
            if a == "-h" || a == "--help" {
                display_usage(&program, 0);
            }
            else if a == "-v" || a == "--version" {
                display_version(&signature);
            }

            if a == "-f" || a == "--file" {
                input = cli.next_argument(i);
            }
            if a == "-o" || a == "--out" {
                output = cli.next_argument(i);
            }
            if a == "-d" || a == "--db" {
                db = cli.next_argument(i);
            }
            if a == "-n" || a == "--no-comments" {
                comments = false;
            }
            if a == "-i" || a == "--ignore-ext" {
                extensions = false;
            }
            if a == "-l" || a == "--verbose" {
                verbose = true;
            }
        }

        if extensions {
            check_extensions(&program, &input, &output);
        }

        if input.len() > 0 && output.len() > 0 {
            convert_json_to_sql(&program, &signature, &input, &output, &db, comments, verbose);
        }
        else if input.len() == 0 {
            display_error(&program, "No input file specified");
        }
        else if output.len() == 0 {
            display_error(&program, "No output file specified");
        }
        else {
            display_error(&program, "Incomplete options provided");
        }
    }
    else {
        display_error(&program, "No options specified");
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use dungeon::{Dungeon, Level};
use geometry::Point;
use grid;
use tile::Tile;
use item;
use fov;

pub struct Player {
    pub position: Point,
    pub depth: u8,
    pub name: [u8; 15],
    pub hp: u8,
    pub tp: u8,
    pub xl: u8,
    pub def: i8,

    // Index with element::Element.
    pub aptitude: [i8; 4],

    // Must be represented as bytes!
    pub inventory: [u8; 8],

    pub appearance_byte: u8,
    pub door_appearance_byte: u8,
    pub wall_appearance_byte: u8,

    // Index with byte::BitNumber.
    pub spell_memory: [bool; 8],

    // Index with timer::Timer.
    pub timer: [u8; 4],

    // The address the player's spells will act on.
    pub selected: u8,

    pub stairs_delta: u8,
    pub timer_delta: u8,
    pub damage_offset: i8,
    pub text_sync: u8,

    // Interface
    pub show_ram: bool,

    pub item_appearance_map: item::AppearanceMap,

    pub visible: HashSet<Point>
}

impl Player {
    // enters first level automatically
    pub fn new(dungeon: &mut Dungeon) -> Player {
        let mut player = Player {
            position: Point(-1, -1),
            depth: 1,
            // TODO naming the player
            name: [97, 98, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            hp: 10,
            tp: 3,
            xl: 1,
            def: 0,
            aptitude: [0, 0, 0, 0],
            inventory: [0, 0, 0, 0, 0, 0, 0, 0],
            appearance_byte: 0b11111111, // white @
            door_appearance_byte: 0b11001010, // brown +
            wall_appearance_byte: 0b01100010, // teal #
            spell_memory: [false; 8],
            timer: [0; 4],
            selected: 0x00,
            stairs_delta: 1,
            timer_delta: 0xFF,
            damage_offset: 0,
            text_sync: 0,
            show_ram: false,
            item_appearance_map: item::random_appearance_map(),
            visible: HashSet::new()
        };
        player.enter_level(dungeon, 1, Tile::StairsUp);
        player
    }

    pub fn current_level<'a>(&self, dungeon: &'a Dungeon) -> &'a Level {
        &dungeon[self.depth as usize - 1]
    }

    pub fn current_level_mut<'a>(&self, dungeon: &'a mut Dungeon) -> &'a mut Level {
        &mut dungeon[self.depth as usize - 1]
    }

    // enter a level: put the player on the appropriate stairs.
    fn enter_level(&mut self, dungeon: &mut Dungeon, depth: u8, entry: Tile) {
        if depth == 0 {
            // TODO: special case for level 0
        } else {
            self.depth = depth;

            let mut level = self.current_level_mut(dungeon);

            for tile_position in grid::RECTANGLE {
                if level.tiles[tile_position] == entry {
                    self.position = tile_position;
                    break
                }
            }

            self.update_visibility(&mut level)
        }

    }

    // TODO: factor these better if they end up growing
    pub fn try_stairs_up(&mut self, dungeon: &mut Dungeon) {
        if self.current_level(dungeon).tiles[self.position] == Tile::StairsUp {
            let new_depth = self.depth - 1;
            self.enter_level(dungeon, new_depth, Tile::StairsDown)
        }
    }
    pub fn try_stairs_down(&mut self, dungeon: &mut Dungeon) {
        if self.current_level(dungeon).tiles[self.position] == Tile::StairsDown {
            let new_depth = self.depth + 1;
            self.enter_level(dungeon, new_depth, Tile::StairsUp)
        }
    }

    // try to walk in given direction
    // TODO: return whether this consumes a turn, handle potential updates from
    // flipping switches
    pub fn step(&mut self, dungeon: &mut Dungeon, direction: Point) {
        let mut level = self.current_level_mut(dungeon);
        let new_position = self.position + direction;

        // TODO: check for monsters
        if grid::RECTANGLE.contains(new_position) {
            match level.tiles[new_position] {
                Tile::Wall => {},
                Tile::Floor | Tile::Doorway | Tile::StairsUp | Tile::StairsDown => {
                    self.position = new_position;
                    self.update_visibility(&mut level)
                },
                Tile::Door => {
                    level.tiles[new_position] = Tile::Doorway;
                    self.update_visibility(&mut level)
                },
                Tile::Switch(bn) => {
                    // TODO: flip switch
                }
            }
        }
    }

    fn update_visibility(&mut self, level: &mut Level) {
        self.visible = fov::calculate(level, self.position);
        level.known_tiles.extend(&self.visible)
    }
}
<commit_msg>player can flip switches<commit_after>use std::collections::HashSet;
use byte;
use dungeon::{Dungeon, Level};
use geometry::Point;
use grid;
use tile::Tile;
use item;
use fov;

pub struct Player {
    pub position: Point,
    pub depth: u8,
    pub name: [u8; 15],
    pub hp: u8,
    pub tp: u8,
    pub xl: u8,
    pub def: i8,

    // Index with element::Element.
    pub aptitude: [i8; 4],

    // Must be represented as bytes!
    pub inventory: [u8; 8],

    pub appearance_byte: u8,
    pub door_appearance_byte: u8,
    pub wall_appearance_byte: u8,

    // Index with byte::BitNumber.
    pub spell_memory: [bool; 8],

    // Index with timer::Timer.
    pub timer: [u8; 4],

    // The address the player's spells will act on.
    pub selected: u8,

    pub stairs_delta: u8,
    pub timer_delta: u8,
    pub damage_offset: i8,
    pub text_sync: u8,

    // Interface
    pub show_ram: bool,

    pub item_appearance_map: item::AppearanceMap,

    pub visible: HashSet<Point>
}

impl Player {
    // enters first level automatically
    pub fn new(dungeon: &mut Dungeon) -> Player {
        let mut player = Player {
            position: Point(-1, -1),
            depth: 1,
            // TODO naming the player
            name: [97, 98, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            hp: 10,
            tp: 3,
            xl: 1,
            def: 0,
            aptitude: [0, 0, 0, 0],
            inventory: [0, 0, 0, 0, 0, 0, 0, 0],
            appearance_byte: 0b11111111, // white @
            door_appearance_byte: 0b11001010, // brown +
            wall_appearance_byte: 0b01100010, // teal #
            spell_memory: [false; 8],
            timer: [0; 4],
            selected: 0x00,
            stairs_delta: 1,
            timer_delta: 0xFF,
            damage_offset: 0,
            text_sync: 0,
            show_ram: false,
            item_appearance_map: item::random_appearance_map(),
            visible: HashSet::new()
        };
        player.enter_level(dungeon, 1, Tile::StairsUp);
        player
    }

    pub fn current_level<'a>(&self, dungeon: &'a Dungeon) -> &'a Level {
        &dungeon[self.depth as usize - 1]
    }

    pub fn current_level_mut<'a>(&self, dungeon: &'a mut Dungeon) -> &'a mut Level {
        &mut dungeon[self.depth as usize - 1]
    }

    // enter a level: put the player on the appropriate stairs.
    fn enter_level(&mut self, dungeon: &mut Dungeon, depth: u8, entry: Tile) {
        if depth == 0 {
            // TODO: special case for level 0
        } else {
            self.depth = depth;

            let mut level = self.current_level_mut(dungeon);

            for tile_position in grid::RECTANGLE {
                if level.tiles[tile_position] == entry {
                    self.position = tile_position;
                    break
                }
            }

            self.update_visibility(&mut level)
        }

    }

    // TODO: factor these better if they end up growing
    pub fn try_stairs_up(&mut self, dungeon: &mut Dungeon) {
        if self.current_level(dungeon).tiles[self.position] == Tile::StairsUp {
            let new_depth = self.depth - 1;
            self.enter_level(dungeon, new_depth, Tile::StairsDown)
        }
    }
    pub fn try_stairs_down(&mut self, dungeon: &mut Dungeon) {
        if self.current_level(dungeon).tiles[self.position] == Tile::StairsDown {
            let new_depth = self.depth + 1;
            self.enter_level(dungeon, new_depth, Tile::StairsUp)
        }
    }

    // try to walk in given direction
    // TODO: return whether this consumes a turn, handle potential updates from
    // flipping switches
    pub fn step(&mut self, dungeon: &mut Dungeon, direction: Point) {
        let mut level = self.current_level_mut(dungeon);
        let new_position = self.position + direction;

        // don't let the player step out of bounds
        if !grid::RECTANGLE.contains(new_position) { return }

        // TODO: check for monsters
        match level.tiles[new_position] {
            Tile::Wall => {},
            Tile::Floor | Tile::Doorway | Tile::StairsUp | Tile::StairsDown => {
                self.position = new_position;
                self.update_visibility(&mut level)
            },
            Tile::Door => {
                level.tiles[new_position] = Tile::Doorway;
                self.update_visibility(&mut level)
            },
            Tile::Switch(bn) => {
                self.selected = byte::flip(self.selected, bn);
            }
        }
    }

    fn update_visibility(&mut self, level: &mut Level) {
        self.visible = fov::calculate(level, self.position);
        level.known_tiles.extend(&self.visible)
    }
}
<|endoftext|>


<commit_before>use std::io::{BufRead, BufReader};
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::thread;

use config::Config;
use line::Line;
use line_storage::LineStorage;
use thread_util::spawn_with_name;

const DUMP_INTERVAL_MS: u32 = 20; // ~10,000 lines per dump on my laptop when piped to `find`

pub enum Event {
    DidFinish,
    DidReadChunk,
}

pub struct Reader {
    chunk: Arc<Mutex<Vec<Arc<Line>>>>,
    is_finished: Arc<AtomicBool>,
    line_storage: Arc<RwLock<LineStorage>>,
    reader: thread::JoinHandle<()>,
}

impl Reader {
    pub fn new(config: Config, line_storage: Arc<RwLock<LineStorage>>) -> Self {
        let chunk = Arc::new(Mutex::new(Vec::new()));
        let is_finished = Arc::new(AtomicBool::new(false));
        let reader = spawn_parked_reader(config, chunk.clone(), is_finished.clone());
        Reader {
            chunk: chunk,
            is_finished: is_finished,
            line_storage: line_storage,
            reader: reader,
        }
    }

    pub fn start(self, tx: Sender<Event>) {
        use self::Event::*;
        self.reader.thread().unpark();
        while !self.is_finished.load(Ordering::Relaxed) {
            thread::sleep_ms(DUMP_INTERVAL_MS);
            let mut chunk = self.chunk.lock().unwrap();
            if chunk.len() > 0 {
                self.line_storage.write().unwrap().put_chunk(chunk.clone());
                chunk.clear();
                drop(chunk);
                tx.send(DidReadChunk).is_ok() || return;
            }
        }
        tx.send(DidFinish).is_ok() || return;
    }
}

fn spawn_parked_reader(config: Config, chunk: Arc<Mutex<Vec<Arc<Line>>>>, is_finished: Arc<AtomicBool>) -> thread::JoinHandle<()> {
    spawn_with_name("reader::reader", move || {
        thread::park();
        let mut buf_reader = BufReader::new(config.input_source());
        loop {
            let mut buf = Vec::new();
            let res = buf_reader.read_until(0xA, &mut buf);
            match res {
                Ok(_) if buf.len() > 0 => {
                    if buf.last() == Some(&0xA) {
                        buf.pop().unwrap();
                    }
                    let mut chunk = chunk.lock().unwrap();
                    chunk.push(Arc::new(Line::new(buf)));
                    drop(chunk);
                }
                Ok(_) => {
                    break;
                }
                Err(_) => {
                    break;
                }
            }
        }
        is_finished.store(true, Ordering::Relaxed);
    })
}
<commit_msg>Reuse line buffer to reduce reallocation<commit_after>use std::io::{BufRead, BufReader};
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::thread;

use config::Config;
use line::Line;
use line_storage::LineStorage;
use thread_util::spawn_with_name;

const DUMP_INTERVAL_MS: u32 = 20; // ~10,000 lines per dump on my laptop when piped to `find`

pub enum Event {
    DidFinish,
    DidReadChunk,
}

pub struct Reader {
    chunk: Arc<Mutex<Vec<Arc<Line>>>>,
    is_finished: Arc<AtomicBool>,
    line_storage: Arc<RwLock<LineStorage>>,
    reader: thread::JoinHandle<()>,
}

impl Reader {
    pub fn new(config: Config, line_storage: Arc<RwLock<LineStorage>>) -> Self {
        let chunk = Arc::new(Mutex::new(Vec::new()));
        let is_finished = Arc::new(AtomicBool::new(false));
        let reader = spawn_parked_reader(config, chunk.clone(), is_finished.clone());
        Reader {
            chunk: chunk,
            is_finished: is_finished,
            line_storage: line_storage,
            reader: reader,
        }
    }

    pub fn start(self, tx: Sender<Event>) {
        use self::Event::*;
        self.reader.thread().unpark();
        while !self.is_finished.load(Ordering::Relaxed) {
            thread::sleep_ms(DUMP_INTERVAL_MS);
            let mut chunk = self.chunk.lock().unwrap();
            if chunk.len() > 0 {
                self.line_storage.write().unwrap().put_chunk(chunk.clone());
                chunk.clear();
                drop(chunk);
                tx.send(DidReadChunk).is_ok() || return;
            }
        }
        tx.send(DidFinish).is_ok() || return;
    }
}

fn spawn_parked_reader(config: Config, chunk: Arc<Mutex<Vec<Arc<Line>>>>, is_finished: Arc<AtomicBool>) -> thread::JoinHandle<()> {
    spawn_with_name("reader::reader", move || {
        thread::park();
        let mut buf = Vec::with_capacity(1024);
        let mut buf_reader = BufReader::new(config.input_source());
        loop {
            buf.clear();
            let res = buf_reader.read_until(0xA, &mut buf);
            match res {
                Ok(_) if buf.len() > 0 => {
                    if buf.last() == Some(&0xA) {
                        buf.pop().unwrap();
                    }
                    let mut chunk = chunk.lock().unwrap();
                    chunk.push(Arc::new(Line::new(buf.clone())));
                    drop(chunk);
                }
                Ok(_) => {
                    break;
                }
                Err(_) => {
                    break;
                }
            }
        }
        is_finished.store(true, Ordering::Relaxed);
    })
}
<|endoftext|>


<commit_before>use std::fmt;
use std::str::{self, FromStr};

use error::{S3Result, S3Error};

/// AWS S3 [region identifier](https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region)
///
/// # Example
/// ```
/// use std::str::FromStr;
/// use s3::region::Region;
///
/// // Parse from a string
/// let region: Region = "us-east-1".parse().unwrap();
///
/// // Choose region directly
/// let region = Region::EuWest2;
/// ```
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Region {
    /// us-east-1
    UsEast1,
    /// us-east-2
    UsEast2,
    /// us-west-1
    UsWest1,
    /// us-west-2
    UsWest2,
    /// ca-central-1
    CaCentral1,
    /// ap-south-1
    ApSouth1,
    /// ap-northeast-1
    ApNortheast1,
    /// ap-northeast-2
    ApNortheast2,
    /// ap-southeast-1
    ApSoutheast1,
    /// ap-southeast-2
    ApSoutheast2,
    /// eu-central-1
    EuCentral1,
    /// eu-west-1
    EuWest1,
    /// eu-west-2
    EuWest2,
    /// eu-west-3
    EuWest3,
    /// sa-east-1
    SaEast1,
    /// Digital Ocean nyc3
    DoNyc3,
}

impl fmt::Display for Region {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Region::*;
        match *self {
            UsEast1 => write!(f, "us-east-1"),
            UsEast2 => write!(f, "us-east-2"),
            UsWest1 => write!(f, "us-west-1"),
            UsWest2 => write!(f, "us-west-2"),
            CaCentral1 => write!(f, "ca-central-1"),
            ApSouth1 => write!(f, "ap-south-1"),
            ApNortheast1 => write!(f, "ap-northeast-1"),
            ApNortheast2 => write!(f, "ap-northeast-2"),
            ApSoutheast1 => write!(f, "ap-southeast-1"),
            ApSoutheast2 => write!(f, "ap-southeast-2"),
            EuCentral1 => write!(f, "eu-central-1"),
            EuWest1 => write!(f, "eu-west-1"),
            EuWest2 => write!(f, "eu-west-2"),
            EuWest3 => write!(f, "eu-west-3"),
            SaEast1 => write!(f, "sa-east-1"),
            DoNyc3 => write!(f, "nyc3"),
        }
    }
}

impl FromStr for Region {
    type Err = S3Error;

    fn from_str(s: &str) -> S3Result<Self> {
        use self::Region::*;
        match s {
            "us-east-1" => Ok(UsEast1),
            "us-east-2" => Ok(UsEast2),
            "us-west-1" => Ok(UsWest1),
            "us-west-2" => Ok(UsWest2),
            "ca-central-1" => Ok(CaCentral1),
            "ap-south-1" => Ok(ApSouth1),
            "ap-northeast-1" => Ok(ApNortheast1),
            "ap-northeast-2" => Ok(ApNortheast2),
            "ap-southeast-1" => Ok(ApSoutheast1),
            "ap-southeast-2" => Ok(ApSoutheast2),
            "eu-central-1" => Ok(EuCentral1),
            "eu-west-1" => Ok(EuWest1),
            "eu-west-2" => Ok(EuWest2),
            "eu-west-3" => Ok(EuWest3),
            "sa-east-1" => Ok(SaEast1),
            "nyc3" => Ok(DoNyc3),
            _ => Err(s.to_string().into()),
        }
    }
}

impl Region {
    pub fn endpoint(&self) -> &str {
        use self::Region::*;
        match *self {
            // Surprisingly, us-east-1 does not have a
            // s3-us-east-1.amazonaws.com DNS record
            UsEast1 => "s3.amazonaws.com",
            UsEast2 => "s3-us-east-2.amazonaws.com",
            UsWest1 => "s3-us-west-1.amazonaws.com",
            UsWest2 => "s3-us-west-2.amazonaws.com",
            CaCentral1 => "s3-ca-central-1.amazonaws.com",
            ApSouth1 => "s3-ap-south-1.amazonaws.com",
            ApNortheast1 => "s3-ap-northeast-1.amazonaws.com",
            ApNortheast2 => "s3-ap-northeast-2.amazonaws.com",
            ApSoutheast1 => "s3-ap-southeast-1.amazonaws.com",
            ApSoutheast2 => "s3-ap-southeast-2.amazonaws.com",
            EuCentral1 => "s3-eu-central-1.amazonaws.com",
            EuWest1 => "s3-eu-west-1.amazonaws.com",
            EuWest2 => "s3-eu-west-2.amazonaws.com",
            EuWest3 => "s3-eu-west-3.amazonaws.com",
            SaEast1 => "s3-sa-east-1.amazonaws.com",
            DoNyc3 => "nyc3.digitaloceanspaces.com",
        }
    }
}
<commit_msg>Add Digital Ocean Amsterdam and Singapore regions.<commit_after>use std::fmt;
use std::str::{self, FromStr};

use error::{S3Result, S3Error};

/// AWS S3 [region identifier](https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region)
///
/// # Example
/// ```
/// use std::str::FromStr;
/// use s3::region::Region;
///
/// // Parse from a string
/// let region: Region = "us-east-1".parse().unwrap();
///
/// // Choose region directly
/// let region = Region::EuWest2;
/// ```
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Region {
    /// us-east-1
    UsEast1,
    /// us-east-2
    UsEast2,
    /// us-west-1
    UsWest1,
    /// us-west-2
    UsWest2,
    /// ca-central-1
    CaCentral1,
    /// ap-south-1
    ApSouth1,
    /// ap-northeast-1
    ApNortheast1,
    /// ap-northeast-2
    ApNortheast2,
    /// ap-southeast-1
    ApSoutheast1,
    /// ap-southeast-2
    ApSoutheast2,
    /// eu-central-1
    EuCentral1,
    /// eu-west-1
    EuWest1,
    /// eu-west-2
    EuWest2,
    /// eu-west-3
    EuWest3,
    /// sa-east-1
    SaEast1,
    /// Digital Ocean nyc3
    DoNyc3,
    /// Digital Ocean ams3
    DoAms3,
    /// Digital Ocean sgp1
    DoSgp1,
}

impl fmt::Display for Region {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Region::*;
        match *self {
            UsEast1 => write!(f, "us-east-1"),
            UsEast2 => write!(f, "us-east-2"),
            UsWest1 => write!(f, "us-west-1"),
            UsWest2 => write!(f, "us-west-2"),
            CaCentral1 => write!(f, "ca-central-1"),
            ApSouth1 => write!(f, "ap-south-1"),
            ApNortheast1 => write!(f, "ap-northeast-1"),
            ApNortheast2 => write!(f, "ap-northeast-2"),
            ApSoutheast1 => write!(f, "ap-southeast-1"),
            ApSoutheast2 => write!(f, "ap-southeast-2"),
            EuCentral1 => write!(f, "eu-central-1"),
            EuWest1 => write!(f, "eu-west-1"),
            EuWest2 => write!(f, "eu-west-2"),
            EuWest3 => write!(f, "eu-west-3"),
            SaEast1 => write!(f, "sa-east-1"),
            DoNyc3 => write!(f, "nyc3"),
            DoAms3 => write!(f, "ams3"),
            DoSgp1 => write!(f, "sgp1"),
        }
    }
}

impl FromStr for Region {
    type Err = S3Error;

    fn from_str(s: &str) -> S3Result<Self> {
        use self::Region::*;
        match s {
            "us-east-1" => Ok(UsEast1),
            "us-east-2" => Ok(UsEast2),
            "us-west-1" => Ok(UsWest1),
            "us-west-2" => Ok(UsWest2),
            "ca-central-1" => Ok(CaCentral1),
            "ap-south-1" => Ok(ApSouth1),
            "ap-northeast-1" => Ok(ApNortheast1),
            "ap-northeast-2" => Ok(ApNortheast2),
            "ap-southeast-1" => Ok(ApSoutheast1),
            "ap-southeast-2" => Ok(ApSoutheast2),
            "eu-central-1" => Ok(EuCentral1),
            "eu-west-1" => Ok(EuWest1),
            "eu-west-2" => Ok(EuWest2),
            "eu-west-3" => Ok(EuWest3),
            "sa-east-1" => Ok(SaEast1),
            "nyc3" => Ok(DoNyc3),
            "ams3" => Ok(DoAms3),
            "sgp1" => Ok(DoSgp1),
            _ => Err(s.to_string().into()),
        }
    }
}

impl Region {
    pub fn endpoint(&self) -> &str {
        use self::Region::*;
        match *self {
            // Surprisingly, us-east-1 does not have a
            // s3-us-east-1.amazonaws.com DNS record
            UsEast1 => "s3.amazonaws.com",
            UsEast2 => "s3-us-east-2.amazonaws.com",
            UsWest1 => "s3-us-west-1.amazonaws.com",
            UsWest2 => "s3-us-west-2.amazonaws.com",
            CaCentral1 => "s3-ca-central-1.amazonaws.com",
            ApSouth1 => "s3-ap-south-1.amazonaws.com",
            ApNortheast1 => "s3-ap-northeast-1.amazonaws.com",
            ApNortheast2 => "s3-ap-northeast-2.amazonaws.com",
            ApSoutheast1 => "s3-ap-southeast-1.amazonaws.com",
            ApSoutheast2 => "s3-ap-southeast-2.amazonaws.com",
            EuCentral1 => "s3-eu-central-1.amazonaws.com",
            EuWest1 => "s3-eu-west-1.amazonaws.com",
            EuWest2 => "s3-eu-west-2.amazonaws.com",
            EuWest3 => "s3-eu-west-3.amazonaws.com",
            SaEast1 => "s3-sa-east-1.amazonaws.com",
            DoNyc3 => "nyc3.digitaloceanspaces.com",
            DoAms3 => "ams3.digitaloceanspaces.com",
            DoSgp1 => "sgp1.digitaloceanspaces.com",
        }
    }
}
<|endoftext|>


<commit_before>use ammonia::Ammonia;
use comrak;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            link_rel: Some("nofollow noopener noreferrer"),
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            ..Ammonia::default()
        };
        MarkdownRenderer { html_sanitizer: html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
wb’
\n");
    }
}
<commit_msg>Re-enable syntax highlighting.<commit_after>use ammonia::Ammonia;
use comrak;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            ("code", ["class"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let allowed_classes = [
            (
                "code",
                [
                    "language-bash",
                    "language-clike",
                    "language-glsl",
                    "language-go",
                    "language-ini",
                    "language-javascript",
                    "language-json",
                    "language-markup",
                    "language-protobuf",
                    "language-ruby",
                    "language-rust",
                    "language-scss",
                    "language-sql",
                    "yaml",
                ].iter()
                    .cloned()
                    .collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            link_rel: Some("nofollow noopener noreferrer"),
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            allowed_classes: allowed_classes,
            ..Ammonia::default()
        };
        MarkdownRenderer { html_sanitizer: html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
wb’
\n");
    }

    #[test]
    fn code_block_with_syntax_highlighting() {
        let code_block = r#"```rust \
                            println!("Hello World"); \
                           ```"#;
        let result = markdown_to_html(code_block).unwrap();
        assert!(result.contains(""));
    }

    #[test]
    fn text_with_forbidden_class_attribute() {
        let text = "
Hello World!
";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
Hello World!
\n");
    }
}
<|endoftext|>


<commit_before>use specs::*;
use templating::*;

use handlebars::{self, Handlebars};
use itertools::Itertools;
use std::collections::BTreeMap;

pub struct CodeGenerator {
    specs:      AMQProtocolDefinition,
    handlebars: Handlebars,
}

impl CodeGenerator {
    pub fn new(specs: AMQProtocolDefinition, templates: AMQPTemplates) -> CodeGenerator {
        let mut handlebars = Handlebars::new();

        handlebars.register_escape_fn(handlebars::no_escape);

        handlebars.register_helper("camel", Box::new(camel_helper));
        handlebars.register_helper("snake", Box::new(snake_helper));

        handlebars.register_template_string("main",     &templates.main).expect("Failed to register main template");
        handlebars.register_template_string("domain",   &templates.domain).expect("Failed to register domain template");
        handlebars.register_template_string("constant", &templates.constant).expect("Failed to register constant template");
        handlebars.register_template_string("class",    &templates.klass).expect("Failed to register class template");
        handlebars.register_template_string("method",   &templates.method).expect("Failed to register method template");
        handlebars.register_template_string("argument", &templates.argument).expect("Failed to register argument template");
        handlebars.register_template_string("property", &templates.property).expect("Failed to register property template");

        CodeGenerator {
            specs:      specs,
            handlebars: handlebars,
        }
    }

    pub fn generate(&self) -> String {
        self.handlebars.render("main", &AMQProtocolDefinitionWrapper {
            protocol:  &self.specs,
            copyright: self.specs.copyright.iter().join(""),
            domains:   self.specs.domains.iter().map(|domain| domain.codegen(&self.handlebars)).join("\n"),
            constants: self.specs.constants.iter().map(|constant| constant.codegen(&self.handlebars)).join("\n"),
            classes:   self.specs.classes.iter().map(|klass| klass.codegen(&self.handlebars)).join("\n"),
        }).expect("Failed to render main template")
    }
}

#[derive(Debug, Serialize)]
struct AMQProtocolDefinitionWrapper<'a> {
    protocol : &'a AMQProtocolDefinition,
    copyright: String,
    domains:   String,
    constants: String,
    classes:   String,
}

pub trait Codegen {
    fn codegen(&self, handlebars: &Handlebars) -> String;
}

trait BasicGetData {
    fn get_data(&self, handlebars: &Handlebars) -> BTreeMap<String, &Self>;
}

trait ClassGetData {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPClassWrapper;
}

trait MethodGetData {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPMethodWrapper;
}

macro_rules! get_data {
    ($t:ty, $key:expr) => {
        impl BasicGetData for $t {
            #[allow(unused_variables)]
            fn get_data(&self, handlebars: &Handlebars) -> BTreeMap<String, &Self> {
                let mut data = BTreeMap::new();
                data.insert($key.to_string(), self);
                data
            }
        }
    }
}

#[derive(Debug, Serialize)]
struct AMQPClassWrapper<'a> {
    #[serde(rename="class")]
    klass:      &'a AMQPClass,
    methods:    String,
    properties: Option<String>,
}

impl ClassGetData for AMQPClass {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPClassWrapper {
        AMQPClassWrapper {
            klass:      self,
            methods:    self.methods.iter().map(|method| method.codegen(&handlebars)).join("\n"),
            properties: match self.properties {
                Some(ref properties) => Some(properties.iter().map(|method| method.codegen(&handlebars)).join("\n")),
                None                 => None,
            },
        }
    }
}

#[derive(Debug, Serialize)]
struct AMQPMethodWrapper<'a> {
    method:    &'a AMQPMethod,
    arguments: String,
}

impl MethodGetData for AMQPMethod {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPMethodWrapper {
        AMQPMethodWrapper {
            method:    self,
            arguments: self.arguments.iter().map(|arg| arg.codegen(&handlebars)).join("\n"),
        }
    }
}

get_data!(AMQPDomain,   "domain");
get_data!(AMQPConstant, "constant");
get_data!(AMQPArgument, "argument");
get_data!(AMQPProperty, "property");

macro_rules! codegen {
    ($t:ty, $name:expr) => {
        impl Codegen for $t {
            fn codegen (&self, handlebars: &Handlebars) -> String {
                handlebars.render($name, &self.get_data(handlebars)).expect(&format!("Failed to render {} template", $name))
            }
        }
    };
}

codegen!(AMQPDomain,   "domain");
codegen!(AMQPConstant, "constant");
codegen!(AMQPClass,    "class");
codegen!(AMQPMethod,   "method");
codegen!(AMQPArgument, "argument");
codegen!(AMQPProperty, "property");

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

    use amq_protocol_types::*;
    use serde_json::Value;
    use templating::AMQPTemplates;

    fn specs() -> AMQProtocolDefinition {
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     vec!["Copyright 1\n".to_string(), "Copyright 2".to_string()],
            domains:       vec![
                AMQPDomain{
                    name:      "domain1".to_string(),
                    amqp_type: AMQPType::ShortInt
                }
            ],
            constants:     vec![
                AMQPConstant {
                    name:  "constant1".to_string(),
                    value: 128,
                    klass: Some("class1".to_string()),
                }
            ],
            classes:       vec![
                AMQPClass {
                    id:         42,
                    methods:    vec![
                        AMQPMethod {
                            id:          64,
                            arguments:   vec![
                                AMQPArgument {
                                    amqp_type:     Some(AMQPType::LongString),
                                    name:          "argument1".to_string(),
                                    default_value: Some(Value::String("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }
                            ],
                            name:        "method1".to_string(),
                            synchronous: Some(true),
                        }
                    ],
                    name:       "class1".to_string(),
                    properties: Some(vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ]),
                }
            ],
        }
    }

    fn templates() -> AMQPTemplates {
        AMQPTemplates {
            main:     r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{copyright}}
port {{protocol.port}}
{{domains}}
{{constants}}
{{classes}}
"#.to_string(),
            domain:   "{{domain.name}}: {{domain.type}}".to_string(),
            constant: "{{constant.name}}({{constant.class}}) = {{constant.value}}".to_string(),
            klass:    r#"
{{class.id}} - {{class.name}}
{{properties}}
{{methods}}
"#.to_string(),
            method:   r#"
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{arguments}}
"#.to_string(),
            argument: "{{argument.name}}({{argument.domain}}): {{argument.type}}".to_string(),
            property: "{{property.name}}: {{property.type}}".to_string(),
        }
    }

    #[test]
    fn main_template() {
        assert_eq!(specs().code_generator(templates()).generate(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: ShortInt
constant1(class1) = 128

42 - class1
property1: LongString

64 - method1
synchronous: true
argument1(domain1): LongString

"#);
    }
}
<commit_msg>codegen: allow specifying own helpers<commit_after>use specs::*;
use templating::*;

use handlebars::{self, Handlebars, HelperDef};
use itertools::Itertools;
use std::collections::BTreeMap;

pub struct CodeGenerator {
    specs:      AMQProtocolDefinition,
    handlebars: Handlebars,
}

impl CodeGenerator {
    pub fn new(specs: AMQProtocolDefinition, templates: AMQPTemplates) -> CodeGenerator {
        let mut handlebars = Handlebars::new();

        handlebars.register_escape_fn(handlebars::no_escape);

        handlebars.register_helper("camel", Box::new(camel_helper));
        handlebars.register_helper("snake", Box::new(snake_helper));

        handlebars.register_template_string("main",     &templates.main).expect("Failed to register main template");
        handlebars.register_template_string("domain",   &templates.domain).expect("Failed to register domain template");
        handlebars.register_template_string("constant", &templates.constant).expect("Failed to register constant template");
        handlebars.register_template_string("class",    &templates.klass).expect("Failed to register class template");
        handlebars.register_template_string("method",   &templates.method).expect("Failed to register method template");
        handlebars.register_template_string("argument", &templates.argument).expect("Failed to register argument template");
        handlebars.register_template_string("property", &templates.property).expect("Failed to register property template");

        CodeGenerator {
            specs:      specs,
            handlebars: handlebars,
        }
    }

    pub fn with_helper(mut self, name: &str, helper: Box<HelperDef + 'static>) -> CodeGenerator {
        self.handlebars.register_helper(name, helper);
        self
    }

    pub fn generate(&self) -> String {
        self.handlebars.render("main", &AMQProtocolDefinitionWrapper {
            protocol:  &self.specs,
            copyright: self.specs.copyright.iter().join(""),
            domains:   self.specs.domains.iter().map(|domain| domain.codegen(&self.handlebars)).join("\n"),
            constants: self.specs.constants.iter().map(|constant| constant.codegen(&self.handlebars)).join("\n"),
            classes:   self.specs.classes.iter().map(|klass| klass.codegen(&self.handlebars)).join("\n"),
        }).expect("Failed to render main template")
    }
}

#[derive(Debug, Serialize)]
struct AMQProtocolDefinitionWrapper<'a> {
    protocol : &'a AMQProtocolDefinition,
    copyright: String,
    domains:   String,
    constants: String,
    classes:   String,
}

pub trait Codegen {
    fn codegen(&self, handlebars: &Handlebars) -> String;
}

trait BasicGetData {
    fn get_data(&self, handlebars: &Handlebars) -> BTreeMap<String, &Self>;
}

trait ClassGetData {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPClassWrapper;
}

trait MethodGetData {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPMethodWrapper;
}

macro_rules! get_data {
    ($t:ty, $key:expr) => {
        impl BasicGetData for $t {
            #[allow(unused_variables)]
            fn get_data(&self, handlebars: &Handlebars) -> BTreeMap<String, &Self> {
                let mut data = BTreeMap::new();
                data.insert($key.to_string(), self);
                data
            }
        }
    }
}

#[derive(Debug, Serialize)]
struct AMQPClassWrapper<'a> {
    #[serde(rename="class")]
    klass:      &'a AMQPClass,
    methods:    String,
    properties: Option<String>,
}

impl ClassGetData for AMQPClass {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPClassWrapper {
        AMQPClassWrapper {
            klass:      self,
            methods:    self.methods.iter().map(|method| method.codegen(&handlebars)).join("\n"),
            properties: match self.properties {
                Some(ref properties) => Some(properties.iter().map(|method| method.codegen(&handlebars)).join("\n")),
                None                 => None,
            },
        }
    }
}

#[derive(Debug, Serialize)]
struct AMQPMethodWrapper<'a> {
    method:    &'a AMQPMethod,
    arguments: String,
}

impl MethodGetData for AMQPMethod {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPMethodWrapper {
        AMQPMethodWrapper {
            method:    self,
            arguments: self.arguments.iter().map(|arg| arg.codegen(&handlebars)).join("\n"),
        }
    }
}

get_data!(AMQPDomain,   "domain");
get_data!(AMQPConstant, "constant");
get_data!(AMQPArgument, "argument");
get_data!(AMQPProperty, "property");

macro_rules! codegen {
    ($t:ty, $name:expr) => {
        impl Codegen for $t {
            fn codegen (&self, handlebars: &Handlebars) -> String {
                handlebars.render($name, &self.get_data(handlebars)).expect(&format!("Failed to render {} template", $name))
            }
        }
    };
}

codegen!(AMQPDomain,   "domain");
codegen!(AMQPConstant, "constant");
codegen!(AMQPClass,    "class");
codegen!(AMQPMethod,   "method");
codegen!(AMQPArgument, "argument");
codegen!(AMQPProperty, "property");

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

    use amq_protocol_types::*;
    use serde_json::Value;
    use templating::AMQPTemplates;

    fn specs() -> AMQProtocolDefinition {
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     vec!["Copyright 1\n".to_string(), "Copyright 2".to_string()],
            domains:       vec![
                AMQPDomain{
                    name:      "domain1".to_string(),
                    amqp_type: AMQPType::ShortInt
                }
            ],
            constants:     vec![
                AMQPConstant {
                    name:  "constant1".to_string(),
                    value: 128,
                    klass: Some("class1".to_string()),
                }
            ],
            classes:       vec![
                AMQPClass {
                    id:         42,
                    methods:    vec![
                        AMQPMethod {
                            id:          64,
                            arguments:   vec![
                                AMQPArgument {
                                    amqp_type:     Some(AMQPType::LongString),
                                    name:          "argument1".to_string(),
                                    default_value: Some(Value::String("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }
                            ],
                            name:        "method1".to_string(),
                            synchronous: Some(true),
                        }
                    ],
                    name:       "class1".to_string(),
                    properties: Some(vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ]),
                }
            ],
        }
    }

    fn templates() -> AMQPTemplates {
        AMQPTemplates {
            main:     r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{copyright}}
port {{protocol.port}}
{{domains}}
{{constants}}
{{classes}}
"#.to_string(),
            domain:   "{{domain.name}}: {{domain.type}}".to_string(),
            constant: "{{constant.name}}({{constant.class}}) = {{constant.value}}".to_string(),
            klass:    r#"
{{class.id}} - {{class.name}}
{{properties}}
{{methods}}
"#.to_string(),
            method:   r#"
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{arguments}}
"#.to_string(),
            argument: "{{argument.name}}({{argument.domain}}): {{argument.type}}".to_string(),
            property: "{{property.name}}: {{property.type}}".to_string(),
        }
    }

    #[test]
    fn main_template() {
        assert_eq!(specs().code_generator(templates()).generate(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: ShortInt
constant1(class1) = 128

42 - class1
property1: LongString

64 - method1
synchronous: true
argument1(domain1): LongString

"#);
    }
}
<|endoftext|>