<commit_before>extern crate rsdocs2docset;
extern crate html5ever;
#[macro_use]
extern crate quick_error;
extern crate clap;
extern crate url;
extern crate sqlite;

use clap::{Arg, App};
use html5ever::driver::{ParseOpts, parse_document};
use html5ever::rcdom::RcDom;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::TreeBuilderOpts;

use std::ffi::OsStr;
use std::fs::{self, DirBuilder, DirEntry, File};
use std::io::{self,Write};
use std::path::Path;

use rsdocs2docset::dom::{manipulator, parser};

type Result<T> = std::result::Result<T, RsDoc2DocsetError>;

quick_error! {
    #[derive(Debug)]
    pub enum RsDoc2DocsetError {
        Io(err: std::io::Error) {
            from()
                cause(err)
                description(err.description())
        }
        Utf8(err: std::string::FromUtf8Error) {
            from()
                cause(err)
                description(err.description())
        }
        Sqlite(err: sqlite::Error) {
            from()
                cause(err)
                description(err.description())
        }
    }
}

fn main() {
    let args = App::new("RsDoc2Docset")
        .version("0.0.1")
        .about("A tool that converts rust docs to Dash docset files")
        .arg(Arg::with_name("indir")
             .short("i")
             .long("rsdoc")
             .value_name("INDIR")
             .help("directory that contains rustdoc files")
             .required(true)
             .takes_value(true))
        .arg(Arg::with_name("name")
             .short("n")
             .long("name")
             .value_name("NAME")
             .help("name of the docset")
             .required(true)
             .takes_value(true))
        .get_matches();

    create_docset(args.value_of("indir").unwrap(), args.value_of("name").unwrap()).expect("error creating docset");
    println!("\n{}.docset successfully created!", args.value_of("name").unwrap())
}

fn create_docset(indir: &str, name: &str) -> Result<()> {
    let db_filename = format!("{}.docset/Contents/Resources/docSet.dsidx", name);
    let db_path = Path::new(&db_filename);

    try!(write_file(Path::new(format!("{}.docset/Contents/Info.plist", name).as_str()), format!(include_str!("Info.plist.tmpl"), name = name, identifier = name.replace(" ", "-").to_lowercase()).as_ref()));
    try!(write_file(Path::new(format!("{}.docset/icon.png", name).as_str()), include_bytes!("icon.png")));
    try!(write_file(Path::new(format!("{}.docset/icon@2x.png", name).as_str()), include_bytes!("icon@2x.png")));

    try!(write_file(db_path, "".as_bytes()));
    try!(sqlite::open(db_path).and_then(|c| c.execute("CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);")));

    try!(docset_from_rs_doc_tree(&Path::new(indir), format!("{}.docset/Contents/Resources/Documents/", name).as_str(), Path::new(db_path), &annotate_file));

    Ok(())
}

fn write_file(path: &Path, data: &[u8]) -> Result<()> {
    let dir = path.parent().unwrap();

    try!(DirBuilder::new()
         .recursive(true)
         .create(dir));

    try!(File::create(path).and_then(|mut f| f.write_all(data)));
    Ok(())
}

fn docset_from_rs_doc_tree(source_dir: &Path, out_dir: &str, db_path: &Path,
                           annotate_file: &Fn(&DirEntry, &str, &Path) -> Result<()>)
    -> Result<()> {
        if !source_dir.exists() {
            return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} does not exist", source_dir.to_str().unwrap())).into());
        }

        if source_dir.is_dir() {
            for entry in fs::read_dir(source_dir)? {
                let entry = entry?;
                if entry.path().is_dir() {
                    try!(docset_from_rs_doc_tree(&entry.path(), &out_dir, &db_path, annotate_file));
                } else {
                    try!(annotate_file(&entry, &out_dir, &db_path));
                }
            }
        }
        Ok(())
    }

fn annotate_file(in_file: &DirEntry, output_prefix: &str, db_path: &Path) -> Result<()> {
    let out_file = Path::new(output_prefix).join(in_file.path());

    if in_file.path().extension() != Some(OsStr::new("html")) {
        try!(write_file(Path::new(&out_file), "".as_bytes()));
        try!(fs::copy(in_file.path(), &out_file));
        //println!("{:70} | ", in_file.path().display());
        print!("."); io::stdout().flush().unwrap();
    } else {
        let opts = ParseOpts { tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() }, ..Default::default() };

        let mut dom = parse_document(RcDom::default(), opts)
            .from_utf8()
            .read_from( &mut File::open(&in_file.path())? )?;

        let entries = parser::find_entry_elements(&mut dom);
        for entry in entries.iter().filter_map(|x| x.as_ref()) {
            //println!("{:70} | {}", in_file.path().display(), entry);

            let sql_cmd = format!("INSERT OR IGNORE INTO searchIndex(name, type, path) VALUES (\"{}\", \"{}\", \"{}#{}\");", entry.entry_name, entry.entry_type, in_file.path().to_str().unwrap(), entry.anchor_name);
            try!(sqlite::open(db_path).and_then(|c| c.execute(&sql_cmd)))
        }
        manipulator::add_dash_links(&mut dom, &entries);

        let mut bytes = vec![];
        try!(serialize(&mut bytes, &dom.document, SerializeOpts::default()));
        try!(write_file(&out_file, bytes.as_ref()));

        print!("o"); io::stdout().flush().unwrap();
    }

    Ok(())
}
<commit_msg>Simplify result type<commit_after>extern crate rsdocs2docset;
extern crate html5ever;
#[macro_use]
extern crate quick_error;
extern crate clap;
extern crate url;
extern crate sqlite;

use clap::{Arg, App};
use html5ever::driver::{ParseOpts, parse_document};
use html5ever::rcdom::RcDom;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::TreeBuilderOpts;

use std::ffi::OsStr;
use std::fs::{self, DirBuilder, DirEntry, File};
use std::io::{self,Write};
use std::path::Path;

use rsdocs2docset::dom::{manipulator, parser};

type Result = std::result::Result<(), RsDoc2DocsetError>;

quick_error! {
    #[derive(Debug)]
    pub enum RsDoc2DocsetError {
        Io(err: std::io::Error) {
            from()
                cause(err)
                description(err.description())
        }
        Utf8(err: std::string::FromUtf8Error) {
            from()
                cause(err)
                description(err.description())
        }
        Sqlite(err: sqlite::Error) {
            from()
                cause(err)
                description(err.description())
        }
    }
}

fn main() {
    let args = App::new("RsDoc2Docset")
        .version("0.0.1")
        .about("A tool that converts rust docs to Dash docset files")
        .arg(Arg::with_name("indir")
             .short("i")
             .long("rsdoc")
             .value_name("INDIR")
             .help("directory that contains rustdoc files")
             .required(true)
             .takes_value(true))
        .arg(Arg::with_name("name")
             .short("n")
             .long("name")
             .value_name("NAME")
             .help("name of the docset")
             .required(true)
             .takes_value(true))
        .get_matches();

    create_docset(args.value_of("indir").unwrap(), args.value_of("name").unwrap()).expect("error creating docset");
    println!("\n{}.docset successfully created!", args.value_of("name").unwrap())
}

fn create_docset(indir: &str, name: &str) -> Result {
    let db_filename = format!("{}.docset/Contents/Resources/docSet.dsidx", name);
    let db_path = Path::new(&db_filename);

    try!(write_file(Path::new(format!("{}.docset/Contents/Info.plist", name).as_str()), format!(include_str!("Info.plist.tmpl"), name = name, identifier = name.replace(" ", "-").to_lowercase()).as_ref()));
    try!(write_file(Path::new(format!("{}.docset/icon.png", name).as_str()), include_bytes!("icon.png")));
    try!(write_file(Path::new(format!("{}.docset/icon@2x.png", name).as_str()), include_bytes!("icon@2x.png")));

    try!(write_file(db_path, "".as_bytes()));
    try!(sqlite::open(db_path).and_then(|c| c.execute("CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);")));

    try!(docset_from_rs_doc_tree(&Path::new(indir), format!("{}.docset/Contents/Resources/Documents/", name).as_str(), Path::new(db_path), &annotate_file));

    Ok(())
}

fn write_file(path: &Path, data: &[u8]) -> Result {
    let dir = path.parent().unwrap();

    try!(DirBuilder::new()
         .recursive(true)
         .create(dir));

    try!(File::create(path).and_then(|mut f| f.write_all(data)));
    Ok(())
}

fn docset_from_rs_doc_tree(source_dir: &Path, out_dir: &str, db_path: &Path,
                           annotate_file: &Fn(&DirEntry, &str, &Path) -> Result)
    -> Result {
        if !source_dir.exists() {
            return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} does not exist", source_dir.to_str().unwrap())).into());
        }

        if source_dir.is_dir() {
            for entry in fs::read_dir(source_dir)? {
                let entry = entry?;
                if entry.path().is_dir() {
                    try!(docset_from_rs_doc_tree(&entry.path(), &out_dir, &db_path, annotate_file));
                } else {
                    try!(annotate_file(&entry, &out_dir, &db_path));
                }
            }
        }
        Ok(())
    }

fn annotate_file(in_file: &DirEntry, output_prefix: &str, db_path: &Path) -> Result {
    let out_file = Path::new(output_prefix).join(in_file.path());

    if in_file.path().extension() != Some(OsStr::new("html")) {
        try!(write_file(Path::new(&out_file), "".as_bytes()));
        try!(fs::copy(in_file.path(), &out_file));
        //println!("{:70} | ", in_file.path().display());
        print!("."); io::stdout().flush().unwrap();
    } else {
        let opts = ParseOpts { tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() }, ..Default::default() };

        let mut dom = parse_document(RcDom::default(), opts)
            .from_utf8()
            .read_from( &mut File::open(&in_file.path())? )?;

        let entries = parser::find_entry_elements(&mut dom);
        for entry in entries.iter().filter_map(|x| x.as_ref()) {
            //println!("{:70} | {}", in_file.path().display(), entry);

            let sql_cmd = format!("INSERT OR IGNORE INTO searchIndex(name, type, path) VALUES (\"{}\", \"{}\", \"{}#{}\");", entry.entry_name, entry.entry_type, in_file.path().to_str().unwrap(), entry.anchor_name);
            try!(sqlite::open(db_path).and_then(|c| c.execute(&sql_cmd)))
        }
        manipulator::add_dash_links(&mut dom, &entries);

        let mut bytes = vec![];
        try!(serialize(&mut bytes, &dom.document, SerializeOpts::default()));
        try!(write_file(&out_file, bytes.as_ref()));

        print!("o"); io::stdout().flush().unwrap();
    }

    Ok(())
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
extern crate rand;

mod tetris;

use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Glutin_Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::GlyphCache;
use graphics::{ Transformed };
use piston::input::Button::{ Keyboard };
use piston::input::Input;
use piston::input::keyboard::Key;
use std::path::Path;

use tetris::*;

pub struct App {
    gl: GlGraphics,
    tetris: Tetris,
    elapsed_time: f64,
    cache: GlyphCache<'static>
}

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const DARK_GRAY: [f32; 4] = [0.1, 0.1, 0.1, 1.0];
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const CYAN: [f32; 4] = [0.0, 1.0, 1.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
const BLUE: [f32; 4] = [0.0, 0.0, 1.0, 1.0];
const ORANGE: [f32; 4] = [1.0, 0.5, 0.0, 1.0];
const YELLOW: [f32; 4] = [1.0, 1.0, 0.0, 1.0];
const PURPLE: [f32; 4] = [0.5, 0.0, 1.0, 1.0];

fn get_shape_color(shape_index: i32) -> [f32; 4] {
    match shape_index {
        0 => PURPLE,
        1 => YELLOW,
        2 => RED,
        3 => GREEN,
        4 => ORANGE,
        5 => BLUE,
        6 => CYAN,
        _ => {
            BLACK
        }
    }
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        const CELL_SIZE: f64 = 30.0;
        const LEFT_MARGIN: f64 = 20f64;
        const TOP_MARGIN: f64 = 30f64;

        const STATUS_LEFT_MARGIN: f64 = 400f64;
        const STATUS_TOP_MARGIN: f64 = 100f64;
        const LINE_HEIGHT: f64 = 40f64;
        const STATUS_PREVIEW_GRID_HEIGHT: f64 = CELL_SIZE * 6f64;

        // so that we can access inside clojure
        let use_cache = &mut self.cache;
        let use_tetris = &mut self.tetris;

        self.gl.draw(args.viewport(), |c, gl| {
            // clear the viewport
            graphics::clear(BLACK, gl);

            // render the current score and level
            let mut text = graphics::Text::new(22);
            text.color = ORANGE;
            let mut transform = c.trans(STATUS_LEFT_MARGIN, STATUS_TOP_MARGIN);
            text.draw(&format!("Level: {}", use_tetris.get_level()), use_cache, &c.draw_state,
                transform.transform, gl);
            transform = transform.trans(0f64, LINE_HEIGHT);

            text.draw(&format!("Score: {}", use_tetris.get_score()), use_cache, &c.draw_state,
                transform.transform, gl);
            transform = transform.trans(0f64, LINE_HEIGHT);

            // render the next shape as a preview of what's coming next
            for point in use_tetris.get_next_shape().iter() {
                let square = graphics::rectangle::square(0f64, 0f64, CELL_SIZE);
                let color = get_shape_color(use_tetris.get_next_shape_index() as i32);
                // render the shape at col 2 and row 2
                let (x, y) = ((2 as i16 + point.x) as f64 * CELL_SIZE,
                    (2 as i16 + point.y) as f64 * CELL_SIZE);
                graphics::rectangle(color, square, transform.trans(x, y).transform, gl);
                let tetromino_border = graphics::Rectangle::new_border(DARK_GRAY, 1.0);
                tetromino_border.draw([0f64, 0f64, CELL_SIZE, CELL_SIZE], &c.draw_state,
                    transform.trans(x, y).transform, gl);
            }
            transform = transform.trans(0f64, STATUS_PREVIEW_GRID_HEIGHT);

            // render GAME OVER text if necessary
            if use_tetris.get_game_over() {
                text.draw(&"GAME OVER", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press 'N' for a new game", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Use arrow keys to move and rotate", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press spacebar to drop", use_cache, &c.draw_state,
                    transform.transform, gl);
                // uncomment if drawing additional text in the status area
                //transform = transform.trans(0f64, LINE_HEIGHT);
            }

            // draw a white border around the game board
            let rect_border = graphics::Rectangle::new_border(WHITE, 1.5);
            rect_border.draw([
                LEFT_MARGIN,
                TOP_MARGIN,
                (CELL_SIZE * COL_COUNT as f64) + 1f64,
                (CELL_SIZE * ROW_COUNT as f64) + 1f64,
            ], &c.draw_state, c.transform, gl);

            // render each cell in the game board
            for col in 0..COL_COUNT as i32 {
                for row in 0..ROW_COUNT as i32 {
                    let cell = use_tetris.get_grid_cell(col, row);
                    if cell.cell_type != GridCellType::Void {
                        let (x, y) = (col as f64 * CELL_SIZE, row as f64 * CELL_SIZE);
                        let square = graphics::rectangle::square(0.0f64, 0.0f64, CELL_SIZE);
                        let color = match cell.cell_type {
                            GridCellType::Shape => get_shape_color(cell.shape_index),
                            GridCellType::Fixed => get_shape_color(cell.shape_index),
                            GridCellType::Ghost => DARK_GRAY,
                            _ => {
                                assert!(false);
                                BLACK
                            }
                        };
                        let transform = c.transform.trans(LEFT_MARGIN, TOP_MARGIN).trans(x, y);
                        graphics::rectangle(color, square, transform, gl);
                        let tetromino_border = graphics::Rectangle::new_border(DARK_GRAY, 1.0);
                        tetromino_border.draw([0f64, 0f64, CELL_SIZE, CELL_SIZE], &c.draw_state, transform, gl);
                    }
                }
            }
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        if self.tetris.get_game_over() {
            self.elapsed_time = 0.0;
        } else {
            // Here we increment the time elapsed between update()'s
            self.elapsed_time += args.dt;
            // if the elapsed time is now greater than the time allotted between ticks, then invoke tetris.tick()
            if self.elapsed_time > self.tetris.get_tick_time() as f64 {
                self.elapsed_time = 0.0;
                self.tetris.tick();
            }
        }
    }

    fn handle_input(&mut self, i: Input) {
        match i {
            Input::Press(Keyboard(Key::Left)) => {
                let col: i32 = self.tetris.get_col();
                self.tetris.set_col(col - 1);
            },

            Input::Press(Keyboard(Key::Right)) => {
                let col: i32 = self.tetris.get_col();
                self.tetris.set_col(col + 1);
            },

            Input::Press(Keyboard(Key::Up)) => {
                self.tetris.rotate(true);
            },

            Input::Press(Keyboard(Key::Down)) => {
                let row: i32 = self.tetris.get_row() + 1;
                self.tetris.set_row(row);
            },

            Input::Press(Keyboard(Key::Space)) => {
                let mut row: i32 = self.tetris.get_row() + 1;
                while self.tetris.set_row(row) {
                    row += 1;
                }
                // hard drop immediately spawns next shape
                self.tetris.tick();
            },

            Input::Press(Keyboard(Key::N)) => {
                self.tetris.start_game();
            },

            _=> {}
        }
    }
}

fn main() {
    start_app();
}

fn start_app() {
    let opengl = OpenGL::_3_2;

    let window = Glutin_Window::new(
        opengl,
        WindowSettings::new("Piston Tetris", [1024, 768]).
            exit_on_esc(true)
    );

    let font_path = Path::new("FiraMono-Bold.ttf");

    let mut app = App {
        gl: GlGraphics::new(opengl),
        tetris: Tetris::new(),
        elapsed_time: 0.0,
        cache: GlyphCache::new(font_path).unwrap(),
    };

    for e in window.events() {
        match e {
            Event::Input(i) => {
                app.handle_input(i.clone());
            }

            Event::Render(_) => {
                if let Some(r) = e.render_args() {
                    app.render(&r);
                }
            }

            Event::Update(_) => {
                if let Some(u) = e.update_args() {
                    app.update(&u);
                }
            }

            _ => {}
        }
    }
}<commit_msg>Performance improvements, and minor changes<commit_after>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
extern crate rand;

mod tetris;

use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Glutin_Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::GlyphCache;
use graphics::{ Transformed };
use piston::input::Button::{ Keyboard };
use piston::input::Input;
use piston::input::keyboard::Key;
use std::path::Path;

use tetris::*;

pub struct App {
    gl: GlGraphics,
    tetris: Tetris,
    elapsed_time: f64,
    cache: GlyphCache<'static>,
    should_redraw: bool
}

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const DARK_GRAY: [f32; 4] = [0.1, 0.1, 0.1, 1.0];
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const CYAN: [f32; 4] = [0.0, 1.0, 1.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
const BLUE: [f32; 4] = [0.0, 0.0, 1.0, 1.0];
const ORANGE: [f32; 4] = [1.0, 0.5, 0.0, 1.0];
const YELLOW: [f32; 4] = [1.0, 1.0, 0.0, 1.0];
const PURPLE: [f32; 4] = [0.5, 0.0, 1.0, 1.0];

fn get_shape_color(shape_index: i32) -> [f32; 4] {
    match shape_index {
        0 => PURPLE,
        1 => YELLOW,
        2 => RED,
        3 => GREEN,
        4 => ORANGE,
        5 => BLUE,
        6 => CYAN,
        _ => {
            BLACK
        }
    }
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        self.should_redraw = false;

        const CELL_SIZE: f64 = 30.0;
        const LEFT_MARGIN: f64 = 20f64;
        const TOP_MARGIN: f64 = 30f64;

        const STATUS_LEFT_MARGIN: f64 = 400f64;
        const STATUS_TOP_MARGIN: f64 = 100f64;
        const LINE_HEIGHT: f64 = 40f64;
        const STATUS_PREVIEW_GRID_HEIGHT: f64 = CELL_SIZE * 6f64;

        // so that we can access inside clojure
        let use_cache = &mut self.cache;
        let use_tetris = &mut self.tetris;

        self.gl.draw(args.viewport(), |c, gl| {
            // clear the viewport
            graphics::clear(BLACK, gl);

            // render the current score and level
            let mut text = graphics::Text::new(22);
            text.color = ORANGE;
            let mut transform = c.trans(STATUS_LEFT_MARGIN, STATUS_TOP_MARGIN);
            text.draw(&format!("Level: {}", use_tetris.get_level()), use_cache, &c.draw_state,
                transform.transform, gl);
            transform = transform.trans(0f64, LINE_HEIGHT);

            text.draw(&format!("Score: {}", use_tetris.get_score()), use_cache, &c.draw_state,
                transform.transform, gl);
            transform = transform.trans(0f64, LINE_HEIGHT);

            // render the next shape as a preview of what's coming next
            for point in use_tetris.get_next_shape().iter() {
                let square = graphics::rectangle::square(0f64, 0f64, CELL_SIZE);
                let color = get_shape_color(use_tetris.get_next_shape_index() as i32);
                // render the shape at col 2 and row 2
                let (x, y) = ((2 as i16 + point.x) as f64 * CELL_SIZE,
                    (2 as i16 + point.y) as f64 * CELL_SIZE);
                graphics::rectangle(color, square, transform.trans(x, y).transform, gl);
                let tetromino_border = graphics::Rectangle::new_border(DARK_GRAY, 1.0);
                tetromino_border.draw([0f64, 0f64, CELL_SIZE, CELL_SIZE], &c.draw_state,
                    transform.trans(x, y).transform, gl);
            }
            transform = transform.trans(0f64, STATUS_PREVIEW_GRID_HEIGHT);

            // render GAME OVER text if necessary
            if use_tetris.get_game_over() {
                text.draw(&"GAME OVER", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press 'N' for a new game", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Use arrow keys to move and rotate", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press spacebar to drop", use_cache, &c.draw_state,
                    transform.transform, gl);
                // uncomment if drawing additional text in the status area
                //transform = transform.trans(0f64, LINE_HEIGHT);
            }

            // draw a white border around the game board
            let rect_border = graphics::Rectangle::new_border(WHITE, 1.5);
            rect_border.draw([
                LEFT_MARGIN,
                TOP_MARGIN,
                (CELL_SIZE * COL_COUNT as f64) + 1f64,
                (CELL_SIZE * ROW_COUNT as f64) + 1f64,
            ], &c.draw_state, c.transform, gl);

            // render each cell in the game board
            for col in 0..COL_COUNT as i32 {
                for row in 0..ROW_COUNT as i32 {
                    let cell = use_tetris.get_grid_cell(col, row);
                    if cell.cell_type != GridCellType::Void {
                        let (x, y) = (col as f64 * CELL_SIZE, row as f64 * CELL_SIZE);
                        let square = graphics::rectangle::square(0.0f64, 0.0f64, CELL_SIZE);
                        let color = match cell.cell_type {
                            GridCellType::Shape => get_shape_color(cell.shape_index),
                            GridCellType::Fixed => get_shape_color(cell.shape_index),
                            GridCellType::Ghost => DARK_GRAY,
                            _ => {
                                assert!(false);
                                BLACK
                            }
                        };
                        let transform = c.transform.trans(LEFT_MARGIN, TOP_MARGIN).trans(x, y);
                        graphics::rectangle(color, square, transform, gl);
                        let tetromino_border = graphics::Rectangle::new_border(DARK_GRAY, 1.0);
                        tetromino_border.draw([0f64, 0f64, CELL_SIZE, CELL_SIZE], &c.draw_state, transform, gl);
                    }
                }
            }
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        if self.tetris.get_game_over() {
            self.elapsed_time = 0.0;
        } else {
            // Here we increment the time elapsed between update()'s
            self.elapsed_time += args.dt;
            // if the elapsed time is now greater than the time allotted between ticks, then invoke tetris.tick()
            if self.elapsed_time > self.tetris.get_tick_time() as f64 {
                self.elapsed_time = 0.0;
                self.tetris.tick();
                self.should_redraw = true;
            }
        }
    }

    fn handle_input(&mut self, i: Input) {
        let prev_should_redraw = self.should_redraw;
        self.should_redraw = true;
        match i {
            Input::Press(Keyboard(Key::Left)) => {
                let col: i32 = self.tetris.get_col();
                self.tetris.set_col(col - 1);
            },

            Input::Press(Keyboard(Key::Right)) => {
                let col: i32 = self.tetris.get_col();
                self.tetris.set_col(col + 1);
            },

            Input::Press(Keyboard(Key::Up)) => {
                self.tetris.rotate(true);
            },

            Input::Press(Keyboard(Key::Down)) => {
                let row: i32 = self.tetris.get_row() + 1;
                self.tetris.set_row(row);
            },

            Input::Press(Keyboard(Key::Space)) => {
                let mut row: i32 = self.tetris.get_row() + 1;
                while self.tetris.set_row(row) {
                    row += 1;
                }
                // hard drop immediately spawns next shape
                self.tetris.tick();
            },

            Input::Press(Keyboard(Key::N)) => {
                self.tetris.start_game();
            },

            _ => {
                self.should_redraw = prev_should_redraw;
            }
        }
    }
}

fn main() {
    start_app();
}

fn start_app() {
    let opengl = OpenGL::_3_2;

    let window = Glutin_Window::new(
        opengl,
        WindowSettings::new("Piston Tetris", [1024, 768])
                       .exit_on_esc(true)
                       .vsync(true)
                       .samples(1)
    );

    let font_path = Path::new("FiraMono-Bold.ttf");

    let mut app = App {
        gl: GlGraphics::new(opengl),
        tetris: Tetris::new(),
        elapsed_time: 0.0,
        cache: GlyphCache::new(font_path).unwrap(),
        should_redraw: true,
    };

    for e in window.events().max_fps(30).ups(30) {
        match e {
            Event::Input(i) => {
                app.handle_input(i);
            }

            Event::Render(args) if app.should_redraw => {
                app.render(&args);
            }

            Event::Update(args) => {
                app.update(&args);
            }

            _ => {}
        }
    }
}<|endoftext|>


<commit_before>extern crate hkg;
extern crate termion;
extern crate rustc_serialize;
extern crate kuchiki;
extern crate chrono;
extern crate cancellation;

#[macro_use]
extern crate log;
extern crate log4rs;

use std::io::{stdout, stdin, Write};
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use rustc_serialize::json;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use hkg::status::*;
use hkg::model::IconItem;
use hkg::state_manager::*;
use hkg::screen_manager::*;
use hkg::resources::*;
use hkg::web::*;
use hkg::responser::*;

fn main() {

    // Initialize
    log4rs::init_file("config/log4rs.yaml", Default::default()).expect("fail to init log4rs");

    // Clear the screen.
    hkg::screen::common::clear_screen();

    let _stdout = stdout();

    // web background services
    let (tx_req, rx_req) = channel::<ChannelItem>();
    let (tx_res, rx_res) = channel::<ChannelItem>();

    let mut app = {

        let stdout = {
            Box::new(_stdout.lock().into_raw_mode().expect("fail to lock stdout"))
        };

        let icon_collection: Box<Vec<IconItem>> = {
            let icon_manifest_string = hkg::utility::readfile(String::from("data/icon.manifest.json"));
            Box::new(json::decode(&icon_manifest_string).expect("fail to lock stdout"))
        };

        hkg::App {
            builder: hkg::builder::Builder::new(),
            state_manager: StateManager::new(),
            screen_manager: ScreenManager::new(),

            // initialize empty page
            list_topic_items: vec![],
            show_item: hkg::builder::Builder::new().default_show_item(),

            status_bar: hkg::screen::status_bar::StatusBar::new(),
            index: hkg::screen::index::Index::new(),
            show: hkg::screen::show::Show::new(icon_collection),

            image_request_count_lock: Arc::new(Mutex::new(0)),
            tx_req: &tx_req,
            rx_res: &rx_res,

            stdout: stdout
        }
    };

    Requester::new(rx_req, tx_res);

    let respsoner = Responser::new();

    let mut index_control = hkg::control::index::Index::new();
    let mut show_control = hkg::control::show::Show::new();

    // topics request
    let status_message = list_page(&mut app.state_manager, &tx_req);
    app.status_bar.append(&app.screen_manager, &status_message);

    loop {

        respsoner.try_recv(&mut app);

        print_screen(&mut app);

        app.status_bar.print(&app.screen_manager);

        app.stdout.flush().expect("fail to flush the stdout");

        if !app.state_manager.is_web_request() {

            let stdin = stdin();

            for c in stdin.keys() {

                if app.screen_manager.is_width_changed() {
                    hkg::screen::common::clear_screen();
                }

                match app.state_manager.get_state() {
                    Status::Startup => {}
                    Status::List => {
                        match index_control.handle(c.ok().expect("fail to get stdin keys"), &mut app) {
                            Some(i) => return if i == 0 { return } else { break },
                            None => {}
                        }
                    }
                    Status::Show => {
                        match show_control.handle(c.ok().expect("fail to get stdin keys"), &mut app) {
                            Some(i) => return if i == 0 { return } else { break },
                            None => {}
                        }
                    }
                }

            }
        }
    }
}

fn list_page(state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: ChannelItemType::Index(ChannelIndexItem {}),
        result: String::from(""),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.set_web_request(true);    // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}

fn print_screen(app: &mut hkg::App) {
    match app.state_manager.get_state() {
        Status::Startup => {}
        Status::List => {
            app.index.print(&mut app.stdout, &app.list_topic_items);
        }
        Status::Show => {
            app.show.print(&mut app.stdout, &app.show_item);
        }
    }
}
<commit_msg>clean unreachable code<commit_after>extern crate hkg;
extern crate termion;
extern crate rustc_serialize;
extern crate kuchiki;
extern crate chrono;
extern crate cancellation;

#[macro_use]
extern crate log;
extern crate log4rs;

use std::io::{stdout, stdin, Write};
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use rustc_serialize::json;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use hkg::status::*;
use hkg::model::IconItem;
use hkg::state_manager::*;
use hkg::screen_manager::*;
use hkg::resources::*;
use hkg::web::*;
use hkg::responser::*;

fn main() {

    // Initialize
    log4rs::init_file("config/log4rs.yaml", Default::default()).expect("fail to init log4rs");

    // Clear the screen.
    hkg::screen::common::clear_screen();

    let _stdout = stdout();

    // web background services
    let (tx_req, rx_req) = channel::<ChannelItem>();
    let (tx_res, rx_res) = channel::<ChannelItem>();

    let mut app = {

        let stdout = {
            Box::new(_stdout.lock().into_raw_mode().expect("fail to lock stdout"))
        };

        let icon_collection: Box<Vec<IconItem>> = {
            let icon_manifest_string = hkg::utility::readfile(String::from("data/icon.manifest.json"));
            Box::new(json::decode(&icon_manifest_string).expect("fail to lock stdout"))
        };

        hkg::App {
            builder: hkg::builder::Builder::new(),
            state_manager: StateManager::new(),
            screen_manager: ScreenManager::new(),

            // initialize empty page
            list_topic_items: vec![],
            show_item: hkg::builder::Builder::new().default_show_item(),

            status_bar: hkg::screen::status_bar::StatusBar::new(),
            index: hkg::screen::index::Index::new(),
            show: hkg::screen::show::Show::new(icon_collection),

            image_request_count_lock: Arc::new(Mutex::new(0)),
            tx_req: &tx_req,
            rx_res: &rx_res,

            stdout: stdout
        }
    };

    Requester::new(rx_req, tx_res);

    let respsoner = Responser::new();

    let mut index_control = hkg::control::index::Index::new();
    let mut show_control = hkg::control::show::Show::new();

    // topics request
    let status_message = list_page(&mut app.state_manager, &tx_req);
    app.status_bar.append(&app.screen_manager, &status_message);

    loop {

        respsoner.try_recv(&mut app);

        print_screen(&mut app);

        app.status_bar.print(&app.screen_manager);

        app.stdout.flush().expect("fail to flush the stdout");

        if !app.state_manager.is_web_request() {

            let stdin = stdin();

            for c in stdin.keys() {

                if app.screen_manager.is_width_changed() {
                    hkg::screen::common::clear_screen();
                }

                match app.state_manager.get_state() {
                    Status::Startup => {}
                    Status::List => {
                        match index_control.handle(c.ok().expect("fail to get stdin keys"), &mut app) {
                            Some(i) => if i == 0 { return } else { break },
                            None => {}
                        }
                    }
                    Status::Show => {
                        match show_control.handle(c.ok().expect("fail to get stdin keys"), &mut app) {
                            Some(i) => if i == 0 { return } else { break },
                            None => {}
                        }
                    }
                }

            }
        }
    }
}

fn list_page(state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: ChannelItemType::Index(ChannelIndexItem {}),
        result: String::from(""),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.set_web_request(true);    // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}

fn print_screen(app: &mut hkg::App) {
    match app.state_manager.get_state() {
        Status::Startup => {}
        Status::List => {
            app.index.print(&mut app.stdout, &app.list_topic_items);
        }
        Status::Show => {
            app.show.print(&mut app.stdout, &app.show_item);
        }
    }
}
<|endoftext|>


<commit_before>// 全形
extern crate rustbox;
extern crate unicode_width;

mod buffer;
mod geometry;
mod window;
mod graphics;
mod errors;
mod logging;
mod mode;
mod hacks;
mod settings;

use std::default::Default;
use std::env;
use std::error::Error;
use std::sync::{Arc, Mutex};
use std::str;
use std::process;

use rustbox::RustBox;

use window::Window;
use geometry::{Point, Size};
use errors::CrbResult;
use mode::Command;
use settings::Settings;

fn main() {
    logging::debug("started");

    // Restart loop.
    loop {
        match startup() {
            Ok(true) => {
                let res = hacks::restart();
                if let Err(e) = res {
                    println!("Fatal error restarting: {}", e);
                }
                break;
            }
            Ok(false) => break,
            Err(e) => println!("Fatal error: {}", e),
        }
    }
}

fn startup() -> Result<bool, Box<Error>> {
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => return Err(Box::new(e)),
    };

    let settings = Arc::new(Mutex::new(Settings::new()));

    let buf1 = match env::args().nth(1) {
        Some(path) => {
            match buffer::Buffer::load_from_file(&path, settings.clone()) {
                Ok(buffer) => buffer,
                _ => buffer::Buffer::new_file(&path, settings.clone()),
            }
        }
        None => buffer::Buffer::empty(settings.clone()),
    };

    let buf1 = Mutex::new(buf1);

    let buf2 = buffer::Buffer::empty(settings.clone());
    let buf2 = Mutex::new(buf2);

    let buf3 = buffer::Buffer::empty(settings.clone());
    let buf3 = Mutex::new(buf3);

    let width = rustbox.width() as i32;
    let height = rustbox.height() as i32;

    let mut window1 = Window::new(buf1, Point::new(0, 0), Size::new(width, height - 10));
    let mut window2 = Window::new(buf2, Point::new(0, height - 10), Size::new(width, 10));
    let mut window3 = Window::new(buf3, Point::new(width / 2, 2), Size::new(width / 2 - 1, 2));

    loop {
        graphics::render(&rustbox, &window1);
        graphics::render(&rustbox, &window2);
        graphics::render(&rustbox, &window3);

        rustbox.present();

        let event = rustbox.poll_event(false);
        match event {
            Ok(rustbox::Event::KeyEvent(key)) => {
                let cmd = mode::map(window1.mode.clone(), key);
                let res = match cmd {
                    Command::Quit => break,
                    Command::MoveUp(_) => window1.move_cursors(&cmd),
                    Command::MoveDown(_) => window1.move_cursors(&cmd),
                    Command::MoveLeft(_) => window1.move_cursors(&cmd),
                    Command::MoveRight(_) => window1.move_cursors(&cmd),
                    Command::Insert(c) => window1.insert(c),
                    Command::Delete(d) => window1.delete(d),
                    Command::NewLine => window1.insert('\n'),
                    Command::Scroll(_) => window1.scroll(&cmd),
                    Command::ChangeMode(m) => {
                        window1.mode = m;
                        Ok(())
                    }
                    Command::RecompileSelf => {
                        // TODO handle error
                        let _ = window2.clear();
                        let res = hacks::recompile();
                        let restart =
                            res.and_then(|output| fill_compilation_buffer(&mut window2, output));
                        if let Ok(true) = restart {
                            return Ok(true);
                        }
                        restart.and(Ok(()))
                    }
                    Command::Save => window1.save(),
                    _ => Ok(()), //TODO show this somewhere
                };
                if let Err(e) = res {
                    logging::debug(&format!("cmd error: {}", e));
                }
            }
            // TODO don't panic...
            Err(e) => panic!("{}", e),
            _ => {}
        }
        // TODO handle errors
        if let Ok(rustbox::Event::KeyEvent(key)) = event {
            let cmd = mode::map(window1.mode.clone(), key);
            let _ = window3.clear();
            let _ = window3.insert_s(&format!("{:?}", event));
            let _ = window3.insert('\n');
            let _ = window3.insert_s(&format!("{:?}", cmd));
        }
    }

    Ok(false)
}

fn fill_compilation_buffer(w: &mut Window, output: process::Output) -> CrbResult<bool> {
    try!(w.clear());
    if output.status.success() {
        try!(w.insert_s("Compilation successful\n"));
        Ok(true)
    } else {
        try!(w.insert_s("Compilation failed\n"));
        match str::from_utf8(&output.stderr) {
            Ok(s) => try!(w.insert_s(s)),
            Err(e) => {
                try!(w.insert_s("<stderr-utf8-error>\n"));
                try!(w.insert_s(&format!("{}", e)));
            }
        };
        try!(w.insert('\n'));
        match str::from_utf8(&output.stdout) {
            Ok(s) => try!(w.insert_s(s)),
            Err(e) => {
                try!(w.insert_s("<stdout-utf8-error>\n"));
                try!(w.insert_s(&format!("{}", e)));
            }
        };
        Ok(false)
    }
}

<commit_msg>remove hong kong<commit_after>extern crate rustbox;
extern crate unicode_width;

mod buffer;
mod geometry;
mod window;
mod graphics;
mod errors;
mod logging;
mod mode;
mod hacks;
mod settings;

use std::default::Default;
use std::env;
use std::error::Error;
use std::sync::{Arc, Mutex};
use std::str;
use std::process;

use rustbox::RustBox;

use window::Window;
use geometry::{Point, Size};
use errors::CrbResult;
use mode::Command;
use settings::Settings;

fn main() {
    logging::debug("started");

    // Restart loop.
    loop {
        match startup() {
            Ok(true) => {
                let res = hacks::restart();
                if let Err(e) = res {
                    println!("Fatal error restarting: {}", e);
                }
                break;
            }
            Ok(false) => break,
            Err(e) => println!("Fatal error: {}", e),
        }
    }
}

fn startup() -> Result<bool, Box<Error>> {
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => return Err(Box::new(e)),
    };

    let settings = Arc::new(Mutex::new(Settings::new()));

    let buf1 = match env::args().nth(1) {
        Some(path) => {
            match buffer::Buffer::load_from_file(&path, settings.clone()) {
                Ok(buffer) => buffer,
                _ => buffer::Buffer::new_file(&path, settings.clone()),
            }
        }
        None => buffer::Buffer::empty(settings.clone()),
    };

    let buf1 = Mutex::new(buf1);

    let buf2 = buffer::Buffer::empty(settings.clone());
    let buf2 = Mutex::new(buf2);

    let buf3 = buffer::Buffer::empty(settings.clone());
    let buf3 = Mutex::new(buf3);

    let width = rustbox.width() as i32;
    let height = rustbox.height() as i32;

    let mut window1 = Window::new(buf1, Point::new(0, 0), Size::new(width, height - 10));
    let mut window2 = Window::new(buf2, Point::new(0, height - 10), Size::new(width, 10));
    let mut window3 = Window::new(buf3, Point::new(width / 2, 2), Size::new(width / 2 - 1, 2));

    loop {
        graphics::render(&rustbox, &window1);
        graphics::render(&rustbox, &window2);
        graphics::render(&rustbox, &window3);

        rustbox.present();

        let event = rustbox.poll_event(false);
        match event {
            Ok(rustbox::Event::KeyEvent(key)) => {
                let cmd = mode::map(window1.mode.clone(), key);
                let res = match cmd {
                    Command::Quit => break,
                    Command::MoveUp(_) => window1.move_cursors(&cmd),
                    Command::MoveDown(_) => window1.move_cursors(&cmd),
                    Command::MoveLeft(_) => window1.move_cursors(&cmd),
                    Command::MoveRight(_) => window1.move_cursors(&cmd),
                    Command::Insert(c) => window1.insert(c),
                    Command::Delete(d) => window1.delete(d),
                    Command::NewLine => window1.insert('\n'),
                    Command::Scroll(_) => window1.scroll(&cmd),
                    Command::ChangeMode(m) => {
                        window1.mode = m;
                        Ok(())
                    }
                    Command::RecompileSelf => {
                        // TODO handle error
                        let _ = window2.clear();
                        let res = hacks::recompile();
                        let restart =
                            res.and_then(|output| fill_compilation_buffer(&mut window2, output));
                        if let Ok(true) = restart {
                            return Ok(true);
                        }
                        restart.and(Ok(()))
                    }
                    Command::Save => window1.save(),
                    _ => Ok(()), //TODO show this somewhere
                };
                if let Err(e) = res {
                    logging::debug(&format!("cmd error: {}", e));
                }
            }
            // TODO don't panic...
            Err(e) => panic!("{}", e),
            _ => {}
        }
        // TODO handle errors
        if let Ok(rustbox::Event::KeyEvent(key)) = event {
            let cmd = mode::map(window1.mode.clone(), key);
            let _ = window3.clear();
            let _ = window3.insert_s(&format!("{:?}", event));
            let _ = window3.insert('\n');
            let _ = window3.insert_s(&format!("{:?}", cmd));
        }
    }

    Ok(false)
}

fn fill_compilation_buffer(w: &mut Window, output: process::Output) -> CrbResult<bool> {
    try!(w.clear());
    if output.status.success() {
        try!(w.insert_s("Compilation successful\n"));
        Ok(true)
    } else {
        try!(w.insert_s("Compilation failed\n"));
        match str::from_utf8(&output.stderr) {
            Ok(s) => try!(w.insert_s(s)),
            Err(e) => {
                try!(w.insert_s("<stderr-utf8-error>\n"));
                try!(w.insert_s(&format!("{}", e)));
            }
        };
        try!(w.insert('\n'));
        match str::from_utf8(&output.stdout) {
            Ok(s) => try!(w.insert_s(s)),
            Err(e) => {
                try!(w.insert_s("<stdout-utf8-error>\n"));
                try!(w.insert_s(&format!("{}", e)));
            }
        };
        Ok(false)
    }
}
<|endoftext|>


<commit_before>use std::rc::Rc;

type Error = &'static str;

#[derive(Debug,Clone)]
enum Code {
    Integer(i64),
    Rational(i64, i64),
    String(String),
    Add(Rc<Code>, Rc<Code>),
    Sub(Rc<Code>, Rc<Code>),
    Div(Rc<Code>, Rc<Code>),
    Mul(Rc<Code>, Rc<Code>),
}

// Calculate the greatest common divisor using Euclid's algorithm.
fn gcd(mut a: i64, mut b: i64) -> i64 {
    let mut c;
    while a != 0 {
        c = a;
        a = b % a;
        b = c;
    }
    b
}

// This normalizes inputs and sometimes even converts to integers.
fn new_rational(mut n: i64, mut d: i64) -> Code {
    let g = gcd(n, d);
    n = n / g;
    d = d / g;
    if d < 0 {
      n = -n;
      d = -d;
    }
    if d == 1 {
        Code::Integer(n)
    }
    else {
        Code::Rational(n, d)
    }
}

fn rational_add(n1: i64, d1: i64, n2: i64, d2: i64) -> Code {
  if d1 == d2 {
    // Fast path for common divisors.
    new_rational(n1 + n2, d1)
  }
  else {
    let g = gcd(d1, d2);
    new_rational(n1 * (d2 / g) + n2 * (d1 / g), d1 / g * d2)
  }

}

fn add(left: &Code, right: &Code) -> Result<Code, Error> {
    match (left, right) {
        (&Code::Integer(a), &Code::Integer(b)) => Ok(Code::Integer(a + b)),
        (&Code::Rational(n, d), &Code::Integer(b)) => Ok(rational_add(n, d, b, 1)),
        (&Code::Integer(a), &Code::Rational(n, d)) => Ok(rational_add(a, 1, n, d)),
        (&Code::Rational(n1, d1), &Code::Rational(n2, d2)) => Ok(rational_add(n1, d1, n2, d2)),
        _ => Err("Add requires two numbers"),
    }
}

fn subtract(left: &Code, right: &Code) -> Result<Code, Error> {
    match (left, right) {
        (&Code::Integer(a), &Code::Integer(b)) => Ok(Code::Integer(a - b)),
        (&Code::Rational(n, d), &Code::Integer(b)) => Ok(rational_add(n, d, -b, 1)),
        (&Code::Integer(a), &Code::Rational(n, d)) => Ok(rational_add(a, 1, -n, d)),
        (&Code::Rational(n1, d1), &Code::Rational(n2, d2)) => Ok(rational_add(n1, d1, -n2, d2)),
        _ => Err("Subtract requires two numbers"),
    }
}

fn multiply(left: &Code, right: &Code) -> Result<Code, Error> {
    match (left, right) {
        (&Code::Integer(a), &Code::Integer(b)) => Ok(Code::Integer(a * b)),
        (&Code::Rational(n, d), &Code::Integer(b)) => Ok(new_rational(n * b, d)),
        (&Code::Integer(a), &Code::Rational(n, d)) => Ok(new_rational(a * n, d)),
        (&Code::Rational(n1, d1), &Code::Rational(n2, d2)) => Ok(new_rational(n1 * n2, d1 * d2)),
        _ => Err("Multiply requires two numbers"),
    }
}

fn divide(left: &Code, right: &Code) -> Result<Code, Error> {
    match (left, right) {
        (&Code::Integer(a), &Code::Integer(b)) => Ok(Code::Rational(a, b)),
        (&Code::Rational(n, d), &Code::Integer(b)) => Ok(new_rational(n, d * b)),
        (&Code::Integer(a), &Code::Rational(n, d)) => Ok(new_rational(a * d, n)),
        (&Code::Rational(n1, d1), &Code::Rational(n2, d2)) => Ok(new_rational(n1 * d2, d1 * n2)),
        _ => Err("Divide requires two numbers"),
    }
}

fn eval(expr: &Code) -> Result<Code, Error> {
    match expr {
        &Code::Add(ref left, ref right) => add(left, right),
        &Code::Sub(ref left, ref right) => subtract(left, right),
        &Code::Mul(ref left, ref right) => multiply(left, right),
        &Code::Div(ref left, ref right) => divide(left, right),
        n @ &Code::Integer(_) => Ok(n.clone()),
        n @ &Code::Rational(_, _) => Ok(n.clone()),
        n @ &Code::String(_) => Ok(n.clone()),
    }
}

fn test(left: Code, right: Code) {
    let a = Rc::new(left);
    let b = Rc::new(right);
    let mut e = Rc::new(Code::Add(a.clone(), b.clone()));
    println!("{:?} = {:?}", e, eval(&e));
    e = Rc::new(Code::Sub(a.clone(), b.clone()));
    println!("{:?} = {:?}", e, eval(&e));
    e = Rc::new(Code::Mul(a.clone(), b.clone()));
    println!("{:?} = {:?}", e, eval(&e));
    e = Rc::new(Code::Div(a.clone(), b.clone()));
    println!("{:?} = {:?}", e, eval(&e));
}

fn main() {
    test(Code::Integer(44), Code::Integer(14));
    test(Code::Integer(14), Code::Integer(44));
    test(Code::Integer(44), Code::Integer(-14));
    test(Code::Integer(14), Code::Integer(-44));
    test(Code::Integer(-44), Code::Integer(14));
    test(Code::Integer(-14), Code::Integer(44));
    test(Code::Integer(-44), Code::Integer(-14));
    test(Code::Integer(-14), Code::Integer(-44));
    test(new_rational(1, 2), new_rational(2, 1));
    test(new_rational(2, 1), new_rational(1, 2));
    test(new_rational(1, 2), new_rational(1, 2));
    test(new_rational(2, 2), new_rational(2, 1));
    test(Code::String(str::to_string("Input string")),
         new_rational(2, 1));
}
<commit_msg>Add recursive eval<commit_after>use std::rc::Rc;

type Error = &'static str;

#[derive(Debug,Clone)]
enum Code {
    Integer(i64),
    Rational(i64, i64),
    String(String),
    Add(Rc<Code>, Rc<Code>),
    Sub(Rc<Code>, Rc<Code>),
    Div(Rc<Code>, Rc<Code>),
    Mul(Rc<Code>, Rc<Code>),
    Neg(Rc<Code>),
}

// Calculate the greatest common divisor using Euclid's algorithm.
fn gcd(mut a: i64, mut b: i64) -> i64 {
    let mut c;
    while a != 0 {
        c = a;
        a = b % a;
        b = c;
    }
    b
}

// This normalizes inputs and sometimes even converts to integers.
fn new_rational(mut n: i64, mut d: i64) -> Code {
    let g = gcd(n, d);
    n = n / g;
    d = d / g;
    if d < 0 {
      n = -n;
      d = -d;
    }
    if d == 1 {
        Code::Integer(n)
    }
    else {
        Code::Rational(n, d)
    }
}

fn rational_add(n1: i64, d1: i64, n2: i64, d2: i64) -> Code {
  if d1 == d2 {
    // Fast path for common divisors.
    new_rational(n1 + n2, d1)
  }
  else {
    let g = gcd(d1, d2);
    new_rational(n1 * (d2 / g) + n2 * (d1 / g), d1 / g * d2)
  }

}

fn add(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a + b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(rational_add(n, d, b, 1)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(rational_add(a, 1, n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(rational_add(n1, d1, n2, d2)),
        _ => Err("Add requires two numbers"),
    }
}

fn subtract(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a - b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(rational_add(n, d, -b, 1)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(rational_add(a, 1, -n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(rational_add(n1, d1, -n2, d2)),
        _ => Err("Subtract requires two numbers"),
    }
}

fn multiply(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a * b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(new_rational(n * b, d)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(new_rational(a * n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(new_rational(n1 * n2, d1 * d2)),
        _ => Err("Multiply requires two numbers"),
    }
}

fn divide(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Rational(a, b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(new_rational(n, d * b)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(new_rational(a * d, n)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(new_rational(n1 * d2, d1 * n2)),
        _ => Err("Divide requires two numbers"),
    }
}

fn negate(num: &Code) -> Result<Code, Error> {
    match num {
        &Code::Integer(a) => Ok(Code::Integer(-a)),
        &Code::Rational(n, d) => Ok(Code::Rational(-n, d)),
        _ => Err("Negate requires a number"),
    }
}

fn eval(expr: &Code) -> Result<Code, Error> {
    match expr {
        &Code::Add(ref left, ref right) => add(left, right),
        &Code::Sub(ref left, ref right) => subtract(left, right),
        &Code::Mul(ref left, ref right) => multiply(left, right),
        &Code::Div(ref left, ref right) => divide(left, right),
        &Code::Neg(ref num) => negate(num),
        n @ &Code::Integer(_) => Ok(n.clone()),
        n @ &Code::Rational(_, _) => Ok(n.clone()),
        n @ &Code::String(_) => Ok(n.clone()),
    }
}

fn test(left: Code, right: Code) {
    let a = Rc::new(left);
    let b = Rc::new(right);
    let mut e = Rc::new(Code::Add(a.clone(), b.clone()));
    println!("{:?} = {:?}", e, eval(&e));
    e = Rc::new(Code::Sub(a.clone(), b.clone()));
    println!("{:?} = {:?}", e, eval(&e));
    e = Rc::new(Code::Mul(a.clone(), b.clone()));
    println!("{:?} = {:?}", e, eval(&e));
    e = Rc::new(Code::Div(a.clone(), b.clone()));
    println!("{:?} = {:?}", e, eval(&e));
    e = Rc::new(Code::Neg(a.clone()));
    println!("{:?} = {:?}", e, eval(&e));
}

fn main() {
    test(Code::Integer(44), Code::Integer(14));
    test(Code::Integer(14), Code::Integer(44));
    test(Code::Integer(44), Code::Integer(-14));
    test(Code::Integer(14), Code::Integer(-44));
    test(Code::Integer(-44), Code::Integer(14));
    test(Code::Integer(-14), Code::Integer(44));
    test(Code::Integer(-44), Code::Integer(-14));
    test(Code::Integer(-14), Code::Integer(-44));
    test(new_rational(1, 2), new_rational(2, 1));
    test(new_rational(2, 1), new_rational(1, 2));
    test(new_rational(1, 2), new_rational(1, 2));
    test(new_rational(2, 2), new_rational(2, 1));
    test(Code::String(str::to_string("Input string")),
         new_rational(2, 1));
    let e = Code::Div(
        Rc::new(Code::Add(
            Rc::new(Code::Rational(1, 2)),
            Rc::new(Code::Rational(1, 3)),
        )),
        Rc::new(Code::Sub(
            Rc::new(Code::Integer(2)),
            Rc::new(Code::Integer(5)),
        )),
    );
    println!("{:?} = {:?}", e, eval(&e));

}
<|endoftext|>


<commit_before>extern crate time;

fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();
    let (year, month, day, yoff) = current_day(time::now());
    let wd = (yoff + month * 3 + day) % dpw;
    let mut moff = yoff;

    for mm in 0..mpy {
        let spaces = (17 - moy[mm].len()) / 2;
        for _ in 0..spaces {
            print!(" ");
        }
        println!("{}", moy[mm]);

        for dd in 0..dpw {
            let mut abbrd = String::from(dow[dd]);
            abbrd.truncate(2);
            print!("{} ", abbrd);
        }

        let dd = if mm == month { day } else { 1000 };
        moff = print_days(moff, dpm, dpw, dd);
        if mm == 2 {
            println!("--  Peer  Day  --");
        }
        else if mm == 6 {
            println!("--Torrent Feast--");
        }
        else if is_leap_year(year) && mm == 10 {
            println!("--Immersion Day--");
        }
    }

    let mut datestr = format!("{}, {:02} of {}", dow[wd], day, moy[month]);
    let mut daystr = format!("{:02}.{:02}", month + 1, day);
    if day == 34 {
        if month == 2 {
            datestr = format!("Peer Day");
            daystr = format!("P");
        }
        else if month == 6 {
            datestr = format!("Torrent Feast");
            daystr = format!("T");
        }
        else if month == 10 {
            datestr = format!("Immersion Feast");
            daystr = format!("T");
        }
    }
    let hol = format!("{}", what_holiday(month, day));
    println!("Today is {} {}{}.  [{}.{}]", datestr, year, hol, year, daystr);
}

fn print_days(moff: usize, dpm: usize, dpw: usize, highlight: usize) -> usize {
    if moff != 0 {
        println!("");
    }

    for _ in 0 .. moff {
        print!("   ");
    }

    for day in 1 .. dpm + 1 {
        if (day + moff) % dpw == 1 {
            println!("");
        }

        let ch = if day == highlight { ">" } else if day + 1 == highlight { "<" } else { " " };
        print!("{:2}{}", day, ch);
    }

    println!("");
    return if moff == 0 { 3 } else { 0 };
}

fn current_day(current_time: time::Tm) -> (usize, usize, usize, usize) {
    let epoch_days_offset = 1483889;
    let seconds_per_day = 86400;
    let now = current_time.to_timespec();
    let days_since_epoch = (epoch_days_offset + now.sec / seconds_per_day) as i32;
    let days_per_year = 365;
    let leap_year = 4;
    let not_leap_year = 33;
    let days_per_month = 33;

    let mut count = 1;
    let mut days_remaining = days_since_epoch;
    let mut year = 0;
    while days_remaining > days_per_year + 1 {
        days_remaining -= days_per_year;
        year += 1;
        if count > not_leap_year {
            count = 1;
        }
        if count % leap_year == 0 {
            days_remaining -= 1;
        }
        count += 1;
    }
    if days_remaining == days_per_year + 1 && count % leap_year == 0 {
        days_remaining = 0;
        year += 1;
    }

    let mut month = 0;
    while days_remaining > days_per_month {
        month += 1;
        days_remaining -= days_per_month;
        if month == 3 || month == 7 || month == 11 {
            days_remaining -= 1;
        }
    }

    let day = days_remaining;
    return (year, month as usize, day as usize + 1, (year % 2 * 3) as usize);
}

fn is_leap_year(year: usize) -> bool {
    let rem = year % 33;
    return rem != 0 && rem % 4 == 0;
}

fn what_holiday(month: usize, day: usize) -> String {
    let mut result = format!("");
    if month == 0 && day == 1 {
        result = format!(", New Year's Day");
    }
    else if month == 2 && day == 12 {
        result = format!(", Purification Day");
    }
    else if month == 2 && day == 24 {
        result = format!(", Document Freedom Day");
    }
    else if month == 3 && day == 33 {
        result = format!(", Hardware Freedom Day");
    }
    else if month == 5 && day == 4 {
        result = format!(", Harvest Gift");
    }
    else if month == 5 && day == 9 {
        result = format!(", Culture Freedom Day");
    }
    else if month == 7 && day == 29 {
        result = format!(", Familytide");
    }
    else if month == 8 && day == 33 {
        result = format!(", Software Freedom Day");
    }
    else if month == 9 && day == 30 {
        result = format!(", Open Access Day");
    }
    else if month == 10 && day == 21 {
        result = format!(", Freedom Day");
    }
    else if month == 0 && day == 27 {
        result = format!(", International Workers' Day");
    }
    return result;
}

<commit_msg>Reduce I/O in month-printing<commit_after>extern crate time;

fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();
    let (year, month, day, yoff) = current_day(time::now());
    let wd = (yoff + month * 3 + day) % dpw;
    let mut moff = yoff;

    for mm in 0..mpy {
        let spaces = (17 - moy[mm].len()) / 2;
        for _ in 0..spaces {
            print!(" ");
        }
        println!("{}", moy[mm]);

        for dd in 0..dpw {
            let mut abbrd = String::from(dow[dd]);
            abbrd.truncate(2);
            print!("{} ", abbrd);
        }

        let dd = if mm == month { day } else { 1000 };
        moff = print_days(moff, dpm, dpw, dd);
        if mm == 2 {
            println!("--  Peer  Day  --");
        }
        else if mm == 6 {
            println!("--Torrent Feast--");
        }
        else if is_leap_year(year) && mm == 10 {
            println!("--Immersion Day--");
        }
    }

    let mut datestr = format!("{}, {:02} of {}", dow[wd], day, moy[month]);
    let mut daystr = format!("{:02}.{:02}", month + 1, day);
    if day == 34 {
        if month == 2 {
            datestr = format!("Peer Day");
            daystr = format!("P");
        }
        else if month == 6 {
            datestr = format!("Torrent Feast");
            daystr = format!("T");
        }
        else if month == 10 {
            datestr = format!("Immersion Feast");
            daystr = format!("T");
        }
    }
    let hol = format!("{}", what_holiday(month, day));
    println!("Today is {} {}{}.  [{}.{}]", datestr, year, hol, year, daystr);
}

fn print_days(moff: usize, dpm: usize, dpw: usize, highlight: usize) -> usize {
    let mut month = format!("");

    if moff != 0 {
        month = format!("{}\n", month);
    }

    for _ in 0 .. moff {
        month = format!("{}   ", month);
    }

    for day in 1 .. dpm + 1 {
        if (day + moff) % dpw == 1 {
            month = format!("{}\n", month);
        }

        let ch = if day == highlight { ">" } else if day + 1 == highlight { "<" } else { " " };
        month = format!("{}{:2}{}", month, day, ch);
    }

    month = format!("{}\n", month);
    print!("{}", month);
    return if moff == 0 { 3 } else { 0 };
}

fn current_day(current_time: time::Tm) -> (usize, usize, usize, usize) {
    let epoch_days_offset = 1483889;
    let seconds_per_day = 86400;
    let now = current_time.to_timespec();
    let days_since_epoch = (epoch_days_offset + now.sec / seconds_per_day) as i32;
    let days_per_year = 365;
    let leap_year = 4;
    let not_leap_year = 33;
    let days_per_month = 33;

    let mut count = 1;
    let mut days_remaining = days_since_epoch;
    let mut year = 0;
    while days_remaining > days_per_year + 1 {
        days_remaining -= days_per_year;
        year += 1;
        if count > not_leap_year {
            count = 1;
        }
        if count % leap_year == 0 {
            days_remaining -= 1;
        }
        count += 1;
    }
    if days_remaining == days_per_year + 1 && count % leap_year == 0 {
        days_remaining = 0;
        year += 1;
    }

    let mut month = 0;
    while days_remaining > days_per_month {
        month += 1;
        days_remaining -= days_per_month;
        if month == 3 || month == 7 || month == 11 {
            days_remaining -= 1;
        }
    }

    let day = days_remaining;
    return (year, month as usize, day as usize + 1, (year % 2 * 3) as usize);
}

fn is_leap_year(year: usize) -> bool {
    let rem = year % 33;
    return rem != 0 && rem % 4 == 0;
}

fn what_holiday(month: usize, day: usize) -> String {
    let mut result = format!("");
    if month == 0 && day == 1 {
        result = format!(", New Year's Day");
    }
    else if month == 2 && day == 12 {
        result = format!(", Purification Day");
    }
    else if month == 2 && day == 24 {
        result = format!(", Document Freedom Day");
    }
    else if month == 3 && day == 33 {
        result = format!(", Hardware Freedom Day");
    }
    else if month == 5 && day == 4 {
        result = format!(", Harvest Gift");
    }
    else if month == 5 && day == 9 {
        result = format!(", Culture Freedom Day");
    }
    else if month == 7 && day == 29 {
        result = format!(", Familytide");
    }
    else if month == 8 && day == 33 {
        result = format!(", Software Freedom Day");
    }
    else if month == 9 && day == 30 {
        result = format!(", Open Access Day");
    }
    else if month == 10 && day == 21 {
        result = format!(", Freedom Day");
    }
    else if month == 0 && day == 27 {
        result = format!(", International Workers' Day");
    }
    return result;
}

<|endoftext|>


<commit_before>extern crate hyper;
extern crate rustc_serialize;
extern crate chrono;
extern crate regex;
extern crate env_logger;
extern crate log;
extern crate docopt;

use std::ascii::AsciiExt;
use std::io::Read;
use std::env;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::fmt;
use std::error::Error;
use std::result;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::local::Local;
use chrono::offset::fixed::FixedOffset;
use chrono::datetime::DateTime;
use chrono::Datelike;

use rustc_serialize::json;
use rustc_serialize::Decodable;
use hyper::client::Client;

use regex::Regex;

use docopt::Docopt;

static USAGE: &'static str = "
Washing State Ferry Schedules

Usage:
  wsf [options] <from> <to>
  wsf (-h | --help)

  <from> and <to> are a prefix of the departing terminal and arriving
  terminal, respectively. For example 'wsf sea ba' is equivalent to
  'wsf Seattle \"Bainbridge Island\"'.

Options:
  -a --all      Show all times for today, not just remaining
  -h --help     Show this screen.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_from: String,
    arg_to: String,
    flag_all: bool,
}

fn run() -> Result<()> {
    env_logger::init()?;

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    let from_in: &str = &args.arg_from.to_ascii_lowercase();
    let to_in: &str = &args.arg_to.to_ascii_lowercase();

    let mut s = Session::new("afddf683-37c5-4d1a-8486-f7004a16d86d");

    let now = Local::now();
    let from = s.terminals()?
        .iter()
        .find(|t| t.Description.to_ascii_lowercase().starts_with(&from_in))
        .ok_or(CliError::BadInput(format!("'{}' is not a known port!", from_in)))?
        .TerminalID;

    let to = s.terminals()?
        .iter()
        .find(|t| t.Description.to_ascii_lowercase().starts_with(&to_in))
        .ok_or(CliError::BadInput(format!("'{}' is not a known port!", from_in)))?
        .TerminalID;

    let tc = s.schedule(from, to)?;

    for time in tc.Times.iter() {
        if args.flag_all {
            println!("{}\t{}\t{}\t{}",
                     time.depart_time().time(),
                     tc.DepartingTerminalName,
                     tc.ArrivingTerminalName,
                     time.VesselName);
        } else {
            if time.depart_time() > now {
                println!("{}\t{}\t{}\t{}",
                         time.depart_time().time(),
                         tc.DepartingTerminalName,
                         tc.ArrivingTerminalName,
                         time.VesselName);
            }
        }
    }
    s.save_cache()
}

fn main() {
    match run() {
        Ok(_) => {}
        Err(e) => {
            // TODO print to STDERR instead of STDOUT
            println!("{}", e);
            std::process::exit(1);
        }
    }
}

struct Session {
    api_key: String,
    client: Client,
    cache: Cache,
    cacheflushdate: String,
    cache_path: String,
    offline: bool,
}

impl Session {
    fn new(api_key: &str) -> Session {
        let mut cache_path: PathBuf = env::home_dir().unwrap();
        cache_path.push(".wsf.cache");
        let cache_path = cache_path.display().to_string();

        let mut s = Session {
            api_key: api_key.to_string(),
            client: Client::new(),
            cache: Cache::load(&cache_path).unwrap_or(Cache::empty()),
            cacheflushdate: String::new(),
            cache_path,
            offline: false,
        };

        // TODO this is kind of gross, cfd as optional to indicate offline, maybe?
        s.offline = match s.get::<String>("/cacheflushdate".to_owned()) {
            Ok(cfd) => {
                s.cacheflushdate = cfd;
                false
            }
            Err(_) => true,
        };
        return s;
    }

    fn save_cache(&mut self) -> Result<()> {
        self.cache.cache_flush_date = self.cacheflushdate.clone();

        let mut f = File::create(&self.cache_path)?;
        let encoded = json::encode(&self.cache)?;
        Ok(f.write_all(encoded.as_bytes())?)
    }

    fn get<T: Decodable>(&self, path: String) -> Result<T> {
        let url = &format!("http://www.wsdot.wa.gov/ferries/api/schedule/rest{}?apiaccesscode={}",
                           path,
                           self.api_key);
        let mut res = self.client.get(url).send()?;
        assert_eq!(res.status, hyper::Ok);

        let mut buf = String::new();
        res.read_to_string(&mut buf)?;
        Ok(json::decode::<T>(&buf)?)
    }

    fn terminals(&mut self) -> Result<Vec<Terminal>> {
        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            return Ok(self.cache.terminals.clone());
        } else {
            let now = Local::today();
            let path = format!("/terminals/{}-{}-{}", now.year(), now.month(), now.day());
            let routes: Vec<Terminal> = self.get(path)?;
            self.cache.terminals = routes.clone();
            return Ok(routes);
        }
    }

    fn schedule(&mut self, from: i32, to: i32) -> Result<TerminalCombo> {
        let mut cache_is_stale = true;
        let cache_key = format!("{} {}", from, to);

        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            if self.cache.sailings.contains_key(&cache_key) {
                // cache is up to date and has route!
                return Ok(self.cache
                    .sailings
                    .get(&cache_key)
                    .expect("checked for key in cache then not found")
                    .clone());
            } else {
                // cache is up to date, but we don't have this route in it
                cache_is_stale = false;
            }
        }

        if cache_is_stale {
            self.cache.sailings.clear();
        }

        let now = Local::now();
        let path = format!("/schedule/{}-{}-{}/{}/{}",
                           now.year(),
                           now.month(),
                           now.day(),
                           from,
                           to);

        let schedule: Schedule = self.get(path)?;

        self.cache.sailings.insert(cache_key, schedule.TerminalCombos[0].clone());
        Ok(schedule.TerminalCombos[0].clone())
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Cache {
    terminals: Vec<Terminal>,
    sailings: HashMap<String, TerminalCombo>,
    cache_flush_date: String,
}

impl Cache {
    fn empty() -> Cache {
        Cache {
            terminals: vec![],
            sailings: HashMap::new(),
            cache_flush_date: String::new(),
        }
    }

    fn load(path: &String) -> Result<Cache> {
        let mut f = File::open(path)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        let cache: Cache = json::decode(&s)?;
        Ok(cache)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Terminal {
    TerminalID: i32,
    Description: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct SailingTime {
    DepartingTime: String,
    ArrivingTime: Option<String>,
    VesselName: String,
}

impl SailingTime {
    // parse date strings of form "/Date(1436318400000-0700)/"
    fn depart_time(&self) -> DateTime<Local> {
        let re = Regex::new(r"^/Date\((\d{10})000-(\d{2})(\d{2})\)/$").unwrap();
        let caps = re.captures(&self.DepartingTime).unwrap();

        let epoch: i64 = caps.at(1).unwrap().parse().unwrap();
        let tz_hours: i32 = caps.at(2).unwrap().parse().unwrap();
        let tz_minutes: i32 = caps.at(3).unwrap().parse().unwrap();

        let nd = NaiveDateTime::from_timestamp(epoch, 0);
        let tz = FixedOffset::west((tz_hours * 3600) + (tz_minutes * 60));
        let fotz: DateTime<FixedOffset> = DateTime::from_utc(nd, tz);
        fotz.with_timezone(&Local)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct TerminalCombo {
    Times: Vec<SailingTime>,
    DepartingTerminalName: String,
    ArrivingTerminalName: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Schedule {
    TerminalCombos: Vec<TerminalCombo>,
}

type Result<T> = result::Result<T, CliError>;

#[derive(Debug)]
enum CliError {
    Log(log::SetLoggerError),
    Parse(rustc_serialize::json::DecoderError),
    SaveCache(rustc_serialize::json::EncoderError),
    Http(hyper::error::Error),
    Io(std::io::Error),
    BadInput(String),
}

impl fmt::Display for CliError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CliError::Log(ref err) => format!("Unable to configure logging: {}", err).fmt(f),
            CliError::Parse(ref err) => format!("Unable to parse WSDOT Data: {}", err).fmt(f),
            CliError::SaveCache(ref err) => format!("Unable to save cache: {}", err).fmt(f),
            CliError::Http(ref err) => format!("Unable to communicate with WSDOT: {}", err).fmt(f),
            CliError::Io(ref err) => format!("Unable to read data: {}", err).fmt(f),
            CliError::BadInput(ref desc) => format!("Unable to understand input: {}", desc).fmt(f),
        }
    }
}

impl Error for CliError {
    fn description(&self) -> &str {
        match *self {
            CliError::BadInput(ref err) => err,
            _ => self.description(),
        }
    }
}

impl From<rustc_serialize::json::EncoderError> for CliError {
    fn from(err: rustc_serialize::json::EncoderError) -> CliError {
        CliError::SaveCache(err)
    }
}

impl From<log::SetLoggerError> for CliError {
    fn from(err: log::SetLoggerError) -> CliError {
        CliError::Log(err)
    }
}

impl From<hyper::error::Error> for CliError {
    fn from(err: hyper::error::Error) -> CliError {
        CliError::Http(err)
    }
}

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

impl From<rustc_serialize::json::DecoderError> for CliError {
    fn from(err: rustc_serialize::json::DecoderError) -> CliError {
        CliError::Parse(err)
    }
}
<commit_msg>print errors to stderr<commit_after>extern crate hyper;
extern crate rustc_serialize;
extern crate chrono;
extern crate regex;
extern crate env_logger;
extern crate log;
extern crate docopt;

use std::ascii::AsciiExt;
use std::io::Read;
use std::env;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::fmt;
use std::error::Error;
use std::result;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::local::Local;
use chrono::offset::fixed::FixedOffset;
use chrono::datetime::DateTime;
use chrono::Datelike;

use rustc_serialize::json;
use rustc_serialize::Decodable;
use hyper::client::Client;

use regex::Regex;

use docopt::Docopt;

static USAGE: &'static str = "
Washing State Ferry Schedules

Usage:
  wsf [options] <from> <to>
  wsf (-h | --help)

  <from> and <to> are a prefix of the departing terminal and arriving
  terminal, respectively. For example 'wsf sea ba' is equivalent to
  'wsf Seattle \"Bainbridge Island\"'.

Options:
  -a --all      Show all times for today, not just remaining
  -h --help     Show this screen.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_from: String,
    arg_to: String,
    flag_all: bool,
}

fn run() -> Result<()> {
    env_logger::init()?;

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    let from_in: &str = &args.arg_from.to_ascii_lowercase();
    let to_in: &str = &args.arg_to.to_ascii_lowercase();

    let mut s = Session::new("afddf683-37c5-4d1a-8486-f7004a16d86d");

    let now = Local::now();
    let from = s.terminals()?.iter()
        .find(|t| t.Description.to_ascii_lowercase().starts_with(&from_in))
        .ok_or(CliError::BadInput(format!("From port, '{}', is not known!", from_in)))?
        .TerminalID;

    let to = s.terminals()?.iter()
        .find(|t| t.Description.to_ascii_lowercase().starts_with(&to_in))
        .ok_or(CliError::BadInput(format!("To port, '{}', is not known !", from_in)))?
        .TerminalID;

    let tc = s.schedule(from, to)?;

    for time in tc.Times.iter() {
        if args.flag_all {
            println!("{}\t{}\t{}\t{}",
                     time.depart_time().time(),
                     tc.DepartingTerminalName,
                     tc.ArrivingTerminalName,
                     time.VesselName);
        } else {
            if time.depart_time() > now {
                println!("{}\t{}\t{}\t{}",
                         time.depart_time().time(),
                         tc.DepartingTerminalName,
                         tc.ArrivingTerminalName,
                         time.VesselName);
            }
        }
    }
    s.save_cache()
}

fn main() {
    match run() {
        Ok(_) => {}
        Err(e) => {
            eprintln!("{}", e);
            std::process::exit(1);
        }
    }
}

struct Session {
    api_key: String,
    client: Client,
    cache: Cache,
    cacheflushdate: String,
    cache_path: String,
    offline: bool,
}

impl Session {
    fn new(api_key: &str) -> Session {
        let mut cache_path: PathBuf = env::home_dir().unwrap();
        cache_path.push(".wsf.cache");
        let cache_path = cache_path.display().to_string();

        let mut s = Session {
            api_key: api_key.to_string(),
            client: Client::new(),
            cache: Cache::load(&cache_path).unwrap_or(Cache::empty()),
            cacheflushdate: String::new(),
            cache_path,
            offline: false,
        };

        // TODO this is kind of gross, cfd as optional to indicate offline, maybe?
        s.offline = match s.get::<String>("/cacheflushdate".to_owned()) {
            Ok(cfd) => {
                s.cacheflushdate = cfd;
                false
            }
            Err(_) => true,
        };
        return s;
    }

    fn save_cache(&mut self) -> Result<()> {
        self.cache.cache_flush_date = self.cacheflushdate.clone();

        let mut f = File::create(&self.cache_path)?;
        let encoded = json::encode(&self.cache)?;
        Ok(f.write_all(encoded.as_bytes())?)
    }

    fn get<T: Decodable>(&self, path: String) -> Result<T> {
        let url = &format!("http://www.wsdot.wa.gov/ferries/api/schedule/rest{}?apiaccesscode={}",
                           path,
                           self.api_key);
        let mut res = self.client.get(url).send()?;
        assert_eq!(res.status, hyper::Ok);

        let mut buf = String::new();
        res.read_to_string(&mut buf)?;
        Ok(json::decode::<T>(&buf)?)
    }

    fn terminals(&mut self) -> Result<Vec<Terminal>> {
        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            return Ok(self.cache.terminals.clone());
        } else {
            let now = Local::today();
            let path = format!("/terminals/{}-{}-{}", now.year(), now.month(), now.day());
            let routes: Vec<Terminal> = self.get(path)?;
            self.cache.terminals = routes.clone();
            return Ok(routes);
        }
    }

    fn schedule(&mut self, from: i32, to: i32) -> Result<TerminalCombo> {
        let mut cache_is_stale = true;
        let cache_key = format!("{} {}", from, to);

        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            if self.cache.sailings.contains_key(&cache_key) {
                // cache is up to date and has route!
                return Ok(self.cache
                    .sailings
                    .get(&cache_key)
                    .expect("checked for key in cache then not found")
                    .clone());
            } else {
                // cache is up to date, but we don't have this route in it
                cache_is_stale = false;
            }
        }

        if cache_is_stale {
            self.cache.sailings.clear();
        }

        let now = Local::now();
        let path = format!("/schedule/{}-{}-{}/{}/{}",
                           now.year(),
                           now.month(),
                           now.day(),
                           from,
                           to);

        let schedule: Schedule = self.get(path)?;

        self.cache.sailings.insert(cache_key, schedule.TerminalCombos[0].clone());
        Ok(schedule.TerminalCombos[0].clone())
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Cache {
    terminals: Vec<Terminal>,
    sailings: HashMap<String, TerminalCombo>,
    cache_flush_date: String,
}

impl Cache {
    fn empty() -> Cache {
        Cache {
            terminals: vec![],
            sailings: HashMap::new(),
            cache_flush_date: String::new(),
        }
    }

    fn load(path: &String) -> Result<Cache> {
        let mut f = File::open(path)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        let cache: Cache = json::decode(&s)?;
        Ok(cache)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Terminal {
    TerminalID: i32,
    Description: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct SailingTime {
    DepartingTime: String,
    ArrivingTime: Option<String>,
    VesselName: String,
}

impl SailingTime {
    // parse date strings of form "/Date(1436318400000-0700)/"
    fn depart_time(&self) -> DateTime<Local> {
        let re = Regex::new(r"^/Date\((\d{10})000-(\d{2})(\d{2})\)/$").unwrap();
        let caps = re.captures(&self.DepartingTime).unwrap();

        let epoch: i64 = caps.at(1).unwrap().parse().unwrap();
        let tz_hours: i32 = caps.at(2).unwrap().parse().unwrap();
        let tz_minutes: i32 = caps.at(3).unwrap().parse().unwrap();

        let nd = NaiveDateTime::from_timestamp(epoch, 0);
        let tz = FixedOffset::west((tz_hours * 3600) + (tz_minutes * 60));
        let fotz: DateTime<FixedOffset> = DateTime::from_utc(nd, tz);
        fotz.with_timezone(&Local)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct TerminalCombo {
    Times: Vec<SailingTime>,
    DepartingTerminalName: String,
    ArrivingTerminalName: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Schedule {
    TerminalCombos: Vec<TerminalCombo>,
}

type Result<T> = result::Result<T, CliError>;

#[derive(Debug)]
enum CliError {
    Log(log::SetLoggerError),
    Parse(rustc_serialize::json::DecoderError),
    SaveCache(rustc_serialize::json::EncoderError),
    Http(hyper::error::Error),
    Io(std::io::Error),
    BadInput(String),
}

impl fmt::Display for CliError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CliError::Log(ref err) => format!("Unable to configure logging: {}", err).fmt(f),
            CliError::Parse(ref err) => format!("Unable to parse WSDOT Data: {}", err).fmt(f),
            CliError::SaveCache(ref err) => format!("Unable to save cache: {}", err).fmt(f),
            CliError::Http(ref err) => format!("Unable to communicate with WSDOT: {}", err).fmt(f),
            CliError::Io(ref err) => format!("Unable to read data: {}", err).fmt(f),
            CliError::BadInput(ref desc) => format!("Unable to understand input: {}", desc).fmt(f),
        }
    }
}

impl Error for CliError {
    fn description(&self) -> &str {
        match *self {
            CliError::BadInput(ref err) => err,
            _ => self.description(),
        }
    }
}

impl From<rustc_serialize::json::EncoderError> for CliError {
    fn from(err: rustc_serialize::json::EncoderError) -> CliError {
        CliError::SaveCache(err)
    }
}

impl From<log::SetLoggerError> for CliError {
    fn from(err: log::SetLoggerError) -> CliError {
        CliError::Log(err)
    }
}

impl From<hyper::error::Error> for CliError {
    fn from(err: hyper::error::Error) -> CliError {
        CliError::Http(err)
    }
}

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

impl From<rustc_serialize::json::DecoderError> for CliError {
    fn from(err: rustc_serialize::json::DecoderError) -> CliError {
        CliError::Parse(err)
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io::Read;
use std::path::Path;

fn main() {
    if let Some(rom_file_name) = env::args().nth(1) {
        println!("ROM: {}", rom_file_name);
        if let Some(rom) = read_rom(rom_file_name) {
            println!("{:?}", rom);
            return;
        } else {
            println!("Could not read ROM file!");
        }
    }

    println!("Usage: chip8 <rom>");
}

fn read_rom<P: AsRef<Path>>(path: P) -> Option<Vec<u8>> {
    match fs::File::open(path) {
        Ok(mut file) => {
            let mut file_buffer = Vec::new();
            match file.read_to_end(&mut file_buffer) {
                Ok(_) => Some(file_buffer),
                Err(ex) => {
                    println!("Could not read from file: {}", ex);
                    None
                }
            }
        }
        Err(ex) => {
            println!("Could not open file: {}", ex);
            None
        }
    }
}
<commit_msg>Reworked read_rom to use io::Result<commit_after>use std::env;
use std::fs;
use std::io;
use std::io::Read;
use std::path::Path;

fn main() {
    if let Some(rom_file_name) = env::args().nth(1) {
        println!("ROM: {}", rom_file_name);
        match read_rom(rom_file_name) {
            Ok(rom) => println!("{:?}", rom),
            Err(err) => println!("Could not read ROM: {}", err),
        };
    }

    println!("Usage: chip8 <rom>");
}

fn read_rom<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
    let mut file = try!(fs::File::open(path));
    let mut file_buffer = Vec::new();
    try!(file.read_to_end(&mut file_buffer));
    Ok(file_buffer)
}
<|endoftext|>


<commit_before>//! SEGIMAP is an IMAP server implementation.
#![feature(macro_rules)]
#![deny(non_camel_case_types)]
#![feature(phase)]

extern crate "rust-crypto" as crypto;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
#[phase(plugin, link)] extern crate log;
extern crate serialize;
extern crate toml;

pub use config::Config;
pub use email::Email;
pub use login::LoginData;
pub use message::Message;
pub use server::Server;
pub use session::Session;
pub use user::User;

use std::io::{Listener, Acceptor, BufferedStream};
use std::sync::Arc;

mod auth;
mod config;
mod email;
mod error;
mod folder;
mod login;
mod message;
mod server;
mod session;
mod user;

/// The file in which user data is stored.
// TODO: add the ability for the user to specify the user data file as an
// argument.
static USER_DATA_FILE: &'static str = "./users.json";

fn main() {
    // Load configuration.
    let config = Config::new();

    // Load the user data from the user data file.
    // TODO: figure out what to do for error handling.
    let users = user::load_users(USER_DATA_FILE.to_string()).unwrap();

    let multipart_message = Message::parse(&Path::new("maildir/new/12345:2,FRS")).unwrap();
    let html_message = Message::parse(&Path::new("maildir/cur/54321:2,FS")).unwrap();

    // Avoid unused variable notices temporarily.
    //println!("Config: {}", config);
    //println!("Users: {}", users);
    //println!("Message: {}", multipart_message);
    //println!("Message: {}", html_message);

    let serv = Arc::new(Server::new(config, users));
    match serv.imap_listener() {
        Err(_) => {
            println!("Error listening on IMAP port!");
        }
        Ok(v) => {
            let mut acceptor = v.listen();
            for stream in acceptor.incoming() {
                match stream {
                    Err(e) => {
                        println!("Error accepting incoming connection!")
                    }
                    Ok(stream) => {
                        let session_serv = serv.clone();
                        spawn(proc() {
                            let mut session = Session::new(BufferedStream::new(stream), session_serv);
                            session.handle();
                        });
                    }
                }
            }
            drop(acceptor);
        }
    }
}
<commit_msg>Change error logging for acceptor<commit_after>//! SEGIMAP is an IMAP server implementation.
#![feature(macro_rules)]
#![deny(non_camel_case_types)]
#![feature(phase)]

extern crate "rust-crypto" as crypto;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
#[phase(plugin, link)] extern crate log;
extern crate serialize;
extern crate toml;

pub use config::Config;
pub use email::Email;
pub use login::LoginData;
pub use message::Message;
pub use server::Server;
pub use session::Session;
pub use user::User;

use std::io::{Listener, Acceptor, BufferedStream};
use std::sync::Arc;

mod auth;
mod config;
mod email;
mod error;
mod folder;
mod login;
mod message;
mod server;
mod session;
mod user;

/// The file in which user data is stored.
// TODO: add the ability for the user to specify the user data file as an
// argument.
static USER_DATA_FILE: &'static str = "./users.json";

fn main() {
    // Load configuration.
    let config = Config::new();

    // Load the user data from the user data file.
    // TODO: figure out what to do for error handling.
    let users = user::load_users(USER_DATA_FILE.to_string()).unwrap();

    let multipart_message = Message::parse(&Path::new("maildir/new/12345:2,FRS")).unwrap();
    let html_message = Message::parse(&Path::new("maildir/cur/54321:2,FS")).unwrap();

    // Avoid unused variable notices temporarily.
    //println!("Config: {}", config);
    //println!("Users: {}", users);
    //println!("Message: {}", multipart_message);
    //println!("Message: {}", html_message);

    let serv = Arc::new(Server::new(config, users));
    match serv.imap_listener() {
        Err(e) => {
            error!("Error listening on IMAP port: {}", e);
        }
        Ok(v) => {
            let mut acceptor = v.listen();
            for stream in acceptor.incoming() {
                match stream {
                    Err(e) => {
                        error!("Error accepting incoming connection: {}", e)
                    }
                    Ok(stream) => {
                        let session_serv = serv.clone();
                        spawn(proc() {
                            let mut session = Session::new(BufferedStream::new(stream), session_serv);
                            session.handle();
                        });
                    }
                }
            }
            drop(acceptor);
        }
    }
}
<|endoftext|>