<commit_before>extern crate elma;
extern crate clap;

use elma::lev::{ObjectType, Level};
use elma::OBJECT_RADIUS;
use clap::{Arg, App};

use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

#[derive(Debug)]
struct Settings<'a> {
    ground: &'a str,
    sky: &'a str,
    apple: &'a str,
    flower: &'a str,
    killer: &'a str,
    player: &'a str,
    stroke: usize,
    scale: usize,
    pad: usize,
    width: usize,
    height: usize,
    max_width: usize,
    max_height: usize,
    complexity: &'a str
}

fn main () {
    // Take care of command line arguments.
    let matches = App::new("elma-lev2map")
                            .version(VERSION)
                            .author("Roger Andersen <hexjelly@hexjelly.com>")
                            .about("Converts Elasto Mania level files to images")
                            .arg(Arg::with_name("input")
                                .short("i").long("input")
                                .value_name("PATH")
                                .help("Path to level file")
                                .takes_value(true).use_delimiter(false).required(true))
                            .arg(Arg::with_name("output")
                                .short("o").long("output")
                                .value_name("PATH")
                                .help("Path to save image file [default: <input>.svg]")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("svg")
                                .long("svg")
                                .help("Specify SVG as output type [default]"))
                            .arg(Arg::with_name("ground")
                                .short("g").long("ground")
                                .value_name("COLOR")
                                .help("Ground fill color, in rgb, hex or name")
                                .use_delimiter(false).takes_value(true)
                                .default_value("#181048"))
                            .arg(Arg::with_name("sky")
                                .short("s")
                                .long("sky")
                                .value_name("COLOR")
                                .help("Sky fill color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("#3078bc")
                                .takes_value(true))
                            .arg(Arg::with_name("pad")
                                .long("pad")
                                .value_name("SIZE")
                                .help("Canvas padding")
                                .default_value("10")
                                .use_delimiter(false)
                                .takes_value(true))
                            .arg(Arg::with_name("scale")
                                .long("scale")
                                .value_name("SIZE")
                                .help("Scale of SVG")
                                .default_value("20")
                                .use_delimiter(false)
                                .takes_value(true))
                            .arg(Arg::with_name("apple")
                                .short("a")
                                .long("apple")
                                .value_name("COLOR")
                                .help("Apple color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("red")
                                .takes_value(true))
                            .arg(Arg::with_name("flower")
                                .short("f")
                                .long("flower")
                                .value_name("COLOR")
                                .help("Flower color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("white")
                                .takes_value(true))
                            .arg(Arg::with_name("killer")
                                .short("k")
                                .long("killer")
                                .value_name("COLOR")
                                .help("Killer color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("black")
                                .takes_value(true))
                            .arg(Arg::with_name("player")
                                .short("p")
                                .long("player")
                                .value_name("COLOR")
                                .help("Player color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("green")
                                .takes_value(true))
                            .arg(Arg::with_name("stroke")
                                .long("stroke")
                                .value_name("SIZE")
                                .help("Line stroke around objects")
                                .use_delimiter(false)
                                .default_value("0")
                                .takes_value(true))
                            .arg(Arg::with_name("height")
                                .long("height")
                                .value_name("SIZE")
                                .help("Height")
                                .conflicts_with("max_height")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("width")
                                .long("width")
                                .value_name("SIZE")
                                .help("Width")
                                .conflicts_with("max_width")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("max_height")
                                .long("maxheight")
                                .value_name("SIZE")
                                .help("Max height")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("max_width")
                                .long("maxwidth")
                                .value_name("SIZE")
                                .help("Max width")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("complexity")
                                .long("complexity")
                                .value_name("CHOICE")
                                .help("What complexity to use for objects")
                                .use_delimiter(false).takes_value(true)
                                .default_value("complex")
                                .possible_values(&["simple", "complex", "mix"]))
                            .get_matches();

    let input_file = Path::new(matches.value_of("input").unwrap());
    let mut output_file;
    if let Some(path) = matches.value_of("output") {
        output_file = PathBuf::from(path);
        if output_file.is_dir() {
            if let Some(file_name) = input_file.file_name() {
                output_file.push(file_name);
                output_file.set_extension("svg");
            }
        }
    } else {
        output_file = PathBuf::from(input_file);
        output_file.set_extension("svg");
    }

    let ground = matches.value_of("ground").unwrap();
    let sky = matches.value_of("sky").unwrap();
    let apple = matches.value_of("apple").unwrap();
    let flower = matches.value_of("flower").unwrap();
    let killer = matches.value_of("killer").unwrap();
    let player = matches.value_of("player").unwrap();
    let stroke = matches.value_of("stroke").unwrap().parse::<usize>().unwrap();
    let scale = matches.value_of("scale").unwrap().parse::<usize>().unwrap();
    let pad = matches.value_of("pad").unwrap().parse::<usize>().unwrap();
    let mut width = 0;
    if let Some(val) = matches.value_of("width") {
        width = val.parse::<usize>().unwrap();
    };
    let mut max_width = 0;
    if let Some(val) = matches.value_of("max_width") {
        max_width = val.parse::<usize>().unwrap();
    };
    let mut height = 0;
    if let Some(val) = matches.value_of("height") {
        height = val.parse::<usize>().unwrap();
    };
    let mut max_height = 0;
    if let Some(val) = matches.value_of("max_height") {
        max_height = val.parse::<usize>().unwrap();
    };
    let complexity = matches.value_of("complexity").unwrap();

    let settings = Settings {
        ground: ground,
        sky: sky,
        apple: apple,
        flower: flower,
        killer: killer,
        player: player,
        stroke: stroke,
        scale: scale,
        pad: pad,
        width: width,
        height: height,
        max_width: max_width,
        max_height: max_height,
        complexity: complexity
    };

    make_svg(input_file, settings, &output_file);
}

fn make_svg (input: &Path, settings: Settings, output: &PathBuf) {

    let level = Level::load(input.to_str().unwrap()).unwrap();
    let mut _killer = false;
    let mut _apple = false;

    // Figure out max and min coordinates.
    let mut max_x = 0_f64;
    let mut max_y = 0_f64;
    let mut min_x = 0_f64;
    let mut min_y = 0_f64;

    for polygon in &level.polygons {
        if !polygon.grass {
            for vertex in &polygon.vertices {
                if vertex.x < min_x { min_x = vertex.x; }
                if vertex.x > max_x { max_x = vertex.x; }
                if vertex.y < min_y { min_y = vertex.y; }
                if vertex.y > max_y { max_y = vertex.y; }
            }
        }
    }

    // Checking coordinates for objects as well, as they could be outside of polygons.
    //
    // In addition to that, keep track of what type of objects the level has, in order to add the necessary ones
    // to the <defs> tag, and skip the ones not in the level to make the file size smaller, if only by a tiny fraction.
    for object in &level.objects {
        if object.position.x - OBJECT_RADIUS < min_x { min_x = object.position.x - OBJECT_RADIUS; }
        if object.position.x > max_x { max_x = object.position.x + OBJECT_RADIUS; }
        if object.position.y - OBJECT_RADIUS < min_y { min_y = object.position.y - OBJECT_RADIUS; }
        if object.position.y > max_y { max_y = object.position.y + OBJECT_RADIUS; }

        match object.object_type {
            ObjectType::Apple { .. } => _apple = true,
            ObjectType::Killer => _killer = true,
            _ => {}
        };
    }

    // Start writing SVG data to buffer.
    let mut buffer = vec![];
    let width = ((max_x + min_x.abs()) * settings.scale as f64) + settings.pad as f64 * 2_f64;
    let height = ((max_y + min_y.abs()) * settings.scale as f64) + settings.pad as f64 * 2_f64;

    buffer.extend_from_slice(br#"<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">"#);
    buffer.extend_from_slice(format!("\r\n<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 {} {}\">\r\n<defs>",
                             width, height).as_bytes());

    // import objects, textures, pictures
    buffer.extend_from_slice(include_bytes!("assets/ground.def"));
    buffer.extend_from_slice(include_bytes!("assets/killer.def"));
    buffer.extend_from_slice(include_bytes!("assets/apple.def"));
    buffer.extend_from_slice(include_bytes!("assets/flower.def"));

    buffer.extend_from_slice(b"</defs>\r\n\t<rect width=\"100%\" height=\"100%\" fill=\"url(#img1)\" />\r\n");

    // Polygons.
    buffer.extend_from_slice(format!("\t<path fill-rule=\"evenodd\" fill=\"{}\" d=\"",
                             settings.sky).as_bytes());
    for polygon in &level.polygons {
        if !polygon.grass {
            for (n, vertex) in polygon.vertices.iter().enumerate() {
                if n == 0 { buffer.extend_from_slice(b"M"); }
                else { buffer.extend_from_slice(b"L"); }
                let x = ((vertex.x + min_x.abs()) * settings.scale as f64) + settings.pad as f64;
                let y = ((vertex.y + min_y.abs()) * settings.scale as f64) + settings.pad as f64;
                buffer.extend_from_slice(format!("{} {} ", x, y).as_bytes());
            }
        }
    }
    buffer.extend_from_slice(b"Z\" />\r\n");

    // Grass polygons.
    buffer.extend_from_slice(b"\t<path fill=\"none\" stroke=\"green\" stroke-width=\"0.7\" d=\"");
    for polygon in &level.polygons {
        if polygon.grass {
            for (n, vertex) in polygon.vertices.iter().enumerate() {
                if n == 0 { buffer.extend_from_slice(b"M"); }
                else { buffer.extend_from_slice(b"L"); }
                let x = ((vertex.x + min_x.abs()) * settings.scale as f64) + settings.pad as f64;
                let y = ((vertex.y + min_y.abs()) * settings.scale as f64) + settings.pad as f64;
                buffer.extend_from_slice(format!("{} {} ", x, y).as_bytes());
            }
        }
    }
    buffer.extend_from_slice(b"Z\" />\r\n");

    // Objects.
    for object in &level.objects {
        let x = ((object.position.x + min_x.abs()) * settings.scale as f64) + settings.pad as f64;
        let y = ((object.position.y + min_y.abs()) * settings.scale as f64) + settings.pad as f64;

        // Simple circles.
        if settings.complexity == "mix" || settings.complexity == "simple" {
            let color = match object.object_type {
                ObjectType::Apple { .. } => settings.apple,
                ObjectType::Exit => settings.flower,
                ObjectType::Killer => settings.killer,
                ObjectType::Player => settings.player
            };

            buffer.extend_from_slice(format!("<circle cx=\"{}\" cy=\"{}\" r=\"{}\" stroke=\"{}\" stroke-width=\"{}\" fill=\"{}\" />\r\n",
                                     x, y, OBJECT_RADIUS * settings.scale as f64, "black", settings.stroke, color).as_bytes());
        }

        // SVG objects
        match object.object_type {
            ObjectType::Apple { .. } => buffer.extend_from_slice(format!("<use x=\"{}\" y=\"{}\" xlink:href=\"#apple\" />\r\n", x - OBJECT_RADIUS * settings.scale as f64, y - OBJECT_RADIUS * settings.scale as f64).as_bytes()),
            ObjectType::Killer => buffer.extend_from_slice(format!("<use x=\"{}\" y=\"{}\" xlink:href=\"#killer\" />\r\n", x - OBJECT_RADIUS * settings.scale as f64, y - OBJECT_RADIUS * settings.scale as f64).as_bytes()),
            ObjectType::Exit => buffer.extend_from_slice(format!("<use x=\"{}\" y=\"{}\" xlink:href=\"#flower\" />\r\n", x - OBJECT_RADIUS * settings.scale as f64, y - OBJECT_RADIUS * settings.scale as f64).as_bytes()),
            _ => {}
        };
    }

    buffer.extend_from_slice(b"</svg>");

    // Write buffer to file.
    let mut file = File::create(&output).unwrap();
    file.write_all(&buffer).unwrap();
    println!("Wrote SVG file: {:?}", output);
}
<commit_msg>fix: add proper complexity flag logic<commit_after>extern crate elma;
extern crate clap;

use elma::lev::{ObjectType, Level};
use elma::OBJECT_RADIUS;
use clap::{Arg, App};

use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

#[derive(Debug)]
struct Settings<'a> {
    ground: &'a str,
    sky: &'a str,
    apple: &'a str,
    flower: &'a str,
    killer: &'a str,
    player: &'a str,
    stroke: usize,
    scale: usize,
    pad: usize,
    width: usize,
    height: usize,
    max_width: usize,
    max_height: usize,
    complexity: &'a str
}

fn main () {
    // Take care of command line arguments.
    // TODO: put into external yaml file
    let matches = App::new("elma-lev2map")
                            .version(VERSION)
                            .author("Roger Andersen <hexjelly@hexjelly.com>")
                            .about("Converts Elasto Mania level files to images")
                            .arg(Arg::with_name("input")
                                .short("i").long("input")
                                .value_name("PATH")
                                .help("Path to level file")
                                .takes_value(true).use_delimiter(false).required(true))
                            .arg(Arg::with_name("output")
                                .short("o").long("output")
                                .value_name("PATH")
                                .help("Path to save image file [default: <input>.svg]")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("svg")
                                .long("svg")
                                .help("Specify SVG as output type [default]"))
                            .arg(Arg::with_name("ground")
                                .short("g").long("ground")
                                .value_name("COLOR")
                                .help("Ground fill color, in rgb, hex or name")
                                .use_delimiter(false).takes_value(true)
                                .default_value("#181048"))
                            .arg(Arg::with_name("sky")
                                .short("s")
                                .long("sky")
                                .value_name("COLOR")
                                .help("Sky fill color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("#3078bc")
                                .takes_value(true))
                            .arg(Arg::with_name("pad")
                                .long("pad")
                                .value_name("SIZE")
                                .help("Canvas padding")
                                .default_value("10")
                                .use_delimiter(false)
                                .takes_value(true))
                            .arg(Arg::with_name("scale")
                                .long("scale")
                                .value_name("SIZE")
                                .help("Scale of SVG")
                                .default_value("20")
                                .use_delimiter(false)
                                .takes_value(true))
                            .arg(Arg::with_name("apple")
                                .short("a")
                                .long("apple")
                                .value_name("COLOR")
                                .help("Apple color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("red")
                                .takes_value(true))
                            .arg(Arg::with_name("flower")
                                .short("f")
                                .long("flower")
                                .value_name("COLOR")
                                .help("Flower color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("white")
                                .takes_value(true))
                            .arg(Arg::with_name("killer")
                                .short("k")
                                .long("killer")
                                .value_name("COLOR")
                                .help("Killer color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("black")
                                .takes_value(true))
                            .arg(Arg::with_name("player")
                                .short("p")
                                .long("player")
                                .value_name("COLOR")
                                .help("Player color, in rgb, hex or name")
                                .use_delimiter(false)
                                .default_value("green")
                                .takes_value(true))
                            .arg(Arg::with_name("stroke")
                                .long("stroke")
                                .value_name("SIZE")
                                .help("Line stroke around objects")
                                .use_delimiter(false)
                                .default_value("0")
                                .takes_value(true))
                            .arg(Arg::with_name("height")
                                .long("height")
                                .value_name("SIZE")
                                .help("Height")
                                .conflicts_with("max_height")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("width")
                                .long("width")
                                .value_name("SIZE")
                                .help("Width")
                                .conflicts_with("max_width")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("max_height")
                                .long("maxheight")
                                .value_name("SIZE")
                                .help("Max height")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("max_width")
                                .long("maxwidth")
                                .value_name("SIZE")
                                .help("Max width")
                                .use_delimiter(false).takes_value(true))
                            .arg(Arg::with_name("complexity")
                                .long("complexity")
                                .value_name("CHOICE")
                                .help("What complexity to use for objects")
                                .use_delimiter(false).takes_value(true)
                                .default_value("complex")
                                .possible_values(&["simple", "complex", "mix"]))
                            .get_matches();

    let input_file = Path::new(matches.value_of("input").unwrap());
    let mut output_file;
    if let Some(path) = matches.value_of("output") {
        output_file = PathBuf::from(path);
        if output_file.is_dir() {
            if let Some(file_name) = input_file.file_name() {
                output_file.push(file_name);
                output_file.set_extension("svg");
            }
        }
    } else {
        output_file = PathBuf::from(input_file);
        output_file.set_extension("svg");
    }

    let ground = matches.value_of("ground").unwrap();
    let sky = matches.value_of("sky").unwrap();
    let apple = matches.value_of("apple").unwrap();
    let flower = matches.value_of("flower").unwrap();
    let killer = matches.value_of("killer").unwrap();
    let player = matches.value_of("player").unwrap();
    let stroke = matches.value_of("stroke").unwrap().parse::<usize>().unwrap();
    let scale = matches.value_of("scale").unwrap().parse::<usize>().unwrap();
    let pad = matches.value_of("pad").unwrap().parse::<usize>().unwrap();
    let mut width = 0;
    // TODO: use clap default thing doh
    if let Some(val) = matches.value_of("width") {
        width = val.parse::<usize>().unwrap();
    };
    let mut max_width = 0;
    if let Some(val) = matches.value_of("max_width") {
        max_width = val.parse::<usize>().unwrap();
    };
    let mut height = 0;
    if let Some(val) = matches.value_of("height") {
        height = val.parse::<usize>().unwrap();
    };
    let mut max_height = 0;
    if let Some(val) = matches.value_of("max_height") {
        max_height = val.parse::<usize>().unwrap();
    };
    let complexity = matches.value_of("complexity").unwrap();

    let settings = Settings {
        ground: ground,
        sky: sky,
        apple: apple,
        flower: flower,
        killer: killer,
        player: player,
        stroke: stroke,
        scale: scale,
        pad: pad,
        width: width,
        height: height,
        max_width: max_width,
        max_height: max_height,
        complexity: complexity
    };

    make_svg(input_file, settings, &output_file);
}

fn make_svg (input: &Path, settings: Settings, output: &PathBuf) {

    let level = Level::load(input.to_str().unwrap()).unwrap();
    let mut _killer = false;
    let mut _apple = false;

    // Figure out max and min coordinates.
    let mut max_x = 0_f64;
    let mut max_y = 0_f64;
    let mut min_x = 0_f64;
    let mut min_y = 0_f64;

    for polygon in &level.polygons {
        if !polygon.grass {
            for vertex in &polygon.vertices {
                if vertex.x < min_x { min_x = vertex.x; }
                if vertex.x > max_x { max_x = vertex.x; }
                if vertex.y < min_y { min_y = vertex.y; }
                if vertex.y > max_y { max_y = vertex.y; }
            }
        }
    }

    // Checking coordinates for objects as well, as they could be outside of polygons.
    //
    // In addition to that, keep track of what type of objects the level has, in order to add the necessary ones
    // to the <defs> tag, and skip the ones not in the level to make the file size smaller, if only by a tiny fraction.
    for object in &level.objects {
        if object.position.x - OBJECT_RADIUS < min_x { min_x = object.position.x - OBJECT_RADIUS; }
        if object.position.x > max_x { max_x = object.position.x + OBJECT_RADIUS; }
        if object.position.y - OBJECT_RADIUS < min_y { min_y = object.position.y - OBJECT_RADIUS; }
        if object.position.y > max_y { max_y = object.position.y + OBJECT_RADIUS; }

        match object.object_type {
            ObjectType::Apple { .. } => _apple = true,
            ObjectType::Killer => _killer = true,
            _ => {}
        };
    }

    // Start writing SVG data to buffer.
    let mut buffer = vec![];
    let width = ((max_x + min_x.abs()) * settings.scale as f64) + settings.pad as f64 * 2_f64;
    let height = ((max_y + min_y.abs()) * settings.scale as f64) + settings.pad as f64 * 2_f64;

    buffer.extend_from_slice(br#"<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">"#);
    buffer.extend_from_slice(format!("\r\n<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 {} {}\">\r\n<defs>",
                             width, height).as_bytes());

    // import objects, textures, pictures
    buffer.extend_from_slice(include_bytes!("assets/ground.def"));
    buffer.extend_from_slice(include_bytes!("assets/killer.def"));
    buffer.extend_from_slice(include_bytes!("assets/apple.def"));
    buffer.extend_from_slice(include_bytes!("assets/flower.def"));

    buffer.extend_from_slice(b"</defs>\r\n\t<rect width=\"100%\" height=\"100%\" fill=\"url(#img1)\" />\r\n");

    // Polygons.
    buffer.extend_from_slice(format!("\t<path fill-rule=\"evenodd\" fill=\"{}\" d=\"",
                             settings.sky).as_bytes());
    for polygon in &level.polygons {
        if !polygon.grass {
            for (n, vertex) in polygon.vertices.iter().enumerate() {
                if n == 0 { buffer.extend_from_slice(b"M"); }
                else { buffer.extend_from_slice(b"L"); }
                let x = ((vertex.x + min_x.abs()) * settings.scale as f64) + settings.pad as f64;
                let y = ((vertex.y + min_y.abs()) * settings.scale as f64) + settings.pad as f64;
                buffer.extend_from_slice(format!("{} {} ", x, y).as_bytes());
            }
        }
    }
    buffer.extend_from_slice(b"Z\" />\r\n");

    // Grass polygons.
    buffer.extend_from_slice(b"\t<path fill=\"none\" stroke=\"green\" stroke-width=\"0.7\" d=\"");
    for polygon in &level.polygons {
        if polygon.grass {
            for (n, vertex) in polygon.vertices.iter().enumerate() {
                if n == 0 { buffer.extend_from_slice(b"M"); }
                else { buffer.extend_from_slice(b"L"); }
                let x = ((vertex.x + min_x.abs()) * settings.scale as f64) + settings.pad as f64;
                let y = ((vertex.y + min_y.abs()) * settings.scale as f64) + settings.pad as f64;
                buffer.extend_from_slice(format!("{} {} ", x, y).as_bytes());
            }
        }
    }
    buffer.extend_from_slice(b"Z\" />\r\n");

    // Objects.
    for object in &level.objects {
        let x = ((object.position.x + min_x.abs()) * settings.scale as f64) + settings.pad as f64;
        let y = ((object.position.y + min_y.abs()) * settings.scale as f64) + settings.pad as f64;

        // Simple circles.
        if settings.complexity == "mix" || settings.complexity == "simple" {
            let color = match object.object_type {
                ObjectType::Apple { .. } => settings.apple,
                ObjectType::Exit => settings.flower,
                ObjectType::Killer => settings.killer,
                ObjectType::Player => settings.player
            };

            buffer.extend_from_slice(format!("<circle cx=\"{}\" cy=\"{}\" r=\"{}\" stroke=\"{}\" stroke-width=\"{}\" fill=\"{}\" />\r\n",
                                     x, y, OBJECT_RADIUS * settings.scale as f64, "black", settings.stroke, color).as_bytes());
        }

        // SVG objects
        if settings.complexity == "mix" || settings.complexity == "complex" {
            match object.object_type {
                ObjectType::Apple { .. } => buffer.extend_from_slice(format!("<use x=\"{}\" y=\"{}\" xlink:href=\"#apple\" />\r\n", x - OBJECT_RADIUS * settings.scale as f64, y - OBJECT_RADIUS * settings.scale as f64).as_bytes()),
                ObjectType::Killer => buffer.extend_from_slice(format!("<use x=\"{}\" y=\"{}\" xlink:href=\"#killer\" />\r\n", x - OBJECT_RADIUS * settings.scale as f64, y - OBJECT_RADIUS * settings.scale as f64).as_bytes()),
                ObjectType::Exit => buffer.extend_from_slice(format!("<use x=\"{}\" y=\"{}\" xlink:href=\"#flower\" />\r\n", x - OBJECT_RADIUS * settings.scale as f64, y - OBJECT_RADIUS * settings.scale as f64).as_bytes()),
                _ => {}
            };
        }
    }

    buffer.extend_from_slice(b"</svg>");

    // Write buffer to file.
    // TODO: remove unwraps doh
    let mut file = File::create(&output).unwrap();
    file.write_all(&buffer).unwrap();
    println!("Wrote SVG file: {:?}", output);
}
<|endoftext|>


<commit_before>#[macro_use] extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate serde;
#[macro_use] extern crate serde_json;
#[macro_use] extern crate glob;
#[macro_use] extern crate uuid;
#[macro_use] extern crate regex;
#[macro_use] extern crate prettytable;
extern crate url;
extern crate config;

use std::process::exit;

pub use cli::CliConfig;
pub use configuration::Configuration;
pub use runtime::{ImagLogger, Runtime};
pub use clap::App;
pub use module::Module;

pub mod cli;
pub mod configuration;
pub mod runtime;
pub mod module;
pub mod storage;
pub mod ui;
pub mod util;

pub use module::bm::BM;

fn main() {
    let yaml = load_yaml!("../etc/cli.yml");
    let app = App::from_yaml(yaml);
    let config = CliConfig::new(app);
    let configuration = Configuration::new(&config);

    let logger = ImagLogger::init(&configuration, &config);
    debug!("Logger created!");

    debug!("CliConfig    : {:?}", &config);
    debug!("Configuration: {:?}", &configuration);

    let rt = Runtime::new(configuration, config);

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

    if let Some(matches) = rt.config.cli_matches.subcommand_matches("bm") {
        let res = BM::new(&rt).exec(matches);
        info!("BM exited with {}", res);
    } else {
        info!("No commandline call...")
    }
}
<commit_msg>Added indent for readability<commit_after>#[macro_use] extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate serde;
#[macro_use] extern crate serde_json;
#[macro_use] extern crate glob;
#[macro_use] extern crate uuid;
#[macro_use] extern crate regex;
#[macro_use] extern crate prettytable;
extern crate url;
extern crate config;

use std::process::exit;

pub use cli::CliConfig;
pub use configuration::Configuration;
pub use runtime::{ImagLogger, Runtime};
pub use clap::App;
pub use module::Module;

pub mod cli;
pub mod configuration;
pub mod runtime;
pub mod module;
pub mod storage;
pub mod ui;
pub mod util;

pub use module::bm::BM;

fn main() {
    let yaml          = load_yaml!("../etc/cli.yml");
    let app           = App::from_yaml(yaml);
    let config        = CliConfig::new(app);
    let configuration = Configuration::new(&config);
    let logger        = ImagLogger::init(&configuration, &config);

    debug!("Logger created!");
    debug!("CliConfig    : {:?}", &config);
    debug!("Configuration: {:?}", &configuration);

    let rt = Runtime::new(configuration, config);

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

    if let Some(matches) = rt.config.cli_matches.subcommand_matches("bm") {
        let res = BM::new(&rt).exec(matches);
        info!("BM exited with {}", res);
    } else {
        info!("No commandline call...")
    }
}
<|endoftext|>


<commit_before>use anyhow::Result;
use crossterm::event::{KeyEvent, KeyModifiers};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use histogram::Histogram;
use pinger::{ping, PingResult};
use std::io;
use std::io::Write;
use std::ops::Add;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset, GraphType, Paragraph};
use tui::{symbols, Terminal};

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.")]
struct Args {
    #[structopt(help = "Host or IP to ping")]
    host: String,
    #[structopt(
        short,
        long,
        default_value = "100",
        help = "Determines the number pings to display."
    )]
    buffer: usize,
}

struct App {
    data: Vec<(f64, f64)>,
    capacity: usize,
    idx: i64,
    window: [f64; 2],
}

impl App {
    fn new(capacity: usize) -> Self {
        App {
            data: Vec::with_capacity(capacity),
            capacity,
            idx: 0,
            window: [0.0, capacity as f64],
        }
    }
    fn update(&mut self, item: Option<Duration>) {
        self.idx += 1;
        if self.data.len() >= self.capacity {
            self.data.remove(0);
            self.window[0] += 1_f64;
            self.window[1] += 1_f64;
        }
        match item {
            Some(dur) => self.data.push((self.idx as f64, dur.as_micros() as f64)),
            None => self.data.push((self.idx as f64, 0_f64)),
        }
    }
    fn stats(&self) -> Histogram {
        let mut hist = Histogram::new();

        for (_, val) in self.data.iter().filter(|v| v.1 != 0f64) {
            hist.increment(*val as u64).unwrap_or(());
        }

        hist
    }
    fn y_axis_bounds(&self) -> [f64; 2] {
        let min = self
            .data
            .iter()
            .map(|v| v.1)
            .fold(f64::INFINITY, |a, b| a.min(b));
        let max = self.data.iter().map(|v| v.1).fold(0f64, |a, b| a.max(b));
        // Add a 10% buffer to the top and bottom
        let max_10_percent = (max * 10_f64) / 100_f64;
        let min_10_percent = (min * 10_f64) / 100_f64;
        [min - min_10_percent, max + max_10_percent]
    }
    fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Split into 5 sections
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let increment = Duration::from_micros((difference / 3f64) as u64);
        let duration = Duration::from_micros(min as u64);

        (0..7)
            .map(|i| Span::raw(format!("{:?}", duration.add(increment * i))))
            .collect()
    }
}

#[derive(Debug)]
enum Event {
    Update(PingResult),
    Input(KeyEvent),
}

fn main() -> Result<()> {
    let args = Args::from_args();
    let mut app = App::new(args.buffer);
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;

    let (key_tx, rx) = mpsc::channel();

    let ping_tx = key_tx.clone();

    let host = args.host.clone();

    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping(host).expect("Error pinging");

        loop {
            ping_tx.send(Event::Update(stream.recv()?))?;
        }
    });

    // Pump keyboard messages into the queue
    thread::spawn(move || -> Result<()> {
        loop {
            if event::poll(Duration::from_secs(1)).unwrap() {
                if let CEvent::Key(key) = event::read().unwrap() {
                    key_tx.send(Event::Input(key)).unwrap();
                }
            }
        }
    });

    loop {
        match rx.recv()? {
            Event::Update(ping_result) => {
                match ping_result {
                    PingResult::Pong(duration) => app.update(Some(duration)),
                    PingResult::Timeout => app.update(None),
                };
                terminal.draw(|f| {
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(2)
                        .constraints(
                            [Constraint::Percentage(5), Constraint::Percentage(10)].as_ref(),
                        )
                        .split(f.size());

                    let header_layout = Layout::default()
                        .direction(Direction::Horizontal)
                        .constraints(
                            [
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                            ]
                            .as_ref(),
                        )
                        .split(chunks[0]);

                    f.render_widget(
                        Paragraph::new(format!("Pinging {}", &args.host)),
                        header_layout[0],
                    );

                    let stats = app.stats();

                    f.render_widget(
                        Paragraph::new(format!(
                            "min {:?}",
                            Duration::from_micros(stats.minimum().unwrap_or(0))
                        )),
                        header_layout[1],
                    );
                    f.render_widget(
                        Paragraph::new(format!(
                            "max {:?}",
                            Duration::from_micros(stats.maximum().unwrap_or(0))
                        )),
                        header_layout[2],
                    );
                    f.render_widget(
                        Paragraph::new(format!(
                            "p95 {:?}",
                            Duration::from_micros(stats.percentile(95.0).unwrap_or(0))
                        )),
                        header_layout[3],
                    );

                    let dataset = Dataset::default()
                        .marker(symbols::Marker::Braille)
                        .style(Style::default().fg(Color::Cyan))
                        .graph_type(GraphType::Line)
                        .data(&app.data);

                    let y_axis_bounds = app.y_axis_bounds();

                    let chart = Chart::new(vec![dataset])
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(app.window),
                        )
                        .y_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );
                    f.render_widget(chart, chunks[1]);
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    break;
                }
                _ => {}
            },
        }
    }

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}
<commit_msg>Now properly kills the worker threads.<commit_after>use anyhow::Result;
use crossterm::event::{KeyEvent, KeyModifiers};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use histogram::Histogram;
use pinger::{ping, PingResult};
use std::io;
use std::io::Write;
use std::ops::Add;
use std::sync::atomic::Ordering;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset, GraphType, Paragraph};
use tui::{symbols, Terminal};

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.")]
struct Args {
    #[structopt(help = "Host or IP to ping")]
    host: String,
    #[structopt(
        short,
        long,
        default_value = "100",
        help = "Determines the number pings to display."
    )]
    buffer: usize,
}

struct App {
    data: Vec<(f64, f64)>,
    capacity: usize,
    idx: i64,
    window: [f64; 2],
}

impl App {
    fn new(capacity: usize) -> Self {
        App {
            data: Vec::with_capacity(capacity),
            capacity,
            idx: 0,
            window: [0.0, capacity as f64],
        }
    }
    fn update(&mut self, item: Option<Duration>) {
        self.idx += 1;
        if self.data.len() >= self.capacity {
            self.data.remove(0);
            self.window[0] += 1_f64;
            self.window[1] += 1_f64;
        }
        match item {
            Some(dur) => self.data.push((self.idx as f64, dur.as_micros() as f64)),
            None => self.data.push((self.idx as f64, 0_f64)),
        }
    }
    fn stats(&self) -> Histogram {
        let mut hist = Histogram::new();

        for (_, val) in self.data.iter().filter(|v| v.1 != 0f64) {
            hist.increment(*val as u64).unwrap_or(());
        }

        hist
    }
    fn y_axis_bounds(&self) -> [f64; 2] {
        let min = self
            .data
            .iter()
            .map(|v| v.1)
            .fold(f64::INFINITY, |a, b| a.min(b));
        let max = self.data.iter().map(|v| v.1).fold(0f64, |a, b| a.max(b));
        // Add a 10% buffer to the top and bottom
        let max_10_percent = (max * 10_f64) / 100_f64;
        let min_10_percent = (min * 10_f64) / 100_f64;
        [min - min_10_percent, max + max_10_percent]
    }
    fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Split into 5 sections
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let increment = Duration::from_micros((difference / 3f64) as u64);
        let duration = Duration::from_micros(min as u64);

        (0..7)
            .map(|i| Span::raw(format!("{:?}", duration.add(increment * i))))
            .collect()
    }
}

#[derive(Debug)]
enum Event {
    Update(PingResult),
    Input(KeyEvent),
}

fn main() -> Result<()> {
    let args = Args::from_args();
    let mut app = App::new(args.buffer);
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;

    let (key_tx, rx) = mpsc::channel();

    let ping_tx = key_tx.clone();

    let killed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));

    let host = args.host.clone();

    // Pump ping messages into the queue
    let killed_ping = std::sync::Arc::clone(&killed);
    let ping_thread = thread::spawn(move || -> Result<()> {
        let stream = ping(host).expect("Error pinging");
        while !killed_ping.load(Ordering::Acquire) {
            ping_tx.send(Event::Update(stream.recv()?))?;
        }
        Ok(())
    });

    // Pump keyboard messages into the queue
    let killed_thread = std::sync::Arc::clone(&killed);
    let key_thread = thread::spawn(move || -> Result<()> {
        while !killed_thread.load(Ordering::Acquire) {
            if event::poll(Duration::from_secs(1)).unwrap() {
                if let CEvent::Key(key) = event::read().unwrap() {
                    key_tx.send(Event::Input(key)).unwrap();
                }
            }
        }
        Ok(())
    });

    loop {
        match rx.recv()? {
            Event::Update(ping_result) => {
                match ping_result {
                    PingResult::Pong(duration) => app.update(Some(duration)),
                    PingResult::Timeout => app.update(None),
                };
                terminal.draw(|f| {
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(2)
                        .constraints(
                            [Constraint::Percentage(5), Constraint::Percentage(10)].as_ref(),
                        )
                        .split(f.size());

                    let header_layout = Layout::default()
                        .direction(Direction::Horizontal)
                        .constraints(
                            [
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                            ]
                            .as_ref(),
                        )
                        .split(chunks[0]);

                    f.render_widget(
                        Paragraph::new(format!("Pinging {}", &args.host)),
                        header_layout[0],
                    );

                    let stats = app.stats();

                    f.render_widget(
                        Paragraph::new(format!(
                            "min {:?}",
                            Duration::from_micros(stats.minimum().unwrap_or(0))
                        )),
                        header_layout[1],
                    );
                    f.render_widget(
                        Paragraph::new(format!(
                            "max {:?}",
                            Duration::from_micros(stats.maximum().unwrap_or(0))
                        )),
                        header_layout[2],
                    );
                    f.render_widget(
                        Paragraph::new(format!(
                            "p95 {:?}",
                            Duration::from_micros(stats.percentile(95.0).unwrap_or(0))
                        )),
                        header_layout[3],
                    );

                    let dataset = Dataset::default()
                        .marker(symbols::Marker::Braille)
                        .style(Style::default().fg(Color::Cyan))
                        .graph_type(GraphType::Line)
                        .data(&app.data);

                    let y_axis_bounds = app.y_axis_bounds();

                    let chart = Chart::new(vec![dataset])
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(app.window),
                        )
                        .y_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );
                    f.render_widget(chart, chunks[1]);
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                _ => {}
            },
        }
    }

    let _ = ping_thread.join().unwrap().unwrap();
    let _ = key_thread.join().unwrap().unwrap();

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate rayon;
extern crate libc;
extern crate rff;

mod interface;

use std::env;
use std::io::{self, BufRead, Write, BufWriter};
use std::process;
use rayon::prelude::*;
use getopts::{Options, Fail};
use rff::Choice;
use interface::{Interface, Error};

fn main() {
    let code = run();
    process::exit(code);
}

fn run() -> i32 {
    let args = env::args().skip(1).collect::<Vec<String>>();
    let mut opts = Options::new();

    opts.optopt("s", "search", "Output sorted matches of QUERY", "QUERY");
    opts.optopt("q", "query", "Use QUERY as the initial search string", "QUERY");
    opts.optflag("", "benchmark", "Run search in benchmark mode");
    opts.optflag("h", "help", "Display this help and exit");
    opts.optflag("v", "version", "Display version information and exit");

    let matches = match opts.parse(args).map_err(translate_parse_error) {
        Ok(matches) => matches,
        Err(err) => {
            println!("{}", err);
            print_usage(opts);
            return 1;
        }
    };

    if matches.opt_present("h") {
        print_usage(opts);
        return 0;
    }

    if matches.opt_present("v") {
        print_version();
        return 0;
    }

    let choices = get_choices();

    if matches.opt_present("benchmark") {
        if !matches.opt_present("s") {
            println!("Must specify -s/--search with --benchmark");
            return 1;
        }

        let search = matches.opt_str("s").unwrap();

        for _ in 0..100 {
            choices.
                par_iter().
                filter_map(|choice| Choice::new(&search, &choice)).
                collect::<Vec<Choice>>().
                sort_by(|a, b| b.partial_cmp(a).unwrap());
        }
    } else if matches.opt_present("s") {
        let search = matches.opt_str("s").unwrap();

        let mut choices = choices.
            par_iter().
            filter_map(|choice| Choice::new(&search, &choice)).
            collect::<Vec<Choice>>();

        choices.sort_by(|a, b| b.partial_cmp(a).unwrap());

        let stdout = io::stdout();
        let mut stdout = BufWriter::new(stdout.lock());

        for choice in choices {
            writeln!(stdout, "{}", choice.text()).unwrap();
        }
    } else {
        let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();

        let opts = interface::Options {
            choices: choices,
            initial: matches.opt_str("q").unwrap_or(String::new())
        };

        let mut interface = Interface::from_opts(opts);

        match interface.run() {
            Ok(result) => println!("{}", result),
            Err(Error::Exit) => return 1,
            Err(e) => {
                println!("error: {:?}", e);
                return 1;
            }
        }
    }

    0
}

fn get_choices() -> Vec<String> {
    let mut lines = vec![];

    let stdin = io::stdin();

    for line in stdin.lock().lines() {
        lines.push(line.unwrap());
    }

    lines
}

fn translate_parse_error(err: Fail) -> String {
    match err {
        Fail::ArgumentMissing(opt) => format!("Argument missing: {}", opt),
        Fail::UnrecognizedOption(opt) => format!("Invalid option: {}", opt),
        Fail::OptionMissing(opt) => format!("Missing option: {}", opt),
        Fail::OptionDuplicated(opt) => format!("Duplicated option: {}", opt),
        Fail::UnexpectedArgument(opt) => format!("Unexpected argument: {}", opt)
    }
}

fn print_usage(opts: Options) {
    let prelude = format!("Usage: {} [options]", env!("CARGO_PKG_NAME"));
    print!("{}", opts.usage(&prelude));
}

fn print_version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}
<commit_msg>Make clippy happy<commit_after>extern crate getopts;
extern crate rayon;
extern crate libc;
extern crate rff;

mod interface;

use std::env;
use std::io::{self, BufRead, Write, BufWriter};
use std::process;
use rayon::prelude::*;
use getopts::{Options, Fail};
use rff::Choice;
use interface::{Interface, Error};

fn main() {
    let code = run();
    process::exit(code);
}

fn run() -> i32 {
    let args = env::args().skip(1).collect::<Vec<String>>();
    let mut opts = Options::new();

    opts.optopt("s", "search", "Output sorted matches of QUERY", "QUERY");
    opts.optopt("q", "query", "Use QUERY as the initial search string", "QUERY");
    opts.optflag("", "benchmark", "Run search in benchmark mode");
    opts.optflag("h", "help", "Display this help and exit");
    opts.optflag("v", "version", "Display version information and exit");

    let matches = match opts.parse(args).map_err(translate_parse_error) {
        Ok(matches) => matches,
        Err(err) => {
            println!("{}", err);
            print_usage(opts);
            return 1;
        }
    };

    if matches.opt_present("h") {
        print_usage(opts);
        return 0;
    }

    if matches.opt_present("v") {
        print_version();
        return 0;
    }

    let choices = get_choices();

    if matches.opt_present("benchmark") {
        if !matches.opt_present("s") {
            println!("Must specify -s/--search with --benchmark");
            return 1;
        }

        let search = matches.opt_str("s").unwrap();

        for _ in 0..100 {
            choices.
                par_iter().
                filter_map(|choice| Choice::new(&search, choice)).
                collect::<Vec<Choice>>().
                sort_by(|a, b| b.partial_cmp(a).unwrap());
        }
    } else if matches.opt_present("s") {
        let search = matches.opt_str("s").unwrap();

        let mut choices = choices.
            par_iter().
            filter_map(|choice| Choice::new(&search, choice)).
            collect::<Vec<Choice>>();

        choices.sort_by(|a, b| b.partial_cmp(a).unwrap());

        let stdout = io::stdout();
        let mut stdout = BufWriter::new(stdout.lock());

        for choice in choices {
            writeln!(stdout, "{}", choice.text()).unwrap();
        }
    } else {
        let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();

        let opts = interface::Options {
            choices: choices,
            initial: matches.opt_str("q").unwrap_or_default()
        };

        let mut interface = Interface::from_opts(opts);

        match interface.run() {
            Ok(result) => println!("{}", result),
            Err(Error::Exit) => return 1,
            Err(e) => {
                println!("error: {:?}", e);
                return 1;
            }
        }
    }

    0
}

fn get_choices() -> Vec<String> {
    let mut lines = vec![];

    let stdin = io::stdin();

    for line in stdin.lock().lines() {
        lines.push(line.unwrap());
    }

    lines
}

fn translate_parse_error(err: Fail) -> String {
    match err {
        Fail::ArgumentMissing(opt) => format!("Argument missing: {}", opt),
        Fail::UnrecognizedOption(opt) => format!("Invalid option: {}", opt),
        Fail::OptionMissing(opt) => format!("Missing option: {}", opt),
        Fail::OptionDuplicated(opt) => format!("Duplicated option: {}", opt),
        Fail::UnexpectedArgument(opt) => format!("Unexpected argument: {}", opt)
    }
}

fn print_usage(opts: Options) {
    let prelude = format!("Usage: {} [options]", env!("CARGO_PKG_NAME"));
    print!("{}", opts.usage(&prelude));
}

fn print_version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate git2;
extern crate yaml_rust;

use backend::Backend;
use cli::cli;
use conf::{Conf,get_configuration,create_default_config,Value,MonitoredRemote};
use constants::*;

use std::collections::HashMap;

use git2::Repository;

// util mod def needs to be first b/c of macro definitions and usage in other modules
#[macro_use]
mod util;
mod backend;
mod cli;
mod conf;
mod constants;

fn format_value(value: Value, data: String) -> String {
    format!("{}{}{}{}", value.pre_format, value.label, data, value.post_format)
}

fn substiute_special_values(s: String, values: &HashMap<String, String>) -> String {
    let mut r:String = s.clone();
    for (k, v) in values {
        r = r.replace(k, &v);
    }
    r
}

// struct for displaying all the data -- the actual program
struct Program {
    out: Vec<String>,
    backend: Backend,
    conf: Conf,
    debug: bool
}

impl Program {
    pub fn new(backend: Backend, conf: Conf, debug: bool) -> Program {
        let out: Vec<String> = Vec::new();
        Program { out: out, backend: backend, conf: conf, debug: debug }
    }

    // add repository state to output buffer
    fn add_repository_state(&mut self) {
        let repo_state = self.backend.get_repository_state();
        if !repo_state.is_empty() {
            self.out.push(repo_state);
        }
    }

    fn add_monitored_branches_state(&mut self) {
        let mut local_branch: String = self.backend.get_current_branch_name();
        let mr = match self.conf.get_remotes_monitoring() {
            Some(x) => x,
            None => return (),
        };
        for monitored_remote in mr {
            let b = match monitored_remote.branch {
                Some(x) => x,
                None => local_branch.clone()
            };
            let (ahead, behind) = match self.backend.get_remote_branch_ahead_behind(&monitored_remote.remote_name, &b) {
                Some(x) => x,
                None => (0, 0),
            };
            if monitored_remote.display_if_uptodate || ahead > 0 || behind > 0 {
                if let (Some(a_v), Some(b_v)) = (
                    self.conf.get_difference_ahead_value(),
                    self.conf.get_difference_behind_value()
                ) {
                    let mut special_values: HashMap<String, String> = HashMap::new();
                    special_values.insert("<BRANCH>".to_string(), b);
                    special_values.insert("<REMOTE>".to_string(), monitored_remote.remote_name);
                    let mut local: String = format!(
                        "{}{}",
                        substiute_special_values(monitored_remote.pre_format, &special_values),
                        substiute_special_values(monitored_remote.post_format, &special_values),
                    );
                    if ahead > 0 {
                        local += &format_value(a_v, ahead.to_string());
                    }
                    if behind > 0 {
                        local += &format_value(b_v, behind.to_string());
                    }
                    self.out.push(local);
                }
            }
        }
    }

    fn add_file_status(&mut self) {
        if let Some(s) = self.backend.get_file_status() {
            if !s.is_empty() {
                let mut o = String::from("");

                if let Some(x) = s.get(NEW_KEY) {
                    match self.conf.get_new_value() {
                        Some(y) => o += &format!("{}{}{}{}", y.pre_format, y.label, x, y.post_format),
                        None => (),
                    };
                }
                if let Some(x) = s.get(CHANGED_KEY) {
                    match self.conf.get_changed_value() {
                        Some(y) => o += &format!("{}{}{}{}", y.pre_format, y.label, x, y.post_format),
                        None => (),
                    };
                }
                if let Some(x) = s.get(STAGED_KEY) {
                    match self.conf.get_staged_value() {
                        Some(y) => o += &format!("{}{}{}{}", y.pre_format, y.label, x, y.post_format),
                        None => (),
                    };
                }
                if let Some(x) = s.get(CONFLICTS_KEY) {
                    match self.conf.get_conflicts_value() {
                        Some(y) => o += &format!("{}{}{}{}", y.pre_format, y.label, x, y.post_format),
                        None => (),
                    };
                }
                self.out.push(o);
            }
        }
    }

    fn populate(&mut self) {
        self.add_repository_state();
        self.add_monitored_branches_state();
        self.add_file_status();
    }

    // print output buffer
    fn output(&self) {
        log!(self, "# of blocks = {}", self.out.len());
        println!("{}", self.out.join("|"));
    }
}

fn main() {
    let app = cli();
    let matches = app.get_matches();

    let debug_enabled = matches.is_present("debug");
    if debug_enabled { println!("Debug messages are enabled."); }

    let repo = match Repository::discover(".") {
        Ok(repo) => repo,
        // not a git repository, ignore
        Err(e) => {
            if debug_enabled { println!("This is not a git repository: {:?}", e); }
            return ();
        }
    };

    let mut conf_path: Option<String> = None;
    if matches.is_present("config") {
        conf_path = Some(String::from(matches.value_of("config").unwrap()));
    }

    let conf = get_configuration(conf_path);

    if matches.is_present(CLI_DEFAULT_CONFIG_SUBC_NAME) {
        let p = get_default_config_path();
        match create_default_config(p.clone()) {
            Ok(path) => {
                println!("Configuration file created at \"{}\"", path);
                return ();
            }
            Err(e) => {
                println!("Failed to create configuration file \"{}\": {}", p.to_str().unwrap(), e);
                return ();
            }
        };
    }

    let backend = Backend{ repo: repo, debug: debug_enabled };
    let mut output = Program::new(backend, conf, debug_enabled);

    output.populate();
    output.output();
}
<commit_msg>quote output when --debug<commit_after>extern crate clap;
extern crate git2;
extern crate yaml_rust;

use backend::Backend;
use cli::cli;
use conf::{Conf,get_configuration,create_default_config,Value,MonitoredRemote};
use constants::*;

use std::collections::HashMap;

use git2::Repository;

// util mod def needs to be first b/c of macro definitions and usage in other modules
#[macro_use]
mod util;
mod backend;
mod cli;
mod conf;
mod constants;

fn format_value(value: Value, data: String) -> String {
    format!("{}{}{}{}", value.pre_format, value.label, data, value.post_format)
}

fn substiute_special_values(s: String, values: &HashMap<String, String>) -> String {
    let mut r:String = s.clone();
    for (k, v) in values {
        r = r.replace(k, &v);
    }
    r
}

// struct for displaying all the data -- the actual program
struct Program {
    out: Vec<String>,
    backend: Backend,
    conf: Conf,
    debug: bool
}

impl Program {
    pub fn new(backend: Backend, conf: Conf, debug: bool) -> Program {
        let out: Vec<String> = Vec::new();
        Program { out: out, backend: backend, conf: conf, debug: debug }
    }

    // add repository state to output buffer
    fn add_repository_state(&mut self) {
        let repo_state = self.backend.get_repository_state();
        if !repo_state.is_empty() {
            self.out.push(repo_state);
        }
    }

    fn add_monitored_branches_state(&mut self) {
        let mut local_branch: String = self.backend.get_current_branch_name();
        let mr = match self.conf.get_remotes_monitoring() {
            Some(x) => x,
            None => return (),
        };
        for monitored_remote in mr {
            let b = match monitored_remote.branch {
                Some(x) => x,
                None => local_branch.clone()
            };
            let (ahead, behind) = match self.backend.get_remote_branch_ahead_behind(&monitored_remote.remote_name, &b) {
                Some(x) => x,
                None => (0, 0),
            };
            if monitored_remote.display_if_uptodate || ahead > 0 || behind > 0 {
                if let (Some(a_v), Some(b_v)) = (
                    self.conf.get_difference_ahead_value(),
                    self.conf.get_difference_behind_value()
                ) {
                    let mut special_values: HashMap<String, String> = HashMap::new();
                    special_values.insert("<BRANCH>".to_string(), b);
                    special_values.insert("<REMOTE>".to_string(), monitored_remote.remote_name);
                    let mut local: String = format!(
                        "{}{}",
                        substiute_special_values(monitored_remote.pre_format, &special_values),
                        substiute_special_values(monitored_remote.post_format, &special_values),
                    );
                    if ahead > 0 {
                        local += &format_value(a_v, ahead.to_string());
                    }
                    if behind > 0 {
                        local += &format_value(b_v, behind.to_string());
                    }
                    self.out.push(local);
                }
            }
        }
    }

    fn add_file_status(&mut self) {
        if let Some(s) = self.backend.get_file_status() {
            if !s.is_empty() {
                let mut o = String::from("");

                if let Some(x) = s.get(NEW_KEY) {
                    match self.conf.get_new_value() {
                        Some(y) => o += &format!("{}{}{}{}", y.pre_format, y.label, x, y.post_format),
                        None => (),
                    };
                }
                if let Some(x) = s.get(CHANGED_KEY) {
                    match self.conf.get_changed_value() {
                        Some(y) => o += &format!("{}{}{}{}", y.pre_format, y.label, x, y.post_format),
                        None => (),
                    };
                }
                if let Some(x) = s.get(STAGED_KEY) {
                    match self.conf.get_staged_value() {
                        Some(y) => o += &format!("{}{}{}{}", y.pre_format, y.label, x, y.post_format),
                        None => (),
                    };
                }
                if let Some(x) = s.get(CONFLICTS_KEY) {
                    match self.conf.get_conflicts_value() {
                        Some(y) => o += &format!("{}{}{}{}", y.pre_format, y.label, x, y.post_format),
                        None => (),
                    };
                }
                self.out.push(o);
            }
        }
    }

    fn populate(&mut self) {
        self.add_repository_state();
        self.add_monitored_branches_state();
        self.add_file_status();
    }

    // print output buffer
    fn output(&self) {
        log!(self, "# of blocks = {}", self.out.len());
        let output = self.out.join("|");
        if self.debug {
            println!("'{}'", output);
        } else {
            println!("{}", output);
        }
    }
}

fn main() {
    let app = cli();
    let matches = app.get_matches();

    let debug_enabled = matches.is_present("debug");
    if debug_enabled { println!("Debug messages are enabled."); }

    let repo = match Repository::discover(".") {
        Ok(repo) => repo,
        // not a git repository, ignore
        Err(e) => {
            if debug_enabled { println!("This is not a git repository: {:?}", e); }
            return ();
        }
    };

    let mut conf_path: Option<String> = None;
    if matches.is_present("config") {
        conf_path = Some(String::from(matches.value_of("config").unwrap()));
    }

    let conf = get_configuration(conf_path);

    if matches.is_present(CLI_DEFAULT_CONFIG_SUBC_NAME) {
        let p = get_default_config_path();
        match create_default_config(p.clone()) {
            Ok(path) => {
                println!("Configuration file created at \"{}\"", path);
                return ();
            }
            Err(e) => {
                println!("Failed to create configuration file \"{}\": {}", p.to_str().unwrap(), e);
                return ();
            }
        };
    }

    let backend = Backend{ repo: repo, debug: debug_enabled };
    let mut output = Program::new(backend, conf, debug_enabled);

    output.populate();
    output.output();
}
<|endoftext|>


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

mod aabb;
mod bvh;
mod camera;
mod color;
mod consts;
mod gl_renderer;
mod index_ptr;
mod input;
mod intersect;
mod light;
mod load;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod scene;
mod stats;
mod texture;
mod triangle;
mod util;
mod vertex;

use std::path::PathBuf;

use chrono::Local;

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

use crate::gl_renderer::GLRenderer;
use crate::input::InputState;
use crate::pt_renderer::{PTRenderer, RenderConfig};

type Float = f64;

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

fn benchmark() {
    let scenes = [
        "plane",
        "cornell-glossy",
        "cornell-water",
        "indirect",
        "conference",
        "sponza",
    ];
    let config = RenderConfig::benchmark();
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let output_dir = root_dir.join("results");
    std::fs::create_dir_all(output_dir.clone()).unwrap();
    let time_stamp = Local::now().format("%F_%H%M%S").to_string();
    for scene_name in &scenes {
        stats::new_scene(scene_name);
        let _t = stats::time("Total");
        let mut pt_renderer = PTRenderer::new();
        println!("{}...", scene_name);
        let (scene, camera) = load::load_cpu_scene(scene_name);
        pt_renderer.offline_render(&scene, &camera, &config);

        // Save timestamped version in a addition to the default image
        let scene_dir = output_dir.join(scene_name);
        std::fs::create_dir_all(scene_dir.clone()).unwrap();
        let timestamped_image = scene_dir.join(format!("{}_{}.png", scene_name, time_stamp));
        let default_image = output_dir.join(scene_name).with_extension("png");
        pt_renderer.save_image(&timestamped_image);
        pt_renderer.save_image(&default_image);
    }
    let stats_file = output_dir.join(format!("benchmark_{}.txt", time_stamp));
    stats::print_and_save(&stats_file);
}

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

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

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

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

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

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent {
                    event: WindowEvent::KeyboardInput { input, .. },
                    ..
                } => match input {
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::Space),
                        ..
                    } => {
                        trace = !trace;
                        if trace {
                            pt_renderer.online_render(&display, &scene, &camera, &config);
                        } else {
                            pt_renderer.stop_threads();
                        }
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::C),
                        ..
                    } => {
                        println!("{:?}, {:?}", camera.pos, camera.rot);
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::N),
                        ..
                    } => {
                        config.normal_mapping = !config.normal_mapping;
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(keycode),
                        ..
                    } => {
                        if !trace {
                            if let Some(res) = load::load_gpu_scene(keycode, &display) {
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        }
                    }
                    _ => (),
                },
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    ..
                } => quit = true,
                _ => (),
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            if trace {
                pt_renderer.stop_threads();
            }
            return;
        }
    }
}
<commit_msg>Enable changing render config on the fly<commit_after>#![feature(duration_float)]
#![feature(euclidean_division)]
#![feature(self_struct_ctor)]
#![feature(try_trait)]

mod aabb;
mod bvh;
mod camera;
mod color;
mod consts;
mod gl_renderer;
mod index_ptr;
mod input;
mod intersect;
mod light;
mod load;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod scene;
mod stats;
mod texture;
mod triangle;
mod util;
mod vertex;

use std::path::PathBuf;

use chrono::Local;

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

use crate::gl_renderer::GLRenderer;
use crate::input::InputState;
use crate::pt_renderer::{PTRenderer, RenderConfig};

type Float = f64;

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

fn benchmark() {
    let scenes = [
        "plane",
        "cornell-glossy",
        "cornell-water",
        "indirect",
        "conference",
        "sponza",
    ];
    let config = RenderConfig::benchmark();
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let output_dir = root_dir.join("results");
    std::fs::create_dir_all(output_dir.clone()).unwrap();
    let time_stamp = Local::now().format("%F_%H%M%S").to_string();
    for scene_name in &scenes {
        stats::new_scene(scene_name);
        let _t = stats::time("Total");
        let mut pt_renderer = PTRenderer::new();
        println!("{}...", scene_name);
        let (scene, camera) = load::load_cpu_scene(scene_name);
        pt_renderer.offline_render(&scene, &camera, &config);

        // Save timestamped version in a addition to the default image
        let scene_dir = output_dir.join(scene_name);
        std::fs::create_dir_all(scene_dir.clone()).unwrap();
        let timestamped_image = scene_dir.join(format!("{}_{}.png", scene_name, time_stamp));
        let default_image = output_dir.join(scene_name).with_extension("png");
        pt_renderer.save_image(&timestamped_image);
        pt_renderer.save_image(&default_image);
    }
    let stats_file = output_dir.join(format!("benchmark_{}.txt", time_stamp));
    stats::print_and_save(&stats_file);
}

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

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

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

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

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

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent {
                    event: WindowEvent::KeyboardInput { input, .. },
                    ..
                } => match input {
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::Space),
                        ..
                    } => {
                        trace = !trace;
                        if trace {
                            pt_renderer.online_render(&display, &scene, &camera, &config);
                        } else {
                            pt_renderer.stop_threads();
                        }
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::C),
                        ..
                    } => {
                        println!("{:?}, {:?}", camera.pos, camera.rot);
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::N),
                        ..
                    } => {
                        config.normal_mapping = !config.normal_mapping;
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::F1),
                        ..
                    } => {
                        config = RenderConfig::default();
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::F2),
                        ..
                    } => {
                        config = RenderConfig::debug_normals();
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::F3),
                        ..
                    } => {
                        config = RenderConfig::forward_normals();
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(keycode),
                        ..
                    } => {
                        if !trace {
                            if let Some(res) = load::load_gpu_scene(keycode, &display) {
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        }
                    }
                    _ => (),
                },
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    ..
                } => quit = true,
                _ => (),
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            if trace {
                pt_renderer.stop_threads();
            }
            return;
        }
    }
}
<|endoftext|>