<commit_before>#![feature(globs, macro_rules, phase)]

extern crate debug;
extern crate piston;
extern crate sdl2;
extern crate sdl2_game_window;
extern crate gfx;
#[phase(plugin)]
extern crate gfx_macros;
extern crate image;
extern crate libc;
extern crate cgmath;
extern crate time;

use sdl2_game_window::GameWindowSDL2 as Window;
use piston::input;
use piston::cam;
use piston::{
    AssetStore,
    GameIterator,
    GameIteratorSettings,
    GameWindow,
    GameWindowSettings,
    Input,
    Render,
    Update,
};

use array::*;
use texture::Texture;

pub mod array;
pub mod cube;
pub mod fps_counter;
pub mod shader;
pub mod texture;
pub mod vecmath;

fn main() {
    let mut window = Window::new(
        piston::shader_version::opengl::OpenGL_3_3,
        GameWindowSettings {
            title: "Hematite".to_string(),
            size: [854, 480],
            fullscreen: false,
            exit_on_esc: true,
        }
    );
    let (mut device, frame) = window.gfx();

    let asset_store = AssetStore::from_folder("../assets");

    // Load texture.
    let texture = asset_store.path("texture.png").unwrap();
    let texture = Texture::from_path(&texture, &mut device).unwrap();

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

    let mut renderer = shader::Renderer::new(device, frame, texture.tex);

    renderer.set_projection(cam::CameraPerspective {
        fov: 70.0,
        near_clip: 0.1,
        far_clip: 1000.0,
        aspect_ratio: {
            let (w, h) = window.get_size();
            (w as f32) / (h as f32)
        }
    }.projection());

    let mut first_person_settings = cam::FirstPersonSettings::default();
    first_person_settings.speed_horizontal = 8.0;
    first_person_settings.speed_vertical = 4.0;
    let mut first_person = cam::FirstPerson::new(
        0.5, 0.5, 4.0,
        first_person_settings
    );

    // Disable V-Sync.
    sdl2::video::gl_set_swap_interval(0);

    let mut fps_counter = fps_counter::FPSCounter::new();

    let mut capture_cursor = false;
    println!("Press C to capture mouse");
    let mut extrapolate_time = false;
    println!("Press X to extrapolate time");
    let mut update_cam_render = false;
    println!("Press Z to update first person camera when rendering");

    let buffer = {
        let mut tri = vec![];
        for face in cube::FaceIterator::new() {
            let xyz = face.vertices([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
            let [u0, v1, u1, v0] = [0.0, 0.75, 0.25, 1.0];
            let uv = [
                [u1, v0],
                [u0, v0],
                [u0, v1],
                [u1, v1]
            ];
            let v = [
                (xyz[0], uv[0], [1.0, 0.0, 0.0]),
                (xyz[1], uv[1], [0.0, 1.0, 0.0]),
                (xyz[2], uv[2], [0.0, 0.0, 1.0]),
                (xyz[3], uv[3], [1.0, 0.0, 1.0])
            ].map(|(xyz, uv, rgb)| shader::Vertex { xyz: xyz, uv: uv, rgb: rgb });

            // Split the clockwise quad into two clockwise triangles.
            tri.push_all([v[0], v[1], v[2]]);
            tri.push_all([v[2], v[3], v[0]]);
        }
        renderer.create_buffer(tri.as_slice())
    };

    let mut last_render = time::precise_time_ns();
    let mut events = GameIterator::new(&mut window, &game_iter_settings);
    for e in events {
        match e {
            Render(_args) => {
                if update_cam_render {
                    let now = time::precise_time_ns();
                    let dt = (now - last_render) as f64 / 1_000_000_000.0f64;
                    first_person.update(dt);
                    last_render = now;
                }

                renderer.set_view(first_person.camera(
                        _args.ext_dt * if extrapolate_time { 0.0 } else { 1.0 }
                    ).orthogonal());
                renderer.reset();
                renderer.render(buffer);
                renderer.end_frame();

                let fps = fps_counter.update();
                let title = format!("Hematite @ {}FPS", fps);
                events.game_window.window.set_title(title.as_slice());
            }
            Input(input::KeyPress { key: input::keyboard::C }) => {
                println!("Turned cursor capture {}",
                    if capture_cursor { "off" } else { "on" });
                capture_cursor = !capture_cursor;

                events.game_window.capture_cursor(capture_cursor);
            },
            Input(input::KeyPress { key: input::keyboard::X }) => {
                println!("Turned extrapolated time {}",
                    if extrapolate_time { "off" } else { "on" });
                extrapolate_time = !extrapolate_time;
            },
            Input(input::KeyPress { key: input::keyboard::Z }) => {
                println!("Turned update camera on render {}",
                    if update_cam_render { "off" } else { "on" });
                update_cam_render = !update_cam_render;
            },
            Input(input::MouseRelativeMove { .. }) => {
                if !capture_cursor {
                    // Don't send the mouse event to the FPS controller.
                    continue;
                }
            }
            _ => {}
        }

        // Camera controller.
        match e {
            Input(ref args) => first_person.input(args),
            Update(piston::UpdateArgs { dt, .. }) =>
                if !update_cam_render { first_person.update(dt) },
            _ => {}
        }
    }
}
<commit_msg>Fixed bug in extrapolated time<commit_after>#![feature(globs, macro_rules, phase)]

extern crate debug;
extern crate piston;
extern crate sdl2;
extern crate sdl2_game_window;
extern crate gfx;
#[phase(plugin)]
extern crate gfx_macros;
extern crate image;
extern crate libc;
extern crate cgmath;
extern crate time;

use sdl2_game_window::GameWindowSDL2 as Window;
use piston::input;
use piston::cam;
use piston::{
    AssetStore,
    GameIterator,
    GameIteratorSettings,
    GameWindow,
    GameWindowSettings,
    Input,
    Render,
    Update,
};

use array::*;
use texture::Texture;

pub mod array;
pub mod cube;
pub mod fps_counter;
pub mod shader;
pub mod texture;
pub mod vecmath;

fn main() {
    let mut window = Window::new(
        piston::shader_version::opengl::OpenGL_3_3,
        GameWindowSettings {
            title: "Hematite".to_string(),
            size: [854, 480],
            fullscreen: false,
            exit_on_esc: true,
        }
    );
    let (mut device, frame) = window.gfx();

    let asset_store = AssetStore::from_folder("../assets");

    // Load texture.
    let texture = asset_store.path("texture.png").unwrap();
    let texture = Texture::from_path(&texture, &mut device).unwrap();

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

    let mut renderer = shader::Renderer::new(device, frame, texture.tex);

    renderer.set_projection(cam::CameraPerspective {
        fov: 70.0,
        near_clip: 0.1,
        far_clip: 1000.0,
        aspect_ratio: {
            let (w, h) = window.get_size();
            (w as f32) / (h as f32)
        }
    }.projection());

    let mut first_person_settings = cam::FirstPersonSettings::default();
    first_person_settings.speed_horizontal = 8.0;
    first_person_settings.speed_vertical = 4.0;
    let mut first_person = cam::FirstPerson::new(
        0.5, 0.5, 4.0,
        first_person_settings
    );

    // Disable V-Sync.
    sdl2::video::gl_set_swap_interval(0);

    let mut fps_counter = fps_counter::FPSCounter::new();

    let mut capture_cursor = false;
    println!("Press C to capture mouse");
    let mut extrapolate_time = false;
    println!("Press X to extrapolate time");
    let mut update_cam_render = false;
    println!("Press Z to update first person camera when rendering");

    let buffer = {
        let mut tri = vec![];
        for face in cube::FaceIterator::new() {
            let xyz = face.vertices([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
            let [u0, v1, u1, v0] = [0.0, 0.75, 0.25, 1.0];
            let uv = [
                [u1, v0],
                [u0, v0],
                [u0, v1],
                [u1, v1]
            ];
            let v = [
                (xyz[0], uv[0], [1.0, 0.0, 0.0]),
                (xyz[1], uv[1], [0.0, 1.0, 0.0]),
                (xyz[2], uv[2], [0.0, 0.0, 1.0]),
                (xyz[3], uv[3], [1.0, 0.0, 1.0])
            ].map(|(xyz, uv, rgb)| shader::Vertex { xyz: xyz, uv: uv, rgb: rgb });

            // Split the clockwise quad into two clockwise triangles.
            tri.push_all([v[0], v[1], v[2]]);
            tri.push_all([v[2], v[3], v[0]]);
        }
        renderer.create_buffer(tri.as_slice())
    };

    let mut last_render = time::precise_time_ns();
    let mut events = GameIterator::new(&mut window, &game_iter_settings);
    for e in events {
        match e {
            Render(_args) => {
                if update_cam_render {
                    let now = time::precise_time_ns();
                    let dt = (now - last_render) as f64 / 1_000_000_000.0f64;
                    first_person.update(dt);
                    last_render = now;
                }

                renderer.set_view(first_person.camera(
                        _args.ext_dt * if extrapolate_time { 1.0 } else { 0.0 }
                    ).orthogonal());
                renderer.reset();
                renderer.render(buffer);
                renderer.end_frame();

                let fps = fps_counter.update();
                let title = format!("Hematite @ {}FPS", fps);
                events.game_window.window.set_title(title.as_slice());
            }
            Input(input::KeyPress { key: input::keyboard::C }) => {
                println!("Turned cursor capture {}",
                    if capture_cursor { "off" } else { "on" });
                capture_cursor = !capture_cursor;

                events.game_window.capture_cursor(capture_cursor);
            },
            Input(input::KeyPress { key: input::keyboard::X }) => {
                println!("Turned extrapolated time {}",
                    if extrapolate_time { "off" } else { "on" });
                extrapolate_time = !extrapolate_time;
            },
            Input(input::KeyPress { key: input::keyboard::Z }) => {
                println!("Turned update camera on render {}",
                    if update_cam_render { "off" } else { "on" });
                update_cam_render = !update_cam_render;
            },
            Input(input::MouseRelativeMove { .. }) => {
                if !capture_cursor {
                    // Don't send the mouse event to the FPS controller.
                    continue;
                }
            }
            _ => {}
        }

        // Camera controller.
        match e {
            Input(ref args) => first_person.input(args),
            Update(piston::UpdateArgs { dt, .. }) =>
                if !update_cam_render { first_person.update(dt) },
            _ => {}
        }
    }
}
<|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 rand::Rand for Color {
    fn rand<R: rand::Rng>(rng: &mut R) -> Self {
        *rng.choose(COLORS).unwrap()
    }
}

struct Grid {
    width: u8,
    height: 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,

            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 {
        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
    }
}

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

implement_vertex!(Vertex, color, position);

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

const VERTEX_SHADER: &str = r#"
    #version 140
    in vec2 position;
    in float color;
    uniform mat4 matrix;
    out float c;
    void main() {
        c = color;
        gl_Position = matrix * vec4(position, 0.0, 1.0);
    }
"#;

/// Map Color::* to RGB values.
const FRAGMENT_SHADER: &str = r#"
    #version 140
    in float c;
    out vec4 color;
    void main() {
        if (c <= 0.0)
            color = vec4(1.0, 0.0, 0.0, 1.0);
        else if (c <= 1.0)
            color = vec4(1.0, 1.0, 0.0, 1.0);
        else if (c <= 2.0)
            color = vec4(0.0, 0.69, 0.0, 1.0);
        else if (c <= 3.0)
            color = vec4(1.0, 0.8, 0.4, 1.0);
        else if (c <= 4.0)
            color = vec4(0.5, 0.0, 0.5, 1.0);
        else if (c <= 5.0)
            color = vec4(0.0, 1.0, 1.0, 1.0);
        else if (c <= 6.0)
            color = vec4(0.0, 0.0, 1.0, 1.0);
        else if (c <= 7.0)
            color = vec4(1.0, 0.0, 1.0, 1.0);
        else
            // Should never happen
            color = vec4(1.0, 1.0, 1.0, 1.0);
    }
"#;

fn main() {
    use clap::{App, Arg};
    use glium::glutin::{ContextBuilder, EventsLoop, WindowBuilder};
    use glutin::{Event, WindowEvent};

    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
        }
    };

    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);

    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())
        };

        let uniforms =
            uniform! {
            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],
            ]
        };

        let mut triangles = vec![];

        for i in 0..grid.cells.len() {
            let x = (i % grid.width as usize) as f32;
            let y = (i / grid.width as usize) as f32;

            let cell_size = 2.0 / grid.height.max(grid.width) as f32;

            let mut quad = generate_rectangle_vertices(
                x * cell_size - 1.0,
                y * cell_size - 1.0,
                (x + 1.0) * cell_size - 1.0,
                (y + 1.0) * cell_size - 1.0,
                grid.cells[i],
            );
            triangles.append(&mut quad);
        }

        let vertex_buffer = glium::VertexBuffer::new(&display, &triangles).unwrap();
        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::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,
    color: Color,
) -> Vec<Vertex> {
    let color = color as u32 as f32;
    vec![
        Vertex {
            color,
            position: [left, bottom],
        },
        Vertex {
            color,
            position: [right, bottom],
        },
        Vertex {
            color,
            position: [left, top],
        },
        Vertex {
            color,
            position: [right, bottom],
        },
        Vertex {
            color,
            position: [right, top],
        },
        Vertex {
            color,
            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>When a game is solved, press Space to start a new game<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 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 {
        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 {
    color: f32,
    position: [f32; 2],
}

implement_vertex!(Vertex, color, position);

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

const VERTEX_SHADER: &str = r#"
    #version 140
    in vec2 position;
    in float color;
    uniform mat4 matrix;
    out float c;
    void main() {
        c = color;
        gl_Position = matrix * vec4(position, 0.0, 1.0);
    }
"#;

/// Map Color::* to RGB values.
const FRAGMENT_SHADER: &str = r#"
    #version 140
    in float c;
    out vec4 color;
    void main() {
        if (c <= 0.0)
            color = vec4(1.0, 0.0, 0.0, 1.0);
        else if (c <= 1.0)
            color = vec4(1.0, 1.0, 0.0, 1.0);
        else if (c <= 2.0)
            color = vec4(0.0, 0.69, 0.0, 1.0);
        else if (c <= 3.0)
            color = vec4(1.0, 0.8, 0.4, 1.0);
        else if (c <= 4.0)
            color = vec4(0.5, 0.0, 0.5, 1.0);
        else if (c <= 5.0)
            color = vec4(0.0, 1.0, 1.0, 1.0);
        else if (c <= 6.0)
            color = vec4(0.0, 0.0, 1.0, 1.0);
        else if (c <= 7.0)
            color = vec4(1.0, 0.0, 1.0, 1.0);
        else
            // Should never happen
            color = vec4(1.0, 1.0, 1.0, 1.0);
    }
"#;

fn main() {
    use clap::{App, Arg};
    use glium::glutin::{ContextBuilder, ElementState, EventsLoop, KeyboardInput, WindowBuilder};
    use glutin::{Event, WindowEvent};

    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
        }
    };

    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);

    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())
        };

        let uniforms =
            uniform! {
            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],
            ]
        };

        let mut triangles = vec![];

        for i in 0..grid.cells.len() {
            let x = (i % grid.width as usize) as f32;
            let y = (i / grid.width as usize) as f32;

            let cell_size = 2.0 / grid.height.max(grid.width) as f32;

            let mut quad = generate_rectangle_vertices(
                x * cell_size - 1.0,
                y * cell_size - 1.0,
                (x + 1.0) * cell_size - 1.0,
                (y + 1.0) * cell_size - 1.0,
                grid.cells[i],
            );
            triangles.append(&mut quad);
        }

        let vertex_buffer = glium::VertexBuffer::new(&display, &triangles).unwrap();
        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,
    color: Color,
) -> Vec<Vertex> {
    let color = color as u32 as f32;
    vec![
        Vertex {
            color,
            position: [left, bottom],
        },
        Vertex {
            color,
            position: [right, bottom],
        },
        Vertex {
            color,
            position: [left, top],
        },
        Vertex {
            color,
            position: [right, bottom],
        },
        Vertex {
            color,
            position: [right, top],
        },
        Vertex {
            color,
            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|>


<commit_before>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate itertools;
extern crate walkdir;

use std::env;
use std::process::{Command, Output};
use clap::{App, SubCommand, AppSettings};
use itertools::Itertools;
use walkdir::{DirEntry, WalkDirIterator};

fn announce(banner: &str) {
    let mut line = String::new();
    for _ in 0..banner.len() {
        line.push('-');
    }
    println!("{}\n{}\n{}", line, banner, line);
}

fn print_ident(buf: Vec<u8>) {
    for line in String::from_utf8_lossy(&buf[..]).lines() {
        println!("        {}", line);
    }
}

fn report_output(output: Output) {
    if output.status.success() {
        print_ident(output.stdout);
    } else {
        print_ident(output.stderr);
    }
    // I am still not sure what is more idiomatic - the 'if' above or the 'match' below
    //
    // match output.status.success() {
    //     true => print_ident(output.stdout),
    //     false => print_ident(output.stderr),
    // }
    println!("");
}

const CARGO: &'static str = "cargo";
const MIN_DEPTH: usize = 1;
const MAX_DEPTH: usize = 1;

fn main() {

    let matches = App::new(CARGO)
                      .bin_name(CARGO)
                      .version(crate_version!())
                      .about("Run cargo command on multiple crates")
                      .setting(AppSettings::SubcommandRequired)
                      .setting(AppSettings::ArgRequiredElseHelp)
                      .subcommand(SubCommand::with_name("multi")
                                      .version(crate_version!())
                                      .setting(AppSettings::ArgRequiredElseHelp)
                                      .setting(AppSettings::TrailingVarArg)
                                      .arg_from_usage("<cmd>... 'cargo command to run'"))
                      .get_matches();

    let mut cargo_cmd = Command::new(CARGO);
    let mut banner = String::from("Executing ") + CARGO;

    if let Some(arg_cmd) = matches.subcommand_matches("multi")
                                  .and_then(|m| m.values_of("cmd")) {
        for arg in arg_cmd {
            cargo_cmd.arg(arg);
            banner = banner + " " + arg;
        }
    }

    let is_crate = |e: &DirEntry| e.path().join("Cargo.toml").exists();
    let display_path = |e: &DirEntry| println!("{}:", e.file_name().to_string_lossy());
    let execute = |e: DirEntry| cargo_cmd.current_dir(e.path()).output().ok();

    if let Ok(cwd) = env::current_dir() {
        announce(&banner);
        walkdir::WalkDir::new(cwd)
            .min_depth(MIN_DEPTH)
            .max_depth(MAX_DEPTH)
            .into_iter()
            .filter_entry(is_crate)
            .filter_map(|e| e.ok())
            .inspect(display_path)
            .filter_map(execute)
            .foreach(report_output);
    }
}
<commit_msg>rustfmt<commit_after>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate itertools;
extern crate walkdir;

use std::env;
use std::process::{Command, Output};
use clap::{App, SubCommand, AppSettings};
use itertools::Itertools;
use walkdir::{DirEntry, WalkDirIterator};

fn announce(banner: &str) {
    let mut line = String::new();
    for _ in 0..banner.len() {
        line.push('-');
    }
    println!("{}\n{}\n{}", line, banner, line);
}

fn print_ident(buf: Vec<u8>) {
    for line in String::from_utf8_lossy(&buf[..]).lines() {
        println!("        {}", line);
    }
}

fn report_output(output: Output) {
    if output.status.success() {
        print_ident(output.stdout);
    } else {
        print_ident(output.stderr);
    }
    // I am still not sure what is more idiomatic - the 'if' above or the 'match' below
    //
    // match output.status.success() {
    //     true => print_ident(output.stdout),
    //     false => print_ident(output.stderr),
    // }
    println!("");
}

const CARGO: &'static str = "cargo";
const MIN_DEPTH: usize = 1;
const MAX_DEPTH: usize = 1;

fn main() {

    let matches = App::new(CARGO)
        .bin_name(CARGO)
        .version(crate_version!())
        .about("Run cargo command on multiple crates")
        .setting(AppSettings::SubcommandRequired)
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("multi")
            .version(crate_version!())
            .setting(AppSettings::ArgRequiredElseHelp)
            .setting(AppSettings::TrailingVarArg)
            .arg_from_usage("<cmd>... 'cargo command to run'"))
        .get_matches();

    let mut cargo_cmd = Command::new(CARGO);
    let mut banner = String::from("Executing ") + CARGO;

    if let Some(arg_cmd) = matches.subcommand_matches("multi")
        .and_then(|m| m.values_of("cmd")) {
        for arg in arg_cmd {
            cargo_cmd.arg(arg);
            banner = banner + " " + arg;
        }
    }

    let is_crate = |e: &DirEntry| e.path().join("Cargo.toml").exists();
    let display_path = |e: &DirEntry| println!("{}:", e.file_name().to_string_lossy());
    let execute = |e: DirEntry| cargo_cmd.current_dir(e.path()).output().ok();

    if let Ok(cwd) = env::current_dir() {
        announce(&banner);
        walkdir::WalkDir::new(cwd)
            .min_depth(MIN_DEPTH)
            .max_depth(MAX_DEPTH)
            .into_iter()
            .filter_entry(is_crate)
            .filter_map(|e| e.ok())
            .inspect(display_path)
            .filter_map(execute)
            .foreach(report_output);
    }
}
<|endoftext|>


<commit_before>#![allow(unknown_lints)]
#![allow(while_let_on_iterator)]

// For a performance boost on Linux
// #![feature(alloc_system)]
// extern crate alloc_system;

extern crate fnv;
extern crate glob;
extern crate liner;
extern crate smallvec;
extern crate smallstring;

#[cfg(all(unix, not(target_os = "redox")))]
extern crate users as users_unix;

#[macro_use] mod parser;
mod builtins;
mod shell;
mod ascii_helpers;
mod types;

use std::io::{stderr, Write, ErrorKind};
use builtins::Builtin;
use shell::Shell;

fn main() {
    let builtins = Builtin::map();
    let mut shell = Shell::new(&builtins);
    shell.evaluate_init_file();

    if "1" == shell.variables.get_var_or_empty("HISTORY_FILE_ENABLED") {
        match shell.context.history.load_history() {
            Ok(()) => {
                // pass
            }
            Err(ref err) if err.kind() == ErrorKind::NotFound => {
                let history_filename = shell.variables.get_var_or_empty("HISTORY_FILE");
                let _ = writeln!(stderr(), "ion: failed to find history file {}: {}", history_filename, err);
            },
            Err(err) => {
                let _ = writeln!(stderr(), "ion: failed to load history: {}", err);
            }
        }
    }
    shell.execute();
}
<commit_msg>Fix loading the history file for liner<commit_after>#![allow(unknown_lints)]
#![allow(while_let_on_iterator)]

// For a performance boost on Linux
// #![feature(alloc_system)]
// extern crate alloc_system;

extern crate fnv;
extern crate glob;
extern crate liner;
extern crate smallvec;
extern crate smallstring;

#[cfg(all(unix, not(target_os = "redox")))]
extern crate users as users_unix;

#[macro_use] mod parser;
mod builtins;
mod shell;
mod ascii_helpers;
mod types;

use std::io::{stderr, Write, ErrorKind};
use builtins::Builtin;
use shell::Shell;

fn main() {
    let builtins = Builtin::map();
    let mut shell = Shell::new(&builtins);
    shell.evaluate_init_file();

    if "1" == shell.variables.get_var_or_empty("HISTORY_FILE_ENABLED") {
        shell.context.history.set_file_name(shell.variables.get_var("HISTORY_FILE"));
        match shell.context.history.load_history() {
            Ok(()) => {
                // pass
            }
            Err(ref err) if err.kind() == ErrorKind::NotFound => {
                let history_filename = shell.variables.get_var_or_empty("HISTORY_FILE");
                let _ = writeln!(stderr(), "ion: failed to find history file {}: {}", history_filename, err);
            },
            Err(err) => {
                let _ = writeln!(stderr(), "ion: failed to load history: {}", err);
            }
        }
    }
    shell.execute();
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate futures;

use futures::future::Future;

use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};

struct ReplicateService;

impl Service for ReplicateService {
    // boilerplate hooking up hyper's server types
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    // The future representing the eventual Response your call will
    // resolve to. This can change to whatever Future you need.
    type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;

    fn call(&self, _request: Request) -> Self::Future {
        // println!("Request debug {:?}", _request);

        // We're currently ignoring the Request
        // And returning an 'ok' Future, which means it's ready
        // immediately, and build a Response with the 'PHRASE' body.
        let body = format!("Method: {method}\nURI: {uri}", method=_request.method(), uri=_request.uri());

        Box::new(futures::future::ok(
            Response::new()
                .with_header(ContentLength(body.len() as u64))
                .with_body(body)
        ))
    }
}

fn main() {
    let addr = "127.0.0.1:8080".parse().unwrap();
    let server = Http::new().bind(&addr, || Ok(ReplicateService)).unwrap();
    server.run().unwrap();
}
<commit_msg>Code cleaup<commit_after>extern crate hyper;
extern crate futures;

use futures::future::Future;

use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};
use hyper::Error;

struct ReplicateService;

impl Service for ReplicateService {
    // boilerplate hooking up hyper's server types
    type Request = Request;
    type Response = Response;
    type Error = Error;

    // The future representing the eventual Response your call will
    // resolve to. This can change to whatever Future you need.
    type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;

    fn call(&self, request: Request) -> Self::Future {
        // println!("Request debug {:?}", _request);

        // We're currently ignoring the Request
        // And returning an 'ok' Future, which means it's ready
        // immediately, and build a Response with the 'PHRASE' body.
        let body = format!("Method: {method}\nURI: {uri}", method=request.method(), uri=request.uri());

        Box::new(futures::future::ok(
            Response::new()
                .with_header(ContentLength(body.len() as u64))
                .with_body(body)
        ))
    }
}

fn main() {
    let addr = "127.0.0.1:8080".parse().unwrap();
    let server = Http::new().bind(&addr, || Ok(ReplicateService)).unwrap();
    server.run().unwrap();
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate hyper;
extern crate rustc_serialize;

use std::io::Read;

use docopt::Docopt;
use hyper::Client;
use hyper::Server;
use hyper::header::Connection;
use hyper::server::Request;
use hyper::server::Response;
use hyper::uri::RequestUri;

const USAGE: &'static str = "
Tarnish

Usage:
  tarnish <proxy>
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_proxy: String,
}

fn proxy(proxy_request: Request, proxy_response: Response) {
    let args: Args = Docopt::new(USAGE)
                         .and_then(|d| d.decode())
                         .unwrap_or_else(|e| e.exit());

    let uri_base = "http://".to_string() + &args.arg_proxy;

    let client = Client::new();

    match (proxy_request.method, proxy_request.uri) {
        (hyper::Get, RequestUri::AbsolutePath(ref path)) => {
            let uri = uri_base + &path;
            let mut response = client.get(&uri)
                                     .header(Connection::close())
                                     .send()
                                     .unwrap();

            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            proxy_response.send(&body.into_bytes()).unwrap();
        }
        _ => {}
    }
}

fn main() {
    Server::http("127.0.0.1:3000").unwrap().handle(proxy);
}
<commit_msg>Don't parse args in the request handler<commit_after>extern crate docopt;
extern crate hyper;
extern crate rustc_serialize;

use std::io::Read;

use docopt::Docopt;
use hyper::Client;
use hyper::Server;
use hyper::header::Connection;
use hyper::server::Handler;
use hyper::server::Request;
use hyper::server::Response;
use hyper::uri::RequestUri;

const USAGE: &'static str = "
Tarnish

Usage:
  tarnish <proxy>
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_proxy: String,
}

struct ProxyHandler {
    uri_base: String,
}

impl Handler for ProxyHandler {
    fn handle(&self, proxy_request: Request, proxy_response: Response) {
        let client = Client::new();

        match (proxy_request.method, proxy_request.uri) {
            (hyper::Get, RequestUri::AbsolutePath(ref path)) => {
                let uri = self.uri_base.clone() + &path;
                let mut response = client.get(&uri)
                                         .header(Connection::close())
                                         .send()
                                         .unwrap();

                let mut body = String::new();
                response.read_to_string(&mut body).unwrap();

                proxy_response.send(&body.into_bytes()).unwrap();
            }
            _ => {}
        }
    }
}

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

    let uri_base = "http://".to_string() + &args.arg_proxy;

    Server::http("127.0.0.1:3000").unwrap().handle(ProxyHandler {
        uri_base: uri_base,
    });
}
<|endoftext|>


<commit_before>extern crate lancelot;
extern crate log;

use failure::{Error, Fail};
use log::{error, info, trace};
use std::env;
use std::process;

use lancelot::arch::*;
use lancelot::workspace::Workspace;

// TODO: removeme
use goblin::{Object};

#[derive(Debug, Fail)]
pub enum MainError {
    #[fail(display = "foo")]
    Foo,
}

pub struct Config {
    pub mode: u8,
    pub filename: String,
}

impl Config {
    pub fn from_args(args: env::Args) -> Result<Config, &'static str> {
        let args: Vec<String> = args.collect();

        if args.len() < 3 {
            return Err("not enough arguments: provide `lancelot.exe 32|64 /path/to/input`");
        }

        let mode = match args[1].as_ref() {
            "32" => 32,
            "64" => 64,
            _ => return Err("invalid mode, pick one of `32` or `64`"),
        };

        let filename = args[2].clone();
        trace!("config: parsed filename: {:?}", filename);

        Ok(Config { mode, filename })
    }
}

pub fn setup_logging(_args: &Config) {
    simplelog::TermLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
        .expect("failed to setup logging");
}

pub fn run(args: &Config) -> Result<(), Error> {
    info!("filename: {:?}", args.filename);

    if args.mode == 32 {
        let mut ws = Workspace::<Arch32>::from_file(&args.filename)?.load()?;
    } else if args.mode == 64 {
        let mut ws = Workspace::<Arch64>::from_file(&args.filename)?.load()?;

        if ws.loader.get_name() == "Windows/64/PE" {
            if let Ok(Object::PE(pe)) = Object::parse(&ws.buf) {
                let entry = pe.entry;
                let exports: Vec<usize> = pe.exports.iter()
                    // re-exports are simply strings that point to a `DLL.export_name` ASCII string.
                    // therefore, they're not functions/code.
                    .filter(|exp| exp.reexport.is_none())
                    .map(|exp| exp.rva).collect();

                info!("PE entry: {:#x}", entry);
                ws.make_insn(entry as i64);
                ws.analyze()?;
                for export in exports.iter() {
                    info!("export: {:#x}", export);
                    ws.make_insn(*export as i64);
                    ws.analyze()?;
                }
            }
        }
    }

    Ok(())
}

fn main() {
    let args = Config::from_args(env::args()).unwrap_or_else(|err| {
        eprintln!("error parsing arguments: {}", err);
        process::exit(1);
    });

    setup_logging(&args);

    if let Err(e) = run(&args) {
        error!("{:?}", e)
    }
}
<commit_msg>main: consume command Result<commit_after>extern crate lancelot;
extern crate log;

use failure::{Error, Fail};
use log::{error, info, trace};
use std::env;
use std::process;

use lancelot::arch::*;
use lancelot::workspace::Workspace;

// TODO: removeme
use goblin::{Object};

#[derive(Debug, Fail)]
pub enum MainError {
    #[fail(display = "foo")]
    Foo,
}

pub struct Config {
    pub mode: u8,
    pub filename: String,
}

impl Config {
    pub fn from_args(args: env::Args) -> Result<Config, &'static str> {
        let args: Vec<String> = args.collect();

        if args.len() < 3 {
            return Err("not enough arguments: provide `lancelot.exe 32|64 /path/to/input`");
        }

        let mode = match args[1].as_ref() {
            "32" => 32,
            "64" => 64,
            _ => return Err("invalid mode, pick one of `32` or `64`"),
        };

        let filename = args[2].clone();
        trace!("config: parsed filename: {:?}", filename);

        Ok(Config { mode, filename })
    }
}

pub fn setup_logging(_args: &Config) {
    simplelog::TermLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
        .expect("failed to setup logging");
}

pub fn run(args: &Config) -> Result<(), Error> {
    info!("filename: {:?}", args.filename);

    if args.mode == 32 {
        let mut ws = Workspace::<Arch32>::from_file(&args.filename)?.load()?;
    } else if args.mode == 64 {
        let mut ws = Workspace::<Arch64>::from_file(&args.filename)?.load()?;

        if ws.loader.get_name() == "Windows/64/PE" {
            if let Ok(Object::PE(pe)) = Object::parse(&ws.buf) {
                let entry = pe.entry;
                let exports: Vec<usize> = pe.exports.iter()
                    // re-exports are simply strings that point to a `DLL.export_name` ASCII string.
                    // therefore, they're not functions/code.
                    .filter(|exp| exp.reexport.is_none())
                    .map(|exp| exp.rva).collect();

                info!("PE entry: {:#x}", entry);
                ws.make_insn(entry as i64)?;
                ws.analyze()?;
                for export in exports.iter() {
                    info!("export: {:#x}", export);
                    ws.make_insn(*export as i64)?;
                    ws.analyze()?;
                }
            }
        }
    }

    Ok(())
}

fn main() {
    let args = Config::from_args(env::args()).unwrap_or_else(|err| {
        eprintln!("error parsing arguments: {}", err);
        process::exit(1);
    });

    setup_logging(&args);

    if let Err(e) = run(&args) {
        error!("{:?}", e)
    }
}
<|endoftext|>


<commit_before>#![crate_type = "bin"]
#![deny(warnings, missing_doc)]
#![feature(macro_rules, phase)]

//! A rust-compress application that allows testing of implemented
//! algorithms and their combinations using a simple command line.
//! Example invocations:
//! echo -n "abracadabra" | ./app bwt | xxd
//! echo "banana" | ./app bwt | ./app -d

#[phase(plugin, link)] extern crate log;
extern crate compress;

use std::collections::HashMap;
use std::{io, os, str};
use compress::{bwt, lz4};
use compress::entropy::ari;

static MAGIC    : u32   = 0x73632172;   //=r!cs

struct Config {
    exe_name: String,
    methods: Vec<String>,
    block_size: uint,
    decompress: bool,
}

impl Config {
    fn query(args: &[String]) -> Config {
        let mut cfg = Config {
            exe_name: args[0].clone(),
            methods: Vec::new(),
            block_size: 1<<16,
            decompress: false,
        };
        let mut handlers: HashMap<&str,|&str, &mut Config|> = HashMap::new();
        handlers.insert("d",|_, cfg| { cfg.decompress = true; });
        handlers.insert("block",|b, cfg| {
            cfg.block_size = from_str(b).unwrap();
        });

        for arg in args.iter().skip(1) {
			let slice = arg.as_slice();
            if slice.starts_with("-") {
                match handlers.mut_iter().find(|&(&k,_)| slice.slice_from(1).starts_with(k)) {
                    Some((k,h)) => (*h)(slice.slice_from(1+k.len()), &mut cfg),
                    None => println!("Warning: unrecognized option: {}", arg.as_slice()),
                }
            }else {
                cfg.methods.push(arg.to_string());
            }
        }
        cfg
    }
}

struct Pass {
    encode: |Box<Writer>, &Config|: 'static -> Box<io::Writer>,
    decode: |Box<Reader>, &Config|: 'static -> Box<io::Reader>,
    info: String,
}

/// main entry point
pub fn main() {
    let mut passes: HashMap<String,Pass> = HashMap::new();
    passes.insert("dummy".to_str(), Pass {
        encode: |w,_| w,
        decode: |r,_| r,
        info: "pass-through".to_str(),
    });
    passes.insert("ari".to_str(), Pass {
        encode: |w,_c| {
            box ari::ByteEncoder::new(w) as Box<Writer>
        },
        decode: |r,_c| {
            box ari::ByteDecoder::new(r) as Box<Reader>
        },
        info: "Adaptive arithmetic byte coder".to_str(),
    });
    passes.insert("bwt".to_str(), Pass {
        encode: |w,c| {
            box bwt::Encoder::new(w, c.block_size) as Box<Writer>
        },
        decode: |r,_c| {
            box bwt::Decoder::new(r, true) as Box<Reader>
        },
        info: "Burrows-Wheeler Transformation".to_str(),
    });
    passes.insert("mtf".to_str(), Pass {
        encode: |w,_c| {
            box bwt::mtf::Encoder::new(w) as Box<Writer>
        },
        decode: |r,_c| {
            box bwt::mtf::Decoder::new(r) as Box<Reader>
        },
        info: "Move-To-Front Transformation".to_str(),
    });
    /* // looks like we are missing the encoder implementation
    passes.insert(~"flate", Pass {
        encode: |w,_c| {
            ~flate::Encoder::new(w, true) as ~Writer
        },
        decode: |r,_c| {
            ~flate::Decoder::new(r, true) as ~Reader
        },
        info: ~"Standardized Ziv-Lempel + Huffman variant",
    });*/
    passes.insert("lz4".to_str(), Pass {
        encode: |w,_c| {
            box lz4::Encoder::new(w) as Box<Writer>
        },
        decode: |r,_c| { // LZ4 decoder seem to work
            box lz4::Decoder::new(r) as Box<Reader>
        },
        info: "Ziv-Lempel derivative, focused at speed".to_str(),
    });

    let config = Config::query(os::args().as_slice());
    let mut input = io::stdin();
    let mut output = io::stdout();
    if config.decompress {
        assert!(config.methods.is_empty(), "Decompression methods are set in stone");
        match input.read_le_u32() {
            Ok(magic) if magic != MAGIC => {
                error!("Input is not a rust-compress archive");
                return
            },
            Err(e) => {
                error!("Unable to read input: {}", e.to_str());
                return
            },
            _ => () //OK
        }
        let methods = Vec::from_fn(input.read_u8().unwrap() as uint, |_| {
            let len = input.read_u8().unwrap() as uint;
            let bytes = input.read_exact(len).unwrap();
            str::from_utf8(bytes.as_slice()).unwrap().to_string()
        });
        let mut rsum: Box<Reader> = box input;
        for met in methods.iter() {
            info!("Found pass {}", *met);
            match passes.find_mut(met) {
                Some(pa) => rsum = (pa.decode)(rsum, &config),
                None => fail!("Pass is not implemented"),
            }
        }
        io::util::copy(&mut rsum, &mut output).unwrap();
    }else if config.methods.is_empty() {
        println!("rust-compress test application");
        println!("Usage:");
        println!("\t{} <options> <method1> .. <methodN> <input >output", config.exe_name);
        println!("Options:");
        println!("\t-d (to decompress)");
        println!("\t-block<N> (BWT block size)");
        println!("Passes:");
        for (name,pa) in passes.iter() {
            println!("\t{} = {}", *name, pa.info);
        }
    }else {
        output.write_le_u32(MAGIC).unwrap();
        output.write_u8(config.methods.len() as u8).unwrap();
        for met in config.methods.iter() {
            output.write_u8(met.len() as u8).unwrap();
            output.write_str(met.as_slice()).unwrap();
        }
        let mut wsum: Box<Writer> = box output;
        for met in config.methods.iter() {
            match passes.find_mut(met) {
                Some(pa) => wsum = (pa.encode)(wsum, &config),
                None => fail!("Pass {} is not implemented", *met)
            }
        }
        io::util::copy(&mut input, &mut wsum).unwrap();
        wsum.flush().unwrap();
    }
}
<commit_msg>Update the `main` executable<commit_after>#![crate_type = "bin"]
#![deny(warnings, missing_doc)]
#![feature(macro_rules, phase)]

//! A rust-compress application that allows testing of implemented
//! algorithms and their combinations using a simple command line.
//! Example invocations:
//! echo -n "abracadabra" | ./app bwt | xxd
//! echo "banana" | ./app bwt | ./app -d

#[phase(plugin, link)] extern crate log;
extern crate compress;

use std::collections::HashMap;
use std::{io, os, str};
use compress::{bwt, lz4};
use compress::entropy::ari;

static MAGIC    : u32   = 0x73632172;   //=r!cs

struct Config {
    exe_name: String,
    methods: Vec<String>,
    block_size: uint,
    decompress: bool,
}

impl Config {
    fn query(args: &[String]) -> Config {
        let mut cfg = Config {
            exe_name: args[0].clone(),
            methods: Vec::new(),
            block_size: 1<<16,
            decompress: false,
        };
        let mut handlers: HashMap<&str,|&str, &mut Config|> = HashMap::new();
        handlers.insert("d",|_, cfg| { cfg.decompress = true; });
        handlers.insert("block",|b, cfg| {
            cfg.block_size = from_str(b).unwrap();
        });

        for arg in args.iter().skip(1) {
			let slice = arg.as_slice();
            if slice.starts_with("-") {
                match handlers.mut_iter().find(|&(&k,_)| slice.slice_from(1).starts_with(k)) {
                    Some((k,h)) => (*h)(slice.slice_from(1+k.len()), &mut cfg),
                    None => println!("Warning: unrecognized option: {}", arg.as_slice()),
                }
            }else {
                cfg.methods.push(arg.to_string());
            }
        }
        cfg
    }
}

struct Pass {
    encode: |Box<Writer>, &Config|: 'static -> Box<io::Writer>,
    decode: |Box<Reader>, &Config|: 'static -> Box<io::Reader>,
    info: String,
}

/// main entry point
pub fn main() {
    let mut passes: HashMap<String,Pass> = HashMap::new();
    passes.insert("dummy".to_string(), Pass {
        encode: |w,_| w,
        decode: |r,_| r,
        info: "pass-through".to_string(),
    });
    passes.insert("ari".to_string(), Pass {
        encode: |w,_c| {
            box ari::ByteEncoder::new(w) as Box<Writer>
        },
        decode: |r,_c| {
            box ari::ByteDecoder::new(r) as Box<Reader>
        },
        info: "Adaptive arithmetic byte coder".to_string(),
    });
    passes.insert("bwt".to_string(), Pass {
        encode: |w,c| {
            box bwt::Encoder::new(w, c.block_size) as Box<Writer>
        },
        decode: |r,_c| {
            box bwt::Decoder::new(r, true) as Box<Reader>
        },
        info: "Burrows-Wheeler Transformation".to_string(),
    });
    passes.insert("mtf".to_string(), Pass {
        encode: |w,_c| {
            box bwt::mtf::Encoder::new(w) as Box<Writer>
        },
        decode: |r,_c| {
            box bwt::mtf::Decoder::new(r) as Box<Reader>
        },
        info: "Move-To-Front Transformation".to_string(),
    });
    /* // looks like we are missing the encoder implementation
    passes.insert(~"flate", Pass {
        encode: |w,_c| {
            ~flate::Encoder::new(w, true) as ~Writer
        },
        decode: |r,_c| {
            ~flate::Decoder::new(r, true) as ~Reader
        },
        info: ~"Standardized Ziv-Lempel + Huffman variant",
    });*/
    passes.insert("lz4".to_string(), Pass {
        encode: |w,_c| {
            box lz4::Encoder::new(w) as Box<Writer>
        },
        decode: |r,_c| { // LZ4 decoder seem to work
            box lz4::Decoder::new(r) as Box<Reader>
        },
        info: "Ziv-Lempel derivative, focused at speed".to_string(),
    });

    let config = Config::query(os::args().as_slice());
    let mut input = io::stdin();
    let mut output = io::stdout();
    if config.decompress {
        assert!(config.methods.is_empty(), "Decompression methods are set in stone");
        match input.read_le_u32() {
            Ok(magic) if magic != MAGIC => {
                error!("Input is not a rust-compress archive");
                return
            },
            Err(e) => {
                error!("Unable to read input: {}", e.to_string());
                return
            },
            _ => () //OK
        }
        let methods = Vec::from_fn(input.read_u8().unwrap() as uint, |_| {
            let len = input.read_u8().unwrap() as uint;
            let bytes = input.read_exact(len).unwrap();
            str::from_utf8(bytes.as_slice()).unwrap().to_string()
        });
        let mut rsum: Box<Reader> = box input;
        for met in methods.iter() {
            info!("Found pass {}", *met);
            match passes.find_mut(met) {
                Some(pa) => rsum = (pa.decode)(rsum, &config),
                None => fail!("Pass is not implemented"),
            }
        }
        io::util::copy(&mut rsum, &mut output).unwrap();
    }else if config.methods.is_empty() {
        println!("rust-compress test application");
        println!("Usage:");
        println!("\t{} <options> <method1> .. <methodN> <input >output", config.exe_name);
        println!("Options:");
        println!("\t-d (to decompress)");
        println!("\t-block<N> (BWT block size)");
        println!("Passes:");
        for (name,pa) in passes.iter() {
            println!("\t{} = {}", *name, pa.info);
        }
    }else {
        output.write_le_u32(MAGIC).unwrap();
        output.write_u8(config.methods.len() as u8).unwrap();
        for met in config.methods.iter() {
            output.write_u8(met.len() as u8).unwrap();
            output.write_str(met.as_slice()).unwrap();
        }
        let mut wsum: Box<Writer> = box output;
        for met in config.methods.iter() {
            match passes.find_mut(met) {
                Some(pa) => wsum = (pa.encode)(wsum, &config),
                None => fail!("Pass {} is not implemented", *met)
            }
        }
        io::util::copy(&mut input, &mut wsum).unwrap();
        wsum.flush().unwrap();
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate clap;
extern crate url;
extern crate rss;
extern crate hyper;
extern crate curl; // https://docs.rs/curl/0.4.6/curl/easy/
extern crate rusqlite;

mod schema;
mod common;

use std::process;
use clap::{App, ArgMatches};
use schema::*;
use url::Url;
use rusqlite::Connection;
use curl::easy::Easy;

fn main() {
    let lock_socket = common::create_app_lock(12345); // https://rosettacode.org/wiki/Category:Rust
    // Sqlite database
    let database_url = "/tmp/rusty.sqlite3";
    let connection = match Connection::open(database_url) {
        Ok(connection) => connection,
        Err(e) => {
            println!(_log, "Error database connection: {} {}", e, database_url;);
            process::exit(1)
        }
    };
    init(&connection); // Initialize database

    // Clap
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml).get_matches();

    match matches.subcommand() {
        ("subscribe", Some(sub_matches)) => subscribe(sub_matches, &connection, _log),
        ("unsubscribe", Some(sub_matches)) => unsubscribe(sub_matches, &connection),
        ("list", Some(_)) => list(&connection, _log),
        ("update", Some(sub_matches)) => {
            let feed_id = sub_matches.value_of("id").unwrap_or("0").parse::<i64>().unwrap();
            update(sub_matches, &connection, _log, feed_id);
        },
        ("pending", Some(_)) => pending(&connection),
        ("download", Some(_)) => download(&connection),
        ("download-dir", Some(sub_matches)) => downloaddir(sub_matches, &connection),
        ("", None) => println!("No subcommand was used"),
        _ => println!("No!"),
    }

    common::remove_app_lock(lock_socket);
}

fn init(connection: &Connection) {
    // execute only the first query if there are several queries. So we need to used split()
    let split = include_str!("sql/init.sql").split(";");
    for s in split {
        if s.trim() == "" {
            continue;
        }
        connection.execute(s, &[]).unwrap();
    }
}

fn subscribe(args: &ArgMatches, connection: &Connection, _log: slog::Logger) {
    match Url::parse(args.value_of("url").unwrap()) {
        Ok(url) => {
            let subscription = Subscription { id: 0, url: url.as_str().to_string(), label: String::new()};
            connection.execute("insert into subscription (url, label) values (?1, '')", &[&subscription.url]).unwrap();
            println!("Subscribing to: {}", subscription.url);
            update(args, connection, _log, connection.last_insert_rowid());
            println!("Subscribed");
        }
        Err(e) => println!("Could not parse url '{}' {}", args.value_of("url").unwrap(), e),
    }
}

fn unsubscribe(args: &ArgMatches, connection: &Connection) {
    use std::io;
    use std::io::prelude::*;
    let mut stdin = io::stdin();
    let id = args.value_of("id").unwrap();
    let mut buffer = [0;1];

    let mut stmt = connection.prepare("select id, url, label from subscription where id = ?1").unwrap();
    let mut rows = stmt.query_map(&[&id], Subscription::map).unwrap();
    match rows.next() {
        Some(row) => {
            let subscription = row.unwrap();
            println!("Unsubscribing from: {}", subscription.url);
            println!("Sure? [y/N]");
            stdin.read_exact(&mut buffer).unwrap();
            if buffer[0] == 121u8 { // 121 is ascii code for 'y'
                connection.execute("delete from subscription where id = ?1", &[&id]).unwrap();
                println!("Unsubscribed from: {}", subscription.url);
            }
        },
        None => println!("Subscription doesn't exist"),
    }
}

fn list(connection: &Connection, _log: slog::Logger) {
    let mut stmt = connection.prepare("select id, url, label from subscription").unwrap();

    println!("Subscriptions list:");
    for row in stmt.query_map(&[], Subscription::map).unwrap(){
        let subscription = row.unwrap();
        println!("    {}: {} ({})", subscription.id, subscription.label, subscription.url);
    }
}

fn update(args: &ArgMatches, connection: &Connection, _log: slog::Logger, feed_id: i64) {
    use hyper::Client; // https://hyper.rs/hyper/v0.10.9/hyper/index.html
    use std::io::Read; // needed for read_to_string trait
    use std::str::FromStr; // needed for FromStr trait on Channel
    use rss::Channel;

    let mut stmt = connection.prepare("select id, url, label from subscription").unwrap();
    let client = Client::new(); // create http client

    let mut as_downloaded = 0;
    if args.is_present("as-downloaded") {
        as_downloaded = 1;
    }

    for row in stmt.query_map(&[], Subscription::map).unwrap() {
        let subscription = row.unwrap();
        println!("Updating {}:", subscription.label);

        if feed_id != subscription.id && feed_id > 0 { // if an id is set, update/populate only this id
            continue;
        }

        let mut res = client.get(subscription.clone()).send().unwrap(); // get query result thanks to IntoUrl trait implement for Subscription
        let mut body = String::new();
        res.read_to_string(&mut body).unwrap(); // extract body from query result

        let channel = Channel::from_str(&body).unwrap(); // parse rss into channel
        connection.execute("update subscription set label = ?1 where id = ?2", &[&channel.title(), &subscription.id]).unwrap(); // update podcast feed name
        for item in channel.items() {
            let url = Url::parse(item.enclosure().unwrap().url()).unwrap();
            let  path_segments = url.path_segments().unwrap();

            // create filename
            let mut filename = String::new();
            for segment in path_segments {
                if segment == "enclosure.mp3" || segment == "listen.mp3" {
                    filename = filename + ".mp3";
                    break;
                }
                filename = segment.to_string();
            }

            let podcast = Podcast { id: 0, subscription_id: subscription.id, url: url.as_str().to_string(), filename: filename};
            let previous_insert_rowid = connection.last_insert_rowid();
            connection.execute("insert or ignore into podcast (subscription_id, url, filename, downloaded) values (?1, ?2, ?3, ?4)", &[&podcast.subscription_id, &podcast.url, &podcast.filename, &as_downloaded]).unwrap();
            if previous_insert_rowid != connection.last_insert_rowid() {
                println!("    New podcast added: {:?}", podcast.filename);
            }

        }
        println!("Updated");
    }
}

fn pending(connection: &Connection) {
    let mut stmt = connection.prepare("select id, subscription_id, url, filename from podcast where downloaded = 0").unwrap();

    println!("Pending list:");
    for row in stmt.query_map(&[], Podcast::map).unwrap(){
        let podcast = row.unwrap();
        println!("    {} ({})", podcast.filename, podcast.url);
    }
}

fn downloaddir(args: &ArgMatches, connection: &Connection) {
    let path = args.value_of("path").unwrap();
    connection.execute("insert or replace into config (key, value) values ('downloaddir', ?1)", &[&path]).unwrap();
    println!("Download dir set to: {}", path);
}

fn download(connection: &Connection) {
    use std::path::Path;
    use std::fs::File;
    use std::io::Write;
    use std::ops::Mul;
    let mut curl = Easy::new();

    curl.progress(true).unwrap();
    curl.follow_location(true).unwrap();
    curl.progress_function( |a, b, _, _| {
        print!("    Downloading: {}%      \r", (b.mul(100_f64)/a).floor());
        true
    }).unwrap();
    let mut stmt = connection.prepare("select id, subscription_id, url, filename from podcast where downloaded = 0").unwrap();

    println!("Download pending podcast:");
    for row in stmt.query_map(&[], Podcast::map).unwrap(){
        let podcast = row.unwrap();
        let temp = "/tmp/".to_string() + podcast.filename.as_str();
        let path = Path::new(&temp);

        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {}",
                               why),
            Ok(file) => file,
        };
        curl.url(&podcast.url).unwrap();
        curl.write_function( move |data| {
            Ok(file.write(data).unwrap())
        }).unwrap();
        println!("    {}", podcast.filename);
        curl.perform().unwrap();
        connection.execute("update podcast set downloaded = 1, downloaded_at = current_timestamp where id = ?1", &[&podcast.id]).unwrap();
    }
}
<commit_msg>Set default database path + Add some UI message<commit_after>#[macro_use] extern crate clap;
extern crate url;
extern crate rss;
extern crate hyper;
extern crate curl; // https://docs.rs/curl/0.4.6/curl/easy/
extern crate rusqlite;

mod schema;
mod common;

use std::process;
use clap::{App, ArgMatches};
use schema::*;
use url::Url;
use rusqlite::Connection;
use curl::easy::Easy;
use std::path::{Path};
use std::fs::{File, create_dir};
use std::env::home_dir;

fn main() {
    let lock_socket = common::create_app_lock(12345); // https://rosettacode.org/wiki/Category:Rust
    // Init Sqlite database
    let db_path = home_dir().expect("/tmp/").into_os_string().into_string().unwrap() + "/.config/rusty";
    if !Path::new(&db_path).exists() { // If path doesn't exist we create it
        create_dir(&db_path).unwrap();
    }
    let database_url = db_path + "/rusty.sqlite3";
    let connection = match Connection::open(&database_url) {
        Ok(connection) => connection,
        Err(e) => {
            println!("Error database connection: {} {}", e, database_url);
            process::exit(1)
        }
    };
    init(&connection); // Initialize database

    // Clap
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml).get_matches();

    match matches.subcommand() {
        ("subscribe", Some(sub_matches)) => subscribe(sub_matches, &connection),
        ("unsubscribe", Some(sub_matches)) => unsubscribe(sub_matches, &connection),
        ("list", Some(_)) => list(&connection),
        ("update", Some(sub_matches)) => {
            let feed_id = sub_matches.value_of("id").unwrap_or("0").parse::<i64>().unwrap();
            update(sub_matches, &connection, feed_id);
        },
        ("pending", Some(_)) => pending(&connection),
        ("download", Some(_)) => download(&connection),
        ("download-dir", Some(sub_matches)) => downloaddir(sub_matches, &connection),
        ("", None) => println!("No subcommand was used"),
        _ => println!("No!"),
    }

    common::remove_app_lock(lock_socket);
}

fn init(connection: &Connection) {
    // execute only the first query if there are several queries. So we need to used split()
    let split = include_str!("sql/init.sql").split(";");
    for s in split {
        if s.trim() == "" {
            continue;
        }
        connection.execute(s, &[]).unwrap();
    }
}

fn subscribe(args: &ArgMatches, connection: &Connection) {
    match Url::parse(args.value_of("url").unwrap()) {
        Ok(url) => {
            let subscription = Subscription { id: 0, url: url.as_str().to_string(), label: String::new()};
            connection.execute("insert into subscription (url, label) values (?1, '')", &[&subscription.url]).unwrap();
            println!("Subscribing to: {}", subscription.url);
            update(args, connection, connection.last_insert_rowid());
            println!("Subscribed");
        }
        Err(e) => println!("Could not parse url '{}' {}", args.value_of("url").unwrap(), e),
    }
}

fn unsubscribe(args: &ArgMatches, connection: &Connection) {
    use std::io;
    use std::io::prelude::*;
    let mut stdin = io::stdin();
    let id = args.value_of("id").unwrap();
    let mut buffer = [0;1];

    let mut stmt = connection.prepare("select id, url, label from subscription where id = ?1").unwrap();
    let mut rows = stmt.query_map(&[&id], Subscription::map).unwrap();
    match rows.next() {
        Some(row) => {
            let subscription = row.unwrap();
            println!("Unsubscribing from: {}", subscription.url);
            println!("Sure? [y/N]");
            stdin.read_exact(&mut buffer).unwrap();
            if buffer[0] == 121u8 { // 121 is ascii code for 'y'
                connection.execute("delete from subscription where id = ?1", &[&id]).unwrap();
                println!("Unsubscribed from: {}", subscription.url);
            }
        },
        None => println!("Subscription doesn't exist"),
    }
}

fn list(connection: &Connection) {
    let mut stmt = connection.prepare("select id, url, label from subscription").unwrap();

    println!("Subscriptions list:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    No subscription");
    }

    for row in stmt.query_map(&[], Subscription::map).unwrap() {
        let subscription = row.unwrap();
        println!("    {}: {} ({})", subscription.id, subscription.label, subscription.url);
    }
}

fn update(args: &ArgMatches, connection: &Connection, feed_id: i64) {
    use hyper::Client; // https://hyper.rs/hyper/v0.10.9/hyper/index.html
    use std::io::Read; // needed for read_to_string trait
    use std::str::FromStr; // needed for FromStr trait on Channel
    use rss::Channel;

    let mut stmt = connection.prepare("select id, url, label from subscription").unwrap();
    let client = Client::new(); // create http client

    let mut as_downloaded = 0;
    if args.is_present("as-downloaded") {
        as_downloaded = 1;
    }

    for row in stmt.query_map(&[], Subscription::map).unwrap() {
        let subscription = row.unwrap();
        println!("Updating {}:", subscription.label);

        if feed_id != subscription.id && feed_id > 0 { // if an id is set, update/populate only this id
            continue;
        }

        let mut res = client.get(subscription.clone()).send().unwrap(); // get query result thanks to IntoUrl trait implement for Subscription
        let mut body = String::new();
        res.read_to_string(&mut body).unwrap(); // extract body from query result

        let channel = Channel::from_str(&body).unwrap(); // parse rss into channel
        connection.execute("update subscription set label = ?1 where id = ?2", &[&channel.title(), &subscription.id]).unwrap(); // update podcast feed name
        for item in channel.items() {
            let url = Url::parse(item.enclosure().unwrap().url()).unwrap();
            let  path_segments = url.path_segments().unwrap();

            // create filename
            let mut filename = String::new();
            for segment in path_segments {
                if segment == "enclosure.mp3" || segment == "listen.mp3" {
                    filename = filename + ".mp3";
                    break;
                }
                filename = segment.to_string();
            }

            let podcast = Podcast { id: 0, subscription_id: subscription.id, url: url.as_str().to_string(), filename: filename};
            let previous_insert_rowid = connection.last_insert_rowid();
            connection.execute("insert or ignore into podcast (subscription_id, url, filename, downloaded) values (?1, ?2, ?3, ?4)", &[&podcast.subscription_id, &podcast.url, &podcast.filename, &as_downloaded]).unwrap();
            if previous_insert_rowid != connection.last_insert_rowid() {
                println!("    New podcast added: {:?}", podcast.filename);
            }

        }
        println!("Updated");
    }
}

fn pending(connection: &Connection) {
    let mut stmt = connection.prepare("select id, subscription_id, url, filename from podcast where downloaded = 0").unwrap();

    println!("Pending list:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    Nothing to download");
    }
    for row in stmt.query_map(&[], Podcast::map).unwrap(){
        let podcast = row.unwrap();
        println!("    {} ({})", podcast.filename, podcast.url);
    }
}

fn downloaddir(args: &ArgMatches, connection: &Connection) {
    let path = args.value_of("path").unwrap();
    connection.execute("insert or replace into config (key, value) values ('downloaddir', ?1)", &[&path]).unwrap();
    println!("Download dir set to: {}", path);
}

fn download(connection: &Connection) {
    use std::io::Write;
    use std::ops::Mul;
    let mut curl = Easy::new();

    curl.progress(true).unwrap();
    curl.follow_location(true).unwrap();
    curl.progress_function( |a, b, _, _| {
        print!("    Downloading: {}%      \r", (b.mul(100_f64)/a).floor());
        true
    }).unwrap();
    let mut stmt = connection.prepare("select id, subscription_id, url, filename from podcast where downloaded = 0").unwrap();

    println!("Download pending podcast:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    Nothing to download");
    }
    for row in stmt.query_map(&[], Podcast::map).unwrap(){
        let podcast = row.unwrap();
        let temp = "/tmp/".to_string() + podcast.filename.as_str();
        let path = Path::new(&temp);

        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {}",
                               why),
            Ok(file) => file,
        };
        curl.url(&podcast.url).unwrap();
        curl.write_function( move |data| {
            Ok(file.write(data).unwrap())
        }).unwrap();
        println!("    {}", podcast.filename);
        curl.perform().unwrap();
        connection.execute("update podcast set downloaded = 1, downloaded_at = current_timestamp where id = ?1", &[&podcast.id]).unwrap();
    }
}
<|endoftext|>


<commit_before>#![feature(if_let, macro_rules, phase, slicing_syntax, tuple_indexing)]

/*!
These are some docs.
*/

extern crate regex;
extern crate serialize;

extern crate csv;
extern crate docopt;
#[phase(plugin)] extern crate docopt_macros;
extern crate stats;
extern crate tabwriter;

use std::io;
use std::os;

macro_rules! try(
    (csv| $e:expr) => (try!($e.map_err(::CliError::from_csv)));
    (io| $e:expr) => (try!($e.map_err(::CliError::from_io)));
    (str| $e:expr) => (try!($e.map_err(::CliError::from_str)));
    ($e:expr) => (
        match $e {
            Ok(e) => e,
            Err(e) => return Err(e)
        }
    );
)

macro_rules! werr(
    ($($arg:tt)*) => (
        match ::std::io::stderr().write_str(format!($($arg)*).as_slice()) {
            Ok(_) => (),
            Err(err) => fail!("{}", err),
        }
    )
)

macro_rules! command_list(
    () => (
"
    cat         Concatenate by row or column
    count       Count records
    fixlengths  Makes all records have same length
    flatten     Show one field per line
    fmt         Format CSV output (change field delimiter)
    frequency   Show frequency tables
    headers     Show header names
    index       Create CSV index for faster access
    join        Join CSV files
    search      Search CSV data with regexes
    select      Select columns from CSV
    slice       Slice records from CSV
    split       Split CSV data into many files
    table       Align CSV data into columns
"
    )
)

docopt!(Args, concat!("
Usage:
    xsv <command> [<args>...]
    xsv [options]

Options:
    -h, --help    Display this message
    --version     Print version info and exit

Commands:", command_list!()),
arg_command: Option<Command>)

fn main() {
    let mut conf = util::arg_config();
    conf.options_first = true;
    let args: Args = docopt::FlagParser::parse_conf(conf)
                                        .unwrap_or_else(|e| e.exit());
    match args.arg_command {
        None => {
            os::set_exit_status(0);
            let msg = concat!(
                "xsv is a suite of CSV command line utilities.

Please choose one of the following commands:",
                command_list!());
            {write!(io::stderr(), "{}", msg)}.unwrap();
        }
        Some(cmd) => {
            match cmd.run() {
                Ok(()) => os::set_exit_status(0),
                Err(ErrFlag(err)) => err.exit(),
                Err(ErrCsv(err)) => {
                    os::set_exit_status(1);
                    let _ = writeln!(io::stderr(), "{}", err.to_string());
                }
                Err(ErrIo(io::IoError { kind: io::BrokenPipe, .. })) => {
                    os::set_exit_status(0);
                }
                Err(ErrIo(err)) => {
                    os::set_exit_status(1);
                    let _ = writeln!(io::stderr(), "{}", err.to_string());
                }
                Err(ErrOther(msg)) => {
                    os::set_exit_status(1);
                    let _ = writeln!(io::stderr(), "{}", msg);
                }
            }
        }
    }
}

#[deriving(Decodable, Show)]
enum Command {
    Cat,
    Count,
    FixLengths,
    Flatten,
    Fmt,
    Frequency,
    Headers,
    Index,
    Join,
    Search,
    Select,
    Slice,
    Split,
    Stats,
    Table,
}

impl Command {
    fn run(self) -> CliResult<()> {
        let argv = os::args();
        let argv: Vec<_> = argv.iter().skip(1).map(|s| s[]).collect();
        let argv = argv[];
        match self {
            Cat => cmd::cat::run(argv),
            Count => cmd::count::run(argv),
            FixLengths => cmd::fixlengths::run(argv),
            Flatten => cmd::flatten::run(argv),
            Fmt => cmd::fmt::run(argv),
            Frequency => cmd::frequency::run(argv),
            Headers => cmd::headers::run(argv),
            Index => cmd::index::run(argv),
            Join => cmd::join::run(argv),
            Search => cmd::search::run(argv),
            Select => cmd::select::run(argv),
            Slice => cmd::slice::run(argv),
            Split => cmd::split::run(argv),
            Stats => cmd::stats::run(argv),
            Table => cmd::table::run(argv),
        }
    }
}

pub type CliResult<T> = Result<T, CliError>;

#[deriving(Show)]
pub enum CliError {
    ErrFlag(docopt::Error),
    ErrCsv(csv::Error),
    ErrIo(io::IoError),
    ErrOther(String),
}

impl CliError {
    pub fn from_flags(v: docopt::Error) -> CliError {
        ErrFlag(v)
    }
    pub fn from_csv(v: csv::Error) -> CliError {
        match v {
            csv::ErrIo(v) => CliError::from_io(v),
            v => ErrCsv(v),
        }
    }
    pub fn from_io(v: io::IoError) -> CliError {
        ErrIo(v)
    }
    pub fn from_str<T: ToString>(v: T) -> CliError {
        ErrOther(v.to_string())
    }
}

mod cmd;
mod config;
mod select;
mod util;
<commit_msg>List `stats` command.<commit_after>#![feature(if_let, macro_rules, phase, slicing_syntax, tuple_indexing)]

/*!
These are some docs.
*/

extern crate regex;
extern crate serialize;

extern crate csv;
extern crate docopt;
#[phase(plugin)] extern crate docopt_macros;
extern crate stats;
extern crate tabwriter;

use std::io;
use std::os;

macro_rules! try(
    (csv| $e:expr) => (try!($e.map_err(::CliError::from_csv)));
    (io| $e:expr) => (try!($e.map_err(::CliError::from_io)));
    (str| $e:expr) => (try!($e.map_err(::CliError::from_str)));
    ($e:expr) => (
        match $e {
            Ok(e) => e,
            Err(e) => return Err(e)
        }
    );
)

macro_rules! werr(
    ($($arg:tt)*) => (
        match ::std::io::stderr().write_str(format!($($arg)*).as_slice()) {
            Ok(_) => (),
            Err(err) => fail!("{}", err),
        }
    )
)

macro_rules! command_list(
    () => (
"
    cat         Concatenate by row or column
    count       Count records
    fixlengths  Makes all records have same length
    flatten     Show one field per line
    fmt         Format CSV output (change field delimiter)
    frequency   Show frequency tables
    headers     Show header names
    index       Create CSV index for faster access
    join        Join CSV files
    search      Search CSV data with regexes
    select      Select columns from CSV
    slice       Slice records from CSV
    split       Split CSV data into many files
    stats       Compute basic statistics
    table       Align CSV data into columns
"
    )
)

docopt!(Args, concat!("
Usage:
    xsv <command> [<args>...]
    xsv [options]

Options:
    -h, --help    Display this message
    --version     Print version info and exit

Commands:", command_list!()),
arg_command: Option<Command>)

fn main() {
    let mut conf = util::arg_config();
    conf.options_first = true;
    let args: Args = docopt::FlagParser::parse_conf(conf)
                                        .unwrap_or_else(|e| e.exit());
    match args.arg_command {
        None => {
            os::set_exit_status(0);
            let msg = concat!(
                "xsv is a suite of CSV command line utilities.

Please choose one of the following commands:",
                command_list!());
            {write!(io::stderr(), "{}", msg)}.unwrap();
        }
        Some(cmd) => {
            match cmd.run() {
                Ok(()) => os::set_exit_status(0),
                Err(ErrFlag(err)) => err.exit(),
                Err(ErrCsv(err)) => {
                    os::set_exit_status(1);
                    let _ = writeln!(io::stderr(), "{}", err.to_string());
                }
                Err(ErrIo(io::IoError { kind: io::BrokenPipe, .. })) => {
                    os::set_exit_status(0);
                }
                Err(ErrIo(err)) => {
                    os::set_exit_status(1);
                    let _ = writeln!(io::stderr(), "{}", err.to_string());
                }
                Err(ErrOther(msg)) => {
                    os::set_exit_status(1);
                    let _ = writeln!(io::stderr(), "{}", msg);
                }
            }
        }
    }
}

#[deriving(Decodable, Show)]
enum Command {
    Cat,
    Count,
    FixLengths,
    Flatten,
    Fmt,
    Frequency,
    Headers,
    Index,
    Join,
    Search,
    Select,
    Slice,
    Split,
    Stats,
    Table,
}

impl Command {
    fn run(self) -> CliResult<()> {
        let argv = os::args();
        let argv: Vec<_> = argv.iter().skip(1).map(|s| s[]).collect();
        let argv = argv[];
        match self {
            Cat => cmd::cat::run(argv),
            Count => cmd::count::run(argv),
            FixLengths => cmd::fixlengths::run(argv),
            Flatten => cmd::flatten::run(argv),
            Fmt => cmd::fmt::run(argv),
            Frequency => cmd::frequency::run(argv),
            Headers => cmd::headers::run(argv),
            Index => cmd::index::run(argv),
            Join => cmd::join::run(argv),
            Search => cmd::search::run(argv),
            Select => cmd::select::run(argv),
            Slice => cmd::slice::run(argv),
            Split => cmd::split::run(argv),
            Stats => cmd::stats::run(argv),
            Table => cmd::table::run(argv),
        }
    }
}

pub type CliResult<T> = Result<T, CliError>;

#[deriving(Show)]
pub enum CliError {
    ErrFlag(docopt::Error),
    ErrCsv(csv::Error),
    ErrIo(io::IoError),
    ErrOther(String),
}

impl CliError {
    pub fn from_flags(v: docopt::Error) -> CliError {
        ErrFlag(v)
    }
    pub fn from_csv(v: csv::Error) -> CliError {
        match v {
            csv::ErrIo(v) => CliError::from_io(v),
            v => ErrCsv(v),
        }
    }
    pub fn from_io(v: io::IoError) -> CliError {
        ErrIo(v)
    }
    pub fn from_str<T: ToString>(v: T) -> CliError {
        ErrOther(v.to_string())
    }
}

mod cmd;
mod config;
mod select;
mod util;
<|endoftext|>