<commit_before>extern crate bismuth;
extern crate glutin;
extern crate nalgebra;

use bismuth::cube::{Axis, Cursor, Geometry, LogWidth, Offset, Root, Spatial};
use bismuth::math::{FPoint3, FScalar, IntoSpace, Matrix4Ext, UPoint2, UPoint3, UScalar, UVector3};
use bismuth::render::{AspectRatio, Camera, Context, Mesh, Projection, Transform};
use glutin::{ElementState, Event, MouseButton, VirtualKeyCode, WindowBuilder};

fn new_root(width: LogWidth) -> Root {
    let cursor = Cursor::at_point_with_span(&UPoint3::origin(), width - 3, &UVector3::new(7, 1, 7));
    let mut root = Root::new(width);
    for mut cube in root.to_cube_mut().subdivide_to_cursor(&cursor).iter_mut() {
        for mut cube in cube.iter_mut() {
            if let Some(leaf) = cube.as_leaf_mut() {
                for axis in Axis::range() {
                    for edge in leaf.geometry.edges_mut(axis.into()) {
                        edge.set_front(Offset::from(2));
                        edge.set_back(Offset::from(14));
                    }
                }
            }
        }
    }
    root
}

fn new_camera<W, C>(window: &W, cube: &C) -> Camera
    where W: AspectRatio,
          C: Spatial
{
    let midpoint: FPoint3 = cube.partition().midpoint().into_space();
    let projection = {
        let mut projection = Projection::default();
        projection.far = cube.partition().width().exp() as FScalar * 2.0;
        projection
    };
    let mut camera = Camera::new(window, &projection);
    camera.look_at(&FPoint3::new(midpoint.x * 0.25, -midpoint.y, midpoint.z * 3.0),
                   &midpoint);
    camera
}

fn main() {
    let mut context = Context::from_glutin_window(WindowBuilder::new()
        .with_title("Bismuth")
        .with_dimensions(640, 480)
        .with_vsync()
        .build()
        .unwrap());
    let width = LogWidth::new(8);
    let mut root = new_root(width);
    let mut mesh = root.to_cube().mesh_buffer();
    let camera = new_camera(&context.window, &root);
    let mut pointer = UPoint2::origin();
    let mut transform = Transform::default();
    'main: loop {
        transform.camera = camera.transform().to_array();
        for event in context.window.poll_events() {
            match event {
                Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape)) |
                Event::Closed => {
                    break 'main;
                }
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    let ray = camera.cast_ray(&context.window, &pointer);
                    let mut edited = false;
                    if let Some(mut cube) = root.to_cube_mut()
                        .at_ray_mut(&ray, LogWidth::min_value()) {
                        if let Some(leaf) = cube.as_leaf_mut() {
                            leaf.geometry = Geometry::empty();
                            edited = true;
                        }
                    }
                    if edited {
                        mesh = root.to_cube().mesh_buffer();
                    }
                }
                Event::MouseMoved(x, y) => {
                    pointer = UPoint2::new(x as UScalar, y as UScalar);
                }
                _ => {}
            }
        }
        context.clear();
        context.set_transform(&transform).unwrap();
        context.draw_mesh_buffer(&mesh);
        context.flush().unwrap();
    }
}
<commit_msg>Fix break in `at_ray_mut` pattern.<commit_after>extern crate bismuth;
extern crate glutin;
extern crate nalgebra;

use bismuth::cube::{Axis, Cursor, Geometry, LogWidth, Offset, Root, Spatial};
use bismuth::math::{FPoint3, FScalar, IntoSpace, Matrix4Ext, UPoint2, UPoint3, UScalar, UVector3};
use bismuth::render::{AspectRatio, Camera, Context, Mesh, Projection, Transform};
use glutin::{ElementState, Event, MouseButton, VirtualKeyCode, WindowBuilder};

fn new_root(width: LogWidth) -> Root {
    let cursor = Cursor::at_point_with_span(&UPoint3::origin(), width - 3, &UVector3::new(7, 1, 7));
    let mut root = Root::new(width);
    for mut cube in root.to_cube_mut().subdivide_to_cursor(&cursor).iter_mut() {
        for mut cube in cube.iter_mut() {
            if let Some(leaf) = cube.as_leaf_mut() {
                for axis in Axis::range() {
                    for edge in leaf.geometry.edges_mut(axis.into()) {
                        edge.set_front(Offset::from(2));
                        edge.set_back(Offset::from(14));
                    }
                }
            }
        }
    }
    root
}

fn new_camera<W, C>(window: &W, cube: &C) -> Camera
    where W: AspectRatio,
          C: Spatial
{
    let midpoint: FPoint3 = cube.partition().midpoint().into_space();
    let projection = {
        let mut projection = Projection::default();
        projection.far = cube.partition().width().exp() as FScalar * 2.0;
        projection
    };
    let mut camera = Camera::new(window, &projection);
    camera.look_at(&FPoint3::new(midpoint.x * 0.25, -midpoint.y, midpoint.z * 3.0),
                   &midpoint);
    camera
}

fn main() {
    let mut context = Context::from_glutin_window(WindowBuilder::new()
        .with_title("Bismuth")
        .with_dimensions(640, 480)
        .with_vsync()
        .build()
        .unwrap());
    let width = LogWidth::new(8);
    let mut root = new_root(width);
    let mut mesh = root.to_cube().mesh_buffer();
    let camera = new_camera(&context.window, &root);
    let mut pointer = UPoint2::origin();
    let mut transform = Transform::default();
    'main: loop {
        transform.camera = camera.transform().to_array();
        for event in context.window.poll_events() {
            match event {
                Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape)) |
                Event::Closed => {
                    break 'main;
                }
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    let ray = camera.cast_ray(&context.window, &pointer);
                    let mut edited = false;
                    if let Some((_, mut cube)) = root.to_cube_mut()
                        .at_ray_mut(&ray, LogWidth::min_value()) {
                        if let Some(leaf) = cube.as_leaf_mut() {
                            leaf.geometry = Geometry::empty();
                            edited = true;
                        }
                    }
                    if edited {
                        mesh = root.to_cube().mesh_buffer();
                    }
                }
                Event::MouseMoved(x, y) => {
                    pointer = UPoint2::new(x as UScalar, y as UScalar);
                }
                _ => {}
            }
        }
        context.clear();
        context.set_transform(&transform).unwrap();
        context.draw_mesh_buffer(&mesh);
        context.flush().unwrap();
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;

extern crate glium_sdl2;
extern crate sdl2;

use std::fmt;
use glium::{Surface, VertexBuffer, Program, index, uniforms};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use glium_sdl2::{DisplayBuild, SDL2Facade};

#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
}
implement_vertex!(Vertex, position);

impl fmt::Display for Vertex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.position[0], self.position[1])
    }
}

fn handle_key(keycode: Keycode) -> bool {
    match keycode {
        Keycode::Escape => false,
        _ => true,
    }
}

fn main() {
    let sdl_context = sdl2::init()
        .unwrap();
    let video_subsystem = sdl_context
        . video()
        .unwrap();
    let display = video_subsystem
        .window("rustroam", 1024, 576)
        .resizable()
        .build_glium()
        .unwrap();

    let v1 = Vertex { position: [-0.5, -0.5] };
    let v2 = Vertex { position: [ 0.0,  0.5] };
    let v3 = Vertex { position: [ 0.5, -0.25] };
    let shape = vec![v1, v2, v3];

    let vertex_buffer = VertexBuffer::new(&display, &shape)
        .unwrap();
    let indices = index::NoIndices(index::PrimitiveType::TrianglesList);

    fn create_shaders(display: &SDL2Facade) -> Program {
        let vertex_shader_src = r#"
            #version 130

            in vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        "#;

        let fragment_shader_src = r#"
            #version 130

            out vec4 color;

            void main() {
                color = vec4(1.0, 0.6, 0.1, 1.0);
            }
        "#;

        Program::from_source(display, vertex_shader_src, fragment_shader_src, None).unwrap()
    }
    let program = create_shaders(&display);

    let mut running = true;
    let mut event_pump = sdl_context.event_pump().unwrap();

    while running {
        let mut target = display.draw();
        target.clear_color(0.05, 0.05, 0.05, 1.0);
        target.draw(&vertex_buffer,
                    &indices,
                    &program,
                    &uniforms::EmptyUniforms,
                    &Default::default())
            .unwrap();
        target
            .finish()
            .unwrap();

        for event in event_pump.poll_iter() {
            match event {
                Event::KeyDown { keycode, .. } => running = handle_key(keycode.unwrap()),
                Event::Quit { .. } => running = false,
                _ => ()
            }
        }
    }
}

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

    #[test]
    fn test_vertex() {
        assert_eq!("(3, 5)", format!("{}", Vertex { position: [3.0, 5.0] }));
    }
}
<commit_msg>test for handle_key<commit_after>#[macro_use]
extern crate glium;

extern crate glium_sdl2;
extern crate sdl2;

use std::fmt;
use glium::{Surface, VertexBuffer, Program, index, uniforms};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use glium_sdl2::{DisplayBuild, SDL2Facade};

#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
}
implement_vertex!(Vertex, position);

impl fmt::Display for Vertex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.position[0], self.position[1])
    }
}

fn handle_key(keycode: Keycode) -> bool {
    match keycode {
        Keycode::Escape => false,
        _ => true,
    }
}

fn main() {
    let sdl_context = sdl2::init()
        .unwrap();
    let video_subsystem = sdl_context
        . video()
        .unwrap();
    let display = video_subsystem
        .window("rustroam", 1024, 576)
        .resizable()
        .build_glium()
        .unwrap();

    let v1 = Vertex { position: [-0.5, -0.5] };
    let v2 = Vertex { position: [ 0.0,  0.5] };
    let v3 = Vertex { position: [ 0.5, -0.25] };
    let shape = vec![v1, v2, v3];

    let vertex_buffer = VertexBuffer::new(&display, &shape)
        .unwrap();
    let indices = index::NoIndices(index::PrimitiveType::TrianglesList);

    fn create_shaders(display: &SDL2Facade) -> Program {
        let vertex_shader_src = r#"
            #version 130

            in vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        "#;

        let fragment_shader_src = r#"
            #version 130

            out vec4 color;

            void main() {
                color = vec4(1.0, 0.6, 0.1, 1.0);
            }
        "#;

        Program::from_source(display, vertex_shader_src, fragment_shader_src, None).unwrap()
    }
    let program = create_shaders(&display);

    let mut running = true;
    let mut event_pump = sdl_context.event_pump().unwrap();

    while running {
        let mut target = display.draw();
        target.clear_color(0.05, 0.05, 0.05, 1.0);
        target.draw(&vertex_buffer,
                    &indices,
                    &program,
                    &uniforms::EmptyUniforms,
                    &Default::default())
            .unwrap();
        target
            .finish()
            .unwrap();

        for event in event_pump.poll_iter() {
            match event {
                Event::KeyDown { keycode, .. } => running = handle_key(keycode.unwrap()),
                Event::Quit { .. } => running = false,
                _ => ()
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::{Vertex, handle_key};
    use sdl2::keyboard::Keycode;

    #[test]
    fn test_vertex() {
        assert_eq!("(3, 5)", format!("{}", Vertex { position: [3.0, 5.0] }));
    }

    #[test]
    fn test_handle_key() {
        assert_eq!(false, handle_key(Keycode::Escape));
        assert_eq!(true, handle_key(Keycode::Space));
    }
}
<|endoftext|>


<commit_before>extern crate rand;
mod game;
use game::{Game, State};
mod user_interface;
use user_interface::UserInterface;
mod dictionary;
use dictionary::Dict;
mod image;
use image::RewardingScheme;

use std::env;
use std::error::Error;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;

const COMMANDLINE_HELP: &str = "\
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
HANGMAN GAME
         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (\")
    `-j                |
      `-._(           /
         |_\\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lives:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: hangman [FILE]...
        hangman (-c|--help)
        hangman

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to 'hangman-words.txt'. In
case no `[FILE]` is found, a template configuration file 'hangman-words.txt' is
written into the current working directory. Multiple `[FILE]`s are concatted.

`[FILE]` is an Ascii file containing 4 different line-types:

- lines starting with `#` is ignored.

- lines starting with `|` are part of an optional Ascii-Art image shown
  progressively in the course of the game. If not defined here, built in
  Ascii-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:
   `:success-rewarding`       Every guessed character shows a bit more of the
                              image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                  image. Choose this mode together with a
                              traditional gallows image (not built in).

- lines starting with `-` are _guessing strings_. At the beginning of the game
  one line is randomly chosen and all characters are hidden.  In
  order to give additional hints it is possible to enclose some characters with
  `+*+`.  These words are then displayed in clear. For example a config line:
  `+- Guess *me*+` is shown in the game as: `_ _ _ _ _ _ m e`.

";

const LIVES: u8 = 7;
const PATHSTR: &str = "hangman-words.txt";
const OFFSET: (usize, usize) = (1, 1);

const CONF_TEMPLATE: &str = "\
### This is a sample word-list for the hangman game

### Sample word-list
#   ----------------
#
# Before every game one line is randomly chosen.
# Empty lines and lines starting with # are ignored.
# Lines with guessing strings must start with '-'.
# Words enclosed with * are not hidden when the game starts:
#   - *guess *me
# shows in the game:
#   g u e s s   _ _
#

- *guess* me
- hang*man*
- good luck

# Lines starting with ':' are game modifier. They change
# the logic how the image is progressively disclosed:
#   ':success-rewarding'       Every guessed character shows a bit more of the
#                              image. This mode is default.
#   ':traditional-rewarding'   Every lost live discloses a bit more of the
#                  image. Choose this mode together with a
#                              traditional gallows image (not built in).

### Sample custom image
#   -------------------
#
# Instead of built in images a word list can use a
# custom image. Lines starting with '|' are interpreted
# as image-lines. Delete '#' in the following lines to
# try out this feature.

#:traditional-rewarding
#|  ______
#|  |    |
#|  |    O
#|  |   /|\\
#|  |    |
#|  |   / \\
#|__|_____
#||      |___
#||_________|
";

const CONF_DEMO: &str = "- *Demo: add own words to config file and *start* again!*";

// ------------------ MAIN ---------------------------------------------

pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
    let mut f = File::open(pathstr)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            println!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(_) => {}
        None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths: Vec<PathBuf> = env::args().skip(1).map(|s| PathBuf::from(s)).collect();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.is_empty() {
        conf_file_paths.push(PathBuf::from(PATHSTR))
    };

    // read and concat all config files given on command line
    let cwd = env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {
        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s) => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        println!(
                            "As no config-file :\n\
                             \t{:?}\n\
                             was found a template file is written in the \
                             current working directory.\n\
                             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                            path, cwd
                        );
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        println!(
                            "Couldn't write hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            Error::description(&why),
                            cwd
                        );
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE GAME

    let mut ui = UserInterface::new(&config, OFFSET);

    let mut dict = Dict::new(&config);
    if dict.len() == 0 {
        println!(
            "No guessing words in config-file(s):\n\
             \t{:?}\n\
             were found. Current working directory is:\n\
             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
            conf_file_paths, cwd
        );
        // wait for [Enter] key
        let input = &mut String::new();
        io::stdin().read_line(input).unwrap();
        dict = Dict::new(CONF_DEMO);
    };

    // PLAY

    'playing: loop {
        let mut game = Game::new(&(dict.get_random_word()), LIVES);
        let chars_to_guess = game.visible_chars();

        // The game loop
        let mut line_buffer = String::new();
        let reader = io::stdin();
        print!("\x1b[2J"); // clear screen
        'running_game: loop {
            if game.lives > 0 || ui.image.rewarding_scheme == RewardingScheme::UnhideWhenLostLife {
                ui.image.disclose(
                    (game.lives as usize, LIVES as usize),
                    (game.visible_chars(), chars_to_guess),
                );
            }
            ui.message = format!("{}", game);
            println!("{}", ui);

            match game.get_state() {
                State::Victory => {
                    println!("Congratulations! You won!");
                    break 'running_game;
                }
                State::Defeat => {
                    println!("Sorry, you lost! Better luck next time!");
                    break 'running_game;
                }
                _ => {}
            }

            print!("Type a letter then type [Enter]: ");
            io::stdout().flush().unwrap();

            // Read next char and send it
            line_buffer.clear();
            reader.read_line(&mut line_buffer).unwrap();

            game.guess(match line_buffer.chars().next() {
                Some(char_) => char_,
                None => continue 'running_game,
            });
        }

        print!("New game? Type [Y]es or [n]o: ");
        io::stdout().flush().unwrap();

        // Read next char
        line_buffer.clear();
        reader.read_line(&mut line_buffer).unwrap();
        println! {"(c) Jens Getreu, 2016."};

        match line_buffer.trim_end().chars().next() {
            Some(char_) if char_ == 'N' || char_ == 'n' => break 'playing,
            Some(_) => {}
            None => {}
        }
    }
}
<commit_msg>group match with identical arm bodies<commit_after>extern crate rand;
mod game;
use game::{Game, State};
mod user_interface;
use user_interface::UserInterface;
mod dictionary;
use dictionary::Dict;
mod image;
use image::RewardingScheme;

use std::env;
use std::error::Error;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;

const COMMANDLINE_HELP: &str = "\
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
HANGMAN GAME
         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (\")
    `-j                |
      `-._(           /
         |_\\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lives:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: hangman [FILE]...
        hangman (-c|--help)
        hangman

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to 'hangman-words.txt'. In
case no `[FILE]` is found, a template configuration file 'hangman-words.txt' is
written into the current working directory. Multiple `[FILE]`s are concatted.

`[FILE]` is an Ascii file containing 4 different line-types:

- lines starting with `#` is ignored.

- lines starting with `|` are part of an optional Ascii-Art image shown
  progressively in the course of the game. If not defined here, built in
  Ascii-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:
   `:success-rewarding`       Every guessed character shows a bit more of the
                              image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                  image. Choose this mode together with a
                              traditional gallows image (not built in).

- lines starting with `-` are _guessing strings_. At the beginning of the game
  one line is randomly chosen and all characters are hidden.  In
  order to give additional hints it is possible to enclose some characters with
  `+*+`.  These words are then displayed in clear. For example a config line:
  `+- Guess *me*+` is shown in the game as: `_ _ _ _ _ _ m e`.

";

const LIVES: u8 = 7;
const PATHSTR: &str = "hangman-words.txt";
const OFFSET: (usize, usize) = (1, 1);

const CONF_TEMPLATE: &str = "\
### This is a sample word-list for the hangman game

### Sample word-list
#   ----------------
#
# Before every game one line is randomly chosen.
# Empty lines and lines starting with # are ignored.
# Lines with guessing strings must start with '-'.
# Words enclosed with * are not hidden when the game starts:
#   - *guess *me
# shows in the game:
#   g u e s s   _ _
#

- *guess* me
- hang*man*
- good luck

# Lines starting with ':' are game modifier. They change
# the logic how the image is progressively disclosed:
#   ':success-rewarding'       Every guessed character shows a bit more of the
#                              image. This mode is default.
#   ':traditional-rewarding'   Every lost live discloses a bit more of the
#                  image. Choose this mode together with a
#                              traditional gallows image (not built in).

### Sample custom image
#   -------------------
#
# Instead of built in images a word list can use a
# custom image. Lines starting with '|' are interpreted
# as image-lines. Delete '#' in the following lines to
# try out this feature.

#:traditional-rewarding
#|  ______
#|  |    |
#|  |    O
#|  |   /|\\
#|  |    |
#|  |   / \\
#|__|_____
#||      |___
#||_________|
";

const CONF_DEMO: &str = "- *Demo: add own words to config file and *start* again!*";

// ------------------ MAIN ---------------------------------------------

pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
    let mut f = File::open(pathstr)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            println!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths: Vec<PathBuf> = env::args().skip(1).map(|s| PathBuf::from(s)).collect();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.is_empty() {
        conf_file_paths.push(PathBuf::from(PATHSTR))
    };

    // read and concat all config files given on command line
    let cwd = env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {
        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s) => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        println!(
                            "As no config-file :\n\
                             \t{:?}\n\
                             was found a template file is written in the \
                             current working directory.\n\
                             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                            path, cwd
                        );
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        println!(
                            "Couldn't write hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            Error::description(&why),
                            cwd
                        );
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE GAME

    let mut ui = UserInterface::new(&config, OFFSET);

    let mut dict = Dict::new(&config);
    if dict.len() == 0 {
        println!(
            "No guessing words in config-file(s):\n\
             \t{:?}\n\
             were found. Current working directory is:\n\
             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
            conf_file_paths, cwd
        );
        // wait for [Enter] key
        let input = &mut String::new();
        io::stdin().read_line(input).unwrap();
        dict = Dict::new(CONF_DEMO);
    };

    // PLAY

    'playing: loop {
        let mut game = Game::new(&(dict.get_random_word()), LIVES);
        let chars_to_guess = game.visible_chars();

        // The game loop
        let mut line_buffer = String::new();
        let reader = io::stdin();
        print!("\x1b[2J"); // clear screen
        'running_game: loop {
            if game.lives > 0 || ui.image.rewarding_scheme == RewardingScheme::UnhideWhenLostLife {
                ui.image.disclose(
                    (game.lives as usize, LIVES as usize),
                    (game.visible_chars(), chars_to_guess),
                );
            }
            ui.message = format!("{}", game);
            println!("{}", ui);

            match game.get_state() {
                State::Victory => {
                    println!("Congratulations! You won!");
                    break 'running_game;
                }
                State::Defeat => {
                    println!("Sorry, you lost! Better luck next time!");
                    break 'running_game;
                }
                _ => {}
            }

            print!("Type a letter then type [Enter]: ");
            io::stdout().flush().unwrap();

            // Read next char and send it
            line_buffer.clear();
            reader.read_line(&mut line_buffer).unwrap();

            game.guess(match line_buffer.chars().next() {
                Some(char_) => char_,
                None => continue 'running_game,
            });
        }

        print!("New game? Type [Y]es or [n]o: ");
        io::stdout().flush().unwrap();

        // Read next char
        line_buffer.clear();
        reader.read_line(&mut line_buffer).unwrap();
        println! {"(c) Jens Getreu, 2016."};

        match line_buffer.trim_end().chars().next() {
            Some(char_) if char_ == 'N' || char_ == 'n' => break 'playing,
            Some(_) | None => {}
        }
    }
}
<|endoftext|>


<commit_before>extern crate regex;
use regex::Regex;

use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;
extern crate time;
use time::Tm;
use time::strptime;
use time::Duration;

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub enum HttpStatus {
    Continue,
    SwitchingProtocols,
    Processing,
    OK,
    Created,
    Accepted,
    NonAuthoritativeInformation,
    NoContent,
    ResetContent,
    PartialContent,
    MultiStatus,
    AlreadyReported,
    IMUsed,
    MultipleChoices,
    MovedPermanently,
    Found,
    SeeOther,
    NotModified,
    UseProxy,
    TemporaryRedirect,
    PermanentRedirect,
    BadRequest,
    Unauthorized,
    PaymentRequired,
    Forbidden,
    NotFound,
    MethodNotAllowed,
    NotAcceptable,
    ProxyAuthenticationRequired,
    RequestTimeout,
    Conflict,
    Gone,
    LengthRequired,
    PreconditionFailed,
    PayloadTooLarge,
    URITooLong,
    UnsupportedMediaType,
    RangeNotSatisfiable,
    ExpectationFailed,
    ImaTeapot,
    MisdirectedRequest,
    UnprocessableEntity,
    Locked,
    FailedDependency,
    UpgradeRequired,
    PreconditionRequired,
    TooManyRequests,
    RequestHeaderFieldsTooLarge,
    UnavailableForLegalReasons,
    InternalServerError,
    NotImplemented,
    BadGateway,
    ServiceUnavailable,
    GatewayTimeout,
    HTTPVersionNotSupported,
    VariantAlsoNegotiates,
    InsufficientStorage,
    LoopDetected,
    NotExtended,
    NetworkAuthenticationRequired,
    Unregistered,
}

impl HttpStatus {
    fn from_code(code: i32) -> HttpStatus {
        match code {
            100 => HttpStatus::Continue,
            101 => HttpStatus::SwitchingProtocols,
            102 => HttpStatus::Processing,
            200 => HttpStatus::OK,
            201 => HttpStatus::Created,
            202 => HttpStatus::Accepted,
            203 => HttpStatus::NonAuthoritativeInformation,
            204 => HttpStatus::NoContent,
            205 => HttpStatus::ResetContent,
            206 => HttpStatus::PartialContent,
            207 => HttpStatus::MultiStatus,
            208 => HttpStatus::AlreadyReported,
            226 => HttpStatus::IMUsed,
            300 => HttpStatus::MultipleChoices,
            301 => HttpStatus::MovedPermanently,
            302 => HttpStatus::Found,
            303 => HttpStatus::SeeOther,
            304 => HttpStatus::NotModified,
            305 => HttpStatus::UseProxy,
            307 => HttpStatus::TemporaryRedirect,
            308 => HttpStatus::PermanentRedirect,
            400 => HttpStatus::BadRequest,
            401 => HttpStatus::Unauthorized,
            402 => HttpStatus::PaymentRequired,
            403 => HttpStatus::Forbidden,
            404 => HttpStatus::NotFound,
            405 => HttpStatus::MethodNotAllowed,
            406 => HttpStatus::NotAcceptable,
            407 => HttpStatus::ProxyAuthenticationRequired,
            408 => HttpStatus::RequestTimeout,
            409 => HttpStatus::Conflict,
            410 => HttpStatus::Gone,
            411 => HttpStatus::LengthRequired,
            412 => HttpStatus::PreconditionFailed,
            413 => HttpStatus::PayloadTooLarge,
            414 => HttpStatus::URITooLong,
            415 => HttpStatus::UnsupportedMediaType,
            416 => HttpStatus::RangeNotSatisfiable,
            417 => HttpStatus::ExpectationFailed,
            418 => HttpStatus::ImaTeapot,
            421 => HttpStatus::MisdirectedRequest,
            422 => HttpStatus::UnprocessableEntity,
            423 => HttpStatus::Locked,
            424 => HttpStatus::FailedDependency,
            426 => HttpStatus::UpgradeRequired,
            428 => HttpStatus::PreconditionRequired,
            429 => HttpStatus::TooManyRequests,
            431 => HttpStatus::RequestHeaderFieldsTooLarge,
            451 => HttpStatus::UnavailableForLegalReasons,
            500 => HttpStatus::InternalServerError,
            501 => HttpStatus::NotImplemented,
            502 => HttpStatus::BadGateway,
            503 => HttpStatus::ServiceUnavailable,
            504 => HttpStatus::GatewayTimeout,
            505 => HttpStatus::HTTPVersionNotSupported,
            506 => HttpStatus::VariantAlsoNegotiates,
            507 => HttpStatus::InsufficientStorage,
            508 => HttpStatus::LoopDetected,
            510 => HttpStatus::NotExtended,
            511 => HttpStatus::NetworkAuthenticationRequired,
            _ => HttpStatus::Unregistered,
        }
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Request {
    id: i32,
    time: Tm,
    url: String,
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Response {
    id: i32,
    time: Tm,
    mime_type: String,
    response_time: Duration,
    http_status: HttpStatus,
}

pub fn open_logfile(path: &str) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(parse_request_line(&line_value));
            println!("{:#?}", r);
            requests.push(r)
        }

        if line_value.contains("<-") {
            let r = try!(parse_response_line(&line_value));
            println!("{:#?}", r);
            responses.push(r)
        }

    }

    Ok((requests, responses))
}

pub fn parse_request_line(log_line: &String) -> Result<Request, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let url = parts[5];

    Ok(Request {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        url: url.to_string()
    })
}

pub fn parse_response_line(log_line: &String) -> Result<Response, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let response_time = parts[parts.len()-1];
    let mime_type = parts[5];
    let status_code = parts[4];

    Ok(Response {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        response_time: Duration::milliseconds(response_time[0..response_time.len()-2].parse().unwrap()),
        mime_type: mime_type.to_string(),
        http_status: HttpStatus::from_code(status_code.parse().unwrap()),
    })
}

fn main() {
    let lines = open_logfile("src/test/simple-1.log");
    let (requests, responses) = lines.unwrap();
    println!("So many requests: {}", requests.len());
    println!("So many responses: {}", responses.len());
}

#[cfg(test)]
mod tests {
	use super::*;
    extern crate time;
    use time::strptime;
    use::time::Duration;

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();

        let expected = Request {
            id: 2,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = parse_request_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            time: strptime("08/Apr/2016:09:58:48 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(10),
            http_status: HttpStatus::OK,
        };

        let result = parse_response_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }
}
<commit_msg>Find matching Response by ID<commit_after>extern crate regex;
use regex::Regex;

use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;
extern crate time;
use time::Tm;
use time::strptime;
use time::Duration;

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub enum HttpStatus {
    Continue,
    SwitchingProtocols,
    Processing,
    OK,
    Created,
    Accepted,
    NonAuthoritativeInformation,
    NoContent,
    ResetContent,
    PartialContent,
    MultiStatus,
    AlreadyReported,
    IMUsed,
    MultipleChoices,
    MovedPermanently,
    Found,
    SeeOther,
    NotModified,
    UseProxy,
    TemporaryRedirect,
    PermanentRedirect,
    BadRequest,
    Unauthorized,
    PaymentRequired,
    Forbidden,
    NotFound,
    MethodNotAllowed,
    NotAcceptable,
    ProxyAuthenticationRequired,
    RequestTimeout,
    Conflict,
    Gone,
    LengthRequired,
    PreconditionFailed,
    PayloadTooLarge,
    URITooLong,
    UnsupportedMediaType,
    RangeNotSatisfiable,
    ExpectationFailed,
    ImaTeapot,
    MisdirectedRequest,
    UnprocessableEntity,
    Locked,
    FailedDependency,
    UpgradeRequired,
    PreconditionRequired,
    TooManyRequests,
    RequestHeaderFieldsTooLarge,
    UnavailableForLegalReasons,
    InternalServerError,
    NotImplemented,
    BadGateway,
    ServiceUnavailable,
    GatewayTimeout,
    HTTPVersionNotSupported,
    VariantAlsoNegotiates,
    InsufficientStorage,
    LoopDetected,
    NotExtended,
    NetworkAuthenticationRequired,
    Unregistered,
}

impl HttpStatus {
    fn from_code(code: i32) -> HttpStatus {
        match code {
            100 => HttpStatus::Continue,
            101 => HttpStatus::SwitchingProtocols,
            102 => HttpStatus::Processing,
            200 => HttpStatus::OK,
            201 => HttpStatus::Created,
            202 => HttpStatus::Accepted,
            203 => HttpStatus::NonAuthoritativeInformation,
            204 => HttpStatus::NoContent,
            205 => HttpStatus::ResetContent,
            206 => HttpStatus::PartialContent,
            207 => HttpStatus::MultiStatus,
            208 => HttpStatus::AlreadyReported,
            226 => HttpStatus::IMUsed,
            300 => HttpStatus::MultipleChoices,
            301 => HttpStatus::MovedPermanently,
            302 => HttpStatus::Found,
            303 => HttpStatus::SeeOther,
            304 => HttpStatus::NotModified,
            305 => HttpStatus::UseProxy,
            307 => HttpStatus::TemporaryRedirect,
            308 => HttpStatus::PermanentRedirect,
            400 => HttpStatus::BadRequest,
            401 => HttpStatus::Unauthorized,
            402 => HttpStatus::PaymentRequired,
            403 => HttpStatus::Forbidden,
            404 => HttpStatus::NotFound,
            405 => HttpStatus::MethodNotAllowed,
            406 => HttpStatus::NotAcceptable,
            407 => HttpStatus::ProxyAuthenticationRequired,
            408 => HttpStatus::RequestTimeout,
            409 => HttpStatus::Conflict,
            410 => HttpStatus::Gone,
            411 => HttpStatus::LengthRequired,
            412 => HttpStatus::PreconditionFailed,
            413 => HttpStatus::PayloadTooLarge,
            414 => HttpStatus::URITooLong,
            415 => HttpStatus::UnsupportedMediaType,
            416 => HttpStatus::RangeNotSatisfiable,
            417 => HttpStatus::ExpectationFailed,
            418 => HttpStatus::ImaTeapot,
            421 => HttpStatus::MisdirectedRequest,
            422 => HttpStatus::UnprocessableEntity,
            423 => HttpStatus::Locked,
            424 => HttpStatus::FailedDependency,
            426 => HttpStatus::UpgradeRequired,
            428 => HttpStatus::PreconditionRequired,
            429 => HttpStatus::TooManyRequests,
            431 => HttpStatus::RequestHeaderFieldsTooLarge,
            451 => HttpStatus::UnavailableForLegalReasons,
            500 => HttpStatus::InternalServerError,
            501 => HttpStatus::NotImplemented,
            502 => HttpStatus::BadGateway,
            503 => HttpStatus::ServiceUnavailable,
            504 => HttpStatus::GatewayTimeout,
            505 => HttpStatus::HTTPVersionNotSupported,
            506 => HttpStatus::VariantAlsoNegotiates,
            507 => HttpStatus::InsufficientStorage,
            508 => HttpStatus::LoopDetected,
            510 => HttpStatus::NotExtended,
            511 => HttpStatus::NetworkAuthenticationRequired,
            _ => HttpStatus::Unregistered,
        }
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Request {
    id: i32,
    time: Tm,
    url: String,
}

impl Request {
    fn get_matching_response<'a>(&'a self, responses: &'a Vec<Response>) -> Option<&'a Response> {
        for response in responses {
            if self.id == response.id {
                return Some(&response)
            }
        }

        Some(&responses[0])
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Response {
    id: i32,
    time: Tm,
    mime_type: String,
    response_time: Duration,
    http_status: HttpStatus,
}

pub fn open_logfile(path: &str) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(parse_request_line(&line_value));
            println!("{:#?}", r);
            requests.push(r)
        }

        if line_value.contains("<-") {
            let r = try!(parse_response_line(&line_value));
            println!("{:#?}", r);
            responses.push(r)
        }

    }

    Ok((requests, responses))
}

pub fn parse_request_line(log_line: &String) -> Result<Request, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let url = parts[5];

    Ok(Request {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        url: url.to_string()
    })
}

pub fn parse_response_line(log_line: &String) -> Result<Response, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let response_time = parts[parts.len()-1];
    let mime_type = parts[5];
    let status_code = parts[4];

    Ok(Response {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        response_time: Duration::milliseconds(response_time[0..response_time.len()-2].parse().unwrap()),
        mime_type: mime_type.to_string(),
        http_status: HttpStatus::from_code(status_code.parse().unwrap()),
    })
}

fn main() {
    let lines = open_logfile("src/test/simple-1.log");
    let (requests, responses) = lines.unwrap();
    println!("So many requests: {}", requests.len());
    println!("So many responses: {}", responses.len());
}

#[cfg(test)]
mod tests {
	use super::*;
    extern crate time;
    use time::strptime;
    use::time::Duration;

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();

        let expected = Request {
            id: 2,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = parse_request_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            time: strptime("08/Apr/2016:09:58:48 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(10),
            http_status: HttpStatus::OK,
        };

        let result = parse_response_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_get_matching_response() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let result = requests[0].get_matching_response(&responses);

        assert_eq!(*result.unwrap(), responses[0]);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

//! # SymbolAPI
//!
//! SymbolAPI is a microservice which accepts lists of debug symbol names + memory addresses, and
//! returns a list of symbolicated function names.
//!

/// The goal is to take an HTTP JSON request such as:
/// ```
/// {"stacks":[
///   [
///     [0,11723767],
///     [1, 65802]
///    ]
///  ],
///  "memoryMap":[
///    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
///    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
///  ],
///  "version":4
/// }
/// ```
/// The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
/// the function names for the corresponding addresses (in the "stacks" array) and returns JSON
/// such as:
/// ```
/// {"symbolicatedStacks": [
///   [
///     "XREMain::XRE_mainRun() (in xul.pdb)",
///     "KiUserCallbackDispatcher (in wntdll.pdb)"]
///   ],
///   "knownModules": [true, true]
/// }
/// ```

extern crate breakpad_symbols;
extern crate flate2;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::env;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use flate2::read::GzDecoder;
use hyper::Client;
use hyper::header::{ContentEncoding, Encoding};
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/// Incoming JSON request format.
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    pub memoryMap: Vec<(String,String)>,
    // index, offset
    pub stacks: Vec<Vec<(i8,u64)>>,
    pub version: u8,
}

/// Outgoing JSON response format.
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    pub symbolicatedStacks: Vec<Vec<String>>,
    pub knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let default_port = 5000;
    let port;
    match env::var("PORT") {
        Ok(val) => port = val,
        Err(_) => {
            println!("$PORT unset, using default {}", default_port);
            port = format!("{}", default_port);
        }
    }

    let address = &*format!("0.0.0.0:{}", port);

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/// Stacks come in as a JSON array, but really is a hash map of `<index, address>`
/// where `index` is the position of the `memoryMap` JSON result.
pub fn stacks_to_stack_map(decoded_stacks: Vec<Vec<(i8,u64)>>) -> HashMap<i8, Vec<u64>> {
    debug!("decoded_stacks: {:?}", decoded_stacks);

    let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();
    for stack in &decoded_stacks[0] {
        let (index, offset) = *stack;

        let mut offsets = vec!();
        if stack_map.contains_key(&index) {
            offsets = stack_map.get(&index).unwrap().clone();
        }
        offsets.push(offset);
        &stack_map.insert(index, offsets);
    }

    debug!("stack_map: {:?}", stack_map);
    stack_map
}

/// Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket via the
/// client function.
pub fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");
            let stack_map = stacks_to_stack_map(decoded.stacks);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = json::encode(&client(symbol_url, decoded.memoryMap, stack_map)).unwrap();
            res.write_all(symbol_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/// Creates multiple client connections to fetch debug symbols from S3 bucket and symbolicate using
/// memory and stack maps, aggregates results and returns SymbolResult.
pub fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> SymbolResponse {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                let is_gzipped = match res.headers.get::<ContentEncoding>() {
                    Some(x) => x.contains(&Encoding::Gzip),
                    None => false,
                };

                let mut body = String::new();
                if is_gzipped {
                    let mut raw_body = vec!();
                    res.read_to_end(&mut raw_body).unwrap();
                    let mut d = match GzDecoder::new(&raw_body[..]) {
                        Ok(x) => x,
                        Err(x) => panic!("cannot gunzip {:?}, {:?}", raw_body, x)
                    };
                    d.read_to_string(&mut body).unwrap();
                } else {
                    res.read_to_string(&mut body).unwrap();
                }

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);

            let mut symbols = vec!();
            let mut known_module = true;
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    debug!("attempt to symbolicate: {} for: {:?}", *address, &full_symbol_path);
                    match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                        Some(x) => symbols.push(x),
                        // return the address rather than function name if symbol not found
                        None => {
                            symbols.push(format!("0x{:x}", address));
                            known_module = false;
                        },
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols, known_module)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    // pass anything with index -1 through
    for stacks in stack_map.get(&-1) {
        for address in stacks {
            symbolicated_stacks.push(
                format!("0x{:x}", address)
            );
        }
    }

    for handle in handles {
        let (debug_file_name, symbols, known_module) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(known_module);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    result
}

/// Returns individual values from the configuration file.
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<commit_msg>explicit panic if JSON encode response fails<commit_after>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

//! # SymbolAPI
//!
//! SymbolAPI is a microservice which accepts lists of debug symbol names + memory addresses, and
//! returns a list of symbolicated function names.
//!

/// The goal is to take an HTTP JSON request such as:
/// ```
/// {"stacks":[
///   [
///     [0,11723767],
///     [1, 65802]
///    ]
///  ],
///  "memoryMap":[
///    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
///    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
///  ],
///  "version":4
/// }
/// ```
/// The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
/// the function names for the corresponding addresses (in the "stacks" array) and returns JSON
/// such as:
/// ```
/// {"symbolicatedStacks": [
///   [
///     "XREMain::XRE_mainRun() (in xul.pdb)",
///     "KiUserCallbackDispatcher (in wntdll.pdb)"]
///   ],
///   "knownModules": [true, true]
/// }
/// ```

extern crate breakpad_symbols;
extern crate flate2;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::env;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use flate2::read::GzDecoder;
use hyper::Client;
use hyper::header::{ContentEncoding, Encoding};
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/// Incoming JSON request format.
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
#[derive(Debug)]
pub struct SymbolRequest {
    pub memoryMap: Vec<(String,String)>,
    // index, offset
    pub stacks: Vec<Vec<(i8,u64)>>,
    pub version: u8,
}

/// Outgoing JSON response format.
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
#[derive(Debug)]
pub struct SymbolResponse {
    pub symbolicatedStacks: Vec<Vec<String>>,
    pub knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let default_port = 5000;
    let port;
    match env::var("PORT") {
        Ok(val) => port = val,
        Err(_) => {
            println!("$PORT unset, using default {}", default_port);
            port = format!("{}", default_port);
        }
    }

    let address = &*format!("0.0.0.0:{}", port);

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/// Stacks come in as a JSON array, but really is a hash map of `<index, address>`
/// where `index` is the position of the `memoryMap` JSON result.
pub fn stacks_to_stack_map(decoded_stacks: Vec<Vec<(i8,u64)>>) -> HashMap<i8, Vec<u64>> {
    debug!("decoded_stacks: {:?}", decoded_stacks);

    let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();
    for stack in &decoded_stacks[0] {
        let (index, offset) = *stack;

        let mut offsets = vec!();
        if stack_map.contains_key(&index) {
            offsets = stack_map.get(&index).unwrap().clone();
        }
        offsets.push(offset);
        &stack_map.insert(index, offsets);
    }

    debug!("stack_map: {:?}", stack_map);
    stack_map
}

/// Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket via the
/// client function.
pub fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");
            let stack_map = stacks_to_stack_map(decoded.stacks);

            // FIXME limit the number of possible threads spawned by client()
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response: SymbolResponse = client(symbol_url, decoded.memoryMap, stack_map);
            let json_response = match json::encode(&symbol_response) {
                Ok(x) => x,
                Err(x) => panic!("cannot JSON encode {:?}, {:?}", symbol_response, x)
            };
            res.write_all(json_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/// Creates multiple client connections to fetch debug symbols from S3 bucket and symbolicate using
/// memory and stack maps, aggregates results and returns SymbolResult.
pub fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> SymbolResponse {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                let is_gzipped = match res.headers.get::<ContentEncoding>() {
                    Some(x) => x.contains(&Encoding::Gzip),
                    None => false,
                };

                let mut body = String::new();
                if is_gzipped {
                    let mut raw_body = vec!();
                    res.read_to_end(&mut raw_body).unwrap();
                    let mut d = match GzDecoder::new(&raw_body[..]) {
                        Ok(x) => x,
                        Err(x) => panic!("cannot gunzip {:?}, {:?}", raw_body, x)
                    };
                    d.read_to_string(&mut body).unwrap();
                } else {
                    res.read_to_string(&mut body).unwrap();
                }

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);

            let mut symbols = vec!();
            let mut known_module = true;
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    debug!("attempt to symbolicate: {} for: {:?}", *address, &full_symbol_path);
                    match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                        Some(x) => symbols.push(x),
                        // return the address rather than function name if symbol not found
                        None => {
                            symbols.push(format!("0x{:x}", address));
                            known_module = false;
                        },
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols, known_module)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    // pass anything with index -1 through
    for stacks in stack_map.get(&-1) {
        for address in stacks {
            symbolicated_stacks.push(
                format!("0x{:x}", address)
            );
        }
    }

    for handle in handles {
        let (debug_file_name, symbols, known_module) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(known_module);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    result
}

/// Returns individual values from the configuration file.
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<|endoftext|>


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

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::GlyphCache;

use graphics::*;

use std::path::Path;
use std::fs::OpenOptions;
use board::Board;

mod block;
mod board;

pub const BLOCK_SIZE: i64 = 30;
pub const BOARD_WIDTH: i64 = 10; // 10 cells across
pub const BOARD_HEIGHT: i64 = 22; // 22 cells up n down

pub const NUM_BOARDS: i64 = 2; // number of boards

pub const FONT_SIZE: u32 = 24;
const SCORE_LEFT_MARGIN: f64 = 15.0;
const SCORE_TOP_MARGIN: f64 = 35.0;

const GAMEOVER_LEFT_MARGIN: f64 = 70.0;
const GAMEOVER_TOP_MARGIN: f64 = 250.0;

const BOARD_BKD_COLOR: [f32; 4] = [0.18, 0.18, 0.18, 1.0]; // dark gray
// const RED: [f32; 4] = [0.96, 0.12, 0.12, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
// const BRIGHT_GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];

pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    boards: Vec<board::Board>, // the game board
    cache: GlyphCache<'static> // for drawing text
}

impl App {
    fn render(&mut self, args: &RenderArgs) {

        self.gl.draw(args.viewport(), |c, gl| {
            // Clear the screen.
            clear(BOARD_BKD_COLOR, gl);
        });

        for (i, board) in &mut self.boards.iter().enumerate() {
            // do nothing if paused
            if board.state == board::GameState::Paused {
                continue
            }

            let font_cache = &mut self.cache;

            // show game over screen if game is done
            if board.state == board::GameState::Over {
                self.gl.draw(args.viewport(), |c, gl| {
                    rectangle(BLACK,
                             [(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                0.0,
                                (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                             c.transform.trans(0.0, 0.0),
                             gl);

                    // draw game over message
                    let mut text = graphics::Text::new(FONT_SIZE);
                    text.color = WHITE;
                    let mut transform: graphics::context::Context =
                                c.trans(GAMEOVER_LEFT_MARGIN + (BLOCK_SIZE * BOARD_WIDTH * i as i64) as f64, GAMEOVER_TOP_MARGIN);
                    text.draw(&format!("GAME OVER"),
                              font_cache,
                              &c.draw_state,
                              transform.transform,
                              gl);
                    transform = transform.trans(0.0, FONT_SIZE as f64 + 40.0);
                    // not centered because i am a weenie
                    text.draw(&format!("   Score: {}", board.score),
                              font_cache,
                              &c.draw_state,
                              transform.transform,
                              gl);

                    // draw border
                    let rect_border = graphics::Rectangle::new_border(WHITE, 0.3);
                    rect_border.draw([(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                     0.0,
                                     (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                     (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                        &c.draw_state,
                        c.transform,
                        gl);
                });

                continue
            }

            // show gameplay screen
            let cells = board.cells;

            let blocks = board.current_piece.blocks;
            let current_x = board.current_piece.x_offset;
            let current_y = board.current_piece.y_offset;
            let piece_color = board.current_piece.color;

            let ghost_blocks = board.ghost_piece.blocks;
            let ghost_x = board.ghost_piece.x_offset;
            let ghost_y = board.ghost_piece.y_offset;
            let ghost_piece_color = board.ghost_piece.color;

            self.gl.draw(args.viewport(), |c, gl| {
                // iterate thru board cells and draw in filled-in blocks
                for row in 0..cells.len() {
                    for col in 0..cells[0].len() {
                        if cells[row][col] == 1 {
                            let x: f64 = col as f64 + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = row as f64;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(WHITE, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // iterate thru current piece and draw its current location
                for row in 0..blocks.len() {
                    for col in 0..blocks[0].len() {
                        if blocks[row][col] == 1 {
                            let x: f64 = (col as f64) + current_x + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = (row as f64) + current_y;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(piece_color, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // DRAW  G H O S T   P I E C E ?
                for row in 0..ghost_blocks.len() {
                    for col in 0..ghost_blocks[0].len() {
                        if ghost_blocks[row][col] == 1 {
                            let x: f64 = (col as f64) + ghost_x + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = (row as f64) + ghost_y;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(ghost_piece_color, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // draw score
                let mut text = graphics::Text::new(FONT_SIZE);
                text.color = WHITE;
                let mut transform: graphics::context::Context =
                            c.trans(SCORE_LEFT_MARGIN + (BLOCK_SIZE as f64) * (BOARD_WIDTH * i as i64) as f64, SCORE_TOP_MARGIN);
                text.draw(&format!("Score: {}", board.score),
                          font_cache,
                          &c.draw_state,
                          transform.transform,
                          gl);

                // draw border
                let rect_border = graphics::Rectangle::new_border(WHITE, 0.3);
                rect_border.draw([(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                 0.0,
                                 (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                 (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                    &c.draw_state,
                    c.transform,
                    gl);
            });

        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        for mut board in &mut self.boards {
            if board.state != board::GameState::Playing {
                continue
            }
            board.advance_board();
        }
    }
}

fn main() {
    // Change this to OpenGL::V2_1 if not working.
    let opengl = OpenGL::V3_2;

    // Create an Glutin window.
    let mut window: Window = WindowSettings::new(
            "t e t r i s",
            [(BLOCK_SIZE * BOARD_WIDTH * NUM_BOARDS) as u32, (BLOCK_SIZE * BOARD_HEIGHT) as u32]
        )
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let font_path = match OpenOptions::new().read(true).open("Lato-Light.ttf") {
        Ok(_) => Path::new("Lato-Light.ttf"),
        Err(_) => {
            match OpenOptions::new().read(true).open("src/Lato-Light.ttf") {
                Ok(_) => Path::new("src/Lato-Light.ttf"),
                Err(_) => panic!("No font file found")
            }
        }
    };

    // Create a new game and run it.
    let mut boards: Vec<board::Board> = Vec::new();
    for i in 0..NUM_BOARDS {
        boards.push(Board::init_board());
    }
    let mut app = App {
        gl: GlGraphics::new(opengl),
        boards: boards,
        cache: GlyphCache::new(font_path).unwrap()
    };

    let mut events = window.events();
    events = events.ups(5);

    while let Some(e) = events.next(&mut window) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }

        if let Some(u) = e.update_args() {
            app.update(&u);
        }

        if let Event::Input(i) = e {
            for board in &mut app.boards {
                board.handle_key_press(&i);
            }
        }
    }
}

<commit_msg>fix compile warning<commit_after>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
extern crate rand;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::GlyphCache;

use graphics::*;

use std::path::Path;
use std::fs::OpenOptions;
use board::Board;

mod block;
mod board;

pub const BLOCK_SIZE: i64 = 30;
pub const BOARD_WIDTH: i64 = 10; // 10 cells across
pub const BOARD_HEIGHT: i64 = 22; // 22 cells up n down

pub const NUM_BOARDS: i64 = 2; // number of boards

pub const FONT_SIZE: u32 = 24;
const SCORE_LEFT_MARGIN: f64 = 15.0;
const SCORE_TOP_MARGIN: f64 = 35.0;

const GAMEOVER_LEFT_MARGIN: f64 = 70.0;
const GAMEOVER_TOP_MARGIN: f64 = 250.0;

const BOARD_BKD_COLOR: [f32; 4] = [0.18, 0.18, 0.18, 1.0]; // dark gray
// const RED: [f32; 4] = [0.96, 0.12, 0.12, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
// const BRIGHT_GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];

pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    boards: Vec<board::Board>, // the game board
    cache: GlyphCache<'static> // for drawing text
}

impl App {
    fn render(&mut self, args: &RenderArgs) {

        self.gl.draw(args.viewport(), |c, gl| {
            // Clear the screen.
            clear(BOARD_BKD_COLOR, gl);
        });

        for (i, board) in &mut self.boards.iter().enumerate() {
            // do nothing if paused
            if board.state == board::GameState::Paused {
                continue
            }

            let font_cache = &mut self.cache;

            // show game over screen if game is done
            if board.state == board::GameState::Over {
                self.gl.draw(args.viewport(), |c, gl| {
                    rectangle(BLACK,
                             [(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                0.0,
                                (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                             c.transform.trans(0.0, 0.0),
                             gl);

                    // draw game over message
                    let mut text = graphics::Text::new(FONT_SIZE);
                    text.color = WHITE;
                    let mut transform: graphics::context::Context =
                                c.trans(GAMEOVER_LEFT_MARGIN + (BLOCK_SIZE * BOARD_WIDTH * i as i64) as f64, GAMEOVER_TOP_MARGIN);
                    text.draw(&format!("GAME OVER"),
                              font_cache,
                              &c.draw_state,
                              transform.transform,
                              gl);
                    transform = transform.trans(0.0, FONT_SIZE as f64 + 40.0);
                    // not centered because i am a weenie
                    text.draw(&format!("   Score: {}", board.score),
                              font_cache,
                              &c.draw_state,
                              transform.transform,
                              gl);

                    // draw border
                    let rect_border = graphics::Rectangle::new_border(WHITE, 0.3);
                    rect_border.draw([(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                     0.0,
                                     (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                     (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                        &c.draw_state,
                        c.transform,
                        gl);
                });

                continue
            }

            // show gameplay screen
            let cells = board.cells;

            let blocks = board.current_piece.blocks;
            let current_x = board.current_piece.x_offset;
            let current_y = board.current_piece.y_offset;
            let piece_color = board.current_piece.color;

            let ghost_blocks = board.ghost_piece.blocks;
            let ghost_x = board.ghost_piece.x_offset;
            let ghost_y = board.ghost_piece.y_offset;
            let ghost_piece_color = board.ghost_piece.color;

            self.gl.draw(args.viewport(), |c, gl| {
                // iterate thru board cells and draw in filled-in blocks
                for row in 0..cells.len() {
                    for col in 0..cells[0].len() {
                        if cells[row][col] == 1 {
                            let x: f64 = col as f64 + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = row as f64;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(WHITE, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // iterate thru current piece and draw its current location
                for row in 0..blocks.len() {
                    for col in 0..blocks[0].len() {
                        if blocks[row][col] == 1 {
                            let x: f64 = (col as f64) + current_x + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = (row as f64) + current_y;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(piece_color, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // DRAW  G H O S T   P I E C E ?
                for row in 0..ghost_blocks.len() {
                    for col in 0..ghost_blocks[0].len() {
                        if ghost_blocks[row][col] == 1 {
                            let x: f64 = (col as f64) + ghost_x + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = (row as f64) + ghost_y;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(ghost_piece_color, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // draw score
                let mut text = graphics::Text::new(FONT_SIZE);
                text.color = WHITE;
                let transform: graphics::context::Context =
                            c.trans(SCORE_LEFT_MARGIN + (BLOCK_SIZE as f64) * (BOARD_WIDTH * i as i64) as f64, SCORE_TOP_MARGIN);
                text.draw(&format!("Score: {}", board.score),
                          font_cache,
                          &c.draw_state,
                          transform.transform,
                          gl);

                // draw border
                let rect_border = graphics::Rectangle::new_border(WHITE, 0.3);
                rect_border.draw([(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                 0.0,
                                 (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                 (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                    &c.draw_state,
                    c.transform,
                    gl);
            });

        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        for mut board in &mut self.boards {
            if board.state != board::GameState::Playing {
                continue
            }
            board.advance_board();
        }
    }
}

fn main() {
    // Change this to OpenGL::V2_1 if not working.
    let opengl = OpenGL::V3_2;

    // Create an Glutin window.
    let mut window: Window = WindowSettings::new(
            "t e t r i s",
            [(BLOCK_SIZE * BOARD_WIDTH * NUM_BOARDS) as u32, (BLOCK_SIZE * BOARD_HEIGHT) as u32]
        )
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let font_path = match OpenOptions::new().read(true).open("Lato-Light.ttf") {
        Ok(_) => Path::new("Lato-Light.ttf"),
        Err(_) => {
            match OpenOptions::new().read(true).open("src/Lato-Light.ttf") {
                Ok(_) => Path::new("src/Lato-Light.ttf"),
                Err(_) => panic!("No font file found")
            }
        }
    };

    // Create a new game and run it.
    let mut boards: Vec<board::Board> = Vec::new();
    for i in 0..NUM_BOARDS {
        boards.push(Board::init_board());
    }
    let mut app = App {
        gl: GlGraphics::new(opengl),
        boards: boards,
        cache: GlyphCache::new(font_path).unwrap()
    };

    let mut events = window.events();
    events = events.ups(5);

    while let Some(e) = events.next(&mut window) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }

        if let Some(u) = e.update_args() {
            app.update(&u);
        }

        if let Event::Input(i) = e {
            for board in &mut app.boards {
                board.handle_key_press(&i);
            }
        }
    }
}

<|endoftext|>


<commit_before>#![feature(rust_2018_preview)]
#![feature(euclidean_division)]
#![feature(try_trait)]

mod aabb;
mod bvh;
mod camera;
mod color;
mod gl_renderer;
mod input;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod scene;
mod stats;
mod triangle;
mod util;
mod vertex;

use std::path::{Path, PathBuf};

use chrono::Local;

use glium::Surface;
use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode};

use crate::gl_renderer::GLRenderer;
use crate::input::InputState;
use crate::pt_renderer::PTRenderer;

fn main() {
    match std::env::args().nth(1).as_ref().map(|s| s.as_str()) {
        Some(_) => benchmark(),
        None => online_render(),
    }
}

fn benchmark() {
    let scenes = ["plane", "cornell-glossy", "cornell-water", "nanosuit", "sibenik",];
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let save_dir = root_dir.join("results");
    std::fs::create_dir_all(save_dir.clone()).unwrap();
    for scene in &scenes {
        let mut output_file = save_dir.join(scene);
        output_file.set_extension("png");
        offline_render(scene, &output_file, 2);
    }
    let stats_file = save_dir.join(Local::now().format("benchmark_%F_%H%M%S.txt").to_string());
    stats::print_and_save(&stats_file);
}

fn offline_render(scene_name: &str, output_file: &Path, iterations: u32) {
    stats::new_scene(scene_name);
    let _t = stats::time("Total");
    let mut pt_renderer = PTRenderer::new();
    println!("{}...", scene_name);
    let (scene, camera) = util::load_cpu_scene(scene_name);
    pt_renderer.offline_render(&scene, &camera, iterations);
    pt_renderer.save_image(output_file);
}

fn online_render() {
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new();
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display = glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let (mut scene, mut gpu_scene, mut camera) =
        util::load_gpu_scene(VirtualKeyCode::Key1, &display).unwrap();
    let gl_renderer = GLRenderer::new(&display);
    let mut pt_renderer = PTRenderer::new();

    let mut input = InputState::new();
    let mut trace = false;
    let mut quit = false;

    loop {
        let mut target = display.draw();

        camera.update_viewport(target.get_dimensions());
        // Don't draw if the window is minimized
        if camera.width != 0 && camera.height != 0 {
            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            if trace {
                pt_renderer.render(&mut target);
            } else {
                gl_renderer.render(&mut target, &gpu_scene, &camera);
            }
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent{event: WindowEvent::KeyboardInput{input, ..}, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Space), ..} => {
                            trace = !trace;
                            if trace {
                                pt_renderer.online_render(&display, &scene, &camera);
                            } else {
                                pt_renderer.stop_threads();
                            }
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(keycode), ..} => {
                            if let Some(res) = util::load_gpu_scene(keycode, &display) {
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        },
                        _ => ()
                    }
                }
                Event::WindowEvent{event: WindowEvent::Closed, ..} => quit = true,
                _ => ()
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            if trace {
                pt_renderer.stop_threads();
            }
            return;
        }
    }
}
<commit_msg>Use conference instead of nanosuit in benchmark<commit_after>#![feature(rust_2018_preview)]
#![feature(euclidean_division)]
#![feature(try_trait)]

mod aabb;
mod bvh;
mod camera;
mod color;
mod gl_renderer;
mod input;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod scene;
mod stats;
mod triangle;
mod util;
mod vertex;

use std::path::{Path, PathBuf};

use chrono::Local;

use glium::Surface;
use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode};

use crate::gl_renderer::GLRenderer;
use crate::input::InputState;
use crate::pt_renderer::PTRenderer;

fn main() {
    match std::env::args().nth(1).as_ref().map(|s| s.as_str()) {
        Some(_) => benchmark(),
        None => online_render(),
    }
}

fn benchmark() {
    let scenes = ["plane", "cornell-glossy", "cornell-water", "conference", "sibenik",];
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let save_dir = root_dir.join("results");
    std::fs::create_dir_all(save_dir.clone()).unwrap();
    for scene in &scenes {
        let mut output_file = save_dir.join(scene);
        output_file.set_extension("png");
        offline_render(scene, &output_file, 2);
    }
    let stats_file = save_dir.join(Local::now().format("benchmark_%F_%H%M%S.txt").to_string());
    stats::print_and_save(&stats_file);
}

fn offline_render(scene_name: &str, output_file: &Path, iterations: u32) {
    stats::new_scene(scene_name);
    let _t = stats::time("Total");
    let mut pt_renderer = PTRenderer::new();
    println!("{}...", scene_name);
    let (scene, camera) = util::load_cpu_scene(scene_name);
    pt_renderer.offline_render(&scene, &camera, iterations);
    pt_renderer.save_image(output_file);
}

fn online_render() {
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new();
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display = glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let (mut scene, mut gpu_scene, mut camera) =
        util::load_gpu_scene(VirtualKeyCode::Key1, &display).unwrap();
    let gl_renderer = GLRenderer::new(&display);
    let mut pt_renderer = PTRenderer::new();

    let mut input = InputState::new();
    let mut trace = false;
    let mut quit = false;

    loop {
        let mut target = display.draw();

        camera.update_viewport(target.get_dimensions());
        // Don't draw if the window is minimized
        if camera.width != 0 && camera.height != 0 {
            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            if trace {
                pt_renderer.render(&mut target);
            } else {
                gl_renderer.render(&mut target, &gpu_scene, &camera);
            }
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent{event: WindowEvent::KeyboardInput{input, ..}, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Space), ..} => {
                            trace = !trace;
                            if trace {
                                pt_renderer.online_render(&display, &scene, &camera);
                            } else {
                                pt_renderer.stop_threads();
                            }
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(keycode), ..} => {
                            if let Some(res) = util::load_gpu_scene(keycode, &display) {
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        },
                        _ => ()
                    }
                }
                Event::WindowEvent{event: WindowEvent::Closed, ..} => quit = true,
                _ => ()
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            if trace {
                pt_renderer.stop_threads();
            }
            return;
        }
    }
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate serde_json;
extern crate vegas_lattice;

use std::fs::File;
use std::io::{self, Read};

use docopt::{ArgvMap, Docopt};
use vegas_lattice::Lattice;

const USAGE: &'static str = "
Vegas lattice.

Usage:
    vlattice check [--compressed] [<input>]
    vlattice (-h | --help)
    vlattice --version

Options:
    -h --help       Show this message.
    --version       Show version and exit.
";

fn check(args: ArgvMap) -> Result<(), Box<std::error::Error>> {
    let mut data = String::new();
    if !args.get_str("<input>").is_empty() {
        let mut file =
            File::open(args.get_str("<input>"))?;
        file.read_to_string(&mut data)?;
    } else {
        io::stdin().read_to_string(&mut data)?;
    };
    let lattice: Lattice = data.parse()?;
    if args.get_bool("--compressed") {
        println!("{}", serde_json::to_string(&lattice).unwrap());
    } else {
        println!("{}", serde_json::to_string_pretty(&lattice).unwrap());
    }
    Ok(())
}

fn main() {
    let args = Docopt::new(USAGE)
        .and_then(|doc| doc.version(Some("0.0.1".to_string())).parse())
        .unwrap_or_else(|e| e.exit());

    if args.get_bool("check") {
        check(args).unwrap_or_else(|e| {
            eprintln!("{}", e);
            std::process::exit(1);
        });
    } else {
        println!("{:?}", args);
    }

}<commit_msg>Change the excecutable name.<commit_after>extern crate docopt;
extern crate serde_json;
extern crate vegas_lattice;

use std::fs::File;
use std::io::{self, Read};

use docopt::{ArgvMap, Docopt};
use vegas_lattice::Lattice;

const USAGE: &'static str = "
Vegas lattice.

Usage:
    vegas-lattice check [--compressed] [<input>]
    vegas-lattice (-h | --help)
    vegas-lattice --version

Options:
    -h --help       Show this message.
    --version       Show version and exit.
";

fn check(args: ArgvMap) -> Result<(), Box<std::error::Error>> {
    let mut data = String::new();
    if !args.get_str("<input>").is_empty() {
        let mut file =
            File::open(args.get_str("<input>"))?;
        file.read_to_string(&mut data)?;
    } else {
        io::stdin().read_to_string(&mut data)?;
    };
    let lattice: Lattice = data.parse()?;
    if args.get_bool("--compressed") {
        println!("{}", serde_json::to_string(&lattice).unwrap());
    } else {
        println!("{}", serde_json::to_string_pretty(&lattice).unwrap());
    }
    Ok(())
}

fn main() {
    let args = Docopt::new(USAGE)
        .and_then(|doc| doc.version(Some("0.0.1".to_string())).parse())
        .unwrap_or_else(|e| e.exit());

    if args.get_bool("check") {
        check(args).unwrap_or_else(|e| {
            eprintln!("{}", e);
            std::process::exit(1);
        });
    } else {
        println!("{:?}", args);
    }

}<|endoftext|>