<commit_before>use std::f64;
use std::fs::File;
use std::io::{self, BufRead, BufReader, BufWriter, Read, StdinLock, StdoutLock, Write};
use std::iter;

use clap::{App, Arg, ArgGroup, ArgMatches};

use inc_stats::{Mode, Percentiles, SummStats};

fn parse_command_line<'a>() -> ArgMatches<'a> {
    App::new("stats")
        .version("1.0")
        .author("Erik Brinkman <erik.brinkman@gmail.com>")
        .about("Compute summary statistics of streams of numbers")
        // Output
        .arg(Arg::with_name("count")
             .short("c")
             .long("count")
             .help("Print count"))
        .arg(Arg::with_name("min")
             .long("min")
             .help("Print min"))
        .arg(Arg::with_name("max")
             .long("max")
             .help("Print max"))
        .arg(Arg::with_name("mean")
             .short("m")
             .long("mean")
             .help("Print mean"))
        .arg(Arg::with_name("sum")
             .long("sum")
             .help("Print sum"))
        .arg(Arg::with_name("stddev")
             .short("s")
             .long("stddev")
             .help("Print sample standard deviation"))
        .arg(Arg::with_name("var")
             .short("v")
             .long("var")
             .help("Print sample variance"))
        .arg(Arg::with_name("stderr")
             .long("stderr")
             .help("Print standard error"))
        .arg(Arg::with_name("median")
             .long("median")
             .help("Print median (Note: computing median takes O(n) space"))
        .arg(Arg::with_name("percentiles")
             .short("p")
             .long("percentiles")
             .takes_value(true)
             .use_delimiter(true)
             .help("Print arbitrary percentiles. The argument should be a comma delimited list of floats in [0, 100] (Note: computing any percentile takes O(n) space)"))
        .arg(Arg::with_name("mode")
             .long("mode")
             .help("Print mode (Note: computing mode takes O(n) space"))
        .arg(Arg::with_name("mode-count")
             .long("mode-count")
             .help("Print the number of times the mode occured (Note: computing mode takes O(n) space"))
        .arg(Arg::with_name("distinct")
             .long("distinct")
             .help("Print the number of distinct values (Note: computing mode takes O(n) space"))
        .arg(Arg::with_name("distinct-nan")
             .long("distinct-nan")
             .help("Print the number of distinct values treading all NaNs as distinct (Note: computing mode takes O(n) space"))
        // Formatting
        .group(ArgGroup::with_name("format").args(&["tsv", "json"]))
        .arg(Arg::with_name("tsv")
             .short("t")
             .long("tsv")
             .help("Output as tsv. This will force tsv output even if only one statistics is requested."))
        .arg(Arg::with_name("json")
             .short("j")
             .long("json")
             .help("Output as compressed json"))
        // IO
        .arg(Arg::with_name("input")
             .short("i")
             .long("input")
             .value_name("file")
             .default_value("-")
             .help("Take input from file"))
        .arg(Arg::with_name("output")
             .short("o")
             .long("output")
             .value_name("file")
             .default_value("-")
             .help("Write output to file"))
        .get_matches()
}

// TODO this technique is probablly better handled by enum_dispatch but it doesn't work for these
// types at the current moment
enum StatsReader<'a> {
    Stdin(StdinLock<'a>),
    File(BufReader<File>),
}

impl<'a> Read for StatsReader<'a> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            StatsReader::Stdin(lock) => lock.read(buf),
            StatsReader::File(file) => file.read(buf),
        }
    }
}

impl<'a> BufRead for StatsReader<'a> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        match self {
            StatsReader::Stdin(lock) => lock.fill_buf(),
            StatsReader::File(file) => file.fill_buf(),
        }
    }

    fn consume(&mut self, amt: usize) {
        match self {
            StatsReader::Stdin(lock) => lock.consume(amt),
            StatsReader::File(file) => file.consume(amt),
        }
    }
}

enum StatsWriter<'a> {
    Stdout(StdoutLock<'a>),
    File(BufWriter<File>),
}

impl<'a> Write for StatsWriter<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self {
            StatsWriter::Stdout(lock) => lock.write(buf),
            StatsWriter::File(file) => file.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self {
            StatsWriter::Stdout(lock) => lock.flush(),
            StatsWriter::File(file) => file.flush(),
        }
    }
}

fn main() {
    let matches = parse_command_line();

    // Setup input
    let stdin = io::stdin();
    let input = match matches.value_of("input") {
        Some("-") => StatsReader::Stdin(stdin.lock()),
        Some(file_name) => StatsReader::File(BufReader::new(
            File::open(file_name)
                .unwrap_or_else(|_| panic!("File \"{}\" does not exist", file_name)),
        )),
        None => unreachable!(),
    };

    // Setup output
    let stdout = io::stdout();
    let mut output = match matches.value_of("output") {
        Some("-") => StatsWriter::Stdout(stdout.lock()),
        Some(file_name) => {
            StatsWriter::File(BufWriter::new(File::open(file_name).unwrap_or_else(|_| {
                panic!("Couldn't open file \"{}\" for writing", file_name)
            })))
        }
        None => unreachable!(),
    };

    // Process input
    let mut stats = SummStats::new();
    let mut percs = Percentiles::new();
    let mut mode = Mode::new();

    let add_mode = ["mode", "mode-count", "distinct", "distinct-nan"]
        .iter()
        .any(|s| matches.is_present(s));
    let add_percs = ["percentiles", "median"]
        .iter()
        .any(|s| matches.is_present(s));
    let add_stats = [
        "count", "min", "max", "mean", "sum", "stddev", "var", "stderr",
    ]
    .iter()
    .any(|s| matches.is_present(s))
        || !(add_mode && add_percs);

    for line in input.lines() {
        for token in line
            .expect("Couldn't read from file")
            .split(char::is_whitespace)
            .filter(|s| !s.is_empty())
        {
            let num: f64 = token
                .parse()
                .unwrap_or_else(|_| panic!("Could not parse \"{}\" as float", token));
            if add_mode {
                mode.add(num);
            }
            if add_percs {
                percs.add(num);
            }
            if add_stats {
                stats.add(num);
            }
        }
    }

    // Generate output
    let results = compute_results(&matches, &stats, &mut percs, &mode);

    // Write output
    if matches.is_present("tsv") {
        write_tsv(&results, &mut output);
    } else if matches.is_present("json") {
        write_json(&results, &mut output);
    } else if results.len() == 1 {
        let (_, val) = results[0];
        writeln!(output, "{}", val).expect("couldn't write to output");
    } else {
        write_tsv(&results, &mut output);
    }
}

fn compute_results(
    matches: &ArgMatches,
    stats: &SummStats<f64>,
    percs: &mut Percentiles<f64>,
    mode: &Mode<f64>,
) -> Vec<(String, f64)> {
    let mut results = Vec::new();
    if matches.is_present("count") {
        results.push((String::from("count"), stats.count() as f64));
    }
    if matches.is_present("min") {
        results.push((String::from("min"), stats.min().unwrap_or(f64::NAN)));
    }
    if matches.is_present("max") {
        results.push((String::from("max"), stats.max().unwrap_or(f64::NAN)));
    }
    if matches.is_present("mean") {
        results.push((String::from("mean"), stats.mean().unwrap_or(f64::NAN)));
    }
    if matches.is_present("sum") {
        results.push((String::from("sum"), stats.sum()));
    }
    if matches.is_present("stddev") {
        results.push((
            String::from("stddev"),
            stats.standard_deviation().unwrap_or(f64::NAN),
        ));
    }
    if matches.is_present("var") {
        results.push((String::from("var"), stats.variance().unwrap_or(f64::NAN)));
    }
    if matches.is_present("stderr") {
        results.push((
            String::from("stderr"),
            stats.standard_error().unwrap_or(f64::NAN),
        ));
    }
    if matches.is_present("percentiles") {
        let percentiles: Vec<f64> = matches
            .values_of("percentiles")
            .unwrap()
            .map(|p| {
                p.parse::<f64>()
                    .unwrap_or_else(|_| panic!("Could not parse \"{}\" as float", p))
            })
            .collect();
        let vals = match percs.percentiles(percentiles.iter().map(|p| p / 100.0)) {
            Err(_) => panic!("percentiles must between 0 and 100: {:?}", percentiles),
            Ok(None) => iter::repeat(f64::NAN).take(percentiles.len()).collect(),
            Ok(Some(pvals)) => pvals,
        };
        for (perc, val) in percentiles.iter().zip(vals) {
            results.push((format!("{}%", perc), val));
        }
    }
    if matches.is_present("median") {
        results.push((String::from("median"), percs.median().unwrap_or(f64::NAN)));
    }
    if matches.is_present("mode") {
        results.push((String::from("mode"), mode.mode().unwrap_or(f64::NAN)));
    }
    if matches.is_present("mode-count") {
        results.push((String::from("mode #"), mode.mode_count() as f64));
    }
    if matches.is_present("distinct") {
        results.push((String::from("distinct"), mode.count_distinct() as f64));
    }
    if matches.is_present("distinct-nan") {
        results.push((
            String::from("distinct (NaN)"),
            mode.count_distinct_nan() as f64,
        ));
    }

    // Defaults
    if results.is_empty() {
        results.push((String::from("count"), stats.count() as f64));
        results.push((String::from("min"), stats.min().unwrap_or(f64::NAN)));
        results.push((String::from("max"), stats.max().unwrap_or(f64::NAN)));
        results.push((String::from("mean"), stats.mean().unwrap_or(f64::NAN)));
        results.push((
            String::from("stddev"),
            stats.standard_deviation().unwrap_or(f64::NAN),
        ));
    }

    results
}

fn write_tsv(results: &[(String, f64)], output: &mut impl Write) {
    for &(ref name, ref val) in results {
        writeln!(output, "{}\t{}", name, val).expect("couldn't write to output");
    }
}

fn write_json(results: &[(String, f64)], output: &mut impl Write) {
    write!(output, "{{").expect("couldn't write to output");
    let mut iter = results.iter();
    let &(ref name, ref val) = iter.next().unwrap();
    write!(output, "\"{}\":{}", name, val).expect("couldn't write to output");
    for &(ref name, ref val) in iter {
        write!(output, ",\"{}\":{}", name, val).expect("couldn't write to output");
    }
    writeln!(output, "}}").expect("couldn't write to output");
}
<commit_msg>update clap to use crate version<commit_after>use std::f64;
use std::fs::File;
use std::io::{self, BufRead, BufReader, BufWriter, Read, StdinLock, StdoutLock, Write};
use std::iter;

use clap::{crate_version, App, Arg, ArgGroup, ArgMatches};

use inc_stats::{Mode, Percentiles, SummStats};

fn parse_command_line<'a>() -> ArgMatches<'a> {
    App::new("stats")
        .version(crate_version!())
        .author("Erik Brinkman <erik.brinkman@gmail.com>")
        .about("Compute summary statistics of streams of numbers")
        // Output
        .arg(Arg::with_name("count")
             .short("c")
             .long("count")
             .help("Print count"))
        .arg(Arg::with_name("min")
             .long("min")
             .help("Print min"))
        .arg(Arg::with_name("max")
             .long("max")
             .help("Print max"))
        .arg(Arg::with_name("mean")
             .short("m")
             .long("mean")
             .help("Print mean"))
        .arg(Arg::with_name("sum")
             .long("sum")
             .help("Print sum"))
        .arg(Arg::with_name("stddev")
             .short("s")
             .long("stddev")
             .help("Print sample standard deviation"))
        .arg(Arg::with_name("var")
             .short("v")
             .long("var")
             .help("Print sample variance"))
        .arg(Arg::with_name("stderr")
             .long("stderr")
             .help("Print standard error"))
        .arg(Arg::with_name("median")
             .long("median")
             .help("Print median (Note: computing median takes O(n) space"))
        .arg(Arg::with_name("percentiles")
             .short("p")
             .long("percentiles")
             .takes_value(true)
             .use_delimiter(true)
             .help("Print arbitrary percentiles. The argument should be a comma delimited list of floats in [0, 100] (Note: computing any percentile takes O(n) space)"))
        .arg(Arg::with_name("mode")
             .long("mode")
             .help("Print mode (Note: computing mode takes O(n) space"))
        .arg(Arg::with_name("mode-count")
             .long("mode-count")
             .help("Print the number of times the mode occured (Note: computing mode takes O(n) space"))
        .arg(Arg::with_name("distinct")
             .long("distinct")
             .help("Print the number of distinct values (Note: computing mode takes O(n) space"))
        .arg(Arg::with_name("distinct-nan")
             .long("distinct-nan")
             .help("Print the number of distinct values treading all NaNs as distinct (Note: computing mode takes O(n) space"))
        // Formatting
        .group(ArgGroup::with_name("format").args(&["tsv", "json"]))
        .arg(Arg::with_name("tsv")
             .short("t")
             .long("tsv")
             .help("Output as tsv. This will force tsv output even if only one statistics is requested."))
        .arg(Arg::with_name("json")
             .short("j")
             .long("json")
             .help("Output as compressed json"))
        // IO
        .arg(Arg::with_name("input")
             .short("i")
             .long("input")
             .value_name("file")
             .default_value("-")
             .help("Take input from file"))
        .arg(Arg::with_name("output")
             .short("o")
             .long("output")
             .value_name("file")
             .default_value("-")
             .help("Write output to file"))
        .get_matches()
}

// TODO this technique is probablly better handled by enum_dispatch but it doesn't work for these
// types at the current moment
enum StatsReader<'a> {
    Stdin(StdinLock<'a>),
    File(BufReader<File>),
}

impl<'a> Read for StatsReader<'a> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            StatsReader::Stdin(lock) => lock.read(buf),
            StatsReader::File(file) => file.read(buf),
        }
    }
}

impl<'a> BufRead for StatsReader<'a> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        match self {
            StatsReader::Stdin(lock) => lock.fill_buf(),
            StatsReader::File(file) => file.fill_buf(),
        }
    }

    fn consume(&mut self, amt: usize) {
        match self {
            StatsReader::Stdin(lock) => lock.consume(amt),
            StatsReader::File(file) => file.consume(amt),
        }
    }
}

enum StatsWriter<'a> {
    Stdout(StdoutLock<'a>),
    File(BufWriter<File>),
}

impl<'a> Write for StatsWriter<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self {
            StatsWriter::Stdout(lock) => lock.write(buf),
            StatsWriter::File(file) => file.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self {
            StatsWriter::Stdout(lock) => lock.flush(),
            StatsWriter::File(file) => file.flush(),
        }
    }
}

fn main() {
    let matches = parse_command_line();

    // Setup input
    let stdin = io::stdin();
    let input = match matches.value_of("input") {
        Some("-") => StatsReader::Stdin(stdin.lock()),
        Some(file_name) => StatsReader::File(BufReader::new(
            File::open(file_name)
                .unwrap_or_else(|_| panic!("File \"{}\" does not exist", file_name)),
        )),
        None => unreachable!(),
    };

    // Setup output
    let stdout = io::stdout();
    let mut output = match matches.value_of("output") {
        Some("-") => StatsWriter::Stdout(stdout.lock()),
        Some(file_name) => {
            StatsWriter::File(BufWriter::new(File::open(file_name).unwrap_or_else(|_| {
                panic!("Couldn't open file \"{}\" for writing", file_name)
            })))
        }
        None => unreachable!(),
    };

    // Process input
    let mut stats = SummStats::new();
    let mut percs = Percentiles::new();
    let mut mode = Mode::new();

    let add_mode = ["mode", "mode-count", "distinct", "distinct-nan"]
        .iter()
        .any(|s| matches.is_present(s));
    let add_percs = ["percentiles", "median"]
        .iter()
        .any(|s| matches.is_present(s));
    let add_stats = [
        "count", "min", "max", "mean", "sum", "stddev", "var", "stderr",
    ]
    .iter()
    .any(|s| matches.is_present(s))
        || !(add_mode && add_percs);

    for line in input.lines() {
        for token in line
            .expect("Couldn't read from file")
            .split(char::is_whitespace)
            .filter(|s| !s.is_empty())
        {
            let num: f64 = token
                .parse()
                .unwrap_or_else(|_| panic!("Could not parse \"{}\" as float", token));
            if add_mode {
                mode.add(num);
            }
            if add_percs {
                percs.add(num);
            }
            if add_stats {
                stats.add(num);
            }
        }
    }

    // Generate output
    let results = compute_results(&matches, &stats, &mut percs, &mode);

    // Write output
    if matches.is_present("tsv") {
        write_tsv(&results, &mut output);
    } else if matches.is_present("json") {
        write_json(&results, &mut output);
    } else if results.len() == 1 {
        let (_, val) = results[0];
        writeln!(output, "{}", val).expect("couldn't write to output");
    } else {
        write_tsv(&results, &mut output);
    }
}

fn compute_results(
    matches: &ArgMatches,
    stats: &SummStats<f64>,
    percs: &mut Percentiles<f64>,
    mode: &Mode<f64>,
) -> Vec<(String, f64)> {
    let mut results = Vec::new();
    if matches.is_present("count") {
        results.push((String::from("count"), stats.count() as f64));
    }
    if matches.is_present("min") {
        results.push((String::from("min"), stats.min().unwrap_or(f64::NAN)));
    }
    if matches.is_present("max") {
        results.push((String::from("max"), stats.max().unwrap_or(f64::NAN)));
    }
    if matches.is_present("mean") {
        results.push((String::from("mean"), stats.mean().unwrap_or(f64::NAN)));
    }
    if matches.is_present("sum") {
        results.push((String::from("sum"), stats.sum()));
    }
    if matches.is_present("stddev") {
        results.push((
            String::from("stddev"),
            stats.standard_deviation().unwrap_or(f64::NAN),
        ));
    }
    if matches.is_present("var") {
        results.push((String::from("var"), stats.variance().unwrap_or(f64::NAN)));
    }
    if matches.is_present("stderr") {
        results.push((
            String::from("stderr"),
            stats.standard_error().unwrap_or(f64::NAN),
        ));
    }
    if matches.is_present("percentiles") {
        let percentiles: Vec<f64> = matches
            .values_of("percentiles")
            .unwrap()
            .map(|p| {
                p.parse::<f64>()
                    .unwrap_or_else(|_| panic!("Could not parse \"{}\" as float", p))
            })
            .collect();
        let vals = match percs.percentiles(percentiles.iter().map(|p| p / 100.0)) {
            Err(_) => panic!("percentiles must between 0 and 100: {:?}", percentiles),
            Ok(None) => iter::repeat(f64::NAN).take(percentiles.len()).collect(),
            Ok(Some(pvals)) => pvals,
        };
        for (perc, val) in percentiles.iter().zip(vals) {
            results.push((format!("{}%", perc), val));
        }
    }
    if matches.is_present("median") {
        results.push((String::from("median"), percs.median().unwrap_or(f64::NAN)));
    }
    if matches.is_present("mode") {
        results.push((String::from("mode"), mode.mode().unwrap_or(f64::NAN)));
    }
    if matches.is_present("mode-count") {
        results.push((String::from("mode #"), mode.mode_count() as f64));
    }
    if matches.is_present("distinct") {
        results.push((String::from("distinct"), mode.count_distinct() as f64));
    }
    if matches.is_present("distinct-nan") {
        results.push((
            String::from("distinct (NaN)"),
            mode.count_distinct_nan() as f64,
        ));
    }

    // Defaults
    if results.is_empty() {
        results.push((String::from("count"), stats.count() as f64));
        results.push((String::from("min"), stats.min().unwrap_or(f64::NAN)));
        results.push((String::from("max"), stats.max().unwrap_or(f64::NAN)));
        results.push((String::from("mean"), stats.mean().unwrap_or(f64::NAN)));
        results.push((
            String::from("stddev"),
            stats.standard_deviation().unwrap_or(f64::NAN),
        ));
    }

    results
}

fn write_tsv(results: &[(String, f64)], output: &mut impl Write) {
    for &(ref name, ref val) in results {
        writeln!(output, "{}\t{}", name, val).expect("couldn't write to output");
    }
}

fn write_json(results: &[(String, f64)], output: &mut impl Write) {
    write!(output, "{{").expect("couldn't write to output");
    let mut iter = results.iter();
    let &(ref name, ref val) = iter.next().unwrap();
    write!(output, "\"{}\":{}", name, val).expect("couldn't write to output");
    for &(ref name, ref val) in iter {
        write!(output, ",\"{}\":{}", name, val).expect("couldn't write to output");
    }
    writeln!(output, "}}").expect("couldn't write to output");
}
<|endoftext|>


<commit_before>#![feature(custom_attribute, custom_derive, proc_macro)]
#[macro_use] extern crate diesel_codegen;
#[macro_use] extern crate diesel;
extern crate dotenv;
extern crate iron;
extern crate mount;
extern crate params;
extern crate pwhash;
extern crate rand;
extern crate router;
extern crate rustc_serialize;
extern crate staticfile;
extern crate time;

use dotenv::dotenv;
use iron::prelude::*;
use staticfile::Static;
use router::Router;
use mount::Mount;
use std::env;
use std::path::Path;

mod api;
mod authentication;
mod database;
mod errors;
mod schema;
mod logger;
mod models;

fn main() {
    dotenv().ok();

    let mut router = Router::new();
    router.get("/", api::v1::index, "api");

    router.post("/login", api::v1::sessions::login, "login");
    router.post("/logout", api::v1::sessions::logout, "logout");

    router.get("/translations", api::v1::translations::index, "translations_index");
    router.post("/translations", api::v1::translations::create, "translations_create");
    router.post("/translations/:id/validate", api::v1::translations::validate, "translations_validate");
    router.get("/translations/:key", api::v1::translations::show, "translations_show");
    router.delete("/translations/:key", api::v1::translations::delete, "translations_delete");

    router.post("/users", api::v1::users::create, "users_create");

    let mut api_chain = Chain::new(router);
    api_chain.link_before(authentication::middleware::AuthenticationMiddleware);

    let mut mount = Mount::new();
    mount.mount("/", Static::new(Path::new("src/frontend/public/")));
    mount.mount("/api/v1", api_chain);

    let mut chain = Chain::new(mount);
    chain.link_before(logger::RequestLogger);
    chain.link_after(logger::RequestLogger);

    let bind = env::var("LUGH_BIND").unwrap_or("localhost:3000".to_string());
    println!("Start application on {}", bind);

    Iron::new(chain).http(bind.as_str())
        .expect("Can’t start application");
}
<commit_msg>Remove `proc_macro` attribute as this is stable now<commit_after>#![feature(custom_attribute, custom_derive)]
#[macro_use] extern crate diesel_codegen;
#[macro_use] extern crate diesel;
extern crate dotenv;
extern crate iron;
extern crate mount;
extern crate params;
extern crate pwhash;
extern crate rand;
extern crate router;
extern crate rustc_serialize;
extern crate staticfile;
extern crate time;

use dotenv::dotenv;
use iron::prelude::*;
use staticfile::Static;
use router::Router;
use mount::Mount;
use std::env;
use std::path::Path;

mod api;
mod authentication;
mod database;
mod errors;
mod schema;
mod logger;
mod models;

fn main() {
    dotenv().ok();

    let mut router = Router::new();
    router.get("/", api::v1::index, "api");

    router.post("/login", api::v1::sessions::login, "login");
    router.post("/logout", api::v1::sessions::logout, "logout");

    router.get("/translations", api::v1::translations::index, "translations_index");
    router.post("/translations", api::v1::translations::create, "translations_create");
    router.post("/translations/:id/validate", api::v1::translations::validate, "translations_validate");
    router.get("/translations/:key", api::v1::translations::show, "translations_show");
    router.delete("/translations/:key", api::v1::translations::delete, "translations_delete");

    router.post("/users", api::v1::users::create, "users_create");

    let mut api_chain = Chain::new(router);
    api_chain.link_before(authentication::middleware::AuthenticationMiddleware);

    let mut mount = Mount::new();
    mount.mount("/", Static::new(Path::new("src/frontend/public/")));
    mount.mount("/api/v1", api_chain);

    let mut chain = Chain::new(mount);
    chain.link_before(logger::RequestLogger);
    chain.link_after(logger::RequestLogger);

    let bind = env::var("LUGH_BIND").unwrap_or("localhost:3000".to_string());
    println!("Start application on {}", bind);

    Iron::new(chain).http(bind.as_str())
        .expect("Can’t start application");
}
<|endoftext|>


<commit_before>extern crate nom_lua53;

use std::io::{self, Read};
use nom_lua53::{parse_all, ParseResult, Statement, Exp};

#[derive(Debug)]
enum LuaValue {
    Integer(isize),
    Float(f64),
    Boolean(bool),
    Str(String),
}

// What do we say? We say "Merci Basile!"
fn lit_to_string(string: &nom_lua53::string::StringLit) -> String {
    return String::from_utf8_lossy(&(string.0)).to_string();
}

fn eval_expr(expr: &Exp) -> std::option::Option<LuaValue> {
    match *expr {
        Exp::Nil => None,
        Exp::Bool(val) => Some(LuaValue::Boolean(val)),
        Exp::Num(val) => Some(match val {
            nom_lua53::num::Numeral::Float(fl) => LuaValue::Float(fl),
            nom_lua53::num::Numeral::Int(i) => LuaValue::Integer(i),
        }),
        Exp::Str(ref s) => Some(LuaValue::Str(lit_to_string(s))),
        _ => None,
    }
}

fn main() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).expect("Couldn't read from stdin");

    match parse_all(&*input) {
        ParseResult::Done(blk) => {
            for stmt in blk.stmts {
                println!("{:?}", stmt);
                match stmt {
                    Statement::LVarAssign(ass) => {
                        let values = ass.vals.expect("There should be some values. Why isn't there any value?!");
                        for (var, val) in ass.vars.iter().zip(values.iter()) {

                            let computed_value = eval_expr(val);
                            match computed_value {
                                Some(lval) =>
                                    println!("Assigning {:?} to {:?}", lval, var),
                                _ => {}
                            }
                        }
                    }
                    Statement::Assignment(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    _ => {}
                }
            }
        }

        ParseResult::Error(rest, ss) => {
            println!("Error. statements == {:#?}", ss);
            println!("rest == '{}'", String::from_utf8_lossy(rest));
        }
    }
}
<commit_msg>Remove an explicit dereference, leaving it to the coercion thingy<commit_after>extern crate nom_lua53;

use std::io::{self, Read};
use nom_lua53::{parse_all, ParseResult, Statement, Exp};

#[derive(Debug)]
enum LuaValue {
    Integer(isize),
    Float(f64),
    Boolean(bool),
    Str(String),
}

// What do we say? We say "Merci Basile!"
fn lit_to_string(string: &nom_lua53::string::StringLit) -> String {
    return String::from_utf8_lossy(&(string.0)).to_string();
}

fn eval_expr(expr: &Exp) -> std::option::Option<LuaValue> {
    match *expr {
        Exp::Nil => None,
        Exp::Bool(val) => Some(LuaValue::Boolean(val)),
        Exp::Num(val) => Some(match val {
            nom_lua53::num::Numeral::Float(fl) => LuaValue::Float(fl),
            nom_lua53::num::Numeral::Int(i) => LuaValue::Integer(i),
        }),
        Exp::Str(ref s) => Some(LuaValue::Str(lit_to_string(s))),
        _ => None,
    }
}

fn main() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).expect("Couldn't read from stdin");

    match parse_all(&input) {
        ParseResult::Done(blk) => {
            for stmt in blk.stmts {
                println!("{:?}", stmt);
                match stmt {
                    Statement::LVarAssign(ass) => {
                        let values = ass.vals.expect("There should be some values. Why isn't there any value?!");
                        for (var, val) in ass.vars.iter().zip(values.iter()) {

                            let computed_value = eval_expr(val);
                            match computed_value {
                                Some(lval) =>
                                    println!("Assigning {:?} to {:?}", lval, var),
                                _ => {}
                            }
                        }
                    }
                    Statement::Assignment(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    _ => {}
                }
            }
        }

        ParseResult::Error(rest, ss) => {
            println!("Error. statements == {:#?}", ss);
            println!("rest == '{}'", String::from_utf8_lossy(rest));
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014-2015 Mathias Hällman
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

extern crate term;

use std::cmp;
use std::ops::{Add, Sub};

#[derive(Copy, PartialEq, Show)]
pub struct Size(pub u16, pub u16);

#[cfg(not(test))]
impl Size {
  fn from_cell(Cell(row, col): Cell) -> Size {
    Size(row, col)
  }
}

#[derive(Copy)]
pub struct Cell(pub u16, pub u16);

#[cfg(not(test))]
impl Cell {
  fn within(self, size: Size) -> Option<Cell> {
    let Cell(cell_row, cell_col) = self;
    let Size(end_row, end_col) = size;
    if cell_row < end_row && cell_col < end_col { Some(self) } else { None }
  }

  fn from_size(Size(row, col): Size) -> Cell {
    Cell(row, col)
  }
}

impl Add for Cell {
  type Output = Cell;
  fn add(self, rhs: Cell) -> Cell {
    let Cell(r1, c1) = self;
    let Cell(r2, c2) = rhs;
    Cell(r1 + r2, c1 + c2)
  }
}

impl Sub for Cell {
  type Output = Cell;
  fn sub(self, rhs: Cell) -> Cell {
    let Cell(r1, c1) = self;
    let Cell(r2, c2) = rhs;
    Cell(cmp::max(r1 as i16 - r2 as i16, 0) as u16,
         cmp::max(c1 as i16 - c2 as i16, 0) as u16)
  }
}

#[derive(Copy)]
pub struct Rect(pub Cell, pub Size);

impl Rect {
  pub fn contains(&self, Cell(row, col): Cell) -> bool {
    let Rect(Cell(start_row, start_col), Size(rows, cols)) = *self;
    row >= start_row && row < start_row + rows &&
      col >= start_col && col < start_col + cols
  }
}

/*
 * Iterates over a region of the screen, defined by a starting cell and a size.
 */
#[cfg(not(test))]
pub struct CellIterator {
  next_cell: Option<Cell>,
  size: Size,
  width: u16,
}

#[cfg(not(test))]
impl CellIterator {
  pub fn new(Rect(start, size): Rect) -> CellIterator {
    let Size(_, rel_end_col) = size;
    let abs_size = Size::from_cell(start + Cell::from_size(size));
    CellIterator {
      next_cell: start.within(abs_size), size: abs_size, width: rel_end_col
    }
  }
}

#[cfg(not(test))]
impl Iterator for CellIterator {
  type Item = Cell;

  fn next(&mut self) -> Option<Cell> {
    let ret = self.next_cell;
    self.next_cell = self.next_cell.and_then(|cell|
      (cell + Cell(0, 1)).within(self.size).or(
        (cell - Cell(0, self.width - 1) + Cell(1, 0)).within(self.size)));
    return ret;
  }
}

/*
 * Screen is the output surface. You can put characters within its borders and
 * clear it again. Go nuts!
 */
#[cfg(not(test))]
pub struct Screen {
  size: Size,
  terminal: Terminal,
}

#[cfg(not(test))]
impl Drop for Screen {
  fn drop(&mut self) {
    self.terminal.clear();
    self.terminal.show_cursor();
    self.terminal.disable_altscreen();
  }
}

#[cfg(not(test))]
impl Screen {
  pub fn setup() -> Result<Screen, String> {
    Terminal::new().map_or(
      Err(String::from_str("Failed creating a terminal for stdout.")),
      |mut terminal| {
        terminal.enable_altscreen();
        terminal.hide_cursor();
        terminal.clear();
        Ok(Screen { size: Size(0, 0), terminal: terminal })
      })
  }

  pub fn update_size(&mut self) -> bool {
    match term_size::size().map(|(rows, cols)| Size(rows, cols)) {
      None               => return false,
      Some(current_size) => {
        let size_changed = current_size != self.size;
        self.size = current_size;
        return size_changed;
      }
    }
  }

  pub fn size(&self) -> Size {
    self.size
  }

  pub fn clear(&mut self) {
    self.terminal.clear();
  }

  pub fn put(&mut self, position: Cell, character: char, fg: Color, bg: Color) {
    position.within(self.size).map(|Cell(row, col)| {
      self.terminal.set_cursor_position(row, col);
      self.terminal.set_fg(fg);
      self.terminal.set_bg(bg);
      self.terminal.put(character);
    });
  }

  pub fn flush(&mut self) {
    self.terminal.flush();
  }
}

/*
 * Terminal is a simple wrapper that provides some helpful methods for common
 * ouput operations.
 */
#[cfg(not(test))]
struct Terminal {
  terminal: Box<term::Terminal<term::WriterWrapper> + Send>,
}

#[cfg(not(test))]
impl Terminal {
  pub fn new() -> Option<Terminal> {
    term::stdout().map(|terminal| Terminal { terminal: terminal })
  }

  pub fn set_fg(&mut self, fg: Color) {
    self.terminal.fg(fg.to_term_color()).unwrap();
  }

  pub fn set_bg(&mut self, bg: Color) {
    self.terminal.bg(bg.to_term_color()).unwrap();
  }

  pub fn clear(&mut self) {
    (write!(self.terminal, "\x1B[2J")).unwrap();
  }

  pub fn enable_altscreen(&mut self) {
    (write!(self.terminal, "\x1B7\x1B[?47h")).unwrap();
  }

  pub fn disable_altscreen(&mut self) {
    (write!(self.terminal, "\x1B[?47l\x1B8")).unwrap();
  }

  pub fn hide_cursor(&mut self) {
    (write!(self.terminal, "\x1B[?25l")).unwrap();
  }

  pub fn show_cursor(&mut self) {
    (write!(self.terminal, "\x1B[?25h")).unwrap();
  }

  pub fn set_cursor_position(&mut self, row: u16, col: u16) {
    // add (1, 1) becase terminal row/col is one-indexed
    (write!(self.terminal, "\x1B[{};{}H", row + 1, col + 1)).unwrap();
  }

  pub fn put(&mut self, character: char) {
    (write!(self.terminal, "{}", character)).unwrap();
  }

  pub fn flush(&mut self) {
    self.terminal.flush().unwrap();
  }
}

/*
 * Color values for terminal output.
 */
#[allow(dead_code)]  // colors are not used much yet
#[cfg(not(test))]
#[derive(Copy)]
pub enum Color {
  Black,
  Red,
  Green,
  Yellow,
  Blue,
  Magenta,
  Cyan,
  White,
  BrightBlack,
  BrightRed,
  BrightGreen,
  BrightYellow,
  BrightBlue,
  BrightMagenta,
  BrightCyan,
  BrightWhite,
}

#[allow(dead_code)]  // colors are not used much yet
#[cfg(not(test))]
impl Color {
  pub fn to_term_color(&self) -> term::color::Color {
    match *self {
      Color::Black         => term::color::BLACK,
      Color::Red           => term::color::RED,
      Color::Green         => term::color::GREEN,
      Color::Yellow        => term::color::YELLOW,
      Color::Blue          => term::color::BLUE,
      Color::Magenta       => term::color::MAGENTA,
      Color::Cyan          => term::color::CYAN,
      Color::White         => term::color::WHITE,
      Color::BrightBlack   => term::color::BRIGHT_BLACK,
      Color::BrightRed     => term::color::BRIGHT_RED,
      Color::BrightGreen   => term::color::BRIGHT_GREEN,
      Color::BrightYellow  => term::color::BRIGHT_YELLOW,
      Color::BrightBlue    => term::color::BRIGHT_BLUE,
      Color::BrightMagenta => term::color::BRIGHT_MAGENTA,
      Color::BrightCyan    => term::color::BRIGHT_CYAN,
      Color::BrightWhite   => term::color::BRIGHT_WHITE,
    }
  }
}

/*
 * Helper module to capture the ugly. Provides a mean to poll the screen size.
 */
#[cfg(not(test))]
mod term_size {
  extern crate libc;

  use self::libc::funcs::bsd44::ioctl;

  const STDOUT_FILENO: libc::c_int = 1;

  #[cfg(target_os = "macos")]
  const TIOCGWINSZ: libc::c_ulong = 0x40087468;
  #[cfg(target_os = "linux")]
  const TIOCGWINSZ: libc::c_int = 0x5413;

  pub fn size() -> Option<(u16, u16)> {
    #[allow(dead_code)]  // not interested in pixel sizes
    struct WinSize {
      rows: libc::c_ushort,  // rows, in screen cells
      cols: libc::c_ushort,  // columns, in screen cells
      h_pixels: libc::c_ushort,  // horizontal size, pixels
      v_pixels: libc::c_ushort,  // vertical size, pixels
    }

    unsafe {
      let mut size = WinSize { rows: 0, cols: 0, h_pixels: 0, v_pixels: 0 };
      match ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
        0 => Some((size.rows as u16, size.cols as u16)),
        _ => None,
      }
    }
  }
}
<commit_msg>Avoid unnecessary drawing to the terminal<commit_after>/*
 * Copyright (c) 2014-2015 Mathias Hällman
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

extern crate term;

use std::cmp;
#[cfg(not(test))]
use std::iter;
use std::ops::{Add, Sub};

#[derive(Copy, PartialEq, Show)]
pub struct Size(pub u16, pub u16);

#[cfg(not(test))]
impl Size {
  fn from_cell(Cell(row, col): Cell) -> Size {
    Size(row, col)
  }
}

#[derive(Copy)]
pub struct Cell(pub u16, pub u16);

#[cfg(not(test))]
impl Cell {
  fn within(self, size: Size) -> Option<Cell> {
    let Cell(cell_row, cell_col) = self;
    let Size(end_row, end_col) = size;
    if cell_row < end_row && cell_col < end_col { Some(self) } else { None }
  }

  fn from_size(Size(row, col): Size) -> Cell {
    Cell(row, col)
  }
}

impl Add for Cell {
  type Output = Cell;
  fn add(self, rhs: Cell) -> Cell {
    let Cell(r1, c1) = self;
    let Cell(r2, c2) = rhs;
    Cell(r1 + r2, c1 + c2)
  }
}

impl Sub for Cell {
  type Output = Cell;
  fn sub(self, rhs: Cell) -> Cell {
    let Cell(r1, c1) = self;
    let Cell(r2, c2) = rhs;
    Cell(cmp::max(r1 as i16 - r2 as i16, 0) as u16,
         cmp::max(c1 as i16 - c2 as i16, 0) as u16)
  }
}

#[derive(Copy)]
pub struct Rect(pub Cell, pub Size);

impl Rect {
  pub fn contains(&self, Cell(row, col): Cell) -> bool {
    let Rect(Cell(start_row, start_col), Size(rows, cols)) = *self;
    row >= start_row && row < start_row + rows &&
      col >= start_col && col < start_col + cols
  }
}

/*
 * Iterates over a region of the screen, defined by a starting cell and a size.
 */
#[cfg(not(test))]
pub struct CellIterator {
  next_cell: Option<Cell>,
  size: Size,
  width: u16,
}

#[cfg(not(test))]
impl CellIterator {
  pub fn new(Rect(start, size): Rect) -> CellIterator {
    let Size(_, rel_end_col) = size;
    let abs_size = Size::from_cell(start + Cell::from_size(size));
    CellIterator {
      next_cell: start.within(abs_size), size: abs_size, width: rel_end_col
    }
  }
}

#[cfg(not(test))]
impl Iterator for CellIterator {
  type Item = Cell;

  fn next(&mut self) -> Option<Cell> {
    let ret = self.next_cell;
    self.next_cell = self.next_cell.and_then(|cell|
      (cell + Cell(0, 1)).within(self.size).or(
        (cell - Cell(0, self.width - 1) + Cell(1, 0)).within(self.size)));
    return ret;
  }
}

/*
 * Screen is the output surface. You can put characters within its borders and
 * clear it again. Go nuts!
 */
#[cfg(not(test))]
pub struct Screen {
  size: Size,
  terminal: Terminal,
  buffer: ScreenBuffer,
}

#[cfg(not(test))]
impl Drop for Screen {
  fn drop(&mut self) {
    self.terminal.clear();
    self.terminal.show_cursor();
    self.terminal.disable_altscreen();
  }
}

#[cfg(not(test))]
impl Screen {
  pub fn setup() -> Result<Screen, String> {
    Terminal::new().map_or(
      Err(String::from_str("Failed creating a terminal for stdout.")),
      |mut terminal| {
        terminal.enable_altscreen();
        terminal.hide_cursor();
        terminal.clear();
        Ok(Screen {
          size: Size(0, 0),
          terminal: terminal,
          buffer: ScreenBuffer::new()
        })
      })
  }

  pub fn update_size(&mut self) -> bool {
    term_size::size().map(|(rows, cols)| Size(rows, cols)).
    and_then(|new_size| if new_size == self.size { None } else { Some({
      self.buffer.resize(new_size);
      self.size = new_size; }) }).
    is_some()
  }

  pub fn size(&self) -> Size {
    self.size
  }

  pub fn clear(&mut self) {
    self.terminal.clear();
    self.buffer.clear();
  }

  pub fn put(&mut self, position: Cell, character: char, fg: Color, bg: Color) {
    position.within(self.size).map(|Cell(row, col)| {
      if self.buffer.update(position, character, fg, bg) {
        self.terminal.set_cursor_position(row, col);
        self.terminal.set_fg(fg);
        self.terminal.set_bg(bg);
        self.terminal.put(character);
      }
    });
  }

  pub fn flush(&mut self) {
    self.terminal.flush();
  }
}

/*
 * ScreenBuffer mirrors what's known to be on the screen, allowing us to draw
 * new information only when necessary.
 */
#[cfg(not(test))]
struct ScreenBuffer {
  cells: Vec<Option<(char, Color, Color)>>,
  width: u16,
}

#[cfg(not(test))]
impl ScreenBuffer {
  fn new() -> ScreenBuffer {
    ScreenBuffer { cells: Vec::new(), width: 0 }
  }

  fn resize(&mut self, Size(rows, cols): Size) {
    let size = rows as uint * cols as uint;
    let diff = size as int - self.cells.len() as int;
    if diff > 0 {
      self.cells.reserve_exact(size);
      self.cells.extend(iter::repeat(None).take(diff as uint))
    }
    else if diff < 0 {
      self.cells.truncate(size);
      self.cells.shrink_to_fit();
    }
    self.width = cols;
  }

  fn clear(&mut self) {
    for i in range(0, self.cells.len()) {
      self.cells[i] = None;
    }
  }

  // a character taking up multiple screen columns is represented in the buffer
  // by one Some(character) followed by Nones in the additional cells it covers
  fn update(&mut self, Cell(row, col): Cell, character: char, fg: Color,
      bg: Color) -> bool {
    let cell = Some((character, fg, bg));
    let idx = (row as uint * self.width as uint) + col as uint;
    let buffer_size = self.cells.len();
    let nones = |&:| range(1, CharExt::width(character, false).unwrap_or(1)).
                     map(|i| idx + i).filter(|i| *i < buffer_size);
    let update =
      self.cells[idx] != cell || nones().any(|i| self.cells[i] != None);
    if update {
      self.cells[idx] = cell;
      for i in nones() { self.cells[i] = None; }
    }
    update
  }
}

/*
 * Terminal is a simple wrapper that provides some helpful methods for common
 * ouput operations.
 */
#[cfg(not(test))]
struct Terminal {
  terminal: Box<term::Terminal<term::WriterWrapper> + Send>,
}

#[cfg(not(test))]
impl Terminal {
  pub fn new() -> Option<Terminal> {
    term::stdout().map(|terminal| Terminal { terminal: terminal })
  }

  pub fn set_fg(&mut self, fg: Color) {
    self.terminal.fg(fg.to_term_color()).unwrap();
  }

  pub fn set_bg(&mut self, bg: Color) {
    self.terminal.bg(bg.to_term_color()).unwrap();
  }

  pub fn clear(&mut self) {
    (write!(self.terminal, "\x1B[2J")).unwrap();
  }

  pub fn enable_altscreen(&mut self) {
    (write!(self.terminal, "\x1B7\x1B[?47h")).unwrap();
  }

  pub fn disable_altscreen(&mut self) {
    (write!(self.terminal, "\x1B[?47l\x1B8")).unwrap();
  }

  pub fn hide_cursor(&mut self) {
    (write!(self.terminal, "\x1B[?25l")).unwrap();
  }

  pub fn show_cursor(&mut self) {
    (write!(self.terminal, "\x1B[?25h")).unwrap();
  }

  pub fn set_cursor_position(&mut self, row: u16, col: u16) {
    // add (1, 1) becase terminal row/col is one-indexed
    (write!(self.terminal, "\x1B[{};{}H", row + 1, col + 1)).unwrap();
  }

  pub fn put(&mut self, character: char) {
    (write!(self.terminal, "{}", character)).unwrap();
  }

  pub fn flush(&mut self) {
    self.terminal.flush().unwrap();
  }
}

/*
 * Color values for terminal output.
 */
#[allow(dead_code)]  // colors are not used much yet
#[cfg(not(test))]
#[derive(Clone, Copy, PartialEq)]
pub enum Color {
  Black,
  Red,
  Green,
  Yellow,
  Blue,
  Magenta,
  Cyan,
  White,
  BrightBlack,
  BrightRed,
  BrightGreen,
  BrightYellow,
  BrightBlue,
  BrightMagenta,
  BrightCyan,
  BrightWhite,
}

#[allow(dead_code)]  // colors are not used much yet
#[cfg(not(test))]
impl Color {
  pub fn to_term_color(&self) -> term::color::Color {
    match *self {
      Color::Black         => term::color::BLACK,
      Color::Red           => term::color::RED,
      Color::Green         => term::color::GREEN,
      Color::Yellow        => term::color::YELLOW,
      Color::Blue          => term::color::BLUE,
      Color::Magenta       => term::color::MAGENTA,
      Color::Cyan          => term::color::CYAN,
      Color::White         => term::color::WHITE,
      Color::BrightBlack   => term::color::BRIGHT_BLACK,
      Color::BrightRed     => term::color::BRIGHT_RED,
      Color::BrightGreen   => term::color::BRIGHT_GREEN,
      Color::BrightYellow  => term::color::BRIGHT_YELLOW,
      Color::BrightBlue    => term::color::BRIGHT_BLUE,
      Color::BrightMagenta => term::color::BRIGHT_MAGENTA,
      Color::BrightCyan    => term::color::BRIGHT_CYAN,
      Color::BrightWhite   => term::color::BRIGHT_WHITE,
    }
  }
}

/*
 * Helper module to capture the ugly. Provides a mean to poll the screen size.
 */
#[cfg(not(test))]
mod term_size {
  extern crate libc;

  use self::libc::funcs::bsd44::ioctl;

  const STDOUT_FILENO: libc::c_int = 1;

  #[cfg(target_os = "macos")]
  const TIOCGWINSZ: libc::c_ulong = 0x40087468;
  #[cfg(target_os = "linux")]
  const TIOCGWINSZ: libc::c_int = 0x5413;

  pub fn size() -> Option<(u16, u16)> {
    #[allow(dead_code)]  // not interested in pixel sizes
    struct WinSize {
      rows: libc::c_ushort,  // rows, in screen cells
      cols: libc::c_ushort,  // columns, in screen cells
      h_pixels: libc::c_ushort,  // horizontal size, pixels
      v_pixels: libc::c_ushort,  // vertical size, pixels
    }

    unsafe {
      let mut size = WinSize { rows: 0, cols: 0, h_pixels: 0, v_pixels: 0 };
      match ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
        0 => Some((size.rows as u16, size.cols as u16)),
        _ => None,
      }
    }
  }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use self::Key::*;
use std::io::{File, Open, Read, Write};
use libc::{c_ushort, c_int, c_ulong};
use std::os::unix::AsRawFd;
use std::io::process::{Command, InheritFd};
use std::iter::repeat;
use std::cmp::min;
use ansi;

use std::thread::Thread;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;

pub struct Screen {
  tty: Terminal,
  original_stty_state: Vec<u8>,
  pub height: u16,
  pub width: u16,
  pub visible_choices: u16,
  pub start_line: u16,
}

impl Screen {
  pub fn open_screen() -> Screen {
    let mut tty = Terminal::open_terminal();
    let current_stty_state = tty.stty(&["-g"]);
    tty.initialize();
    let (cols, rows) = tty.winsize().unwrap();
    let visible_choices = min(20, rows - 1);
    let start_line = rows - visible_choices - 1;
    Screen {
      tty: tty,
      original_stty_state: current_stty_state,
      height: rows,
      width: cols,
      visible_choices: visible_choices,
      start_line: start_line,
    }
  }

  pub fn restore_tty(&mut self) {
    self.tty.stty(&[String::from_utf8(self.original_stty_state.clone()).unwrap().as_slice()]);
  }

  pub fn move_cursor(&mut self, line: u16, column: u16) {
    self.tty.write(ansi::setpos(line, column).as_slice());
  }

  pub fn move_cursor_to_bottom(&mut self) {
    let end_line = self.start_line + self.visible_choices;
    self.move_cursor(end_line, 0);
    self.write("\n");
  }

  pub fn blank_screen(&mut self) {
    let start_line = self.start_line;
    self.move_cursor(start_line, 0);
    let blank_line = repeat(' ').take(self.width as usize).collect::<String>();
    for _ in range(0, self.height) {
      self.tty.write(blank_line.as_bytes());
    }
    self.move_cursor(start_line, 0);
  }

  pub fn show_cursor(&mut self) {
    self.tty.write(ansi::show_cursor().as_slice());
  }

  pub fn hide_cursor(&mut self) {
    self.tty.write(ansi::hide_cursor().as_slice());
  }

  pub fn write(&mut self, s: &str) {
    self.tty.write(s.as_bytes());
  }

  pub fn write_inverted(&mut self, s: &str) {
    self.tty.write(ansi::inverse().as_slice());
    self.tty.write(s.as_bytes());
    self.tty.write(ansi::reset().as_slice());
  }

  // Return all buffered keystrokes, or the next key if buffer is empty.
  pub fn get_buffered_keys(&mut self) -> Vec<Key> {
    let mut ret = Vec::new();
    while let Ok(byte) = self.tty.input.try_recv() {
      ret.push(Terminal::translate_byte(byte));
    }
    if ret.is_empty() {
      let byte = self.tty.input.recv().unwrap();
      ret.push(Terminal::translate_byte(byte));
    }
    ret
  }
}

impl Drop for Screen {
  fn drop(&mut self) {
    self.restore_tty();
  }
}

pub enum Key {
  Char(char),
  Control(char),
  Enter,
  Backspace,
}

struct Terminal {
  input: Receiver<u8>,
  output: File,
}

impl Terminal {
  fn open_terminal() -> Terminal {
    let term_path = Path::new("/dev/tty");
    let mut input_file = File::open_mode(&term_path, Open, Read).unwrap();
    let output_file = File::open_mode(&term_path, Open, Write).unwrap();
    let (tx, rx): (Sender<u8>, Receiver<u8>) = mpsc::channel();
    Thread::spawn(move || {
      loop {
        tx.send(input_file.read_byte().unwrap()).unwrap();
      }
    });
    Terminal {
      input: rx,
      output: output_file
    }
  }

  fn initialize(&mut self) {
    self.stty(&["raw", "-echo", "cbreak"]);
  }

  fn stty(&mut self, args: &[&str]) -> Vec<u8> {
    let term_input = File::open_mode(&Path::new("/dev/tty"), Open, Read).unwrap();
    let fd = term_input.as_raw_fd();
    let mut process = match Command::new("stty").args(args).stdin(InheritFd(fd)).spawn() {
      Ok(p) => p,
      Err(e) => panic!("Command failed: {}", e),
    };

    process.stdout.as_mut().unwrap().read_to_end().unwrap()
  }

  fn getchar(&mut self) -> Key {
    let byte = self.input.recv().unwrap();
    Terminal::translate_byte(byte)
  }

  fn translate_byte(byte: u8) -> Key {
    if byte == '\r' as u8 {
      Enter
    } else if byte == 127 {
      Backspace
    } else if byte & 96 == 0 {
      Control((byte + 96u8) as char)
    } else {
      Char(byte as char)
    }
  }

  fn write(&mut self, s: &[u8]) {
    self.output.write(s.as_slice()).unwrap();
  }

  fn writeln(&mut self, s: &str) {
    self.output.write_line(s).unwrap();
  }

  fn winsize(&self) -> Option<(u16, u16)> {
    extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
    const TIOCGWINSZ: c_ulong = 0x40087468;

    #[repr(C)]
    struct TermSize {
      rows: c_ushort,
      cols: c_ushort,
      x: c_ushort,
      y: c_ushort,
    }

    let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
    if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
      Some((size.cols as u16, size.rows as u16))
    } else {
      None
    }
  }
}
<commit_msg>Make 'restore_tty' private<commit_after>#![allow(dead_code)]

use self::Key::*;
use std::io::{File, Open, Read, Write};
use libc::{c_ushort, c_int, c_ulong};
use std::os::unix::AsRawFd;
use std::io::process::{Command, InheritFd};
use std::iter::repeat;
use std::cmp::min;
use ansi;

use std::thread::Thread;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;

pub struct Screen {
  tty: Terminal,
  original_stty_state: Vec<u8>,
  pub height: u16,
  pub width: u16,
  pub visible_choices: u16,
  pub start_line: u16,
}

impl Screen {
  pub fn open_screen() -> Screen {
    let mut tty = Terminal::open_terminal();
    let current_stty_state = tty.stty(&["-g"]);
    tty.initialize();
    let (cols, rows) = tty.winsize().unwrap();
    let visible_choices = min(20, rows - 1);
    let start_line = rows - visible_choices - 1;
    Screen {
      tty: tty,
      original_stty_state: current_stty_state,
      height: rows,
      width: cols,
      visible_choices: visible_choices,
      start_line: start_line,
    }
  }

  fn restore_tty(&mut self) {
    self.tty.stty(&[String::from_utf8(self.original_stty_state.clone()).unwrap().as_slice()]);
  }

  pub fn move_cursor(&mut self, line: u16, column: u16) {
    self.tty.write(ansi::setpos(line, column).as_slice());
  }

  pub fn move_cursor_to_bottom(&mut self) {
    let end_line = self.start_line + self.visible_choices;
    self.move_cursor(end_line, 0);
    self.write("\n");
  }

  pub fn blank_screen(&mut self) {
    let start_line = self.start_line;
    self.move_cursor(start_line, 0);
    let blank_line = repeat(' ').take(self.width as usize).collect::<String>();
    for _ in range(0, self.height) {
      self.tty.write(blank_line.as_bytes());
    }
    self.move_cursor(start_line, 0);
  }

  pub fn show_cursor(&mut self) {
    self.tty.write(ansi::show_cursor().as_slice());
  }

  pub fn hide_cursor(&mut self) {
    self.tty.write(ansi::hide_cursor().as_slice());
  }

  pub fn write(&mut self, s: &str) {
    self.tty.write(s.as_bytes());
  }

  pub fn write_inverted(&mut self, s: &str) {
    self.tty.write(ansi::inverse().as_slice());
    self.tty.write(s.as_bytes());
    self.tty.write(ansi::reset().as_slice());
  }

  // Return all buffered keystrokes, or the next key if buffer is empty.
  pub fn get_buffered_keys(&mut self) -> Vec<Key> {
    let mut ret = Vec::new();
    while let Ok(byte) = self.tty.input.try_recv() {
      ret.push(Terminal::translate_byte(byte));
    }
    if ret.is_empty() {
      let byte = self.tty.input.recv().unwrap();
      ret.push(Terminal::translate_byte(byte));
    }
    ret
  }
}

impl Drop for Screen {
  fn drop(&mut self) {
    self.restore_tty();
  }
}

pub enum Key {
  Char(char),
  Control(char),
  Enter,
  Backspace,
}

struct Terminal {
  input: Receiver<u8>,
  output: File,
}

impl Terminal {
  fn open_terminal() -> Terminal {
    let term_path = Path::new("/dev/tty");
    let mut input_file = File::open_mode(&term_path, Open, Read).unwrap();
    let output_file = File::open_mode(&term_path, Open, Write).unwrap();
    let (tx, rx): (Sender<u8>, Receiver<u8>) = mpsc::channel();
    Thread::spawn(move || {
      loop {
        tx.send(input_file.read_byte().unwrap()).unwrap();
      }
    });
    Terminal {
      input: rx,
      output: output_file
    }
  }

  fn initialize(&mut self) {
    self.stty(&["raw", "-echo", "cbreak"]);
  }

  fn stty(&mut self, args: &[&str]) -> Vec<u8> {
    let term_input = File::open_mode(&Path::new("/dev/tty"), Open, Read).unwrap();
    let fd = term_input.as_raw_fd();
    let mut process = match Command::new("stty").args(args).stdin(InheritFd(fd)).spawn() {
      Ok(p) => p,
      Err(e) => panic!("Command failed: {}", e),
    };

    process.stdout.as_mut().unwrap().read_to_end().unwrap()
  }

  fn getchar(&mut self) -> Key {
    let byte = self.input.recv().unwrap();
    Terminal::translate_byte(byte)
  }

  fn translate_byte(byte: u8) -> Key {
    if byte == '\r' as u8 {
      Enter
    } else if byte == 127 {
      Backspace
    } else if byte & 96 == 0 {
      Control((byte + 96u8) as char)
    } else {
      Char(byte as char)
    }
  }

  fn write(&mut self, s: &[u8]) {
    self.output.write(s.as_slice()).unwrap();
  }

  fn writeln(&mut self, s: &str) {
    self.output.write_line(s).unwrap();
  }

  fn winsize(&self) -> Option<(u16, u16)> {
    extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
    const TIOCGWINSZ: c_ulong = 0x40087468;

    #[repr(C)]
    struct TermSize {
      rows: c_ushort,
      cols: c_ushort,
      x: c_ushort,
      y: c_ushort,
    }

    let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
    if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
      Some((size.cols as u16, size.rows as u16))
    } else {
      None
    }
  }
}
<|endoftext|>


<commit_before>//
//    Copyright (C) 2015  Rory McCann
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Affero General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Affero General Public License for more details.
//
//    You should have received a copy of the GNU Affero General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

extern crate num;
extern crate regex;

use std::env;
use std::fmt;
use num::integer::{div_rem, Integer};
use regex::Regex;

struct Duration<T> {
    weeks: T,
    days: T,
    hours: T,
    minutes: T,
    seconds: T,
}

impl<T:Integer> Duration<T> {
    fn new() -> Duration<T> {
        Duration{ weeks: T::zero(), days: T::zero(), hours: T::zero(), minutes: T::zero(), seconds: T::zero() }
    }

    fn set_weeks<'a>(&'a mut self, weeks: T) -> &'a mut Duration<T> {
        self.weeks = weeks;
        self
    }
    fn set_days<'a>(&'a mut self, days: T) -> &'a mut Duration<T> {
        self.days = days;
        self
    }
    fn set_hours<'a>(&'a mut self, hours: T) -> &'a mut Duration<T> {
        self.hours = hours;
        self
    }
    fn set_minutes<'a>(&'a mut self, minutes: T) -> &'a mut Duration<T> {
        self.minutes = minutes;
        self
    }
    fn set_seconds<'a>(&'a mut self, seconds: T) -> &'a mut Duration<T> {
        self.seconds = seconds;
        self
    }

}

impl fmt::Display for Duration<u64> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut writing = false;
        if self.weeks != 0 {
            try!(write!(f, "{}wk", self.weeks));
            writing = true;
        }
        if self.days != 0 || writing {
            try!(write!(f, "{}dy", self.days));
            writing = true;
        }
        if self.hours != 0 || writing {
            try!(write!(f, "{}hr", self.hours));
            writing = true;
        }
        if self.minutes != 0 || writing {
            if writing {
                try!(write!(f, "{:02}m", self.minutes));
            } else {
                try!(write!(f, "{}m", self.minutes));
            }
            writing = true;
        }
        if self.seconds != 0 || writing {
            if writing {
                try!(write!(f, "{:02}s", self.seconds));
            } else {
                try!(write!(f, "{}s", self.seconds));
            }
            //writing = true;
        }
        Ok(())
    }
}

fn sec2duration(seconds: u64) -> Duration<u64> {
    let (minutes, seconds) = div_rem(seconds, 60);
    let (hours, minutes) = div_rem(minutes, 60);
    let (days, hours) = div_rem(hours, 24);
    let (weeks, days) = div_rem(days, 7);

    Duration{ weeks: weeks, days: days, hours: hours, minutes: minutes, seconds: seconds }
}

// Converts a textual input to the number of seconds this represents. String error if something went wrong.
fn input2sec(input:String) -> Result<u64, String> {
    // TODO replace with regex! macro so the is guaranteed to be OK

    if let Some(cap) = Regex::new(r"^\s*(?P<sec>[0-9]+)(\.[0-9]+)?\s*(s|sec|seconds?)?\s*$").unwrap().captures(&input) {
        // Just numbers, interpret as seconds. Truncate the sub-seconds
        let sec: u64 = try!(try!(cap.name("sec").ok_or("0")).parse().or(Err("Invalid seconds")));
        Ok(sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<min>[0-9]+)\s*(m|min|minutes?)\s*((?P<sec>[0-9]+)(\.[0-9]+)?\s*(s|sec|seconds?)?)?\s*$").unwrap().captures(&input) {
        // XmYs. Truncate the subseconds
        let sec: u64 = try!(cap.name("sec").unwrap_or(&"0").parse::<u64>().or(Err("Invalid seconds")));
        let min: u64 = try!(cap.name("min").unwrap_or(&"0").parse::<u64>().or(Err("Invalid minutes")));
        Ok(min*60 + sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<min>[0-9]+):(?P<sec>[0-9][0-9])\s*$").unwrap().captures(&input) {
        // 0:00
        let sec: u64 = try!(cap.name("sec").unwrap_or(&"0").parse::<u64>().or(Err("Invalid seconds")));
        let min: u64 = try!(cap.name("min").unwrap_or(&"0").parse::<u64>().or(Err("Invalid minutes")));
        Ok(min*60 + sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<ms>[0-9]+)\s*(ms|msec|milliseconds?)\s*$").unwrap().captures(&input) {
        // Milliseconds
        let ms: u64 = try!(try!(cap.name("ms").ok_or("0")).parse().or(Err("Invalid milliseconds")));
        let sec = ms / 1_000;
        Ok(sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<ms>[0-9]+)\s*(μs|μsec|microsec|microseconds?)\s*$").unwrap().captures(&input) {
        // Microseconds
        let ms: u64 = try!(try!(cap.name("ms").ok_or("0")).parse().or(Err("Invalid microseconds")));
        let sec = ms / 1_000_000;
        Ok(sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<ms>[0-9]+)\s*(ns|nsec|nanosec|nanoseconds?)\s*$").unwrap().captures(&input) {
        // Nanoseconds
        let ms: u64 = try!(try!(cap.name("ms").ok_or("0")).parse().or(Err("Invalid microseconds")));
        let sec = ms / 1_000_000_000;
        Ok(sec)
    } else {
        Err("Invalid input".to_string())
    }

}

fn prettytime(input: Vec<String>) -> Result<String, String> {
    if input.len() < 1 { return Err("No input".to_string()); }

    let input = input.join(" ");
    let seconds: u64 = try!(input2sec(input));

    let duration = sec2duration(seconds);

    Ok(format!("{}", duration))
}

fn main() {
    let args: Vec<_> = env::args().skip(1).collect();
    match prettytime(args) {
        Ok(s) => { println!("{}", s); },
        Err(s) => { println!("{}", s); }
    }
}

#[test]
fn test_print_duration() {
    assert_eq!("", format!("{}", Duration::new()));
    assert_eq!("1s", format!("{}", Duration::new().set_seconds(1)));
    assert_eq!("1m00s", format!("{}", Duration::new().set_minutes(1)));
    assert_eq!("1m03s", format!("{}", Duration::new().set_minutes(1).set_seconds(3)));
    assert_eq!("1hr00m00s", format!("{}", Duration::new().set_hours(1)));
    assert_eq!("1dy0hr00m00s", format!("{}", Duration::new().set_days(1)));
    assert_eq!("1wk0dy0hr00m00s", format!("{}", Duration::new().set_weeks(1)));
}

#[test]
fn test_parse_input() {
    // simple seconds
    assert_eq!(input2sec("10".to_string()), Ok(10));
    assert_eq!(input2sec("10s".to_string()), Ok(10));
    assert_eq!(input2sec("10 s".to_string()), Ok(10));
    assert_eq!(input2sec("10 sec".to_string()), Ok(10));
    assert_eq!(input2sec("10 foos".to_string()), Err("Invalid input".to_string()));

    // seconds with precision
    assert_eq!(input2sec("10.5".to_string()), Ok(10));
    assert_eq!(input2sec("10.5s".to_string()), Ok(10));
    assert_eq!(input2sec("10.2 s".to_string()), Ok(10));
    assert_eq!(input2sec("10.1 sec".to_string()), Ok(10));

    // minutes
    assert_eq!(input2sec("2m".to_string()), Ok(120));
    assert_eq!(input2sec("2m 20".to_string()), Ok(140));
    assert_eq!(input2sec("2m 20s".to_string()), Ok(140));
    assert_eq!(input2sec("2m20s".to_string()), Ok(140));
    assert_eq!(input2sec("   2 m   20  s   ".to_string()), Ok(140));
    assert_eq!(input2sec("2:20".to_string()), Ok(140));
    assert_eq!(input2sec("02:20".to_string()), Ok(140));
    assert_eq!(input2sec("100:00".to_string()), Ok(6000));

    assert_eq!(input2sec("10m12.123s".to_string()), Ok(612));

    // milliseconds
    assert_eq!(input2sec("2000ms".to_string()), Ok(2));
    assert_eq!(input2sec("2ms".to_string()), Ok(0));
    assert_eq!(input2sec("2000 milliseconds".to_string()), Ok(2));
    assert_eq!(input2sec("1500ms".to_string()), Ok(1));
    assert_eq!(input2sec("1100ms".to_string()), Ok(1));
    assert_eq!(input2sec("1999ms".to_string()), Ok(1));

    // microseconds
    assert_eq!(input2sec("2000000microsec".to_string()), Ok(2));
    assert_eq!(input2sec("2000000μsec".to_string()), Ok(2));
    assert_eq!(input2sec("2000000 μs".to_string()), Ok(2));

    // nanoseconds
    assert_eq!(input2sec("2000000000ns".to_string()), Ok(2));
    assert_eq!(input2sec("2000000000 ns".to_string()), Ok(2));
    assert_eq!(input2sec("2000000000 nsec".to_string()), Ok(2));
    assert_eq!(input2sec("2000000000 nanosec".to_string()), Ok(2));
}

<commit_msg>exist with error code<commit_after>//
//    Copyright (C) 2015  Rory McCann
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Affero General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Affero General Public License for more details.
//
//    You should have received a copy of the GNU Affero General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

extern crate num;
extern crate regex;

use std::env;
use std::fmt;
use num::integer::{div_rem, Integer};
use regex::Regex;

struct Duration<T> {
    weeks: T,
    days: T,
    hours: T,
    minutes: T,
    seconds: T,
}

impl<T:Integer> Duration<T> {
    fn new() -> Duration<T> {
        Duration{ weeks: T::zero(), days: T::zero(), hours: T::zero(), minutes: T::zero(), seconds: T::zero() }
    }

    fn set_weeks<'a>(&'a mut self, weeks: T) -> &'a mut Duration<T> {
        self.weeks = weeks;
        self
    }
    fn set_days<'a>(&'a mut self, days: T) -> &'a mut Duration<T> {
        self.days = days;
        self
    }
    fn set_hours<'a>(&'a mut self, hours: T) -> &'a mut Duration<T> {
        self.hours = hours;
        self
    }
    fn set_minutes<'a>(&'a mut self, minutes: T) -> &'a mut Duration<T> {
        self.minutes = minutes;
        self
    }
    fn set_seconds<'a>(&'a mut self, seconds: T) -> &'a mut Duration<T> {
        self.seconds = seconds;
        self
    }

}

impl fmt::Display for Duration<u64> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut writing = false;
        if self.weeks != 0 {
            try!(write!(f, "{}wk", self.weeks));
            writing = true;
        }
        if self.days != 0 || writing {
            try!(write!(f, "{}dy", self.days));
            writing = true;
        }
        if self.hours != 0 || writing {
            try!(write!(f, "{}hr", self.hours));
            writing = true;
        }
        if self.minutes != 0 || writing {
            if writing {
                try!(write!(f, "{:02}m", self.minutes));
            } else {
                try!(write!(f, "{}m", self.minutes));
            }
            writing = true;
        }
        if self.seconds != 0 || writing {
            if writing {
                try!(write!(f, "{:02}s", self.seconds));
            } else {
                try!(write!(f, "{}s", self.seconds));
            }
            //writing = true;
        }
        Ok(())
    }
}

fn sec2duration(seconds: u64) -> Duration<u64> {
    let (minutes, seconds) = div_rem(seconds, 60);
    let (hours, minutes) = div_rem(minutes, 60);
    let (days, hours) = div_rem(hours, 24);
    let (weeks, days) = div_rem(days, 7);

    Duration{ weeks: weeks, days: days, hours: hours, minutes: minutes, seconds: seconds }
}

// Converts a textual input to the number of seconds this represents. String error if something went wrong.
fn input2sec(input:String) -> Result<u64, String> {
    // TODO replace with regex! macro so the is guaranteed to be OK

    if let Some(cap) = Regex::new(r"^\s*(?P<sec>[0-9]+)(\.[0-9]+)?\s*(s|sec|seconds?)?\s*$").unwrap().captures(&input) {
        // Just numbers, interpret as seconds. Truncate the sub-seconds
        let sec: u64 = try!(try!(cap.name("sec").ok_or("0")).parse().or(Err("Invalid seconds")));
        Ok(sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<min>[0-9]+)\s*(m|min|minutes?)\s*((?P<sec>[0-9]+)(\.[0-9]+)?\s*(s|sec|seconds?)?)?\s*$").unwrap().captures(&input) {
        // XmYs. Truncate the subseconds
        let sec: u64 = try!(cap.name("sec").unwrap_or(&"0").parse::<u64>().or(Err("Invalid seconds")));
        let min: u64 = try!(cap.name("min").unwrap_or(&"0").parse::<u64>().or(Err("Invalid minutes")));
        Ok(min*60 + sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<min>[0-9]+):(?P<sec>[0-9][0-9])\s*$").unwrap().captures(&input) {
        // 0:00
        let sec: u64 = try!(cap.name("sec").unwrap_or(&"0").parse::<u64>().or(Err("Invalid seconds")));
        let min: u64 = try!(cap.name("min").unwrap_or(&"0").parse::<u64>().or(Err("Invalid minutes")));
        Ok(min*60 + sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<ms>[0-9]+)\s*(ms|msec|milliseconds?)\s*$").unwrap().captures(&input) {
        // Milliseconds
        let ms: u64 = try!(try!(cap.name("ms").ok_or("0")).parse().or(Err("Invalid milliseconds")));
        let sec = ms / 1_000;
        Ok(sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<ms>[0-9]+)\s*(μs|μsec|microsec|microseconds?)\s*$").unwrap().captures(&input) {
        // Microseconds
        let ms: u64 = try!(try!(cap.name("ms").ok_or("0")).parse().or(Err("Invalid microseconds")));
        let sec = ms / 1_000_000;
        Ok(sec)
    } else if let Some(cap) = Regex::new(r"^\s*(?P<ms>[0-9]+)\s*(ns|nsec|nanosec|nanoseconds?)\s*$").unwrap().captures(&input) {
        // Nanoseconds
        let ms: u64 = try!(try!(cap.name("ms").ok_or("0")).parse().or(Err("Invalid microseconds")));
        let sec = ms / 1_000_000_000;
        Ok(sec)
    } else {
        Err("Invalid input".to_string())
    }

}

fn prettytime(input: Vec<String>) -> Result<String, String> {
    if input.len() < 1 { return Err("No input".to_string()); }

    let input = input.join(" ");
    let seconds: u64 = try!(input2sec(input));

    let duration = sec2duration(seconds);

    Ok(format!("{}", duration))
}

fn main() {
    let args: Vec<_> = env::args().skip(1).collect();
    match prettytime(args) {
        Ok(s) => { println!("{}", s); },
        Err(s) => { println!("{}", s); std::process::exit(1); }
    }
}

#[test]
fn test_print_duration() {
    assert_eq!("", format!("{}", Duration::new()));
    assert_eq!("1s", format!("{}", Duration::new().set_seconds(1)));
    assert_eq!("1m00s", format!("{}", Duration::new().set_minutes(1)));
    assert_eq!("1m03s", format!("{}", Duration::new().set_minutes(1).set_seconds(3)));
    assert_eq!("1hr00m00s", format!("{}", Duration::new().set_hours(1)));
    assert_eq!("1dy0hr00m00s", format!("{}", Duration::new().set_days(1)));
    assert_eq!("1wk0dy0hr00m00s", format!("{}", Duration::new().set_weeks(1)));
}

#[test]
fn test_parse_input() {
    // simple seconds
    assert_eq!(input2sec("10".to_string()), Ok(10));
    assert_eq!(input2sec("10s".to_string()), Ok(10));
    assert_eq!(input2sec("10 s".to_string()), Ok(10));
    assert_eq!(input2sec("10 sec".to_string()), Ok(10));
    assert_eq!(input2sec("10 foos".to_string()), Err("Invalid input".to_string()));

    // seconds with precision
    assert_eq!(input2sec("10.5".to_string()), Ok(10));
    assert_eq!(input2sec("10.5s".to_string()), Ok(10));
    assert_eq!(input2sec("10.2 s".to_string()), Ok(10));
    assert_eq!(input2sec("10.1 sec".to_string()), Ok(10));

    // minutes
    assert_eq!(input2sec("2m".to_string()), Ok(120));
    assert_eq!(input2sec("2m 20".to_string()), Ok(140));
    assert_eq!(input2sec("2m 20s".to_string()), Ok(140));
    assert_eq!(input2sec("2m20s".to_string()), Ok(140));
    assert_eq!(input2sec("   2 m   20  s   ".to_string()), Ok(140));
    assert_eq!(input2sec("2:20".to_string()), Ok(140));
    assert_eq!(input2sec("02:20".to_string()), Ok(140));
    assert_eq!(input2sec("100:00".to_string()), Ok(6000));

    assert_eq!(input2sec("10m12.123s".to_string()), Ok(612));

    // milliseconds
    assert_eq!(input2sec("2000ms".to_string()), Ok(2));
    assert_eq!(input2sec("2ms".to_string()), Ok(0));
    assert_eq!(input2sec("2000 milliseconds".to_string()), Ok(2));
    assert_eq!(input2sec("1500ms".to_string()), Ok(1));
    assert_eq!(input2sec("1100ms".to_string()), Ok(1));
    assert_eq!(input2sec("1999ms".to_string()), Ok(1));

    // microseconds
    assert_eq!(input2sec("2000000microsec".to_string()), Ok(2));
    assert_eq!(input2sec("2000000μsec".to_string()), Ok(2));
    assert_eq!(input2sec("2000000 μs".to_string()), Ok(2));

    // nanoseconds
    assert_eq!(input2sec("2000000000ns".to_string()), Ok(2));
    assert_eq!(input2sec("2000000000 ns".to_string()), Ok(2));
    assert_eq!(input2sec("2000000000 nsec".to_string()), Ok(2));
    assert_eq!(input2sec("2000000000 nanosec".to_string()), Ok(2));
}

<|endoftext|>


<commit_before>use std::env;
use std::error::Error;
use std::fs::File;
use std::path::Path;

extern crate csv_checker;

fn main() {
    // Skip the first arg since it's the program itself
    let paths = env::args().skip(1);

    if paths.len() == 0 {
        panic!("Provide some paths!");
    }

    for arg in paths {
        println!("= {} =", arg);

        let path = Path::new(&arg);

        let file = match File::open(&path) {
            Err(why) => panic!("couldn't open: {}", Error::description(&why)),
            Ok(file) => file,
        };

        let errors: Vec<csv_checker::CSVError> = csv_checker::errors_for_csv(file);

        for error in errors {
            println!("error at line {}, col {}", error.line, error.col);
        }
    }
}
<commit_msg>Non-zero exit status when errors found<commit_after>use std::env;
use std::error::Error;
use std::fs::File;
use std::path::Path;

extern crate csv_checker;

fn errors_for_args() -> i32 {
    let mut exit_code: i32 = 0;

    // Skip the first arg since it's the program itself
    let paths = env::args().skip(1);

    if paths.len() == 0 {
        panic!("Provide some paths!");
    }

    for arg in paths {
        println!("= {} =", arg);

        let path = Path::new(&arg);

        let file = match File::open(&path) {
            Err(why) => panic!("couldn't open: {}", Error::description(&why)),
            Ok(file) => file,
        };

        let errors: Vec<csv_checker::CSVError> = csv_checker::errors_for_csv(file);

        if errors.len() > 0 {
            exit_code = 1;
        }

        for error in errors {
            println!("error at line {}, col {}", error.line, error.col);
        }
    }

    exit_code
}

fn main() {
    let exit_code = errors_for_args();
    std::process::exit(exit_code);
}
<|endoftext|>