<commit_before>use clap::{Arg, App, SubCommand};
use super::{APP_VERSION, APP_NAME};

pub fn build_cli() -> App<'static, 'static> {
    App::new(APP_NAME)
        .version(APP_VERSION)
        .author("James K. <james.kominick@gmail.com>")
        .about("Postgres/SQLite/MySQL migration manager")
        .subcommand(SubCommand::with_name("self")
                    .about("Self referential things")
                    .subcommand(SubCommand::with_name("update")
                        .about("Update to the latest binary release, replacing this binary")
                        .arg(Arg::with_name("no_confirm")
                             .help("Skip download/update confirmation")
                             .long("no-confirm")
                             .short("y")
                             .required(false)
                             .takes_value(false))
                        .arg(Arg::with_name("quiet")
                             .help("Suppress unnecessary download output (progress bar)")
                             .long("quiet")
                             .short("q")
                             .required(false)
                             .takes_value(false)))
                    .subcommand(SubCommand::with_name("bash-completions")
                        .about("Generate bash completions & output to stdout or a file if specified")
                        .subcommand(SubCommand::with_name("install")
                            .about("Installs generated bash completions")
                            .arg(Arg::with_name("path")
                                .help("Path to install bash completions at")
                                .long("path")
                                .default_value("/etc/bash_completion.d/migrant")
                                .takes_value(true)))))
        .subcommand(SubCommand::with_name("init")
            .about("Initialize project config")
            .arg(Arg::with_name("type")
                 .long("type")
                 .short("t")
                 .takes_value(true)
                 .help("Specify the database type (sqlite|postgres)"))
            .arg(Arg::with_name("location")
                 .long("location")
                 .short("l")
                 .takes_value(true)
                 .help("Directory to initialize in"))
            .arg(Arg::with_name("no-confirm")
                 .long("no-confirm")
                 .takes_value(false)
                 .help("Disable interactive prompts")))
        .subcommand(SubCommand::with_name("setup")
            .about("Setup migration table"))
        .subcommand(SubCommand::with_name("connect-string")
            .about("Print out the connection string for postgres, or file-path for sqlite"))
        .subcommand(SubCommand::with_name("list")
            .about("List status of applied and available migrations"))
        .subcommand(SubCommand::with_name("apply")
            .about("Moves up or down (applies up/down.sql) one migration. Default direction is up unless specified with `-d/--down`.")
            .arg(Arg::with_name("down")
                .long("down")
                .short("d")
                .help("Applies `down.sql` migrations"))
            .arg(Arg::with_name("all")
                .long("all")
                .short("a")
                .help("Applies all available migrations"))
            .arg(Arg::with_name("force")
                .long("force")
                .help("Applies the migration and treats it as if it were successful"))
            .arg(Arg::with_name("fake")
                .long("fake")
                .help("Updates the migrations table as if the specified migration was applied")))
        .subcommand(SubCommand::with_name("redo")
            .about("Shortcut for running the latest `down` and `up` migration. Can be augmented with `all` and `force`")
            .arg(Arg::with_name("all")
                .long("all")
                .short("a")
                .help("Applies all available migrations"))
            .arg(Arg::with_name("force")
                .long("force")
                .help("Applies the migration and treats it as if it were successful")))
        .subcommand(SubCommand::with_name("new")
            .about("Create new migration up/down files")
            .arg(Arg::with_name("tag")
                 .required(true)
                 .help("tag to use for new migration")))
        .subcommand(SubCommand::with_name("shell")
            .about("Open a repl connection"))
        .subcommand(SubCommand::with_name("edit")
            .about("Edit a migration file by tag name")
            .arg(Arg::with_name("tag")
                 .required(true)
                 .help("Tag name"))
            .arg(Arg::with_name("down")
                 .long("down")
                 .short("d")
                 .help("Edit the down.sql file")))
        .subcommand(SubCommand::with_name("which-config")
            .about("Display the path to the configuration file being used"))
}

<commit_msg>update email<commit_after>use clap::{Arg, App, SubCommand};
use super::{APP_VERSION, APP_NAME};

pub fn build_cli() -> App<'static, 'static> {
    App::new(APP_NAME)
        .version(APP_VERSION)
        .author("James K. <james@kominick.com>")
        .about("Postgres/SQLite/MySQL migration manager")
        .subcommand(SubCommand::with_name("self")
                    .about("Self referential things")
                    .subcommand(SubCommand::with_name("update")
                        .about("Update to the latest binary release, replacing this binary")
                        .arg(Arg::with_name("no_confirm")
                             .help("Skip download/update confirmation")
                             .long("no-confirm")
                             .short("y")
                             .required(false)
                             .takes_value(false))
                        .arg(Arg::with_name("quiet")
                             .help("Suppress unnecessary download output (progress bar)")
                             .long("quiet")
                             .short("q")
                             .required(false)
                             .takes_value(false)))
                    .subcommand(SubCommand::with_name("bash-completions")
                        .about("Generate bash completions & output to stdout or a file if specified")
                        .subcommand(SubCommand::with_name("install")
                            .about("Installs generated bash completions")
                            .arg(Arg::with_name("path")
                                .help("Path to install bash completions at")
                                .long("path")
                                .default_value("/etc/bash_completion.d/migrant")
                                .takes_value(true)))))
        .subcommand(SubCommand::with_name("init")
            .about("Initialize project config")
            .arg(Arg::with_name("type")
                 .long("type")
                 .short("t")
                 .takes_value(true)
                 .help("Specify the database type (sqlite|postgres)"))
            .arg(Arg::with_name("location")
                 .long("location")
                 .short("l")
                 .takes_value(true)
                 .help("Directory to initialize in"))
            .arg(Arg::with_name("no-confirm")
                 .long("no-confirm")
                 .takes_value(false)
                 .help("Disable interactive prompts")))
        .subcommand(SubCommand::with_name("setup")
            .about("Setup migration table"))
        .subcommand(SubCommand::with_name("connect-string")
            .about("Print out the connection string for postgres, or file-path for sqlite"))
        .subcommand(SubCommand::with_name("list")
            .about("List status of applied and available migrations"))
        .subcommand(SubCommand::with_name("apply")
            .about("Moves up or down (applies up/down.sql) one migration. Default direction is up unless specified with `-d/--down`.")
            .arg(Arg::with_name("down")
                .long("down")
                .short("d")
                .help("Applies `down.sql` migrations"))
            .arg(Arg::with_name("all")
                .long("all")
                .short("a")
                .help("Applies all available migrations"))
            .arg(Arg::with_name("force")
                .long("force")
                .help("Applies the migration and treats it as if it were successful"))
            .arg(Arg::with_name("fake")
                .long("fake")
                .help("Updates the migrations table as if the specified migration was applied")))
        .subcommand(SubCommand::with_name("redo")
            .about("Shortcut for running the latest `down` and `up` migration. Can be augmented with `all` and `force`")
            .arg(Arg::with_name("all")
                .long("all")
                .short("a")
                .help("Applies all available migrations"))
            .arg(Arg::with_name("force")
                .long("force")
                .help("Applies the migration and treats it as if it were successful")))
        .subcommand(SubCommand::with_name("new")
            .about("Create new migration up/down files")
            .arg(Arg::with_name("tag")
                 .required(true)
                 .help("tag to use for new migration")))
        .subcommand(SubCommand::with_name("shell")
            .about("Open a repl connection"))
        .subcommand(SubCommand::with_name("edit")
            .about("Edit a migration file by tag name")
            .arg(Arg::with_name("tag")
                 .required(true)
                 .help("Tag name"))
            .arg(Arg::with_name("down")
                 .long("down")
                 .short("d")
                 .help("Edit the down.sql file")))
        .subcommand(SubCommand::with_name("which-config")
            .about("Display the path to the configuration file being used"))
}

<|endoftext|>


<commit_before>extern crate rand;

use atty::Stream;
use std;

use rand::Rng;

use std::thread::sleep;
use std::time::Duration;

use std::io::Write;
use std::io::stdout;

// A struct to contain info we need to print with every character
pub struct Control {
    pub seed: f64,
    pub spread: f64,
    pub frequency: f64,
    pub background_mode: bool,
    pub dialup_mode: bool,
}

// This used to have more of a reason to exist, however now all its functionality is in
// print_chars_lol(). It takes in an iterator over lines and prints them all.
// At the end, it resets the foreground color
pub fn print_lines_lol<I: Iterator<Item=S>, S: AsRef<str>>(lines: I, c: &mut Control) {
    for line in lines {
        print_chars_lol(line.as_ref().chars().chain(Some('\n')), c, false);
    }
    print!("\x1b[39m");
}

// Takes in s an iterator over characters
// duplicates escape sequences, otherwise prints printable characters with colored_print
// Print newlines correctly, resetting background
// If constantly_flush is on, it won't wait till a newline to flush stdout
pub fn print_chars_lol<I: Iterator<Item=char>>(mut iter: I, c: &mut Control, constantly_flush: bool) {
    let original_seed = c.seed;
    let mut ignore_whitespace = c.background_mode;

    if !atty::is(Stream::Stdout) {
        for s in iter {
            print!("{}", s);
        }
        return;
    }

    while let Some(character) = iter.next() {
        match character {
        // Consume escape sequences
        '\x1b' => {
            // Escape sequences seem to be one of many different categories
            // CSI sequences are \e \[ [bytes in 0x30-0x3F] [bytes in 0x20-0x2F] [final byte in 0x40-0x7E]
            // nF Escape seq are \e [bytes in 0x20-0x2F] [byte in 0x30-0x7E]
            // Fp Escape seq are \e [byte in 0x30-0x3F] [I have no idea, but `sl` creates one where the next byte is the end of the escape sequence, so assume that]
            // Fe Escape seq are \e [byte in 0x40-0x5F] [I have no idea, '' though sl doesn't make one]
            // Fs Escape seq are \e [byte in 0x60-0x7E] [I have no idea, '' though sl doesn't make one]
            // Otherwise the next byte is the whole escape sequence (maybe? I can't exactly tell, but I will go with it)
            // We will consume up to, but not through, the next printable character
            // In addition, we print everything in the escape sequence, even if it is a color (that will be overriden)
            print!("\x1b");
            let mut escape_sequence_character = iter.next().expect("Escape character with no escape sequence after it");
            print!("{}", escape_sequence_character);
            match escape_sequence_character {
            '[' => {
                loop {
                    escape_sequence_character = iter.next().expect("CSI escape sequence did not terminate");
                    print!("{}", escape_sequence_character);
                    match escape_sequence_character {
                    '\x30' ..= '\x3F' => continue,
                    '\x20' ..= '\x2F' => {
                        loop {
                            escape_sequence_character = iter.next().expect("CSI escape sequence did not terminate");
                            print!("{}", escape_sequence_character);
                            match escape_sequence_character {
                            '\x20' ..= '\x2F' => continue,
                            '\x40' ..= '\x7E' => break,
                            _ => panic!("CSI escape sequence terminated with an incorrect value"),
                            }
                        }
                        break;
                    },
                    '\x40' ..= '\x7E' => break,
                    _ => panic!("CSI escape sequence terminated with an incorrect value"),
                    }
                }
            },
            '\x20' ..= '\x2F' => {
                loop {
                    escape_sequence_character = iter.next().expect("nF escape sequence did not terminate");
                    print!("{}", escape_sequence_character);
                    match escape_sequence_character {
                    '\x20' ..= '\x2F' => continue,
                    '\x30' ..= '\x7E' => break,
                    _ => panic!("nF escape sequence terminated with an incorrect value"),
                    }
                }
            },
//            '\x30' ..= '\x3F' => panic!("Fp escape sequences are not supported"),
//            '\x40' ..= '\x5F' => panic!("Fe escape sequences are not supported"),
//            '\x60' ..= '\x7E' => panic!("Fs escape sequences are not supported"),
            // be lazy and assume in all other cases we consume exactly 1 byte
            _ => (),
            }
        },
        // Newlines print escape sequences to end background prints, and in dialup mode sleep, and
        // reset the seed of the coloring and the value of ignore_whitespace
        '\n' => {
            if atty::is(Stream::Stdout) {
                // Reset the background color only, as we don't have to reset the foreground till
                // the end of the program
                // We reset the background here because otherwise it bleeds all the way to the next line
                if c.background_mode {
                    print!("\x1b[49m");
                }
            }
            println!();
            if c.dialup_mode {
                let stall = Duration::from_millis(rand::thread_rng().gen_range(30, 700));
                sleep(stall);
            }

            c.seed = original_seed + 1.0; // Reset the seed, but bump it a bit
            ignore_whitespace = c.background_mode;
        },
        // If not an escape sequence or a newline, print a colorful escape sequence and then the
        // character
        _ => {
            // In background mode, don't print colorful whitespace until the first printable character
            if ignore_whitespace && character.is_whitespace() {
                print!("{}", character);
                continue;
            } else {
                ignore_whitespace = false;
            }

            colored_print(character, c);
            c.seed += 1.0;
        },
        }

        // If we should constantly flush, flush after each completed sequence, and also reset
        // colors because otherwise weird things happen
        if constantly_flush {
            reset_colors(c);
            stdout().flush().unwrap();
        }
    }
}

fn reset_colors(c: &Control) {
    if atty::is(Stream::Stdout) {
        // Reset the background color
        if c.background_mode {
            print!("\x1b[49m");
        }

        // Reset the foreground color
        print!("\x1b[39m");
    }
}

fn colored_print(character: char, c: &mut Control) {
    if c.background_mode {
        let bg = get_color_tuple(c);
        let fg = calc_fg_color(bg);
        print!(
            "\x1b[38;2;{};{};{};48;2;{};{};{}m{}",
            fg.0, fg.1, fg.2, bg.0, bg.1, bg.2, character
        );
    } else {
        let fg = get_color_tuple(c);
        print!("\x1b[38;2;{};{};{}m{}", fg.0, fg.1, fg.2, character);
    }
}

fn calc_fg_color(bg: (u8, u8, u8)) -> (u8, u8, u8) {
    // Currently, it only computes the forground clolor based on some threshold
    // on grayscale value.
    // TODO: Add a better algorithm for computing forground color
    if conv_grayscale(bg) > 0xA0_u8 {
        (0u8, 0u8, 0u8)
    } else {
        (0xffu8, 0xffu8, 0xffu8)
    }
}

fn linear_to_srgb(intensity: f64) -> f64 {
    if intensity <= 0.003_130_8 {
        12.92 * intensity
    } else {
        1.055 * intensity.powf(1.0 / 2.4) - 0.055
    }
}

fn srgb_to_linear(intensity: f64) -> f64 {
    if intensity < 0.04045 {
        intensity / 12.92
    } else {
        ((intensity + 0.055) / 1.055).powf(2.4)
    }
}

fn conv_grayscale(color: (u8, u8, u8)) -> u8 {
    // See https://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
    const SCALE: f64 = 256.0;

    // Changing SRGB to Linear for gamma correction
    let red = srgb_to_linear(f64::from(color.0) / SCALE);
    let green = srgb_to_linear(f64::from(color.1) / SCALE);
    let blue = srgb_to_linear(f64::from(color.2) / SCALE);

    // Converting to grayscale
    let gray_linear = red * 0.299 + green * 0.587 + blue * 0.114;

    // Gamma correction
    let gray_srgb = linear_to_srgb(gray_linear);

    (gray_srgb * SCALE) as u8
}

fn get_color_tuple(c: &Control) -> (u8, u8, u8) {
    let i = c.frequency * c.seed / c.spread;
    let red = i.sin() * 127.00 + 128.00;
    let green = (i + (std::f64::consts::PI * 2.00 / 3.00)).sin() * 127.00 + 128.00;
    let blue = (i + (std::f64::consts::PI * 4.00 / 3.00)).sin() * 127.00 + 128.00;

    (red as u8, green as u8, blue as u8)
}
<commit_msg>Add the link to ANSI escape sequences that I based my parsing off of<commit_after>extern crate rand;

use atty::Stream;
use std;

use rand::Rng;

use std::thread::sleep;
use std::time::Duration;

use std::io::Write;
use std::io::stdout;

// A struct to contain info we need to print with every character
pub struct Control {
    pub seed: f64,
    pub spread: f64,
    pub frequency: f64,
    pub background_mode: bool,
    pub dialup_mode: bool,
}

// This used to have more of a reason to exist, however now all its functionality is in
// print_chars_lol(). It takes in an iterator over lines and prints them all.
// At the end, it resets the foreground color
pub fn print_lines_lol<I: Iterator<Item=S>, S: AsRef<str>>(lines: I, c: &mut Control) {
    for line in lines {
        print_chars_lol(line.as_ref().chars().chain(Some('\n')), c, false);
    }
    print!("\x1b[39m");
}

// Takes in s an iterator over characters
// duplicates escape sequences, otherwise prints printable characters with colored_print
// Print newlines correctly, resetting background
// If constantly_flush is on, it won't wait till a newline to flush stdout
pub fn print_chars_lol<I: Iterator<Item=char>>(mut iter: I, c: &mut Control, constantly_flush: bool) {
    let original_seed = c.seed;
    let mut ignore_whitespace = c.background_mode;

    if !atty::is(Stream::Stdout) {
        for s in iter {
            print!("{}", s);
        }
        return;
    }

    while let Some(character) = iter.next() {
        match character {
        // Consume escape sequences
        '\x1b' => {
            // Escape sequences seem to be one of many different categories: https://en.wikipedia.org/wiki/ANSI_escape_code
            // CSI sequences are \e \[ [bytes in 0x30-0x3F] [bytes in 0x20-0x2F] [final byte in 0x40-0x7E]
            // nF Escape seq are \e [bytes in 0x20-0x2F] [byte in 0x30-0x7E]
            // Fp Escape seq are \e [byte in 0x30-0x3F] [I have no idea, but `sl` creates one where the next byte is the end of the escape sequence, so assume that]
            // Fe Escape seq are \e [byte in 0x40-0x5F] [I have no idea, '' though sl doesn't make one]
            // Fs Escape seq are \e [byte in 0x60-0x7E] [I have no idea, '' though sl doesn't make one]
            // Otherwise the next byte is the whole escape sequence (maybe? I can't exactly tell, but I will go with it)
            // We will consume up to, but not through, the next printable character
            // In addition, we print everything in the escape sequence, even if it is a color (that will be overriden)
            print!("\x1b");
            let mut escape_sequence_character = iter.next().expect("Escape character with no escape sequence after it");
            print!("{}", escape_sequence_character);
            match escape_sequence_character {
            '[' => {
                loop {
                    escape_sequence_character = iter.next().expect("CSI escape sequence did not terminate");
                    print!("{}", escape_sequence_character);
                    match escape_sequence_character {
                    '\x30' ..= '\x3F' => continue,
                    '\x20' ..= '\x2F' => {
                        loop {
                            escape_sequence_character = iter.next().expect("CSI escape sequence did not terminate");
                            print!("{}", escape_sequence_character);
                            match escape_sequence_character {
                            '\x20' ..= '\x2F' => continue,
                            '\x40' ..= '\x7E' => break,
                            _ => panic!("CSI escape sequence terminated with an incorrect value"),
                            }
                        }
                        break;
                    },
                    '\x40' ..= '\x7E' => break,
                    _ => panic!("CSI escape sequence terminated with an incorrect value"),
                    }
                }
            },
            '\x20' ..= '\x2F' => {
                loop {
                    escape_sequence_character = iter.next().expect("nF escape sequence did not terminate");
                    print!("{}", escape_sequence_character);
                    match escape_sequence_character {
                    '\x20' ..= '\x2F' => continue,
                    '\x30' ..= '\x7E' => break,
                    _ => panic!("nF escape sequence terminated with an incorrect value"),
                    }
                }
            },
//            '\x30' ..= '\x3F' => panic!("Fp escape sequences are not supported"),
//            '\x40' ..= '\x5F' => panic!("Fe escape sequences are not supported"),
//            '\x60' ..= '\x7E' => panic!("Fs escape sequences are not supported"),
            // be lazy and assume in all other cases we consume exactly 1 byte
            _ => (),
            }
        },
        // Newlines print escape sequences to end background prints, and in dialup mode sleep, and
        // reset the seed of the coloring and the value of ignore_whitespace
        '\n' => {
            if atty::is(Stream::Stdout) {
                // Reset the background color only, as we don't have to reset the foreground till
                // the end of the program
                // We reset the background here because otherwise it bleeds all the way to the next line
                if c.background_mode {
                    print!("\x1b[49m");
                }
            }
            println!();
            if c.dialup_mode {
                let stall = Duration::from_millis(rand::thread_rng().gen_range(30, 700));
                sleep(stall);
            }

            c.seed = original_seed + 1.0; // Reset the seed, but bump it a bit
            ignore_whitespace = c.background_mode;
        },
        // If not an escape sequence or a newline, print a colorful escape sequence and then the
        // character
        _ => {
            // In background mode, don't print colorful whitespace until the first printable character
            if ignore_whitespace && character.is_whitespace() {
                print!("{}", character);
                continue;
            } else {
                ignore_whitespace = false;
            }

            colored_print(character, c);
            c.seed += 1.0;
        },
        }

        // If we should constantly flush, flush after each completed sequence, and also reset
        // colors because otherwise weird things happen
        if constantly_flush {
            reset_colors(c);
            stdout().flush().unwrap();
        }
    }
}

fn reset_colors(c: &Control) {
    if atty::is(Stream::Stdout) {
        // Reset the background color
        if c.background_mode {
            print!("\x1b[49m");
        }

        // Reset the foreground color
        print!("\x1b[39m");
    }
}

fn colored_print(character: char, c: &mut Control) {
    if c.background_mode {
        let bg = get_color_tuple(c);
        let fg = calc_fg_color(bg);
        print!(
            "\x1b[38;2;{};{};{};48;2;{};{};{}m{}",
            fg.0, fg.1, fg.2, bg.0, bg.1, bg.2, character
        );
    } else {
        let fg = get_color_tuple(c);
        print!("\x1b[38;2;{};{};{}m{}", fg.0, fg.1, fg.2, character);
    }
}

fn calc_fg_color(bg: (u8, u8, u8)) -> (u8, u8, u8) {
    // Currently, it only computes the forground clolor based on some threshold
    // on grayscale value.
    // TODO: Add a better algorithm for computing forground color
    if conv_grayscale(bg) > 0xA0_u8 {
        (0u8, 0u8, 0u8)
    } else {
        (0xffu8, 0xffu8, 0xffu8)
    }
}

fn linear_to_srgb(intensity: f64) -> f64 {
    if intensity <= 0.003_130_8 {
        12.92 * intensity
    } else {
        1.055 * intensity.powf(1.0 / 2.4) - 0.055
    }
}

fn srgb_to_linear(intensity: f64) -> f64 {
    if intensity < 0.04045 {
        intensity / 12.92
    } else {
        ((intensity + 0.055) / 1.055).powf(2.4)
    }
}

fn conv_grayscale(color: (u8, u8, u8)) -> u8 {
    // See https://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
    const SCALE: f64 = 256.0;

    // Changing SRGB to Linear for gamma correction
    let red = srgb_to_linear(f64::from(color.0) / SCALE);
    let green = srgb_to_linear(f64::from(color.1) / SCALE);
    let blue = srgb_to_linear(f64::from(color.2) / SCALE);

    // Converting to grayscale
    let gray_linear = red * 0.299 + green * 0.587 + blue * 0.114;

    // Gamma correction
    let gray_srgb = linear_to_srgb(gray_linear);

    (gray_srgb * SCALE) as u8
}

fn get_color_tuple(c: &Control) -> (u8, u8, u8) {
    let i = c.frequency * c.seed / c.spread;
    let red = i.sin() * 127.00 + 128.00;
    let green = (i + (std::f64::consts::PI * 2.00 / 3.00)).sin() * 127.00 + 128.00;
    let blue = (i + (std::f64::consts::PI * 4.00 / 3.00)).sin() * 127.00 + 128.00;

    (red as u8, green as u8, blue as u8)
}
<|endoftext|>


<commit_before>use std::path::MAIN_SEPARATOR;
use std::process::Command;

use clap::{App, Arg, Error};

#[derive(Debug)]
pub struct Args {
    pub cmd: String,
    pub paths: Vec<String>,
    pub filters: Vec<String>,
    pub ignores: Vec<String>,
    pub clear_screen: bool,
    pub signal: Option<String>,
    pub restart: bool,
    pub debounce: u64,
    pub debug: bool,
    pub run_initially: bool,
    pub no_shell: bool,
    pub no_vcs_ignore: bool,
    pub once: bool,
    pub poll: bool,
    pub poll_interval: u32,
}

#[cfg(target_family = "windows")]
pub fn clear_screen() {
    let _ = Command::new("cls").status();
}

#[cfg(target_family = "unix")]
pub fn clear_screen() {
    let _ = Command::new("clear").status();
}

#[allow(unknown_lints)]
#[allow(or_fun_call)]
pub fn get_args() -> Args {
    let args = App::new("watchexec")
        .version(crate_version!())
        .about("Execute commands when watched files change")
        .arg(Arg::with_name("command")
                 .help("Command to execute")
                 .multiple(true)
                 .required(true))
        .arg(Arg::with_name("extensions")
                 .help("Comma-separated list of file extensions to watch (js,css,html)")
                 .short("e")
                 .long("exts")
                 .takes_value(true))
        .arg(Arg::with_name("path")
                 .help("Watch a specific directory")
                 .short("w")
                 .long("watch")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true))
        .arg(Arg::with_name("clear")
                 .help("Clear screen before executing command")
                 .short("c")
                 .long("clear"))
        .arg(Arg::with_name("restart")
                 .help("Restart the process if it's still running")
                 .short("r")
                 .long("restart"))
        .arg(Arg::with_name("signal")
                 .help("Send signal to process upon changes, e.g. SIGHUP")
                 .short("s")
                 .long("signal")
                 .takes_value(true)
                 .number_of_values(1)
                 .value_name("signal"))
        .arg(Arg::with_name("kill")
                 .help("Send SIGKILL to child processes (deprecated, use -s SIGKILL instead)")
                 .short("k")
                 .long("kill"))
        .arg(Arg::with_name("debounce")
                 .help("Set the timeout between detected change and command execution, defaults to 500ms")
                 .takes_value(true)
                 .value_name("milliseconds")
                 .short("d")
                 .long("debounce"))
        .arg(Arg::with_name("verbose")
                 .help("Print debugging messages to stderr")
                 .short("v")
                 .long("verbose"))
        .arg(Arg::with_name("filter")
                 .help("Ignore all modifications except those matching the pattern")
                 .short("f")
                 .long("filter")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true)
                 .value_name("pattern"))
        .arg(Arg::with_name("ignore")
                 .help("Ignore modifications to paths matching the pattern")
                 .short("i")
                 .long("ignore")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true)
                 .value_name("pattern"))
        .arg(Arg::with_name("no-vcs-ignore")
                 .help("Skip auto-loading of .gitignore files for filtering")
                 .long("no-vcs-ignore"))
        .arg(Arg::with_name("no-default-ignore")
                 .help("Skip auto-ignoring of commonly ignored globs")
                 .long("no-default-ignore"))
        .arg(Arg::with_name("postpone")
                 .help("Wait until first change to execute command")
                 .short("p")
                 .long("postpone"))
        .arg(Arg::with_name("poll")
                 .help("Forces polling mode")
                 .long("force-poll")
                 .value_name("interval"))
        .arg(Arg::with_name("no-shell")
                 .help("Do not wrap command in 'sh -c' resp. 'cmd.exe /C'")
                 .short("n")
                 .long("no-shell"))
        .arg(Arg::with_name("once").short("1").hidden(true))
        .get_matches();

    let cmd = values_t!(args.values_of("command"), String)
        .unwrap()
        .join(" ");
    let paths = values_t!(args.values_of("path"), String).unwrap_or(vec![String::from(".")]);

    // Treat --kill as --signal SIGKILL (for compatibility with older syntax)
    let signal = if args.is_present("kill") {
        Some("SIGKILL".to_string())
    } else {
        // Convert Option<&str> to Option<String>
        args.value_of("signal").map(str::to_string)
    };

    let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(vec![]);

    if let Some(extensions) = args.values_of("extensions") {
        for exts in extensions {
            filters.extend(exts.split(',')
                               .filter(|ext| !ext.is_empty())
                               .map(|ext| format!("*.{}", ext.replace(".", ""))));

        }
    }

    let mut ignores = vec![];
    let default_ignores = vec![format!("**{}.DS_Store", MAIN_SEPARATOR),
                               String::from("*.pyc"),
                               String::from("*.swp"),
                               format!("**{}.git{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
                               format!("**{}.hg{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
                               format!("**{}.svn{}**", MAIN_SEPARATOR, MAIN_SEPARATOR)];

    if args.occurrences_of("no-default-ignore") == 0 {
        ignores.extend(default_ignores)
    };
    ignores.extend(values_t!(args.values_of("ignore"), String).unwrap_or(vec![]));

    let poll_interval = if args.occurrences_of("poll") > 0 {
        value_t!(args.value_of("poll"), u32).unwrap_or_else(|e| e.exit())
    } else {
        1000
    };

    let debounce = if args.occurrences_of("debounce") > 0 {
        value_t!(args.value_of("debounce"), u64).unwrap_or_else(|e| e.exit())
     } else {
         500
     };

    if signal.is_some() && args.is_present("postpone") {
        // TODO: Error::argument_conflict() might be the better fit, usage was unclear, though
        Error::value_validation_auto("--postpone and --signal are mutually exclusive".to_string())
            .exit();
    }

    if signal.is_some() && args.is_present("kill") {
        // TODO: Error::argument_conflict() might be the better fit, usage was unclear, though
        Error::value_validation_auto("--kill and --signal is ambiguous.\n       Hint: Use only '--signal SIGKILL' without --kill".to_string())
            .exit();
    }

    Args {
        cmd: cmd,
        paths: paths,
        filters: filters,
        ignores: ignores,
        signal: signal,
        clear_screen: args.is_present("clear"),
        restart: args.is_present("restart"),
        debounce: debounce,
        debug: args.is_present("verbose"),
        run_initially: !args.is_present("postpone"),
        no_shell: args.is_present("no-shell"),
        no_vcs_ignore: args.is_present("no-vcs-ignore"),
        once: args.is_present("once"),
        poll: args.occurrences_of("poll") > 0,
        poll_interval: poll_interval,
    }
}
<commit_msg>Use `tput reset` to clear screen.<commit_after>use std::path::MAIN_SEPARATOR;
use std::process::Command;

use clap::{App, Arg, Error};

#[derive(Debug)]
pub struct Args {
    pub cmd: String,
    pub paths: Vec<String>,
    pub filters: Vec<String>,
    pub ignores: Vec<String>,
    pub clear_screen: bool,
    pub signal: Option<String>,
    pub restart: bool,
    pub debounce: u64,
    pub debug: bool,
    pub run_initially: bool,
    pub no_shell: bool,
    pub no_vcs_ignore: bool,
    pub once: bool,
    pub poll: bool,
    pub poll_interval: u32,
}

#[cfg(target_family = "windows")]
pub fn clear_screen() {
    let _ = Command::new("cls").status();
}

#[cfg(target_family = "unix")]
pub fn clear_screen() {
    let _ = Command::new("tput").arg("reset").status();
}

#[allow(unknown_lints)]
#[allow(or_fun_call)]
pub fn get_args() -> Args {
    let args = App::new("watchexec")
        .version(crate_version!())
        .about("Execute commands when watched files change")
        .arg(Arg::with_name("command")
                 .help("Command to execute")
                 .multiple(true)
                 .required(true))
        .arg(Arg::with_name("extensions")
                 .help("Comma-separated list of file extensions to watch (js,css,html)")
                 .short("e")
                 .long("exts")
                 .takes_value(true))
        .arg(Arg::with_name("path")
                 .help("Watch a specific directory")
                 .short("w")
                 .long("watch")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true))
        .arg(Arg::with_name("clear")
                 .help("Clear screen before executing command")
                 .short("c")
                 .long("clear"))
        .arg(Arg::with_name("restart")
                 .help("Restart the process if it's still running")
                 .short("r")
                 .long("restart"))
        .arg(Arg::with_name("signal")
                 .help("Send signal to process upon changes, e.g. SIGHUP")
                 .short("s")
                 .long("signal")
                 .takes_value(true)
                 .number_of_values(1)
                 .value_name("signal"))
        .arg(Arg::with_name("kill")
                 .help("Send SIGKILL to child processes (deprecated, use -s SIGKILL instead)")
                 .short("k")
                 .long("kill"))
        .arg(Arg::with_name("debounce")
                 .help("Set the timeout between detected change and command execution, defaults to 500ms")
                 .takes_value(true)
                 .value_name("milliseconds")
                 .short("d")
                 .long("debounce"))
        .arg(Arg::with_name("verbose")
                 .help("Print debugging messages to stderr")
                 .short("v")
                 .long("verbose"))
        .arg(Arg::with_name("filter")
                 .help("Ignore all modifications except those matching the pattern")
                 .short("f")
                 .long("filter")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true)
                 .value_name("pattern"))
        .arg(Arg::with_name("ignore")
                 .help("Ignore modifications to paths matching the pattern")
                 .short("i")
                 .long("ignore")
                 .number_of_values(1)
                 .multiple(true)
                 .takes_value(true)
                 .value_name("pattern"))
        .arg(Arg::with_name("no-vcs-ignore")
                 .help("Skip auto-loading of .gitignore files for filtering")
                 .long("no-vcs-ignore"))
        .arg(Arg::with_name("no-default-ignore")
                 .help("Skip auto-ignoring of commonly ignored globs")
                 .long("no-default-ignore"))
        .arg(Arg::with_name("postpone")
                 .help("Wait until first change to execute command")
                 .short("p")
                 .long("postpone"))
        .arg(Arg::with_name("poll")
                 .help("Forces polling mode")
                 .long("force-poll")
                 .value_name("interval"))
        .arg(Arg::with_name("no-shell")
                 .help("Do not wrap command in 'sh -c' resp. 'cmd.exe /C'")
                 .short("n")
                 .long("no-shell"))
        .arg(Arg::with_name("once").short("1").hidden(true))
        .get_matches();

    let cmd = values_t!(args.values_of("command"), String)
        .unwrap()
        .join(" ");
    let paths = values_t!(args.values_of("path"), String).unwrap_or(vec![String::from(".")]);

    // Treat --kill as --signal SIGKILL (for compatibility with older syntax)
    let signal = if args.is_present("kill") {
        Some("SIGKILL".to_string())
    } else {
        // Convert Option<&str> to Option<String>
        args.value_of("signal").map(str::to_string)
    };

    let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(vec![]);

    if let Some(extensions) = args.values_of("extensions") {
        for exts in extensions {
            filters.extend(exts.split(',')
                               .filter(|ext| !ext.is_empty())
                               .map(|ext| format!("*.{}", ext.replace(".", ""))));

        }
    }

    let mut ignores = vec![];
    let default_ignores = vec![format!("**{}.DS_Store", MAIN_SEPARATOR),
                               String::from("*.pyc"),
                               String::from("*.swp"),
                               format!("**{}.git{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
                               format!("**{}.hg{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
                               format!("**{}.svn{}**", MAIN_SEPARATOR, MAIN_SEPARATOR)];

    if args.occurrences_of("no-default-ignore") == 0 {
        ignores.extend(default_ignores)
    };
    ignores.extend(values_t!(args.values_of("ignore"), String).unwrap_or(vec![]));

    let poll_interval = if args.occurrences_of("poll") > 0 {
        value_t!(args.value_of("poll"), u32).unwrap_or_else(|e| e.exit())
    } else {
        1000
    };

    let debounce = if args.occurrences_of("debounce") > 0 {
        value_t!(args.value_of("debounce"), u64).unwrap_or_else(|e| e.exit())
     } else {
         500
     };

    if signal.is_some() && args.is_present("postpone") {
        // TODO: Error::argument_conflict() might be the better fit, usage was unclear, though
        Error::value_validation_auto("--postpone and --signal are mutually exclusive".to_string())
            .exit();
    }

    if signal.is_some() && args.is_present("kill") {
        // TODO: Error::argument_conflict() might be the better fit, usage was unclear, though
        Error::value_validation_auto("--kill and --signal is ambiguous.\n       Hint: Use only '--signal SIGKILL' without --kill".to_string())
            .exit();
    }

    Args {
        cmd: cmd,
        paths: paths,
        filters: filters,
        ignores: ignores,
        signal: signal,
        clear_screen: args.is_present("clear"),
        restart: args.is_present("restart"),
        debounce: debounce,
        debug: args.is_present("verbose"),
        run_initially: !args.is_present("postpone"),
        no_shell: args.is_present("no-shell"),
        no_vcs_ignore: args.is_present("no-vcs-ignore"),
        once: args.is_present("once"),
        poll: args.occurrences_of("poll") > 0,
        poll_interval: poll_interval,
    }
}
<|endoftext|>


<commit_before>use rand;
use ansi_term::Colour::{Green, Yellow, Red, White, Blue};
use rand::distributions::{IndependentSample, Range};
use scoped_threadpool::Pool;
use std::collections::HashSet;
use std::process;
use std::sync::Arc;

use format::{OutputFormat};
use gtfs::*;
use tfl::*;

pub fn fetch_lines(format: OutputFormat, thread_number: u32, sample_size: Option<usize>) {
    let lines = load_lines(DataSource::API, thread_number, sample_size);

    match format {
        OutputFormat::GTFS => transform_gtfs(lines),
        _ => process::exit(0),
    }
}

pub fn transform(format: OutputFormat, thread_number: u32, sample_size: Option<usize>) {
    let lines = load_lines(DataSource::Cache, thread_number, sample_size);

    match format {
        OutputFormat::GTFS => transform_gtfs(lines),
        _ => process::exit(0),
    }
}

fn load_lines(data_source: DataSource, thread_number: u32, sample_size: Option<usize>) -> Vec<Line> {
    let mut pool = Pool::new(thread_number);
    let client = Arc::new(Client::new());

    let mut lines = match data_source {
        DataSource::Cache => client.get_cached_lines(),
        DataSource::API   => client.get_lines(),
    };

    if let Some(n) = sample_size {
        let limit = lines.len();

        if n <= limit {
            let between = Range::new(0usize, limit);
            let mut rng = rand::thread_rng();
            let seed = between.ind_sample(&mut rng);
            let (r, s) = if (seed + n) > limit {
                             ((limit - seed), limit)
                         } else {
                             (seed, (seed + n))
                         };

            println!("Sample: {:?}", (r, s));

            lines = lines[r .. s].to_vec();
        }
    }

    pool.scoped(|scope| {
        for line in &mut lines {
            let client = client.clone();
            scope.execute(move || {
                line.inbound_sequence = client.get_sequence(&line.id, "inbound");
                line.outbound_sequence = client.get_sequence(&line.id, "outbound");
                line.stops = Some(client.get_stops(&line.id));
                for route_section in &mut line.routeSections {
                    println!("Getting Timetable for Line: {}, Route Section: {} ...", line.name, route_section.name);
                    route_section.timetable = client.get_timetable(&line.id, &route_section.originator, &route_section.destination);
                }
            });
        }
    });

    lines
}

fn transform_gtfs(lines: Vec<Line>) {
    let mut line_count = 0;
    let mut line_ids: HashSet<String> = HashSet::new();
    let mut route_section_count = 0;
    let mut route_section_ids: HashSet<String> = HashSet::new();
    let mut schedule_names: HashSet<String> = HashSet::new();

    for line in &lines {
        let is_duplicated = match line_ids.contains(&line.id) {
            true => Red.paint("yes"),
            false => Green.paint("no"),
        };

        println!("{}; Duplicate: {}", line, is_duplicated);

        for route_section in &line.routeSections {
            let has_timetable = match route_section.timetable {
                Some(ref timetable) => {
                    let names = timetable.schedule_names();
                    schedule_names = schedule_names.union(&names).cloned().collect::<HashSet<String>>();
                    names.is_empty()
                },
                None => false,
            };

            let id = route_section_id(&line, &route_section);
            println!("\t{}, Has Timetable: {}, Duplicate: {}", id, has_timetable, route_section_ids.contains(&id));
            route_section_ids.insert(id.clone());
            route_section_count += 1;
        }
        line_count += 1;
        line_ids.insert(line.id.clone());
    }

    if lines.is_empty() {
        println!("No lines found in the cache, try fetching some data first");
        process::exit(0);
    }

    println!("Duplicate Lines: {}, Duplicate Route Sections: {}", line_count - line_ids.len(), route_section_count-route_section_ids.len());

    println!("Schedule Names:");
    for schedule_name in &schedule_names {
        println!("\t{}", schedule_name);
    }

    // Generate CSV files from fetched data
    write_gtfs(&lines);
}
<commit_msg>Generalise sample function<commit_after>use rand;
use ansi_term::Colour::{Green, Yellow, Red, White, Blue};
use rand::distributions::{IndependentSample, Range};
use scoped_threadpool::Pool;
use std::collections::HashSet;
use std::process;
use std::sync::Arc;

use format::{OutputFormat};
use gtfs::*;
use tfl::*;

pub fn fetch_lines(format: OutputFormat, thread_number: u32, sample_size: Option<usize>) {
    let lines = load_lines(DataSource::API, thread_number, sample_size);

    match format {
        OutputFormat::GTFS => transform_gtfs(lines),
        _ => process::exit(0),
    }
}

pub fn transform(format: OutputFormat, thread_number: u32, sample_size: Option<usize>) {
    let lines = load_lines(DataSource::Cache, thread_number, sample_size);

    match format {
        OutputFormat::GTFS => transform_gtfs(lines),
        _ => process::exit(0),
    }
}

fn sample<T: Clone>(xs: Vec<T>, size: usize) -> Vec<T> {
    let len = xs.len();

    if size > len { return xs }

    let between = Range::new(0usize, (len - size));
    let mut rng = rand::thread_rng();
    let seed = between.ind_sample(&mut rng);
    let lower = seed;
    let upper = seed + size;

    println!("{}: {}..{}", Green.bold().paint("Sample window"), lower, upper);

    xs[lower .. upper].to_vec()
}

#[test]
fn sample_test() {
    assert_eq!(sample(vec![0; 100], 200).len(), 100);
    assert_eq!(sample(vec![0; 100], 10).len(), 10);
}

fn load_lines(data_source: DataSource, thread_number: u32, sample_size: Option<usize>) -> Vec<Line> {
    let mut pool = Pool::new(thread_number);
    let client = Arc::new(Client::new());

    let mut lines = match data_source {
        DataSource::Cache => client.get_cached_lines(),
        DataSource::API   => client.get_lines(),
    };

    if let Some(n) = sample_size {
        lines = sample(lines, n);
    }

    pool.scoped(|scope| {
        for line in &mut lines {
            let client = client.clone();
            scope.execute(move || {
                line.inbound_sequence = client.get_sequence(&line.id, "inbound");
                line.outbound_sequence = client.get_sequence(&line.id, "outbound");
                line.stops = Some(client.get_stops(&line.id));
                for route_section in &mut line.routeSections {
                    println!("{} Timetable", Green.bold().paint("Getting"));
                    println!("\tLine: {}", Blue.bold().paint(line.name.clone()));
                    println!("\tRoute Section: {} ...", White.bold().paint(route_section.name.clone()));
                    route_section.timetable = client.get_timetable(&line.id, &route_section.originator, &route_section.destination);
                }
            });
        }
    });

    lines
}

fn transform_gtfs(lines: Vec<Line>) {
    let mut line_count = 0;
    let mut line_ids: HashSet<String> = HashSet::new();
    let mut route_section_count = 0;
    let mut route_section_ids: HashSet<String> = HashSet::new();
    let mut schedule_names: HashSet<String> = HashSet::new();

    for line in &lines {
        let is_duplicated = match line_ids.contains(&line.id) {
            true => Red.paint("yes"),
            false => Green.paint("no"),
        };

        println!("{}; Duplicate: {}", line, is_duplicated);

        for route_section in &line.routeSections {
            let has_timetable = match route_section.timetable {
                Some(ref timetable) => {
                    let names = timetable.schedule_names();
                    schedule_names = schedule_names.union(&names).cloned().collect::<HashSet<String>>();
                    names.is_empty()
                },
                None => false,
            };

            let id = route_section_id(&line, &route_section);
            println!("\t{}, Has Timetable: {}, Duplicate: {}", id, has_timetable, route_section_ids.contains(&id));
            route_section_ids.insert(id.clone());
            route_section_count += 1;
        }
        line_count += 1;
        line_ids.insert(line.id.clone());
    }

    if lines.is_empty() {
        println!("No lines found in the cache, try fetching some data first");
        process::exit(0);
    }

    println!("Duplicate Lines: {}, Duplicate Route Sections: {}", line_count - line_ids.len(), route_section_count-route_section_ids.len());

    println!("Schedule Names:");
    for schedule_name in &schedule_names {
        println!("\t{}", schedule_name);
    }

    // Generate CSV files from fetched data
    write_gtfs(&lines);
}
<|endoftext|>


<commit_before>use std;

use std::fmt::Display;
use std::fmt::Formatter;

use std::str::FromStr;

#[derive(Debug)]
pub enum Error {
  InvalidCommand(String),
  TooFewArguments(String, usize, usize),
  NoCommand,
  UnknownError(String),
}

impl Display for Error {
  fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
    match *self {
      Error::InvalidCommand(ref cmd) => write!(f, "invalid command: {}", cmd),
      Error::TooFewArguments(ref cmd, ref expected, ref actual) => write!(f, "command {} expects {} arguments, got {}", cmd, expected, actual),
      Error::NoCommand => write!(f, "no command given"),
      Error::UnknownError(ref msg) => write!(f, "unexpected error: {}", msg),
    }
  }
}

impl Error {
  pub fn unknown<S: ToString>(msg: S) -> Error {
    Error::UnknownError(msg.to_string())
  }
}

type Result<V> = std::result::Result<V, Error>;

#[derive(Debug, PartialEq, Eq)]
pub enum Command {
  Init,

  ListKeys,

  PutString(String, String),
  Drop(String),

  CreateEmptyList(String),
  PushListValue(String, String),
  PopListValue(String),
  ClearList(String),

  Get(String),
}

fn assert_length(v: &Vec<String>, l: usize) -> Result<&Vec<String>> {
  if v.len() >= l {
    Ok(v)
  } else {
    Err(Error::TooFewArguments(v[0].clone(), l, v.len()))
  }
}

impl FromStr for Command {
  type Err = Error;

  fn from_str(s: &str) -> Result<Self> {
    let split = s.split(' ').map(|x| x.to_string()).collect();

    Command::from_strings(split)
  }
}

impl Command {
  pub fn from_strings(strings: Vec<String>) -> Result<Self> {
    if strings.len() == 0 {
      return Err(Error::NoCommand);
    }

    match strings[0].as_str() {
      "init" => Ok(Command::Init),

      "put" => assert_length(&strings, 3).map(|v| Command::PutString(v[1].clone(), v[2..].join(" "))),
      "drop" => assert_length(&strings, 2).map(|v| Command::Drop(v[1].clone())),

      "createEmptyList" => assert_length(&strings, 2).map(|v| Command::CreateEmptyList(v[1].clone())),
      "pushListValue" => assert_length(&strings, 3).map(|v| Command::PushListValue(v[1].clone(), v[2..].join(" "))),
      "popListValue" => assert_length(&strings, 2).map(|v| Command::PopListValue(v[1].clone())),
      "clearList" => assert_length(&strings, 2).map(|v| Command::ClearList(v[1].clone())),

      "get" => assert_length(&strings, 2).map(|v| Command::Get(v[1].clone())),

      "listKeys" => Ok(Command::ListKeys),

      cmd => Err(Error::InvalidCommand(cmd.to_string()))
    }
  }
}

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

  #[test]
  fn test_read_init() {
    let cmd = Command::from_str("init").unwrap();

    assert_eq!(cmd, Command::Init);
  }

  #[test]
  fn test_put_string() {
    let cmd = Command::from_str("put bla gna").unwrap();

    assert_eq!(cmd, Command::PutString("bla".to_string(), "gna".to_string()));
  }

  #[test]
  fn test_empty() {
    let err = Command::from_strings(Vec::new()).err().unwrap();

    if let Error::NoCommand = err {
    } else {
      assert!(false);
    }
  }

  #[test]
  fn test_put_string_wrong_fmt() {
    let err = Command::from_str("put bla,gna").err().unwrap();

    if let Error::TooFewArguments(_, expected, actual) = err {
      assert_eq!(expected, 3);
      assert_eq!(actual, 2);
    } else {
      assert!(false);
    }
  }

  #[test]
  fn test_invalid_command() {
    let err = Command::from_str("invalid test").err().unwrap();

    if let Error::InvalidCommand(cmd) = err {
      assert_eq!(cmd, "invalid".to_string());
    } else {
      assert!(false);
    }
  }

  #[test]
  fn test_from_vec() {
    let strings = "put bla gna".split(' ').map(|x| x.to_string()).collect();
    let cmd = Command::from_strings(strings).unwrap();

    assert_eq!(cmd, Command::PutString("bla".to_string(), "gna".to_string()));
  }

  #[test]
  #[should_panic]
  fn test_fail() {
    Command::from_str("invalid test").unwrap();
  }
}
<commit_msg>style<commit_after>use std;

use std::fmt::Display;
use std::fmt::Formatter;

use std::str::FromStr;

#[derive(Debug)]
pub enum Error {
  InvalidCommand(String),
  TooFewArguments(String, usize, usize),
  NoCommand,
  UnknownError(String),
}

impl Display for Error {
  fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
    match *self {
      Error::InvalidCommand(ref cmd) => write!(f, "invalid command: {}", cmd),
      Error::TooFewArguments(ref cmd, ref expected, ref actual) => write!(f, "command {} expects {} arguments, got {}", cmd, expected, actual),
      Error::NoCommand => write!(f, "no command given"),
      Error::UnknownError(ref msg) => write!(f, "unexpected error: {}", msg),
    }
  }
}

impl Error {
  pub fn unknown<S: ToString>(msg: S) -> Error {
    Error::UnknownError(msg.to_string())
  }
}

type Result<V> = std::result::Result<V, Error>;

#[derive(Debug, PartialEq, Eq)]
pub enum Command {
  Init,

  ListKeys,

  PutString(String, String),
  Drop(String),

  CreateEmptyList(String),
  PushListValue(String, String),
  PopListValue(String),
  ClearList(String),

  Get(String),
}

fn assert_length(v: &Vec<String>, l: usize) -> Result<&Vec<String>> {
  if v.len() >= l {
    Ok(v)
  } else {
    Err(Error::TooFewArguments(v[0].clone(), l, v.len()))
  }
}

impl FromStr for Command {
  type Err = Error;

  fn from_str(s: &str) -> Result<Self> {
    let split = s.split(' ').map(|x| x.to_string()).collect();

    Command::from_strings(split)
  }
}

impl Command {
  pub fn from_strings(strings: Vec<String>) -> Result<Self> {
    if strings.len() == 0 {
      return Err(Error::NoCommand);
    }

    match strings[0].as_str() {
      "init" => Ok(Command::Init),

      "put" => assert_length(&strings, 3).map(|v| Command::PutString(v[1].clone(), v[2..].join(" "))),
      "drop" => assert_length(&strings, 2).map(|v| Command::Drop(v[1].clone())),

      "createEmptyList" => assert_length(&strings, 2).map(|v| Command::CreateEmptyList(v[1].clone())),
      "pushListValue" => assert_length(&strings, 3).map(|v| Command::PushListValue(v[1].clone(), v[2..].join(" "))),
      "popListValue" => assert_length(&strings, 2).map(|v| Command::PopListValue(v[1].clone())),
      "clearList" => assert_length(&strings, 2).map(|v| Command::ClearList(v[1].clone())),

      "get" => assert_length(&strings, 2).map(|v| Command::Get(v[1].clone())),

      "listKeys" => Ok(Command::ListKeys),

      cmd => Err(Error::InvalidCommand(cmd.to_string()))
    }
  }
}

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

  #[test]
  fn test_read_init() {
    let cmd = Command::from_str("init").unwrap();

    assert_eq!(cmd, Command::Init);
  }

  #[test]
  fn test_put_string() {
    let cmd = Command::from_str("put bla gna").unwrap();

    assert_eq!(cmd, Command::PutString("bla".to_string(), "gna".to_string()));
  }

  #[test]
  fn test_empty() {
    let err = Command::from_strings(Vec::new()).err().unwrap();

    if let Error::NoCommand = err {} else {
      assert!(false);
    }
  }

  #[test]
  fn test_put_string_wrong_fmt() {
    let err = Command::from_str("put bla,gna").err().unwrap();

    if let Error::TooFewArguments(_, expected, actual) = err {
      assert_eq!(expected, 3);
      assert_eq!(actual, 2);
    } else {
      assert!(false);
    }
  }

  #[test]
  fn test_invalid_command() {
    let err = Command::from_str("invalid test").err().unwrap();

    if let Error::InvalidCommand(cmd) = err {
      assert_eq!(cmd, "invalid".to_string());
    } else {
      assert!(false);
    }
  }

  #[test]
  fn test_from_vec() {
    let strings = "put bla gna".split(' ').map(|x| x.to_string()).collect();
    let cmd = Command::from_strings(strings).unwrap();

    assert_eq!(cmd, Command::PutString("bla".to_string(), "gna".to_string()));
  }

  #[test]
  #[should_panic]
  fn test_fail() {
    Command::from_str("invalid test").unwrap();
  }
}
<|endoftext|>


<commit_before>use super::interconnect::Interconnect;
use super::memory::END_RESERVED;
use super::rand;
use super::time::{Duration, SteadyTime};

const INSTRUCTION_SIZE: u16 = 2;

pub struct Cpu {
    v: [u8; 16],
    pc: u16,
    i: u16,
    stack: Vec<u16>,

    delay_timer: u8,
    sound_timer: u8,

    delay_start: SteadyTime,
}

impl Cpu {
    pub fn new() -> Cpu {
        Cpu{
            v: [0; 16],
            pc: END_RESERVED as u16,
            i: 0,
            stack: Vec::new(),
            delay_timer: 0,
            sound_timer: 0,
            delay_start: SteadyTime::now(),
        }
    }

    pub fn step(&mut self, interconnect: &mut Interconnect) {
        let instr = interconnect.mem.read_word(self.pc);

        let opcode = (instr >> 12) as u8;
        let nnn = instr & 0xfff;
        let x = ((instr >> 8) & 0xf) as usize;
        let y = ((instr >> 4) & 0xf) as usize;
        let n = (instr & 0xf) as usize;
        let kk = (instr & 0xff) as u8;
        self.pc += INSTRUCTION_SIZE;

        match opcode {
            0x0 => {
                match kk {
                    0xe0 => {
                        // 00E0 - CLS
                        interconnect.graphics.clear();
                    }
                    0xee => {
                        // 00EE - RET
                        let ret = self.stack.pop().expect("ret without func call");
                        self.pc = ret;
                    }
                    _ => {
                        // 0nnn - SYS addr
                        // This instruction is only used on the old computers on which Chip-8 was
                        // originally implemented. It is ignored by modern interpreters.
                    }
                }
            }
            0x1 => {
                // 1nnn - JP addr
                self.pc = nnn;
            }
            0x2 => {
                // 2nnn - CALL addr
                self.stack.push(self.pc);
                self.pc = nnn;
            }
            0x3 => {
                // 3xkk - SE Vx, byte
                if self.v[x] == kk {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x4 => {
                // 4xkk - SNE Vx, byte
                if self.v[x] != kk {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x5 => {
                // 5xy0 - SE Vx, Vy
                if self.v[x] == self.v[y] {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x6 => {
                // 6xkk - LD Vx, byte
                self.v[x as usize] = kk;
            }
            0x7 => {
                // 7xkk - ADD Vx, byte
                self.v[x] = self.v[x].wrapping_add(kk as u8);
            }
            0x8 => {
                match n {
                    0x0 => {
                        // 8xy0 - LD Vx, Vy
                        self.v[x] = self.v[y];
                    }
                    0x1 => {
                        // 8xy1 - OR Vx, Vy
                        self.v[x] = self.v[x] | self.v[y];
                    }
                    0x2 => {
                        // 8xy2 - AND Vx, Vy
                        self.v[x] = self.v[x] & self.v[y];
                    }
                    0x3 => {
                        // 8xy3 - XOR Vx, Vy
                        self.v[x] = self.v[x] ^ self.v[y];
                    }
                    0x4 => {
                        // 8xy4 - ADD Vx, Vy
                        let ret = (self.v[x] as u16) + (self.v[y] as u16);
                        if ret > 255 {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = ret as u8;
                    }
                    0x5 => {
                        // 8xy5 - SUB Vx, Vy
                        if self.v[x] > self.v[y] {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = self.v[x].wrapping_sub(self.v[y]);
                    }
                    0x6 => {
                        // 8xy6 - SHR Vx {, Vy}
                        self.v[0xf] = self.v[x] & 0x1;
                        self.v[x] >>= 1;
                    }
                    0x7 => {
                        // 8xy7 - SUBN Vx, Vy
                        if self.v[y] > self.v[x] {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = self.v[y].wrapping_sub(self.v[x]);
                    }
                    0xE => {
                        self.v[0xf] = self.v[x] >> 7;
                        self.v[x] <<= 1;
                    }
                    _ => panic!("Unrecognized 8 variant {:x}({:x})", instr, n),
                }
            }
            0x9 => {
                // 9xy0 - SNE Vx, Vy
                if self.v[x] != self.v[y] {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0xa => {
                // Annn - LD I, addr
                self.i = nnn;
            }
            0xb => {
                // Bnnn - JP V0, addr
                self.pc = (self.v[0] as u16) + nnn;
            }
            0xc => {
                // Cxkk - RND Vx, byte
                self.v[x] = rand::random::<u8>() & kk;
            }
            0xd => {
                // Dxyn - DRW Vx, Vy, nibble
                let mut sprite = vec![0; n as usize];
                let vx = self.v[x] as usize;
                let vy = self.v[y] as usize;

                for i in 0..n {
                    sprite[i] = interconnect.mem.read_byte(self.i + i as u16);
                }

                self.v[0xf] = interconnect.graphics.draw(vx, vy, sprite);
            }
            0xe => {
                match kk {
                    0x9e => {
                        // Ex9E - SKP Vx
                        if interconnect.input.key_pressed(self.v[x]) {
                            self.pc += INSTRUCTION_SIZE;
                        }
                    }
                    0xa1 => {
                        // ExA1 - SKNP Vx
                        if !interconnect.input.key_pressed(self.v[x]) {
                            self.pc += INSTRUCTION_SIZE;
                        }
                    }
                    _ => panic!("Unrecognized e variant {:x}({:x})", instr, kk),
                }
            }
            0xf => {
                match kk {
                    0x07 => {
                        // Fx07 - LD Vx, DT
                        self.v[x] = self.delay_timer;
                    }
                    0x0a => {
                        // Fx0A - LD Vx, K
                        match interconnect.input.any_key_pressed() {
                            Some(index) => self.v[x] = index,
                            None => self.pc -= INSTRUCTION_SIZE,
                        }
                    }
                    0x15 => {
                        // Fx15 - LD DT, Vx
                        let val = self.v[x];
                        self.delay_timer = val;
                    }
                    0x18 => {
                        // Fx18 - LD ST, Vx
                        self.sound_timer = self.v[x];
                    }
                    0x1E => {
                        // Fx1E - ADD I, Vx
                        self.i += self.v[x] as u16;
                    }
                    0x29 => {
                        // Fx29 - LD F, Vx
                        let val = self.v[x];
                        self.i = interconnect.mem.get_digit_sprite(val);
                    }
                    0x33 => {
                        // Fx33 - LD B, Vx
                        let val = self.v[x];
                        interconnect.mem.write_byte(self.i, val / 100);
                        interconnect.mem.write_byte(self.i + 1, (val % 100) / 10);
                        interconnect.mem.write_byte(self.i + 2, val % 10);
                    }
                    0x55 => {
                        // Fx55 - LD [I], Vx
                        for i in 0..(x + 1) {
                            interconnect.mem.write_byte(self.i + i as u16, self.v[i]);
                        }
                    }
                    0x65 => {
                        // Fx65 - LD Vx, [I]
                        for i in 0..(x+1) {
                            self.v[i] = interconnect.mem.read_byte(self.i + i as u16);
                        }
                    }
                    _ => panic!("Unrecognized f variant {:x}({:x})", instr, kk),
                }
            }

            _ => panic!("Unrecognized instruction 0x{:x} ({:x})", instr, opcode),
        }

        self.handle_timers();
    }

    fn handle_timers(&mut self) {
        if self.delay_timer > 0 {
            let now = SteadyTime::now();
            if now - self.delay_start > Duration::milliseconds(16) {
                self.delay_start = now;
                self.delay_timer -= 1;
            }
        }
    }
}
<commit_msg>Implement fixed sized stack properly<commit_after>use super::interconnect::Interconnect;
use super::memory::END_RESERVED;
use super::rand;
use super::time::{Duration, SteadyTime};

const INSTRUCTION_SIZE: u16 = 2;
const STACK_SIZE: usize = 16;

pub struct Cpu {
    v: [u8; 16],
    pc: u16,
    i: u16,
    stack: [u16; STACK_SIZE],
    stack_index: usize,

    delay_timer: u8,
    sound_timer: u8,

    delay_start: SteadyTime,
}

impl Cpu {
    pub fn new() -> Cpu {
        Cpu{
            v: [0; 16],
            pc: END_RESERVED as u16,
            i: 0,
            stack: [0;16],
            stack_index: 0,
            delay_timer: 0,
            sound_timer: 0,
            delay_start: SteadyTime::now(),
        }
    }

    pub fn step(&mut self, interconnect: &mut Interconnect) {
        let instr = interconnect.mem.read_word(self.pc);

        let opcode = (instr >> 12) as u8;
        let nnn = instr & 0xfff;
        let x = ((instr >> 8) & 0xf) as usize;
        let y = ((instr >> 4) & 0xf) as usize;
        let n = (instr & 0xf) as usize;
        let kk = (instr & 0xff) as u8;
        self.pc += INSTRUCTION_SIZE;

        match opcode {
            0x0 => {
                match kk {
                    0xe0 => {
                        // 00E0 - CLS
                        interconnect.graphics.clear();
                    }
                    0xee => {
                        // 00EE - RET
                        let ret = self.pop();
                        self.pc = ret;
                    }
                    _ => {
                        // 0nnn - SYS addr
                        // This instruction is only used on the old computers on which Chip-8 was
                        // originally implemented. It is ignored by modern interpreters.
                    }
                }
            }
            0x1 => {
                // 1nnn - JP addr
                self.pc = nnn;
            }
            0x2 => {
                // 2nnn - CALL addr
                let pc = self.pc;
                self.push(pc);
                self.pc = nnn;
            }
            0x3 => {
                // 3xkk - SE Vx, byte
                if self.v[x] == kk {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x4 => {
                // 4xkk - SNE Vx, byte
                if self.v[x] != kk {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x5 => {
                // 5xy0 - SE Vx, Vy
                if self.v[x] == self.v[y] {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x6 => {
                // 6xkk - LD Vx, byte
                self.v[x as usize] = kk;
            }
            0x7 => {
                // 7xkk - ADD Vx, byte
                self.v[x] = self.v[x].wrapping_add(kk as u8);
            }
            0x8 => {
                match n {
                    0x0 => {
                        // 8xy0 - LD Vx, Vy
                        self.v[x] = self.v[y];
                    }
                    0x1 => {
                        // 8xy1 - OR Vx, Vy
                        self.v[x] = self.v[x] | self.v[y];
                    }
                    0x2 => {
                        // 8xy2 - AND Vx, Vy
                        self.v[x] = self.v[x] & self.v[y];
                    }
                    0x3 => {
                        // 8xy3 - XOR Vx, Vy
                        self.v[x] = self.v[x] ^ self.v[y];
                    }
                    0x4 => {
                        // 8xy4 - ADD Vx, Vy
                        let ret = (self.v[x] as u16) + (self.v[y] as u16);
                        if ret > 255 {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = ret as u8;
                    }
                    0x5 => {
                        // 8xy5 - SUB Vx, Vy
                        if self.v[x] > self.v[y] {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = self.v[x].wrapping_sub(self.v[y]);
                    }
                    0x6 => {
                        // 8xy6 - SHR Vx {, Vy}
                        self.v[0xf] = self.v[x] & 0x1;
                        self.v[x] >>= 1;
                    }
                    0x7 => {
                        // 8xy7 - SUBN Vx, Vy
                        if self.v[y] > self.v[x] {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = self.v[y].wrapping_sub(self.v[x]);
                    }
                    0xE => {
                        self.v[0xf] = self.v[x] >> 7;
                        self.v[x] <<= 1;
                    }
                    _ => panic!("Unrecognized 8 variant {:x}({:x})", instr, n),
                }
            }
            0x9 => {
                // 9xy0 - SNE Vx, Vy
                if self.v[x] != self.v[y] {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0xa => {
                // Annn - LD I, addr
                self.i = nnn;
            }
            0xb => {
                // Bnnn - JP V0, addr
                self.pc = (self.v[0] as u16) + nnn;
            }
            0xc => {
                // Cxkk - RND Vx, byte
                self.v[x] = rand::random::<u8>() & kk;
            }
            0xd => {
                // Dxyn - DRW Vx, Vy, nibble
                let mut sprite = vec![0; n as usize];
                let vx = self.v[x] as usize;
                let vy = self.v[y] as usize;

                for i in 0..n {
                    sprite[i] = interconnect.mem.read_byte(self.i + i as u16);
                }

                self.v[0xf] = interconnect.graphics.draw(vx, vy, sprite);
            }
            0xe => {
                match kk {
                    0x9e => {
                        // Ex9E - SKP Vx
                        if interconnect.input.key_pressed(self.v[x]) {
                            self.pc += INSTRUCTION_SIZE;
                        }
                    }
                    0xa1 => {
                        // ExA1 - SKNP Vx
                        if !interconnect.input.key_pressed(self.v[x]) {
                            self.pc += INSTRUCTION_SIZE;
                        }
                    }
                    _ => panic!("Unrecognized e variant {:x}({:x})", instr, kk),
                }
            }
            0xf => {
                match kk {
                    0x07 => {
                        // Fx07 - LD Vx, DT
                        self.v[x] = self.delay_timer;
                    }
                    0x0a => {
                        // Fx0A - LD Vx, K
                        match interconnect.input.any_key_pressed() {
                            Some(index) => self.v[x] = index,
                            None => self.pc -= INSTRUCTION_SIZE,
                        }
                    }
                    0x15 => {
                        // Fx15 - LD DT, Vx
                        let val = self.v[x];
                        self.delay_timer = val;
                    }
                    0x18 => {
                        // Fx18 - LD ST, Vx
                        self.sound_timer = self.v[x];
                    }
                    0x1E => {
                        // Fx1E - ADD I, Vx
                        self.i += self.v[x] as u16;
                    }
                    0x29 => {
                        // Fx29 - LD F, Vx
                        let val = self.v[x];
                        self.i = interconnect.mem.get_digit_sprite(val);
                    }
                    0x33 => {
                        // Fx33 - LD B, Vx
                        let val = self.v[x];
                        interconnect.mem.write_byte(self.i, val / 100);
                        interconnect.mem.write_byte(self.i + 1, (val % 100) / 10);
                        interconnect.mem.write_byte(self.i + 2, val % 10);
                    }
                    0x55 => {
                        // Fx55 - LD [I], Vx
                        for i in 0..(x + 1) {
                            interconnect.mem.write_byte(self.i + i as u16, self.v[i]);
                        }
                    }
                    0x65 => {
                        // Fx65 - LD Vx, [I]
                        for i in 0..(x+1) {
                            self.v[i] = interconnect.mem.read_byte(self.i + i as u16);
                        }
                    }
                    _ => panic!("Unrecognized f variant {:x}({:x})", instr, kk),
                }
            }

            _ => panic!("Unrecognized instruction 0x{:x} ({:x})", instr, opcode),
        }

        self.handle_timers();
    }

    fn handle_timers(&mut self) {
        if self.delay_timer > 0 {
            let now = SteadyTime::now();
            if now - self.delay_start > Duration::milliseconds(16) {
                self.delay_start = now;
                self.delay_timer -= 1;
            }
        }
    }

    fn pop(&mut self) -> u16 {
        if self.stack_index == 0 {
            panic!("pop from empty stack");
        }
        self.stack_index -= 1;
        self.stack[self.stack_index]
    }

    fn push(&mut self, val: u16) {
        if self.stack_index == STACK_SIZE {
            panic!("push to full stack");
        }
        self.stack[self.stack_index] = val;
        self.stack_index += 1;
    }
}
<|endoftext|>


<commit_before>use std::old_io::{fs, IoResult};
use file::{File, GREY};

#[cfg(feature="git")] use ansi_term::ANSIString;
#[cfg(feature="git")] use ansi_term::Colour::*;
#[cfg(feature="git")] use git2;

/// A **Dir** provides a cached list of the file paths in a directory that's
/// being listed.
///
/// This object gets passed to the Files themselves, in order for them to
/// check the existence of surrounding files, then highlight themselves
/// accordingly. (See `File#get_source_files`)
pub struct Dir {
    contents: Vec<Path>,
    path: Path,
    git: Option<Git>,
}

impl Dir {
    /// Create a new Dir object filled with all the files in the directory
    /// pointed to by the given path. Fails if the directory can't be read, or
    /// isn't actually a directory.
    pub fn readdir(path: &Path) -> IoResult<Dir> {
        fs::readdir(path).map(|paths| Dir {
            contents: paths,
            path: path.clone(),
            git: Git::scan(path).ok(),
        })
    }

    /// Produce a vector of File objects from an initialised directory,
    /// printing out an error if any of the Files fail to be created.
    pub fn files(&self) -> Vec<File> {
        let mut files = vec![];

        for path in self.contents.iter() {
            match File::from_path(path, Some(self)) {
                Ok(file) => files.push(file),
                Err(e)   => println!("{}: {}", path.display(), e),
            }
        }

        files
    }

    /// Whether this directory contains a file with the given path.
    pub fn contains(&self, path: &Path) -> bool {
        self.contents.contains(path)
    }

    /// Append a path onto the path specified by this directory.
    pub fn join(&self, child: Path) -> Path {
        self.path.join(child)
    }

    /// Return whether there's a Git repository on or above this directory.
    pub fn has_git_repo(&self) -> bool {
        self.git.is_some()
    }

    /// Get a string describing the Git status of the given file.
    pub fn git_status(&self, path: &Path, prefix_lookup: bool) -> String {
        match (&self.git, prefix_lookup) {
            (&Some(ref git), false) => git.status(path),
            (&Some(ref git), true)  => git.dir_status(path),
            (&None, _)              => GREY.paint("--").to_string(),
        }
    }
}

/// Container of Git statuses for all the files in this folder's Git repository.
#[cfg(feature="git")]
struct Git {
    statuses: Vec<(Vec<u8>, git2::Status)>,
}

#[cfg(feature="git")]
impl Git {

    /// Discover a Git repository on or above this directory, scanning it for
    /// the files' statuses if one is found.
    fn scan(path: &Path) -> Result<Git, git2::Error> {
        let repo = try!(git2::Repository::discover(path));
        let statuses = try!(repo.statuses(None)).iter()
                                                .map(|e| (e.path_bytes().to_vec(), e.status()))
                                                .collect();
        Ok(Git { statuses: statuses })
    }

    /// Get the status for the file at the given path, if present.
    fn status(&self, path: &Path) -> String {
        let status = self.statuses.iter()
                                  .find(|p| p.0 == path.as_vec());
        match status {
            Some(&(_, s)) => format!("{}{}", Git::index_status(s), Git::working_tree_status(s)),
            None => GREY.paint("--").to_string(),
        }
    }

    /// Get the combined status for all the files whose paths begin with the
    /// path that gets passed in. This is used for getting the status of
    /// directories, which don't really have an 'official' status.
    fn dir_status(&self, dir: &Path) -> String {
        let status = self.statuses.iter()
                                  .filter(|p| p.0.starts_with(dir.as_vec()))
                                  .fold(git2::Status::empty(), |a, b| a | b.1);
        match status {
            s => format!("{}{}", Git::index_status(s), Git::working_tree_status(s)),
        }
    }

    /// The character to display if the file has been modified, but not staged.
    fn working_tree_status(status: git2::Status) -> ANSIString<'static> {
        match status {
            s if s.contains(git2::STATUS_WT_NEW) => Green.paint("A"),
            s if s.contains(git2::STATUS_WT_MODIFIED) => Blue.paint("M"),
            s if s.contains(git2::STATUS_WT_DELETED) => Red.paint("D"),
            s if s.contains(git2::STATUS_WT_RENAMED) => Yellow.paint("R"),
            s if s.contains(git2::STATUS_WT_TYPECHANGE) => Purple.paint("T"),
            _ => GREY.paint("-"),
        }
    }

    /// The character to display if the file has been modified, and the change
    /// has been staged.
    fn index_status(status: git2::Status) -> ANSIString<'static> {
        match status {
            s if s.contains(git2::STATUS_INDEX_NEW) => Green.paint("A"),
            s if s.contains(git2::STATUS_INDEX_MODIFIED) => Blue.paint("M"),
            s if s.contains(git2::STATUS_INDEX_DELETED) => Red.paint("D"),
            s if s.contains(git2::STATUS_INDEX_RENAMED) => Yellow.paint("R"),
            s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => Purple.paint("T"),
            _ => GREY.paint("-"),
        }
    }
}

#[cfg(not(feature="git"))]
struct Git;

#[cfg(not(feature="git"))]
impl Git {
    fn scan(_: &Path) -> Result<Git, ()> {
        // Don't do anything without Git support
        Err(())
    }

    fn status(&self, _: &Path) -> String {
        // The Err above means that this should never happen
        panic!("Tried to access a Git repo without Git support!");
    }

    fn dir_status(&self, path: &Path) -> String {
        self.status(path)
    }
}
<commit_msg>What the heck?<commit_after>use std::old_io::{fs, IoResult};
use file::{File, GREY};

#[cfg(feature="git")] use ansi_term::ANSIString;
#[cfg(feature="git")] use ansi_term::Colour::*;
#[cfg(feature="git")] use git2;

/// A **Dir** provides a cached list of the file paths in a directory that's
/// being listed.
///
/// This object gets passed to the Files themselves, in order for them to
/// check the existence of surrounding files, then highlight themselves
/// accordingly. (See `File#get_source_files`)
pub struct Dir {
    contents: Vec<Path>,
    path: Path,
    git: Option<Git>,
}

impl Dir {
    /// Create a new Dir object filled with all the files in the directory
    /// pointed to by the given path. Fails if the directory can't be read, or
    /// isn't actually a directory.
    pub fn readdir(path: &Path) -> IoResult<Dir> {
        fs::readdir(path).map(|paths| Dir {
            contents: paths,
            path: path.clone(),
            git: Git::scan(path).ok(),
        })
    }

    /// Produce a vector of File objects from an initialised directory,
    /// printing out an error if any of the Files fail to be created.
    pub fn files(&self) -> Vec<File> {
        let mut files = vec![];

        for path in self.contents.iter() {
            match File::from_path(path, Some(self)) {
                Ok(file) => files.push(file),
                Err(e)   => println!("{}: {}", path.display(), e),
            }
        }

        files
    }

    /// Whether this directory contains a file with the given path.
    pub fn contains(&self, path: &Path) -> bool {
        self.contents.contains(path)
    }

    /// Append a path onto the path specified by this directory.
    pub fn join(&self, child: Path) -> Path {
        self.path.join(child)
    }

    /// Return whether there's a Git repository on or above this directory.
    pub fn has_git_repo(&self) -> bool {
        self.git.is_some()
    }

    /// Get a string describing the Git status of the given file.
    pub fn git_status(&self, path: &Path, prefix_lookup: bool) -> String {
        match (&self.git, prefix_lookup) {
            (&Some(ref git), false) => git.status(path),
            (&Some(ref git), true)  => git.dir_status(path),
            (&None, _)              => GREY.paint("--").to_string(),
        }
    }
}

/// Container of Git statuses for all the files in this folder's Git repository.
#[cfg(feature="git")]
struct Git {
    statuses: Vec<(Vec<u8>, git2::Status)>,
}

#[cfg(feature="git")]
impl Git {

    /// Discover a Git repository on or above this directory, scanning it for
    /// the files' statuses if one is found.
    fn scan(path: &Path) -> Result<Git, git2::Error> {
        let repo = try!(git2::Repository::discover(path));
        let statuses = try!(repo.statuses(None)).iter()
                                                .map(|e| (e.path_bytes().to_vec(), e.status()))
                                                .collect();
        Ok(Git { statuses: statuses })
    }

    /// Get the status for the file at the given path, if present.
    fn status(&self, path: &Path) -> String {
        let status = self.statuses.iter()
                                  .find(|p| p.0 == path.as_vec());
        match status {
            Some(&(_, s)) => format!("{}{}", Git::index_status(s), Git::working_tree_status(s)),
            None => GREY.paint("--").to_string(),
        }
    }

    /// Get the combined status for all the files whose paths begin with the
    /// path that gets passed in. This is used for getting the status of
    /// directories, which don't really have an 'official' status.
    fn dir_status(&self, dir: &Path) -> String {
        let s = self.statuses.iter()
                             .filter(|p| p.0.starts_with(dir.as_vec()))
                             .fold(git2::Status::empty(), |a, b| a | b.1);

        format!("{}{}", Git::index_status(s), Git::working_tree_status(s))
    }

    /// The character to display if the file has been modified, but not staged.
    fn working_tree_status(status: git2::Status) -> ANSIString<'static> {
        match status {
            s if s.contains(git2::STATUS_WT_NEW) => Green.paint("A"),
            s if s.contains(git2::STATUS_WT_MODIFIED) => Blue.paint("M"),
            s if s.contains(git2::STATUS_WT_DELETED) => Red.paint("D"),
            s if s.contains(git2::STATUS_WT_RENAMED) => Yellow.paint("R"),
            s if s.contains(git2::STATUS_WT_TYPECHANGE) => Purple.paint("T"),
            _ => GREY.paint("-"),
        }
    }

    /// The character to display if the file has been modified, and the change
    /// has been staged.
    fn index_status(status: git2::Status) -> ANSIString<'static> {
        match status {
            s if s.contains(git2::STATUS_INDEX_NEW) => Green.paint("A"),
            s if s.contains(git2::STATUS_INDEX_MODIFIED) => Blue.paint("M"),
            s if s.contains(git2::STATUS_INDEX_DELETED) => Red.paint("D"),
            s if s.contains(git2::STATUS_INDEX_RENAMED) => Yellow.paint("R"),
            s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => Purple.paint("T"),
            _ => GREY.paint("-"),
        }
    }
}

#[cfg(not(feature="git"))]
struct Git;

#[cfg(not(feature="git"))]
impl Git {
    fn scan(_: &Path) -> Result<Git, ()> {
        // Don't do anything without Git support
        Err(())
    }

    fn status(&self, _: &Path) -> String {
        // The Err above means that this should never happen
        panic!("Tried to access a Git repo without Git support!");
    }

    fn dir_status(&self, path: &Path) -> String {
        self.status(path)
    }
}
<|endoftext|>


<commit_before>use std::fmt::Debug;
use std::sync::mpsc;

use rayon::prelude::*;
use rayon;
use anymap;

#[derive(Clone, Default, Debug)]
pub struct Component {
    data: i32,
}

// #[derive(Clone, Default)]
// pub struct VecResource<T> {
//     data: Vec<T>,
// }

#[derive(Clone, Default, Debug)]
pub struct VecResource<T>
    where T: Debug
{
    data: Vec<T>,
}

impl<T> VecResource<T>
    where T: Debug
{
    pub fn new() -> Self {
        VecResource { data: Vec::new() }
    }
}

pub struct Entity(u32);

// I feel a little ghetto making one global event type for everything,
// but we'll roll with it for now.
pub struct World<E> {
    entities: Vec<Entity>,
    components: anymap::AnyMap,
    inputs: Vec<InputChannel<E>>,
    outputs: Vec<OutputChannel<E>>
}

pub type InputChannel<T> = mpsc::Receiver<T>;
pub type OutputChannel<T> = mpsc::Sender<T>;

impl<E> World<E> where E: Send + Sync {
    pub fn new() -> Self {
        World {
            entities: Vec::new(),
            components: anymap::AnyMap::new(),
            inputs: Vec::new(),
            outputs: Vec::new(),
        }
    }

    fn register<T>(&mut self)
        where T: Default + 'static
    {
        self.components.insert(T::default());
    }

    fn run<F, T>(&mut self, inputs: Vec<E>, f: F) -> Vec<E>
        where F: Fn((&T, &E)) -> E + Sync,
              T: Debug + Send + Sync + 'static
    {
        if let Some(resource) = self.components.get::<VecResource<T>>() {
            let d: &[T] = &resource.data;
            let mut v = Vec::new();
            d.par_iter().zip(&inputs).map(f).collect_into(&mut v);
            v
        } else {
            panic!("Tried to run a system on an unknown component type");
        }
    }

    fn next_entity(&self) -> Entity {
        use std::u32;
        assert!(self.entities.len() < u32::MAX as usize);
        Entity(self.entities.len() as u32)
    }

    fn create_entity(&mut self) {
        let e = self.next_entity();
        self.entities.push(e);
        let vr: &mut VecResource<u32> = self.components.entry().or_insert_with(VecResource::new);
        vr.data.push(self.entities.len() as u32);

        let (tx, rx) = mpsc::channel();
        self.inputs.push(rx);
        self.outputs.push(tx);
    }

    // fn send_to_entity(&self, entity: Entity, event: E) {
    //     self.
    // }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_world_thingy() {
        let entity_count = 10;
        let mut w = World::new();
        for i in 0..entity_count {
            w.create_entity();
        }
        println!("Bar");
        let mut inputs = Vec::new();
        inputs.resize(entity_count, 1u32);
        // Event input just being a vector is tricksy because it may not
        // be the same length as the entities vector.  It needs to be a particular
        // per-entity mapping of one kind or another.
        // Making it efficient so we don't need to always re-allocate a new vector
        // for returning new events will be a little wacky too.
        let results = w.run(inputs, |(x1, x2): (&u32, &u32)| {
            println!("Xs are is {:?} {:?}", x1, x2);
            x1 + x2
        });
        println!("Results are {:?}", results);
        let mut desired_results: Vec<u32> = Vec::new();
        desired_results.extend(&[2, 3, 4, 5, 6, 7, 8, 9, 10, 11][..]);
        assert_eq!(results, desired_results);
    }
}
<commit_msg>Going to leave event enhancements out until we can update components.<commit_after>use std::fmt::Debug;
use std::sync::mpsc;

use rayon::prelude::*;
use rayon;
use anymap;

#[derive(Clone, Default, Debug)]
pub struct Component {
    data: i32,
}

// #[derive(Clone, Default)]
// pub struct VecResource<T> {
//     data: Vec<T>,
// }

#[derive(Clone, Default, Debug)]
pub struct VecResource<T>
    where T: Debug
{
    data: Vec<T>,
}

impl<T> VecResource<T>
    where T: Debug
{
    pub fn new() -> Self {
        VecResource { data: Vec::new() }
    }
}

pub struct Entity(u32);

// I feel a little ghetto making one global event type for everything,
// but we'll roll with it for now.
pub struct World<E> {
    entities: Vec<Entity>,
    components: anymap::AnyMap,
    inputs: Vec<InputChannel<E>>,
    outputs: Vec<OutputChannel<E>>
}

pub type InputChannel<T> = mpsc::Receiver<T>;
pub type OutputChannel<T> = mpsc::Sender<T>;

impl<E> World<E> where E: Send + Sync {
    pub fn new() -> Self {
        World {
            entities: Vec::new(),
            components: anymap::AnyMap::new(),
            inputs: Vec::new(),
            outputs: Vec::new(),
        }
    }

    fn register<T>(&mut self)
        where T: Default + 'static
    {
        self.components.insert(T::default());
    }

    fn run<F, C>(&mut self, inputs: Vec<E>, f: F) -> Vec<C>
        where F: Fn((&C, &E)) -> C + Sync,
              C: Debug + Send + Sync + 'static
    {
        if let Some(resource) = self.components.get::<VecResource<C>>() {
            let d: &[C] = &resource.data;
            let mut v = Vec::new();
            d.par_iter().zip(&inputs).map(f).collect_into(&mut v);
            v
        } else {
            panic!("Tried to run a system on an unknown component type");
        }
    }

    fn next_entity(&self) -> Entity {
        use std::u32;
        assert!(self.entities.len() < u32::MAX as usize);
        Entity(self.entities.len() as u32)
    }

    fn create_entity<T>(&mut self, component: T) where T: Debug + Send + Sync + 'static {
        let e = self.next_entity();
        self.entities.push(e);
        let vr: &mut VecResource<T> = self.components.entry().or_insert_with(VecResource::new);
        vr.data.push(component);

        let (tx, rx) = mpsc::channel();
        self.inputs.push(rx);
        self.outputs.push(tx);
    }

    // fn send_to_entity(&self, entity: Entity, event: E) {
    //     self.
    // }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_world_thingy() {
        let entity_count = 10;
        let mut w = World::new();
        for i in 0..entity_count {
            w.create_entity(i*i);
        }
        let mut inputs = Vec::new();
        inputs.resize(entity_count, 1usize);
        // Event input just being a vector is tricksy because it may not
        // be the same length as the entities vector.  It needs to be a particular
        // per-entity mapping of one kind or another.
        // Making it efficient so we don't need to always re-allocate a new vector
        // for returning new events will be a little wacky too.
        let results = w.run(inputs, |(comp, event): (&u32, &usize)| {
            println!("Component: {} Event: {}", comp, event);
            comp + (*event as u32)
        });
        println!("Results are {:?}", results);
        let mut desired_results: Vec<u32> = Vec::new();
        desired_results.extend(&[2, 3, 4, 5, 6, 7, 8, 9, 10, 11][..]);
        assert_eq!(results, desired_results);
    }
}
<|endoftext|>