<commit_before>#![feature(globs)]
extern crate debug;

extern crate graphics;
extern crate piston;
extern crate opengl_graphics;
extern crate sdl2_game_window;

use std::os;

use graphics::*;
use opengl_graphics::Gl;
use piston::*;
use sdl2_game_window::GameWindowSDL2;

use lightsout::{StructLevel, Level};

mod lightsout;

fn cmd_main() {
	let sx:Option<uint> = from_str(os::args()[1].as_slice());
	let sx:uint = match sx {
		Some(x) => x,
		None => 4
	};
	let sy:Option<uint> = from_str(os::args()[2].as_slice());
	let sy:uint = match sy {
		Some(y) => y,
		None => 4
	};
	let mut l:StructLevel = Level::new(sx, sy);
	println!("{}", l);
	println!("solved: {}", l.is_solved());

	let sol = lightsout::solve(&mut l);

	println!("{}", sol);
}

struct Env {
	mousex: f64,
	mousey: f64,
	window_width: f64,
	window_height: f64,
}

#[inline(always)]
fn pt_in_rect(px:f64, py:f64, rx: f64, ry:f64, w:f64, h:f64) -> bool {
	px >= rx && px <= rx+h && py >= ry && py <= ry + h
}

fn clear_board(sx: uint, sy: uint, c: &Context, gl: &mut Gl) {
	let margin = 10.0;
	let w = 60.0;
	let h = 50.0;
	c.rect(0.0, 0.0, margin + (margin + w) * (sx as f64) + 1.0, margin + (margin + h) * (sy as f64) + 1.0)
		.rgb(0.0, 0.0, 0.0).draw(gl);
}

fn render_level(level: &Level, env: &Env, c: &Context, gl: &mut Gl) {
	let (sx, sy) = level.size();
	let margin = 10.0;
	let w = 60.0;
	let h = 50.0;
	for y in range(0, sy) {
		for x in range(0, sx) {
			let cx = margin + (margin + w) * (x as f64);
			let cy = margin + (margin + h) * (y as f64);
			if pt_in_rect(env.mousex, env.mousey, cx, cy, w, h) {
				c.rect(cx-1.0, cy-1.0, w+2.0, h+2.0).rgb(1.0, 1.0, 0.0).draw(gl);
			}
			let c = c.rect(cx, cy, w, h);
			if level.get(x, y).unwrap() == 0 {
				c.rgb(0.4, 0.4, 0.4)
			} else {
				c.rgb(0.8, 0.8, 0.8)
			}.draw(gl);
		}
	}
}

fn mouse_to_level(level: &Level, mx: f64, my: f64) -> Option<(uint, uint)> {
	let (sx, sy) = level.size();
	let margin = 10.0;
	let w = 60.0;
	let h = 50.0;
	if !pt_in_rect(mx, my,
		       margin, margin,
		       margin + (w + margin) * sx as f64, margin + (h + margin) * sy as f64) {
		return None
	}

	let lx = (mx - margin) / (margin + w);
	let ly = (my - margin) / (margin + h);
	println!("lx={} ly={}", lx, ly);
	let ulx = lx as uint;
	let uly = ly as uint;
	let sx = margin + (margin + w) * (ulx as f64);
	let sy = margin + (margin + h) * (uly as f64);
	if pt_in_rect(mx, my, sx, sy, w, h) {
		Some((ulx, uly))
	} else {
		None
	}
}

fn make_ai_move(level: &mut StructLevel) {
	let sol = lightsout::solve(level);
	println!("solution: {}", sol);
	match sol {
		Some(moves) => {
			if moves.len() > 0 {
				let move = moves[0];
				let (mx, my) = move;
				level.make_move(mx, my);
			}
		},
		None => {}
	};
}

fn main() {
	let mut window = GameWindowSDL2::new(
		GameWindowSettings {
			title: "Rust Lights Out".to_string(),
			size: [800u32, 600u32],
			fullscreen: false,
			exit_on_esc: true,
		}
	);
	println!("window: {:?}", window);

	let game_iter_settings = GameIteratorSettings {
		updates_per_second: 120,
		max_frames_per_second: 60,
	};

	let ref mut gl = Gl::new();
	let mut level:StructLevel = Level::new(5, 5);

	let mut env = Env {
		mousex: 0f64,
		mousey: 0f64,
		window_width: window.get_settings().size[0] as f64,
		window_height: window.get_settings().size[1] as f64,
	};

	level.make_move(2, 2);

	for event in GameIterator::new(&mut window, &game_iter_settings) {
		match event {
			Render(args) => {
				gl.viewport(0, 0, args.width as i32, args.height as i32);
				let c = Context::abs(args.width as f64, args.height as f64);
				c.rgb(1.0, 0.0, 0.0)
					.rect(0.0, 0.0, 100.0, 100.0)
					.draw(gl);
				clear_board(9, 9, &c, gl);
				render_level(&level, &env, &c, gl);
			},
			MouseMove(args) => {
				env.mousex = args.x;
				env.mousey = args.y;
			},
			MousePress(args) => {
				println!("{:?} {:?}", args, env);
				match mouse_to_level(&level, env.mousex, env.mousey) {
					Some((x, y)) => { level.make_move(x, y); },
					_ => {}
				}
			},
			KeyPress(args) => {
				println!("{:?} {:?}", args, env);
				match args.key {
					keyboard::Space => make_ai_move(&mut level),
					keyboard::Left
					| keyboard::Right
					| keyboard::Up
					| keyboard::Down => {
						let change_level_size = |dx: uint, dy: uint| {
							let (sx, sy) = level.size();
							if sx + dx > 1 && sy + dy > 1
									&& sx + dx < 10 && sy + dy < 10 {
								level = Level::new(sx + dx, sy + dy);
								println!("new level: {}", level);
							}
						};
						match args.key {
							keyboard::Up => change_level_size(0, -1),
							keyboard::Right => change_level_size(1, 0),
							keyboard::Down => change_level_size(0, 1),
							keyboard::Left => change_level_size(-1, 0),
							_ => {},
						};
					},
					_ => {}
				}
			},
			_ => {}
		}
	}
}
<commit_msg>fix typo.<commit_after>#![feature(globs)]
extern crate debug;

extern crate graphics;
extern crate piston;
extern crate opengl_graphics;
extern crate sdl2_game_window;

use std::os;

use graphics::*;
use opengl_graphics::Gl;
use piston::*;
use sdl2_game_window::GameWindowSDL2;

use lightsout::{StructLevel, Level};

mod lightsout;

fn cmd_main() {
	let sx:Option<uint> = from_str(os::args()[1].as_slice());
	let sx:uint = match sx {
		Some(x) => x,
		None => 4
	};
	let sy:Option<uint> = from_str(os::args()[2].as_slice());
	let sy:uint = match sy {
		Some(y) => y,
		None => 4
	};
	let mut l:StructLevel = Level::new(sx, sy);
	println!("{}", l);
	println!("solved: {}", l.is_solved());

	let sol = lightsout::solve(&mut l);

	println!("{}", sol);
}

struct Env {
	mousex: f64,
	mousey: f64,
	window_width: f64,
	window_height: f64,
}

#[inline(always)]
fn pt_in_rect(px:f64, py:f64, rx: f64, ry:f64, w:f64, h:f64) -> bool {
	px >= rx && px <= rx + w && py >= ry && py <= ry + h
}

fn clear_board(sx: uint, sy: uint, c: &Context, gl: &mut Gl) {
	let margin = 10.0;
	let w = 60.0;
	let h = 50.0;
	c.rect(0.0, 0.0, margin + (margin + w) * (sx as f64) + 1.0, margin + (margin + h) * (sy as f64) + 1.0)
		.rgb(0.0, 0.0, 0.0).draw(gl);
}

fn render_level(level: &Level, env: &Env, c: &Context, gl: &mut Gl) {
	let (sx, sy) = level.size();
	let margin = 10.0;
	let w = 60.0;
	let h = 50.0;
	for y in range(0, sy) {
		for x in range(0, sx) {
			let cx = margin + (margin + w) * (x as f64);
			let cy = margin + (margin + h) * (y as f64);
			if pt_in_rect(env.mousex, env.mousey, cx, cy, w, h) {
				c.rect(cx-1.0, cy-1.0, w+2.0, h+2.0).rgb(1.0, 1.0, 0.0).draw(gl);
			}
			let c = c.rect(cx, cy, w, h);
			if level.get(x, y).unwrap() == 0 {
				c.rgb(0.4, 0.4, 0.4)
			} else {
				c.rgb(0.8, 0.8, 0.8)
			}.draw(gl);
		}
	}
}

fn mouse_to_level(level: &Level, mx: f64, my: f64) -> Option<(uint, uint)> {
	let (sx, sy) = level.size();
	let margin = 10.0;
	let w = 60.0;
	let h = 50.0;
	if !pt_in_rect(mx, my,
		       margin, margin,
		       margin + (w + margin) * sx as f64, margin + (h + margin) * sy as f64) {
		return None
	}

	let lx = (mx - margin) / (margin + w);
	let ly = (my - margin) / (margin + h);
	println!("lx={} ly={}", lx, ly);
	let ulx = lx as uint;
	let uly = ly as uint;
	let sx = margin + (margin + w) * (ulx as f64);
	let sy = margin + (margin + h) * (uly as f64);
	if pt_in_rect(mx, my, sx, sy, w, h) {
		Some((ulx, uly))
	} else {
		None
	}
}

fn make_ai_move(level: &mut StructLevel) {
	let sol = lightsout::solve(level);
	println!("solution: {}", sol);
	match sol {
		Some(moves) => {
			if moves.len() > 0 {
				let move = moves[0];
				let (mx, my) = move;
				level.make_move(mx, my);
			}
		},
		None => {}
	};
}

fn main() {
	let mut window = GameWindowSDL2::new(
		GameWindowSettings {
			title: "Rust Lights Out".to_string(),
			size: [800u32, 600u32],
			fullscreen: false,
			exit_on_esc: true,
		}
	);
	println!("window: {:?}", window);

	let game_iter_settings = GameIteratorSettings {
		updates_per_second: 120,
		max_frames_per_second: 60,
	};

	let ref mut gl = Gl::new();
	let mut level:StructLevel = Level::new(5, 5);

	let mut env = Env {
		mousex: 0f64,
		mousey: 0f64,
		window_width: window.get_settings().size[0] as f64,
		window_height: window.get_settings().size[1] as f64,
	};

	level.make_move(2, 2);

	for event in GameIterator::new(&mut window, &game_iter_settings) {
		match event {
			Render(args) => {
				gl.viewport(0, 0, args.width as i32, args.height as i32);
				let c = Context::abs(args.width as f64, args.height as f64);
				c.rgb(1.0, 0.0, 0.0)
					.rect(0.0, 0.0, 100.0, 100.0)
					.draw(gl);
				clear_board(9, 9, &c, gl);
				render_level(&level, &env, &c, gl);
			},
			MouseMove(args) => {
				env.mousex = args.x;
				env.mousey = args.y;
			},
			MousePress(args) => {
				println!("{:?} {:?}", args, env);
				match mouse_to_level(&level, env.mousex, env.mousey) {
					Some((x, y)) => { level.make_move(x, y); },
					_ => {}
				}
			},
			KeyPress(args) => {
				println!("{:?} {:?}", args, env);
				match args.key {
					keyboard::Space => make_ai_move(&mut level),
					keyboard::Left
					| keyboard::Right
					| keyboard::Up
					| keyboard::Down => {
						let change_level_size = |dx: uint, dy: uint| {
							let (sx, sy) = level.size();
							if sx + dx > 1 && sy + dy > 1
									&& sx + dx < 10 && sy + dy < 10 {
								level = Level::new(sx + dx, sy + dy);
								println!("new level: {}", level);
							}
						};
						match args.key {
							keyboard::Up => change_level_size(0, -1),
							keyboard::Right => change_level_size(1, 0),
							keyboard::Down => change_level_size(0, 1),
							keyboard::Left => change_level_size(-1, 0),
							_ => {},
						};
					},
					_ => {}
				}
			},
			_ => {}
		}
	}
}
<|endoftext|>


<commit_before>// This file is part of sokoban-rs
// Copyright 2015 Sébastien Watteau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This is an implementation of Sokoban in Rust.

#[macro_use]
extern crate bitflags;
extern crate clap;
extern crate sdl2;
extern crate sdl2_image;
extern crate xml;

use std::path::Path;
use std::fs::File;
use std::io::BufReader;
use std::str::FromStr;
use clap::{App, Arg};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2_image::INIT_PNG;
use xml::reader::EventReader;
use xml::reader::events::*;

pub mod game;
pub mod render;

use game::Level;
use render::Drawer;

pub fn main() {
    let matches = App::new("sokoban-rs")
        .version("0.2.0")
        .author("Sébastien Watteau")
        .about("An implementation of Sokoban in the Rust programming language.")
        .arg(Arg::with_name("slc_file")
             .help("a Sokoban level collection (SLC) file")
             .required(true)
        )
        .arg(Arg::with_name("fullscreen")
             .help("Launches the game in fullscreen mode")
             .short("f")
             .long("fullscreen")
        )
        .get_matches();

    let slc_file = matches.value_of("slc_file").unwrap();

    let sdl_context = sdl2::init().unwrap_or_else(|err| {
        println!("Failed to initialize an SDL context: {}", err);
        std::process::exit(1);
    });

    let video_subsystem = sdl_context.video().unwrap_or_else(|err| {
        println!("Failed to initialize the video subsystem: {}", err);
        std::process::exit(1);
    });

    sdl2_image::init(INIT_PNG);

    let mut window_builder = video_subsystem.window("sokoban-rs", 1024, 768);
    if matches.is_present("fullscreen") {
        window_builder.fullscreen();
    } else {
        window_builder.position_centered();
    }

    let window = window_builder.opengl()
        .build()
        .unwrap_or_else(|err| {
            println!("Failed to create the window: {}", err);
            std::process::exit(1);
        });

    let renderer = window.renderer()
        .build()
        .unwrap_or_else(|err| {
            println!("Failed to get an SDL renderer for the main window: {}", err);
            std::process::exit(1);
        });

    let mut drawer = Drawer::new(renderer);

    let mut collection = load_slc_file(Path::new(&slc_file)).into_iter();
    let mut reference_level = collection.next().unwrap();
    let mut level = reference_level.clone();

    let mut running = true;
    let mut event_pump = sdl_context.event_pump().unwrap();
    let mut skip = false;
    while running {
        if level.is_completed() || skip {
            match collection.next() {
                Some(l) => {
                    level = l;
                    reference_level = level.clone();
                    skip = false;
                },
                None => {
                    break;
                }
            }
        }
        drawer.draw(&level);

        match event_pump.wait_event() {
            Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                running = false
            },
            Event::KeyDown { keycode: Some(Keycode::Left), .. } => {
                level.step(game::Direction::Left);
            }
            Event::KeyDown { keycode: Some(Keycode::Right), .. } => {
                level.step(game::Direction::Right);
            }
            Event::KeyDown { keycode: Some(Keycode::Up), .. } => {
                level.step(game::Direction::Up);
            }
            Event::KeyDown { keycode: Some(Keycode::Down), .. } => {
                level.step(game::Direction::Down);
            }
            Event::KeyDown { keycode: Some(Keycode::R), .. } => {
                level = reference_level.clone();
            }
            Event::KeyDown { keycode: Some(Keycode::N), .. } => {
                skip = true;
            }
            _ => {}
        }
    }
    sdl2_image::quit();
}

/// Builds levels from a level collection file in the SLC format.
fn load_slc_file(path: &Path) -> Vec<Level> {
    let mut collection = Vec::new();
    let reader = BufReader::new(File::open(&path).unwrap());
    let mut parser = EventReader::new(reader);

    let mut level_str = String::new();
    let mut reading_level = false;
    for e in parser.events() {
        match e {
            XmlEvent::StartElement { name, .. } => {
                reading_level = name.local_name == "L";
            },
            XmlEvent::EndElement { name } => {
                if name.local_name == "Level" {
                    collection.push(Level::from_str(&level_str).unwrap());
                    level_str.clear();
                }
            },
            XmlEvent::Characters(ref data) => {
                if reading_level {
                    level_str.push_str(data);
                    level_str.push('\n');
                }
            }
            _ => {}
        }
    }

    collection
}
<commit_msg>Added options to set the window's resolution (width and height)<commit_after>// This file is part of sokoban-rs
// Copyright 2015 Sébastien Watteau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This is an implementation of Sokoban in Rust.

#[macro_use]
extern crate bitflags;
extern crate clap;
extern crate sdl2;
extern crate sdl2_image;
extern crate xml;

use std::path::Path;
use std::fs::File;
use std::io::BufReader;
use std::str::FromStr;
use clap::{App, Arg};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2_image::INIT_PNG;
use xml::reader::EventReader;
use xml::reader::events::*;

pub mod game;
pub mod render;

use game::Level;
use render::Drawer;

pub fn main() {
    let matches = App::new("sokoban-rs")
        .version("0.2.0")
        .author("Sébastien Watteau")
        .about("An implementation of Sokoban in the Rust programming language.")
        .arg(Arg::with_name("slc_file")
             .help("a Sokoban level collection (SLC) file")
             .required(true)
        )
        .arg(Arg::with_name("fullscreen")
             .help("Launches the game in fullscreen mode")
             .short("f")
             .long("fullscreen")
        )
        .arg(Arg::with_name("width")
             .help("The width of the window in pixels")
             .short("w")
             .long("width")
             .takes_value(true)
             .requires("height")
        )
        .arg(Arg::with_name("height")
             .help("The height of the window in pixels")
             .short("h")
             .long("height")
             .takes_value(true)
             .requires("width")
        )
        .get_matches();

    let slc_file = matches.value_of("slc_file").unwrap();

    let width: u32 = matches.value_of("width")
        .unwrap_or("1024")
        .parse()
        .unwrap_or_else(|err| {
            panic!("Couldn't parse width as an integer: {}", err);
        });
    let height: u32 = matches.value_of("height")
        .unwrap_or("768")
        .parse()
        .unwrap_or_else(|err| {
            panic!("Couldn't parse height as an integer: {}", err);
        });

    let sdl_context = sdl2::init().unwrap_or_else(|err| {
        println!("Failed to initialize an SDL context: {}", err);
        std::process::exit(1);
    });

    let video_subsystem = sdl_context.video().unwrap_or_else(|err| {
        println!("Failed to initialize the video subsystem: {}", err);
        std::process::exit(1);
    });

    sdl2_image::init(INIT_PNG);

    let mut window_builder = video_subsystem.window("sokoban-rs", width, height);
    if matches.is_present("fullscreen") {
        window_builder.fullscreen();
    } else {
        window_builder.position_centered();
    }

    let window = window_builder.opengl()
        .build()
        .unwrap_or_else(|err| {
            println!("Failed to create the window: {}", err);
            std::process::exit(1);
        });

    let renderer = window.renderer()
        .build()
        .unwrap_or_else(|err| {
            println!("Failed to get an SDL renderer for the main window: {}", err);
            std::process::exit(1);
        });

    let mut drawer = Drawer::new(renderer);

    let mut collection = load_slc_file(Path::new(&slc_file)).into_iter();
    let mut reference_level = collection.next().unwrap();
    let mut level = reference_level.clone();

    let mut running = true;
    let mut event_pump = sdl_context.event_pump().unwrap();
    let mut skip = false;
    while running {
        if level.is_completed() || skip {
            match collection.next() {
                Some(l) => {
                    level = l;
                    reference_level = level.clone();
                    skip = false;
                },
                None => {
                    break;
                }
            }
        }
        drawer.draw(&level);

        match event_pump.wait_event() {
            Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                running = false
            },
            Event::KeyDown { keycode: Some(Keycode::Left), .. } => {
                level.step(game::Direction::Left);
            }
            Event::KeyDown { keycode: Some(Keycode::Right), .. } => {
                level.step(game::Direction::Right);
            }
            Event::KeyDown { keycode: Some(Keycode::Up), .. } => {
                level.step(game::Direction::Up);
            }
            Event::KeyDown { keycode: Some(Keycode::Down), .. } => {
                level.step(game::Direction::Down);
            }
            Event::KeyDown { keycode: Some(Keycode::R), .. } => {
                level = reference_level.clone();
            }
            Event::KeyDown { keycode: Some(Keycode::N), .. } => {
                skip = true;
            }
            _ => {}
        }
    }
    sdl2_image::quit();
}

/// Builds levels from a level collection file in the SLC format.
fn load_slc_file(path: &Path) -> Vec<Level> {
    let mut collection = Vec::new();
    let reader = BufReader::new(File::open(&path).unwrap());
    let mut parser = EventReader::new(reader);

    let mut level_str = String::new();
    let mut reading_level = false;
    for e in parser.events() {
        match e {
            XmlEvent::StartElement { name, .. } => {
                reading_level = name.local_name == "L";
            },
            XmlEvent::EndElement { name } => {
                if name.local_name == "Level" {
                    collection.push(Level::from_str(&level_str).unwrap());
                    level_str.clear();
                }
            },
            XmlEvent::Characters(ref data) => {
                if reading_level {
                    level_str.push_str(data);
                    level_str.push('\n');
                }
            }
            _ => {}
        }
    }

    collection
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

#[macro_use]
extern crate serde_derive;

extern crate serde_json;

mod timesheet;

fn main() {
    let mut ts = timesheet::Session::new();

    /* Handle command line arguments with clap */
    let matches = clap_app!(trk =>
        (version: "0.1")
        (author: "Rafael B. <mediumendian@gmail.com>")
        (about: "Create timesheets from git history and meta info")
            (@arg CONFIG: -c --config +takes_value "[UNUSED] Sets a custom config file")
            (@arg debug: -d ... "[UNUSED] Sets the level of debugging information")

            (@subcommand beginperiod =>
                (about: "Begin period")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand beginsession =>
                    (about: "Begin session")
                    (version: "0.1")
                    (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand endperiod =>
                (about: "End period")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand endsession =>
                (about: "End session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand pause =>
                (about: "Pause current session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand proceed =>
                (about: "Proceed with currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand meta =>
                (about: "Proceed with currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg text: +required "Meta information about work")
            )
            (@subcommand commit =>
                (about: "add a commit to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg hash: +required "Commit hash id")
            )
            (@subcommand branch =>
                (about: "add a topic to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg name: +required "New branch name")
            )
       )
            .get_matches();

    // Gets a value for config if supplied by user, or defaults to "default.conf"
    let config = matches.value_of("config").unwrap_or("default.conf");
    println!("Value for config: {}", config);

    // You can handle information about subcommands by requesting their matches by name
    // TODO: Find a way to do this in a better way than an if sequence
    if matches.is_present("beginperiod") {
        ts.append_event(timesheet::Event::BeginPeriod)
    }
    if matches.is_present("beginsession") {
        ts.append_event(timesheet::Event::BeginSession)
    }
    if matches.is_present("endperiod") {
        ts.append_event(timesheet::Event::EndPeriod)
    }
    if matches.is_present("endsession") {
        ts.append_event(timesheet::Event::EndSession)
    }

    if matches.is_present("pause") {
        ts.append_event(timesheet::Event::Pause)
    }
    if matches.is_present("proceed") {
        ts.append_event(timesheet::Event::Proceed)
    }

    match matches.subcommand() {
        ("meta", Some(sub_input)) => {
            let metatext = sub_input.value_of("text").unwrap();
            ts.append_event(timesheet::Event::Meta { text: metatext.to_string() });
        }
        ("commit", Some(sub_input)) => {
            let commit_hash = sub_input.value_of("hash").unwrap();
            let hash_parsed = u64::from_str_radix(commit_hash, 16).unwrap();
            ts.append_event(timesheet::Event::Commit { hash: hash_parsed });
        }
        ("branch", Some(sub_input)) => {
            let branch_name = sub_input.value_of("name").unwrap();
            ts.append_event(timesheet::Event::Branch { name: branch_name.to_string() });
        }
        _ => {}
    }
    println!("{:?}", ts);

    // Convert the Point to a JSON string.
    let serialized = serde_json::to_string(&ts).unwrap();

    // Prints serialized = {"x":1,"y":2}
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a Point.
    let deserialized: timesheet::Session = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized = Point { x: 1, y: 2 }
    println!("deserialized = {:?}", deserialized);
}

<commit_msg>rename timesheet variable to session<commit_after>#[macro_use]
extern crate clap;

#[macro_use]
extern crate serde_derive;

extern crate serde_json;

mod timesheet;

fn main() {
    let mut session = timesheet::Session::new();

    /* Handle command line arguments with clap */
    let matches = clap_app!(trk =>
        (version: "0.1")
        (author: "Rafael B. <mediumendian@gmail.com>")
        (about: "Create timesheets from git history and meta info")
            (@arg CONFIG: -c --config +takes_value "[UNUSED] Sets a custom config file")
            (@arg debug: -d ... "[UNUSED] Sets the level of debugging information")

            (@subcommand beginperiod =>
                (about: "Begin period")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand beginsession =>
                    (about: "Begin session")
                    (version: "0.1")
                    (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand endperiod =>
                (about: "End period")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand endsession =>
                (about: "End session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand pause =>
                (about: "Pause current session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand proceed =>
                (about: "Proceed with currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand meta =>
                (about: "Proceed with currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg text: +required "Meta information about work")
            )
            (@subcommand commit =>
                (about: "add a commit to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg hash: +required "Commit hash id")
            )
            (@subcommand branch =>
                (about: "add a topic to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg name: +required "New branch name")
            )
       )
            .get_matches();

    // Gets a value for config if supplied by user, or defaults to "default.conf"
    let config = matches.value_of("config").unwrap_or("default.conf");
    println!("Value for config: {}", config);

    // You can handle information about subcommands by requesting their matches by name
    // TODO: Find a way to do this in a better way than an if sequence
    if matches.is_present("beginperiod") {
        session.append_event(timesheet::Event::BeginPeriod)
    }
    if matches.is_present("beginsession") {
        session.append_event(timesheet::Event::BeginSession)
    }
    if matches.is_present("endperiod") {
        session.append_event(timesheet::Event::EndPeriod)
    }
    if matches.is_present("endsession") {
        session.append_event(timesheet::Event::EndSession)
    }

    if matches.is_present("pause") {
        session.append_event(timesheet::Event::Pause)
    }
    if matches.is_present("proceed") {
        session.append_event(timesheet::Event::Proceed)
    }

    match matches.subcommand() {
        ("meta", Some(sub_input)) => {
            let metatext = sub_input.value_of("text").unwrap();
            session.append_event(timesheet::Event::Meta { text: metatext.to_string() });
        }
        ("commit", Some(sub_input)) => {
            let commit_hash = sub_input.value_of("hash").unwrap();
            let hash_parsed = u64::from_str_radix(commit_hash, 16).unwrap();
            session.append_event(timesheet::Event::Commit { hash: hash_parsed });
        }
        ("branch", Some(sub_input)) => {
            let branch_name = sub_input.value_of("name").unwrap();
            session.append_event(timesheet::Event::Branch { name: branch_name.to_string() });
        }
        _ => {}
    }
    println!("{:?}", session);

    // Convert the Point to a JSON string.
    let serialized = serde_json::to_string(&session).unwrap();

    // Prints serialized = {"x":1,"y":2}
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a Point.
    let deserialized: timesheet::Session = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized = Point { x: 1, y: 2 }
    println!("deserialized = {:?}", deserialized);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serenity;
extern crate rand;
extern crate typemap;

use std::env;
use std::result::Result as StdResult;
use typemap::Key;

use rand::Rng;
use rand::thread_rng;

use serenity::Result;
use serenity::client::{Client, Context};
use serenity::model;
use serenity::utils;

const PREFIX: &str = "f>";

command!(info(_ctx, msg, _args) {
    msg.channel_id.send_message(|cm| {
        cm.embed(|e| {
            e.title("lfalchbot")
             .colour(utils::Colour::blue())
             .description("(c) LFalch.com 2017")
             .footer(|f| f.text(serenity::constants::USER_AGENT))
        })
    }).unwrap();
});

command!(setgame(ctx, _msg, args) {
    ctx.set_game(model::Game::playing(&args.join(" ")));
});

command!(rpn(_ctx, msg, args) {
    match calculate(&args) {
        Ok(r) => msg.reply(&format!("Result: {}", r)),
        Err(e) => msg.reply(&format!("Error: {:?}", e))
    }.unwrap();
});

const CSGO_MSGS: [&str; 6] = [
    "Vi varmer op med en comp!",
    "Jeg er på!",
    "Jeg er mere til Call of Duty ..",
    "Сука блядь!",
    "-skyder en bot for at få dens Bizon-",
    "Mongoskrald!"
];

const BEARTOOTH: [&str; 3] = [
    "I’m not useless! I’m just the king of excuses!",
    "One life and one decision! Make sure it ends with you still living!",
    "Lorteskat på T-shirts."
];

const RESPONSES: [&str; 4] = [
    "Undskyld, kan ikke snakke lige nu :(",
    "Hey, kan jeg ringe igen senere?",
    "Hva' så, din noob!? :P",
    "Ad, hvem er du?"
];

const REDALERT: [&str; 4] = [
    "Your base is under a salt!",
    "Det gamle lortespil?",
    "Jeg er mere til CSGO.",
    "\"Mine depleted\" ..."
];

struct BotUser;

impl Key for BotUser {
    type Value = String;
}

fn main() {
    let token = env::var("DISCORD_TOKEN")
        .expect("Expected a token in the environment");
    let mut client = Client::new(&token);

    client.with_framework(|f| f
        .configure(|c| c.prefix(PREFIX))
        .command("ping", |c| c.exec_str("Pong!"))
        .on("info", info)
        .on("setgame", setgame)
        .on("rpn", rpn)
    );

    {
        let mut data = client.data.lock().unwrap();
        data.insert::<BotUser>(String::default());
    }

    client.on_ready(|ctx, ready| {
        println!("{} is connected!", ready.user.name);
        println!("Guilds:");
        for name in ready.guilds.iter().map(|g| g.id().get().unwrap().name) {
            println!("    {}", name);
        }
        {
            let mut data = ctx.data.lock().unwrap();
            *data.get_mut::<BotUser>().unwrap() = ready.user.tag();
        }
    });

    client.on_message(on_message);

    if let Err(why) = client.start() {
        println!("Client error: {:?}", why);
    }
}

fn send_random(chl: model::ChannelId, list: &[&str]) -> Result<model::Message> {
    let i = thread_rng().gen_range(0, list.len());
    chl.say(list[i])
}

macro_rules! joke {
    ($s:expr; $($trigger:expr),+; $bl:block) => (
        if $($s.contains($trigger))||* $bl
    );
    ($s:expr, $channel_id:expr; $($trigger:expr),+;; $joke:expr) => (
        joke!($s; $($trigger),*; {
            $channel_id.say($joke).unwrap();
        })
    );
    ($s:expr, $channel_id:expr; $($trigger:expr),+; $jokes:expr) => (
        joke!($s; $($trigger),*; {
            send_random($channel_id, &$jokes).unwrap();
        })
    );
}

fn on_message(ctx: Context, msg: model::Message) {
    if msg.author.bot {
        return
    }
    let s: String = msg.content.chars()
        .filter(|c| c.is_alphanumeric())
        .flat_map(|c|c.to_lowercase())
        .collect();

    joke!(s, msg.channel_id; "css", "source";; "Hvor er mine skins!?");
    joke!(s, msg.channel_id; "csgo", "counterstrike", "globaloffensive"; CSGO_MSGS);
    joke!(s, msg.channel_id; "mc", "minecraft";; "MINECRAFT!");
    joke!(s, msg.channel_id; "beartooth"; BEARTOOTH);
    joke!(s, msg.channel_id; "rep";; "Rep mig!");
    joke!(s, msg.channel_id; "ftl";; "Zoltan shield OP");
    joke!(s, msg.channel_id; "bindingofisaac";; "Mom OP");
    joke!(s, msg.channel_id; "meme";; "krydrede migmig'er");
    joke!(s, msg.channel_id; "gunsoficarus";; "Spillere online: 85");
    joke!(s, msg.channel_id; "doom";; "Rip and tear!");
    joke!(s, msg.channel_id; "dyinglight";; "Det dér Left 4 Dead-spil?");
    joke!(s; "english"; {
        msg.channel_id.send_message(|cm| {
            cm.embed(|e| {
                e.image("http://dev.lfalch.com/english.jpg")
            })
        }).unwrap();
    });
    joke!(s, msg.channel_id; "ra3", "redalert"; REDALERT);
    joke!(s, msg.channel_id; "rusland", "russia", "росси", "russisk",
        "russian", "русск", "russer";; "Communism is the ultimate goal of socialism.");

    let user = {
        ctx.data.lock().unwrap().get::<BotUser>().unwrap().clone()
    };
    if msg.mentions.iter().map(|u| u.tag()).any(|u| u == user) {
        send_random(msg.channel_id, &RESPONSES).unwrap();
    }
}

#[derive(Debug, Copy, Clone)]
pub enum RpnError<'a> {
    StackTooSmall,
    UnknownOperator(&'a str)
}

use RpnError::*;

pub fn calculate<'a, T: IntoIterator<Item=&'a String>>(operations: T) -> StdResult<f64, RpnError<'a>> {
    let mut stack = Vec::new();

    for operation in operations {
        if let Some(d) = operation.parse::<f64>().ok() {
            stack.push(d)
        }else{
            calc(operation, &mut stack)?
        }
    }

    stack.pop().ok_or(StackTooSmall)
}

fn calc<'a>(op: &'a str, stack: &mut Vec<f64>) -> StdResult<(), RpnError<'a>>{
    let res = match (stack.pop(), stack.pop()){
        (Some(op1), Some(op2)) => match op{
            "+"|"add" => op2 + op1,
            "-"|"sub" => op2 - op1,
            "/"|"div" => op2 / op1,
            "*"|"mul" => op2 * op1,
            "^"|"pow" => op2.powf(op1),
            "log" => op2.log(op1),
            "hypot" => op2.hypot(op1),
            "%"|"rem" => op2 % op1,
            "|"|"or" => (op2 as i64 | op1 as i64) as f64,
            "&"|"and" => (op2 as i64 & op1 as i64) as f64,
            "xor" => (op2  as i64 ^ op1 as i64) as f64,
            _ => return Err(UnknownOperator(op))
        },
        _ => return Err(StackTooSmall)
    };
    Ok(stack.push(res))
}
<commit_msg>Add joke<commit_after>#[macro_use]
extern crate serenity;
extern crate rand;
extern crate typemap;

use std::env;
use std::result::Result as StdResult;
use typemap::Key;

use rand::Rng;
use rand::thread_rng;

use serenity::Result;
use serenity::client::{Client, Context};
use serenity::model;
use serenity::utils;

const PREFIX: &str = "f>";

command!(info(_ctx, msg, _args) {
    msg.channel_id.send_message(|cm| {
        cm.embed(|e| {
            e.title("lfalchbot")
             .colour(utils::Colour::blue())
             .description("(c) LFalch.com 2017")
             .footer(|f| f.text(serenity::constants::USER_AGENT))
        })
    }).unwrap();
});

command!(setgame(ctx, _msg, args) {
    ctx.set_game(model::Game::playing(&args.join(" ")));
});

command!(rpn(_ctx, msg, args) {
    match calculate(&args) {
        Ok(r) => msg.reply(&format!("Result: {}", r)),
        Err(e) => msg.reply(&format!("Error: {:?}", e))
    }.unwrap();
});

const CSGO_MSGS: [&str; 6] = [
    "Vi varmer op med en comp!",
    "Jeg er på!",
    "Jeg er mere til Call of Duty ..",
    "Сука блядь!",
    "-skyder en bot for at få dens Bizon-",
    "Mongoskrald!"
];

const BEARTOOTH: [&str; 3] = [
    "I’m not useless! I’m just the king of excuses!",
    "One life and one decision! Make sure it ends with you still living!",
    "Lorteskat på T-shirts."
];

const RESPONSES: [&str; 4] = [
    "Undskyld, kan ikke snakke lige nu :(",
    "Hey, kan jeg ringe igen senere?",
    "Hva' så, din noob!? :P",
    "Ad, hvem er du?"
];

const REDALERT: [&str; 4] = [
    "Your base is under a salt!",
    "Det gamle lortespil?",
    "Jeg er mere til CSGO.",
    "\"Mine depleted\" ..."
];

struct BotUser;

impl Key for BotUser {
    type Value = String;
}

fn main() {
    let token = env::var("DISCORD_TOKEN")
        .expect("Expected a token in the environment");
    let mut client = Client::new(&token);

    client.with_framework(|f| f
        .configure(|c| c.prefix(PREFIX))
        .command("ping", |c| c.exec_str("Pong!"))
        .on("info", info)
        .on("setgame", setgame)
        .on("rpn", rpn)
    );

    {
        let mut data = client.data.lock().unwrap();
        data.insert::<BotUser>(String::default());
    }

    client.on_ready(|ctx, ready| {
        println!("{} is connected!", ready.user.name);
        println!("Guilds:");
        for name in ready.guilds.iter().map(|g| g.id().get().unwrap().name) {
            println!("    {}", name);
        }
        {
            let mut data = ctx.data.lock().unwrap();
            *data.get_mut::<BotUser>().unwrap() = ready.user.tag();
        }
    });

    client.on_message(on_message);

    if let Err(why) = client.start() {
        println!("Client error: {:?}", why);
    }
}

fn send_random(chl: model::ChannelId, list: &[&str]) -> Result<model::Message> {
    let i = thread_rng().gen_range(0, list.len());
    chl.say(list[i])
}

macro_rules! joke {
    ($s:expr; $($trigger:expr),+; $bl:block) => (
        if $($s.contains($trigger))||* $bl
    );
    ($s:expr, $channel_id:expr; $($trigger:expr),+;; $joke:expr) => (
        joke!($s; $($trigger),*; {
            $channel_id.say($joke).unwrap();
        })
    );
    ($s:expr, $channel_id:expr; $($trigger:expr),+; $jokes:expr) => (
        joke!($s; $($trigger),*; {
            send_random($channel_id, &$jokes).unwrap();
        })
    );
}

fn on_message(ctx: Context, msg: model::Message) {
    if msg.author.bot {
        return
    }
    let s: String = msg.content.chars()
        .filter(|c| c.is_alphanumeric())
        .flat_map(|c|c.to_lowercase())
        .collect();

    joke!(s, msg.channel_id; "css", "source";; "Hvor er mine skins!?");
    joke!(s, msg.channel_id; "csgo", "counterstrike", "globaloffensive"; CSGO_MSGS);
    joke!(s, msg.channel_id; "mc", "minecraft";; "MINECRAFT!");
    joke!(s, msg.channel_id; "beartooth"; BEARTOOTH);
    joke!(s, msg.channel_id; "rep";; "Rep mig!");
    joke!(s, msg.channel_id; "ftl";; "Zoltan shield OP");
    joke!(s, msg.channel_id; "bindingofisaac";; "Mom OP");
    joke!(s, msg.channel_id; "meme";; "krydrede migmig'er");
    joke!(s, msg.channel_id; "gunsoficarus";; "Spillere online: 85");
    joke!(s, msg.channel_id; "doom";; "Rip and tear!");
    joke!(s, msg.channel_id; "dyinglight";; "Det dér Left 4 Dead-spil?");
    joke!(s; "english"; {
        msg.channel_id.send_message(|cm| {
            cm.embed(|e| {
                e.image("http://dev.lfalch.com/english.jpg")
            })
        }).unwrap();
    });
    joke!(s, msg.channel_id; "warthunder", "wt", "thunder", "tankspil";; "Jeg hader World of Tanks!");
    joke!(s, msg.channel_id; "ra3", "redalert"; REDALERT);
    joke!(s, msg.channel_id; "rusland", "russia", "росси", "russisk",
        "russian", "русск", "russer";; "Communism is the ultimate goal of socialism.");

    let user = {
        ctx.data.lock().unwrap().get::<BotUser>().unwrap().clone()
    };
    if msg.mentions.iter().map(|u| u.tag()).any(|u| u == user) {
        send_random(msg.channel_id, &RESPONSES).unwrap();
    }
}

#[derive(Debug, Copy, Clone)]
pub enum RpnError<'a> {
    StackTooSmall,
    UnknownOperator(&'a str)
}

use RpnError::*;

pub fn calculate<'a, T: IntoIterator<Item=&'a String>>(operations: T) -> StdResult<f64, RpnError<'a>> {
    let mut stack = Vec::new();

    for operation in operations {
        if let Some(d) = operation.parse::<f64>().ok() {
            stack.push(d)
        }else{
            calc(operation, &mut stack)?
        }
    }

    stack.pop().ok_or(StackTooSmall)
}

fn calc<'a>(op: &'a str, stack: &mut Vec<f64>) -> StdResult<(), RpnError<'a>>{
    let res = match (stack.pop(), stack.pop()){
        (Some(op1), Some(op2)) => match op{
            "+"|"add" => op2 + op1,
            "-"|"sub" => op2 - op1,
            "/"|"div" => op2 / op1,
            "*"|"mul" => op2 * op1,
            "^"|"pow" => op2.powf(op1),
            "log" => op2.log(op1),
            "hypot" => op2.hypot(op1),
            "%"|"rem" => op2 % op1,
            "|"|"or" => (op2 as i64 | op1 as i64) as f64,
            "&"|"and" => (op2 as i64 & op1 as i64) as f64,
            "xor" => (op2  as i64 ^ op1 as i64) as f64,
            _ => return Err(UnknownOperator(op))
        },
        _ => return Err(StackTooSmall)
    };
    Ok(stack.push(res))
}
<|endoftext|>


<commit_before>
// YAML files as iptables configuration sources

// Rust Core
use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::process::exit;

// Crate.io
extern crate clap;
extern crate yaml_rust;

use clap::{Arg, App};
use yaml_rust::{Yaml, YamlLoader};

fn main() {

    let app = App::new("iptables-compose")
        .version("1.0.0")
        .global_version(true)
        .unified_help_message(true)
        .arg_required_else_help(true)
        .about("\nYAML files as iptables configuration sources")
        .arg(Arg::with_name("CONFIG")
             .multiple(true)
             .short("c")
             .long("config")
             .help("yaml file as iptables configuration source\n")
             .conflicts_with("license")
             .takes_value(true))
        .args_from_usage("-l --license 'Prints License'");

    let matches = app.get_matches();

    if let Some(ref f_paths) = matches.values_of("CONFIG") {
        for f_path in f_paths.iter() {
            read_yaml(f_path);
        }
    }

    if matches.is_present("license") {
        print_license();
    }

}

fn print_license() {
    let s:String = "
The MIT License (MIT)

Copyright (c) 2015 Samuel <sam@infinitely.io>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the \"Software\"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
".to_string();
    println!("{}", s);
}

fn read_yaml(f_path: &str) {

    // Create a path for yaml configuration file
    let path = Path::new(&f_path);
    let display = path.display();

    // Open file
    let mut file = match File::open(&path) {
        Err(_)   => {
            println!("Failed to open file \"{}\"", display);
            exit(1);
        },
        Ok(file) => file,
    };

    // Create buffer string and read content
    let mut buffer_str = String::new();
    match file.read_to_string(&mut buffer_str) {
        Err(_)  => {
            println!("Failed to read file \"{}\"", display);
            exit(1);
        },
        Ok(str) => str,
    };

    // Parse yaml file
    let yaml = match YamlLoader::load_from_str(&buffer_str) {
        Err(_)  => {
            println!("Failed to parse yaml data: \"{}\"", display);
            exit(1);
        },
        Ok(yaml) => yaml,
    };

    // Check if yaml file has no data, exits program if no data
    if yaml.is_empty() {
        println!("\"{}\" has no configuration(s)", display);
        exit(1);
    };

    // Validate configuration template format
    match yaml[0].as_hash() {
        Some(doc) => doc,
        None => {
            println!("Configuration template format is invalid: {}", display);
            exit(1);
        }
    };

    // Parse configuration from YAML configuration file
    parse_yaml(&yaml[0]);

}

fn parse_yaml(doc: &Yaml) {
    // Reset all rules
    reset_rules();
    // Read all rules from yaml
    match doc {
        // Parse if template data is a hash object
        &Yaml::Hash(ref h) => {
            for (k, v) in h {
                match k.as_str().unwrap() {
                    // Parse `filter` rules
                    "filter" | "FILTER" => parse_filter(k, v),
                    // Parse custom section
                    _ => parse_section(k, v)
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}

fn reset_rules() {
    let mut s:String = "# reset all rules".to_string();
    s.push_str("\niptables -F");
    s.push_str("\niptables -t nat -F");
    s.push_str("\niptables -t mangle -F");
    s.push_str("\niptables -X");
    println!("{}", s);
}

fn parse_section(id: &Yaml, doc: &Yaml) {
    match doc {
        &Yaml::Hash(ref h) => {
            println!("# {} rules", id.as_str().unwrap());
            for (k, v) in h {
                let k = k.as_str().unwrap();
                match k {
                    "ports" | "PORTS" => parse_ports(v),
                    _ => {
                        println!("Rules \"{}\" is not available", k);
                        exit(1);
                    }
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}

fn parse_ports(doc: &Yaml) {
    match doc {
        &Yaml::Array(ref v) => {
            for x in v {
                println!("{:?}", x);
            }
        },
        _ => {
            println!("Configuration \"ports\" format is invalid");
            exit(1);
        }
    }
}

fn parse_filter(id: &Yaml, doc: &Yaml) {
    // Check if filter section exists
    match doc {
        &Yaml::Hash(ref h) => {
            println!("# {} rules", id.as_str().unwrap());
            for (k, v) in h {
                let k = k.as_str().unwrap();
                let v = v.as_str().unwrap();
                match k {
                    "input" | "output" | "forward" | "INPUT" | "OUTPUT" | "FORWARD" => {
                        match v {
                            "drop" | "reject" | "accept" | "DROP" | "REJECT" | "ACCEPT" => println!("iptables -P {} {}", k, v),
                            _ => {
                                println!("Rules \"{}\" only accept options of \"drop\",\"reject\" or \"accept\"", k);
                                exit(1);
                            }
                        }
                    },
                    _ => println!("iptables -P {}", k)
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}
<commit_msg>Completed basic parsing and rules generating<commit_after>
// YAML files as iptables configuration sources

// Rust Core
use std::ascii::AsciiExt;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::exit;

// Crate.io
extern crate clap;
extern crate yaml_rust;

use clap::{Arg, App};
use yaml_rust::{Yaml, YamlLoader};

fn main() {

    let app = App::new("iptables-compose")
        .version("1.0.0")
        .global_version(true)
        .unified_help_message(true)
        .arg_required_else_help(true)
        .about("\nYAML files as iptables configuration sources")
        .arg(Arg::with_name("CONFIG")
             .multiple(true)
             .short("c")
             .long("config")
             .help("yaml file as iptables configuration source\n")
             .conflicts_with("license")
             .takes_value(true))
        .args_from_usage("-l --license 'Prints License'");

    let matches = app.get_matches();

    if let Some(ref f_paths) = matches.values_of("CONFIG") {
        for f_path in f_paths.iter() {
            read_yaml(f_path);
        }
    }

    if matches.is_present("license") {
        print_license();
    }

}

fn print_license() {
    let s:String = "
The MIT License (MIT)

Copyright (c) 2015 Samuel <sam@infinitely.io>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the \"Software\"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
".to_string();
    println!("{}", s);
}

fn read_yaml(f_path: &str) {

    // Create a path for yaml configuration file
    let path = Path::new(&f_path);
    let display = path.display();

    // Open file
    let mut file = match File::open(&path) {
        Err(_)   => {
            println!("Failed to open file \"{}\"", display);
            exit(1);
        },
        Ok(file) => file,
    };

    // Create buffer string and read content
    let mut buffer_str = String::new();
    match file.read_to_string(&mut buffer_str) {
        Err(_)  => {
            println!("Failed to read file \"{}\"", display);
            exit(1);
        },
        Ok(str) => str,
    };

    // Parse yaml file
    let yaml = match YamlLoader::load_from_str(&buffer_str) {
        Err(_)  => {
            println!("Failed to parse yaml data: \"{}\"", display);
            exit(1);
        },
        Ok(yaml) => yaml,
    };

    // Check if yaml file has no data, exits program if no data
    if yaml.is_empty() {
        println!("\"{}\" has no configuration(s)", display);
        exit(1);
    };

    // Validate configuration template format
    match yaml[0].as_hash() {
        Some(doc) => doc,
        None => {
            println!("Configuration template format is invalid: {}", display);
            exit(1);
        }
    };

    // Parse configuration from YAML configuration file
    parse_yaml(&yaml[0]);

}

fn parse_yaml(doc: &Yaml) {
    // Reset all rules
    reset_rules();
    // Read all rules from yaml
    match doc {
        // Parse if template data is a hash object
        &Yaml::Hash(ref h) => {
            for (k, v) in h {
                match k.as_str().unwrap() {
                    // Parse `filter` rules
                    "filter" | "FILTER" => parse_filter(k, v),
                    // Parse custom section
                    _ => parse_section(k, v)
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}

fn reset_rules() {
    let mut s:String = "# reset all rules".to_string();
    s.push_str("\niptables -F");
    s.push_str("\niptables -t nat -F");
    s.push_str("\niptables -t mangle -F");
    s.push_str("\niptables -X");
    s.push_str("\n# defaults settings and accepts your current connection");
    s.push_str("\niptables -t nat -A POSTROUTING -j MASQUERADE");
    s.push_str("\niptables -A INPUT -i lo -j ACCEPT");
    s.push_str("\niptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT");
    println!("{}", s);
}

fn parse_section(id: &Yaml, doc: &Yaml) {
    match doc {
        &Yaml::Hash(ref h) => {
            println!("# {} rules", id.as_str().unwrap());
            for (k, v) in h {
                let k = k.as_str().unwrap();
                match k {
                    "ports" | "PORTS" => parse_ports(v),
                    _ => {
                        println!("Rules \"{}\" is not available", k);
                        exit(1);
                    }
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}

fn parse_ports(doc: &Yaml) {
    match doc {
        &Yaml::Array(ref v) => {
            for x in v {
                parse_port_item(x);
            }
        },
        _ => {
            println!("Configuration \"ports\" format is invalid");
            exit(1);
        }
    }
}

fn parse_port_item(doc: &Yaml) {
    match doc {
        &Yaml::Hash(_) => {
            if doc["port"].is_badvalue() {
                println!("Port is not defined");
            }
            let port = doc["port"].as_i64();
            match port {
                Some(port) if port > -1 => (),
                _ => {
                    if port.unwrap() <= -1 {
                        println!("Port has to be greater or equals to 0");
                    } else {
                        println!("Port is not invalid");
                    }
                    exit(1);
                }
            }
            let mut cmd:String = "iptables".to_string();
            if ! doc["forward"].is_badvalue() {
                cmd.push_str(" -t nat -A PREROUTING");
            } else {
                let direction = doc["type"].as_str().unwrap_or("input");
                match direction {
                    "input" | "output" | "forward" | "INPUT" | "OUTPUT" | "FORWARD" => {
                        cmd.push_str(" -I ");
                        cmd.push_str(&direction.to_ascii_uppercase());
                    },
                    _ => {
                        println!("Direction value is invalid");
                        exit(1);
                    }
                }
            }
            match doc["subnet"] {
                Yaml::Array(ref v) => {
                    for x in v {
                        cmd.push_str(" -s ");
                        cmd.push_str(x.as_str().unwrap_or("0.0.0.0/0"));
                    }
                },
                _ => {}
            }
            let protocol = doc["protocol"].as_str().unwrap_or("tcp");
            match protocol {
                "tcp" | "udp" | "TCP" | "UDP" => {
                    cmd.push_str(" -p ");
                    cmd.push_str(protocol);
                    cmd.push_str(" -m ");
                    cmd.push_str(protocol);
                },
                _ => {
                    println!("Protocol value is invalid");
                    exit(1);
                }
            }
            cmd.push_str(" --dport ");
            cmd.push_str(&port.unwrap().to_string());
            if ! doc["forward"].is_badvalue() {
                let forward = doc["forward"].as_i64();
                match forward {
                    Some(forward) if forward > -1 => (),
                    _ => {
                        if forward.unwrap() <= -1 {
                            println!("forward port has to be greater or equals to 0");
                        } else {
                            println!("forward port is not invalid");
                        }
                        exit(1);
                    }
                }
                cmd.push_str(" -j REDIRECT --to-port ");
                cmd.push_str(&forward.unwrap().to_string());
            } else {
                let allow = doc["allow"].as_bool().unwrap_or(true);
                cmd.push_str(" -j ");
                match allow {
                    true => {
                        cmd.push_str("ACCEPT");
                    },
                    _ => {
                        cmd.push_str("DROP");
                    }
                }
            }
            println!("{}", cmd);
        },
        _ => {
            println!("Configuration \"ports\" item format is invalid");
            exit(1);
        }
    }
}

fn parse_filter(id: &Yaml, doc: &Yaml) {
    // Check if filter section exists
    match doc {
        &Yaml::Hash(ref h) => {
            println!("# {} rules", id.as_str().unwrap());
            for (k, v) in h {
                let k = k.as_str().unwrap();
                let v = v.as_str().unwrap();
                match k {
                    "input" | "output" | "forward" | "INPUT" | "OUTPUT" | "FORWARD" => {
                        match v {
                            "drop" | "reject" | "accept" | "DROP" | "REJECT" | "ACCEPT" => println!("iptables -P {} {}", k.to_ascii_uppercase(), v.to_ascii_uppercase()),
                            _ => {
                                println!("Rules \"{}\" only accept options of \"drop\",\"reject\" or \"accept\"", k);
                                exit(1);
                            }
                        }
                    },
                    _ => println!("iptables -P {}", k.to_ascii_uppercase())
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;
#[macro_use]
extern crate glium;
extern crate rand;

use std::collections::{HashSet, VecDeque};

use glium::Surface;
use glium::glutin;

/// Maximum frames per second.
const FPS: u32 = 30;

#[derive(Clone, Copy, Debug, PartialEq)]
enum Color {
    Red,
    Yellow,
    Green,
    LightBrown,
    Purple,
    Cyan,
    Blue,
    Fuchsia,
}

const COLORS: &[Color] = &[
    Color::Red,
    Color::Yellow,
    Color::Green,
    Color::LightBrown,
    Color::Purple,
    Color::Cyan,
    Color::Blue,
    Color::Fuchsia,
];

impl Color {
    fn to_rgb(self) -> (u8, u8, u8) {
        match self {
            Color::Red => (255, 0, 0),
            Color::Yellow => (255, 255, 0),
            Color::Green => (0, 176, 0),
            Color::LightBrown => (255, 204, 102),
            Color::Purple => (128, 0, 128),
            Color::Cyan => (0, 255, 255),
            Color::Blue => (0, 0, 255),
            Color::Fuchsia => (255, 0, 255),
        }
    }
}

impl rand::Rand for Color {
    fn rand<R: rand::Rng>(rng: &mut R) -> Self {
        *rng.choose(COLORS).unwrap()
    }
}

struct Grid {
    width: u8,
    height: u8,
    num_colors: u8,

    cells: Vec<Color>,
    population: Vec<u16>,

    max_clicks: u16,
    num_clicks: u16,
}

impl Grid {
    pub fn new(num_colors: u8, size: u8) -> Self {
        use rand::distributions::{IndependentSample, Range};
        let between = Range::new(0, num_colors as usize);
        let mut rng = rand::thread_rng();

        let cells: Vec<Color> = (0..size as u64 * size as u64)
            .map(|_| COLORS[between.ind_sample(&mut rng)])
            .collect();
        let max_clicks = 25 * 2 * size as u16 * num_colors as u16 / (2 * 14 * 6);
        let mut population = vec![0; COLORS.len()];
        for &c in &cells {
            population[c as usize] += 1;
        }

        Self {
            width: size,
            height: size,
            num_colors,

            cells,
            population,

            max_clicks,
            num_clicks: 0,
        }
    }

    fn index(&self, row: u8, column: u8) -> usize {
        self.width as usize * row as usize + column as usize
    }

    fn current_color(&self) -> Color {
        self.cells[0]
    }

    fn solved(&self) -> bool {
        let mut colors_present = 0;
        for &x in &self.population {
            if x > 0 {
                colors_present += 1;
            }
        }
        colors_present == 1
    }

    pub fn click(&mut self, row: u8, column: u8) -> bool {
        if row >= self.height || column >= self.width {
            return false;
        }

        let i = self.index(row, column);
        let new_color = self.cells[i];
        if self.current_color() == new_color {
            return false;
        }

        self.num_clicks += 1;
        self.flood(new_color);
        self.solved()
    }

    /// Flood the grid from the top left cell and return the number of cell which are connected to
    /// the top left cell by cells of indentical color.
    fn flood(&mut self, new_color: Color) {
        let current_color = self.current_color();

        let rows = self.height as usize;
        let columns = self.width as usize;

        let mut visited = HashSet::new();
        let mut queue = VecDeque::new();
        queue.push_back(0);

        while let Some(i) = queue.pop_front() {
            if self.cells[i] == new_color {
                continue;
            }
            let c = self.cells[i];
            self.population[c as usize] -= 1;
            self.cells[i] = new_color;
            self.population[new_color as usize] += 1;
            visited.insert(i);

            if i % columns > 0 && self.cells[i - 1] == current_color &&
                !visited.contains(&(i - 1))
            {
                queue.push_back(i - 1);
            }
            if i % columns < columns - 1 && self.cells[i + 1] == current_color &&
                !visited.contains(&(i + 1))
            {
                queue.push_back(i + 1);
            }
            if i >= columns && self.cells[i - columns] == current_color &&
                !visited.contains(&(i - columns))
            {
                queue.push_back(i - columns);
            }
            if i < (rows - 1) * columns && self.cells[i + columns] == current_color &&
                !visited.contains(&(i + columns))
            {
                queue.push_back(i + columns);
            }
        }
    }

    pub fn aspect_ratio(&self) -> f32 {
        self.height as f32 / self.width as f32
    }

    pub fn reset(&mut self) {
        use rand::distributions::{IndependentSample, Range};
        let between = Range::new(0, self.num_colors as usize);
        let mut rng = rand::thread_rng();

        self.cells.iter_mut().for_each(|x| {
            *x = COLORS[between.ind_sample(&mut rng)]
        });

        self.population = vec![0; COLORS.len()];
        for &c in &self.cells {
            self.population[c as usize] += 1;
        }
        self.num_clicks = 0;
    }
}

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

implement_vertex!(Vertex, position);

const TITLE: &str = "Flood-It";

const VERTEX_SHADER: &str = r#"
    #version 140

    in  vec2 position;
    out vec2 v_tex_coords;

    uniform vec2 size;
    uniform mat4 matrix;

    void main() {
        v_tex_coords = 0.5 * (position + vec2(1.0, 1.0));
        gl_Position = matrix * vec4(position, 0.0, 1.0);
    }
"#;

/// Map Color::* to RGB values.
const FRAGMENT_SHADER: &str = r#"
    #version 140

    in  vec2 v_tex_coords;
    out vec4 color;

    uniform sampler2D colors;

    void main() {
        color = texture(colors, v_tex_coords);
    }
"#;

fn parse_args() -> (u8, u8) {
    use clap::{App, Arg};

    let matches = App::new(TITLE)
        .author(env!("CARGO_PKG_AUTHORS"))
        .version(env!("CARGO_PKG_VERSION"))
        .arg(
            Arg::with_name("colors")
                .takes_value(true)
                .help("The number of different colors")
                .default_value("6"),
        )
        .arg(
            Arg::with_name("size")
                .takes_value(true)
                .help("The height and width of the grid")
                .default_value("14"),
        )
        .get_matches();

    let colors = {
        let tmp: u8 = matches
            .value_of("colors")
            .and_then(|x| x.parse().ok())
            .unwrap_or(6);
        if tmp < 3 || tmp > 8 {
            panic!("Flood-It only supports 3 through 8 (inclusive) colors.");
        } else {
            tmp
        }
    };
    let size: u8 = {
        let tmp: u8 = matches
            .value_of("size")
            .and_then(|x| x.parse().ok())
            .unwrap_or(14);
        if tmp < 2 {
            panic!("Flood-It needs a grid of at least 2x2 cells.");
        } else {
            tmp
        }
    };

    (colors, size)
}

fn main() {
    use std::borrow::Cow;
    use glium::glutin::{ContextBuilder, ElementState, EventsLoop, KeyboardInput, WindowBuilder};
    use glium::glutin::{Event, WindowEvent};
    use glium::texture::{RawImage2d, Texture2d, Texture2dDataSink};

    let (colors, size) = parse_args();

    let mut events_loop = EventsLoop::new();
    let window = WindowBuilder::new().with_title(TITLE);
    let context = ContextBuilder::new();
    let display = glium::Display::new(window, context, &events_loop).unwrap();

    let program = glium::Program::from_source(&display, VERTEX_SHADER, FRAGMENT_SHADER, None)
        .unwrap();

    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let params = glium::DrawParameters {
        backface_culling: glium::draw_parameters::BackfaceCullingMode::CullCounterClockwise,
        ..Default::default()
    };

    let mut grid = Grid::new(colors, size);
    let grid_aspect_ratio = grid.aspect_ratio();

    let mut cursor_position = (0.0, 0.0);

    let rect = generate_rectangle_vertices(-1.0, -1.0, 1.0, 1.0);
    let vertex_buffer = glium::VertexBuffer::new(&display, &rect).unwrap();

    main_loop(|| {
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        let (width, height) = target.get_dimensions();
        let screen_aspect_ratio = height as f32 / width as f32;

        let (ratio_x, ratio_y) = if screen_aspect_ratio < grid_aspect_ratio {
            (screen_aspect_ratio, 1.0)
        } else {
            (1.0, screen_aspect_ratio.recip())
        };

        // Create a texture representation of the colored grid
        let cell_colors: Vec<_> = grid.cells.iter().map(|x| x.to_rgb()).collect();
        let cell_image = RawImage2d::from_raw(
            Cow::from(cell_colors),
            grid.width as u32,
            grid.height as u32,
        );
        let cell_texture = Texture2d::new(&display, cell_image).unwrap();

        let uniforms =
            uniform! {
                size: (grid.width as f32, grid.height as f32),
                colors: cell_texture.sampled()
                    //.wrap_function(glium::uniforms::SamplerWrapFunction::Clamp)
                    .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
                matrix: [
                    [ratio_x, 0.0, 0.0, 0.0],
                    [0.0, -ratio_y, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0_f32],
                ]
        };
        target
            .draw(&vertex_buffer, &indices, &program, &uniforms, &params)
            .unwrap();

        target.finish().unwrap();

        let mut closed = false;
        events_loop.poll_events(|event| match event {
            Event::WindowEvent { event, .. } => {
                match event {
                    WindowEvent::Closed => closed = true,

                    WindowEvent::MouseMoved { position, .. } => cursor_position = position,

                    WindowEvent::KeyboardInput {
                        input: KeyboardInput {
                            virtual_keycode: Some(key),
                            state: ElementState::Released,
                            ..
                        },
                        ..
                    } => {
                        use glium::glutin::VirtualKeyCode;
                        match key {
                            VirtualKeyCode::Space | VirtualKeyCode::N | VirtualKeyCode::R => {
                                if grid.solved() {
                                    grid.reset();
                                }
                            }
                            _ => (),
                        }
                    }

                    WindowEvent::MouseInput {
                        state: glium::glutin::ElementState::Released,
                        button: glium::glutin::MouseButton::Left,
                        ..
                    } => {
                        let offsets = {
                            let offset = (width as f64 - height as f64).abs() / 2.0;
                            if screen_aspect_ratio < grid_aspect_ratio {
                                (offset, 0.0)
                            } else {
                                (0.0, offset)
                            }
                        };
                        if cursor_position.0 - offsets.0 >= 0.0 &&
                            cursor_position.1 - offsets.1 >= 0.0
                        {
                            let column = ((cursor_position.0 - offsets.0) * grid.width as f64 /
                                              (width as f64 - 2.0 * offsets.0))
                                .floor() as u8;
                            let row = ((cursor_position.1 - offsets.1) * grid.height as f64 /
                                           (height as f64 - 2.0 * offsets.1))
                                .floor() as u8;
                            if grid.click(row, column) {
                                if grid.num_clicks <= grid.max_clicks {
                                    println!(
                                        "You win! You used {} out of {} available moves.",
                                        grid.num_clicks,
                                        grid.max_clicks
                                    );
                                } else {
                                    println!(
                                        "You lose. You took {} moves but should have \
                                                 finished in {}.",
                                        grid.num_clicks,
                                        grid.max_clicks
                                    );
                                }
                            }
                        }
                    }
                    _ => (),
                }
            }
            _ => (),
        });
        !closed
    });
}

fn generate_rectangle_vertices(left: f32, bottom: f32, right: f32, top: f32) -> Vec<Vertex> {
    vec![
        Vertex { position: [left, bottom] },
        Vertex { position: [right, bottom] },
        Vertex { position: [left, top] },
        Vertex { position: [right, bottom] },
        Vertex { position: [right, top] },
        Vertex { position: [left, top] },
    ]
}

fn main_loop<F: FnMut() -> bool>(mut callback: F) {
    use std::time::{Duration, Instant};
    use std::thread;

    let one_frame = Duration::new(0, 10 ^ 9 / FPS + 1);
    let mut last_frame = Instant::now();

    while callback() {
        let now = Instant::now();
        let remaining = one_frame.checked_sub(now - last_frame).unwrap_or_default();
        last_frame = now;
        thread::sleep(remaining);
    }
}
<commit_msg>Press Q to quit<commit_after>extern crate clap;
#[macro_use]
extern crate glium;
extern crate rand;

use std::collections::{HashSet, VecDeque};

use glium::Surface;
use glium::glutin;

/// Maximum frames per second.
const FPS: u32 = 30;

#[derive(Clone, Copy, Debug, PartialEq)]
enum Color {
    Red,
    Yellow,
    Green,
    LightBrown,
    Purple,
    Cyan,
    Blue,
    Fuchsia,
}

const COLORS: &[Color] = &[
    Color::Red,
    Color::Yellow,
    Color::Green,
    Color::LightBrown,
    Color::Purple,
    Color::Cyan,
    Color::Blue,
    Color::Fuchsia,
];

impl Color {
    fn to_rgb(self) -> (u8, u8, u8) {
        match self {
            Color::Red => (255, 0, 0),
            Color::Yellow => (255, 255, 0),
            Color::Green => (0, 176, 0),
            Color::LightBrown => (255, 204, 102),
            Color::Purple => (128, 0, 128),
            Color::Cyan => (0, 255, 255),
            Color::Blue => (0, 0, 255),
            Color::Fuchsia => (255, 0, 255),
        }
    }
}

impl rand::Rand for Color {
    fn rand<R: rand::Rng>(rng: &mut R) -> Self {
        *rng.choose(COLORS).unwrap()
    }
}

struct Grid {
    width: u8,
    height: u8,
    num_colors: u8,

    cells: Vec<Color>,
    population: Vec<u16>,

    max_clicks: u16,
    num_clicks: u16,
}

impl Grid {
    pub fn new(num_colors: u8, size: u8) -> Self {
        use rand::distributions::{IndependentSample, Range};
        let between = Range::new(0, num_colors as usize);
        let mut rng = rand::thread_rng();

        let cells: Vec<Color> = (0..size as u64 * size as u64)
            .map(|_| COLORS[between.ind_sample(&mut rng)])
            .collect();
        let max_clicks = 25 * 2 * size as u16 * num_colors as u16 / (2 * 14 * 6);
        let mut population = vec![0; COLORS.len()];
        for &c in &cells {
            population[c as usize] += 1;
        }

        Self {
            width: size,
            height: size,
            num_colors,

            cells,
            population,

            max_clicks,
            num_clicks: 0,
        }
    }

    fn index(&self, row: u8, column: u8) -> usize {
        self.width as usize * row as usize + column as usize
    }

    fn current_color(&self) -> Color {
        self.cells[0]
    }

    fn solved(&self) -> bool {
        let mut colors_present = 0;
        for &x in &self.population {
            if x > 0 {
                colors_present += 1;
            }
        }
        colors_present == 1
    }

    pub fn click(&mut self, row: u8, column: u8) -> bool {
        if row >= self.height || column >= self.width {
            return false;
        }

        let i = self.index(row, column);
        let new_color = self.cells[i];
        if self.current_color() == new_color {
            return false;
        }

        self.num_clicks += 1;
        self.flood(new_color);
        self.solved()
    }

    /// Flood the grid from the top left cell and return the number of cell which are connected to
    /// the top left cell by cells of indentical color.
    fn flood(&mut self, new_color: Color) {
        let current_color = self.current_color();

        let rows = self.height as usize;
        let columns = self.width as usize;

        let mut visited = HashSet::new();
        let mut queue = VecDeque::new();
        queue.push_back(0);

        while let Some(i) = queue.pop_front() {
            if self.cells[i] == new_color {
                continue;
            }
            let c = self.cells[i];
            self.population[c as usize] -= 1;
            self.cells[i] = new_color;
            self.population[new_color as usize] += 1;
            visited.insert(i);

            if i % columns > 0 && self.cells[i - 1] == current_color &&
                !visited.contains(&(i - 1))
            {
                queue.push_back(i - 1);
            }
            if i % columns < columns - 1 && self.cells[i + 1] == current_color &&
                !visited.contains(&(i + 1))
            {
                queue.push_back(i + 1);
            }
            if i >= columns && self.cells[i - columns] == current_color &&
                !visited.contains(&(i - columns))
            {
                queue.push_back(i - columns);
            }
            if i < (rows - 1) * columns && self.cells[i + columns] == current_color &&
                !visited.contains(&(i + columns))
            {
                queue.push_back(i + columns);
            }
        }
    }

    pub fn aspect_ratio(&self) -> f32 {
        self.height as f32 / self.width as f32
    }

    pub fn reset(&mut self) {
        use rand::distributions::{IndependentSample, Range};
        let between = Range::new(0, self.num_colors as usize);
        let mut rng = rand::thread_rng();

        self.cells.iter_mut().for_each(|x| {
            *x = COLORS[between.ind_sample(&mut rng)]
        });

        self.population = vec![0; COLORS.len()];
        for &c in &self.cells {
            self.population[c as usize] += 1;
        }
        self.num_clicks = 0;
    }
}

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

implement_vertex!(Vertex, position);

const TITLE: &str = "Flood-It";

const VERTEX_SHADER: &str = r#"
    #version 140

    in  vec2 position;
    out vec2 v_tex_coords;

    uniform vec2 size;
    uniform mat4 matrix;

    void main() {
        v_tex_coords = 0.5 * (position + vec2(1.0, 1.0));
        gl_Position = matrix * vec4(position, 0.0, 1.0);
    }
"#;

/// Map Color::* to RGB values.
const FRAGMENT_SHADER: &str = r#"
    #version 140

    in  vec2 v_tex_coords;
    out vec4 color;

    uniform sampler2D colors;

    void main() {
        color = texture(colors, v_tex_coords);
    }
"#;

fn parse_args() -> (u8, u8) {
    use clap::{App, Arg};

    let matches = App::new(TITLE)
        .author(env!("CARGO_PKG_AUTHORS"))
        .version(env!("CARGO_PKG_VERSION"))
        .arg(
            Arg::with_name("colors")
                .takes_value(true)
                .help("The number of different colors")
                .default_value("6"),
        )
        .arg(
            Arg::with_name("size")
                .takes_value(true)
                .help("The height and width of the grid")
                .default_value("14"),
        )
        .get_matches();

    let colors = {
        let tmp: u8 = matches
            .value_of("colors")
            .and_then(|x| x.parse().ok())
            .unwrap_or(6);
        if tmp < 3 || tmp > 8 {
            panic!("Flood-It only supports 3 through 8 (inclusive) colors.");
        } else {
            tmp
        }
    };
    let size: u8 = {
        let tmp: u8 = matches
            .value_of("size")
            .and_then(|x| x.parse().ok())
            .unwrap_or(14);
        if tmp < 2 {
            panic!("Flood-It needs a grid of at least 2x2 cells.");
        } else {
            tmp
        }
    };

    (colors, size)
}

fn main() {
    use std::borrow::Cow;
    use glium::glutin::{ContextBuilder, ElementState, EventsLoop, KeyboardInput, WindowBuilder};
    use glium::glutin::{Event, WindowEvent};
    use glium::texture::{RawImage2d, Texture2d, Texture2dDataSink};

    let (colors, size) = parse_args();

    let mut events_loop = EventsLoop::new();
    let window = WindowBuilder::new().with_title(TITLE);
    let context = ContextBuilder::new();
    let display = glium::Display::new(window, context, &events_loop).unwrap();

    let program = glium::Program::from_source(&display, VERTEX_SHADER, FRAGMENT_SHADER, None)
        .unwrap();

    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let params = glium::DrawParameters {
        backface_culling: glium::draw_parameters::BackfaceCullingMode::CullCounterClockwise,
        ..Default::default()
    };

    let mut grid = Grid::new(colors, size);
    let grid_aspect_ratio = grid.aspect_ratio();

    let mut cursor_position = (0.0, 0.0);

    let rect = generate_rectangle_vertices(-1.0, -1.0, 1.0, 1.0);
    let vertex_buffer = glium::VertexBuffer::new(&display, &rect).unwrap();

    main_loop(|| {
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        let (width, height) = target.get_dimensions();
        let screen_aspect_ratio = height as f32 / width as f32;

        let (ratio_x, ratio_y) = if screen_aspect_ratio < grid_aspect_ratio {
            (screen_aspect_ratio, 1.0)
        } else {
            (1.0, screen_aspect_ratio.recip())
        };

        // Create a texture representation of the colored grid
        let cell_colors: Vec<_> = grid.cells.iter().map(|x| x.to_rgb()).collect();
        let cell_image = RawImage2d::from_raw(
            Cow::from(cell_colors),
            grid.width as u32,
            grid.height as u32,
        );
        let cell_texture = Texture2d::new(&display, cell_image).unwrap();

        let uniforms =
            uniform! {
                size: (grid.width as f32, grid.height as f32),
                colors: cell_texture.sampled()
                    //.wrap_function(glium::uniforms::SamplerWrapFunction::Clamp)
                    .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
                matrix: [
                    [ratio_x, 0.0, 0.0, 0.0],
                    [0.0, -ratio_y, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0_f32],
                ]
        };
        target
            .draw(&vertex_buffer, &indices, &program, &uniforms, &params)
            .unwrap();

        target.finish().unwrap();

        let mut closed = false;
        events_loop.poll_events(|event| match event {
            Event::WindowEvent { event, .. } => {
                match event {
                    WindowEvent::Closed => closed = true,

                    WindowEvent::MouseMoved { position, .. } => cursor_position = position,

                    WindowEvent::KeyboardInput {
                        input: KeyboardInput {
                            virtual_keycode: Some(key),
                            state: ElementState::Released,
                            ..
                        },
                        ..
                    } => {
                        use glium::glutin::VirtualKeyCode;
                        match key {
                            VirtualKeyCode::Space | VirtualKeyCode::N | VirtualKeyCode::R => {
                                if grid.solved() {
                                    grid.reset();
                                }
                            }
                            VirtualKeyCode::Q => closed = true,
                            _ => (),
                        }
                    }

                    WindowEvent::MouseInput {
                        state: glium::glutin::ElementState::Released,
                        button: glium::glutin::MouseButton::Left,
                        ..
                    } => {
                        let offsets = {
                            let offset = (width as f64 - height as f64).abs() / 2.0;
                            if screen_aspect_ratio < grid_aspect_ratio {
                                (offset, 0.0)
                            } else {
                                (0.0, offset)
                            }
                        };
                        if cursor_position.0 - offsets.0 >= 0.0 &&
                            cursor_position.1 - offsets.1 >= 0.0
                        {
                            let column = ((cursor_position.0 - offsets.0) * grid.width as f64 /
                                              (width as f64 - 2.0 * offsets.0))
                                .floor() as u8;
                            let row = ((cursor_position.1 - offsets.1) * grid.height as f64 /
                                           (height as f64 - 2.0 * offsets.1))
                                .floor() as u8;
                            if grid.click(row, column) {
                                if grid.num_clicks <= grid.max_clicks {
                                    println!(
                                        "You win! You used {} out of {} available moves.",
                                        grid.num_clicks,
                                        grid.max_clicks
                                    );
                                } else {
                                    println!(
                                        "You lose. You took {} moves but should have \
                                                 finished in {}.",
                                        grid.num_clicks,
                                        grid.max_clicks
                                    );
                                }
                            }
                        }
                    }
                    _ => (),
                }
            }
            _ => (),
        });
        !closed
    });
}

fn generate_rectangle_vertices(left: f32, bottom: f32, right: f32, top: f32) -> Vec<Vertex> {
    vec![
        Vertex { position: [left, bottom] },
        Vertex { position: [right, bottom] },
        Vertex { position: [left, top] },
        Vertex { position: [right, bottom] },
        Vertex { position: [right, top] },
        Vertex { position: [left, top] },
    ]
}

fn main_loop<F: FnMut() -> bool>(mut callback: F) {
    use std::time::{Duration, Instant};
    use std::thread;

    let one_frame = Duration::new(0, 10 ^ 9 / FPS + 1);
    let mut last_frame = Instant::now();

    while callback() {
        let now = Instant::now();
        let remaining = one_frame.checked_sub(now - last_frame).unwrap_or_default();
        last_frame = now;
        thread::sleep(remaining);
    }
}
<|endoftext|>