<commit_before>extern crate cargo_update;
extern crate tabwriter;
extern crate lazysort;
extern crate regex;
extern crate git2;

use std::process::{Command, exit};
use std::io::{Write, stdout};
use tabwriter::TabWriter;
use lazysort::SortedBy;
use std::fmt::Display;
use git2::Repository;
#[cfg(target_os="windows")]
use std::fs::File;
use regex::Regex;
use std::env;
use std::fs;

fn main() {
    let result = actual_main().err().unwrap_or(0);
    exit(result);
}

fn actual_main() -> Result<(), i32> {
    let opts = cargo_update::Options::parse();

    if cfg!(target_os = "windows") {
        let old_version_r = Regex::new(r"cargo-install-update\.exe-v.+").unwrap();
        for old_version in fs::read_dir(env::current_exe().unwrap().parent().unwrap().canonicalize().unwrap())
            .unwrap()
            .map(Result::unwrap)
            .filter(|f| old_version_r.is_match(&f.file_name().into_string().unwrap())) {
            fs::remove_file(old_version.path()).unwrap();
        }
    }

    let crates_file = cargo_update::ops::resolve_crates_file(opts.crates_file.1.clone());
    let configuration = try!(cargo_update::ops::PackageConfig::read(&crates_file.with_file_name(".install_config.toml")));
    let mut packages = cargo_update::ops::installed_main_repo_packages(&crates_file);

    if !opts.filter.is_empty() {
        packages.retain(|p| configuration.get(&p.name).map(|p_cfg| opts.filter.iter().all(|f| f.matches(p_cfg))).unwrap_or(false));
    }
    match (opts.all, opts.to_update.is_empty()) {
        (true, true) => {}
        (true, false) => {
            for pkg in cargo_update::ops::intersect_packages(&packages, &opts.to_update, opts.install).into_iter() {
                if packages.iter().find(|p| p.name == pkg.name).is_none() {
                    packages.push(pkg);
                }
            }
        }
        (false, true) => {
            panic!("No packages to update and -a not specified, this should've been caught by option parser (please report to \
                    http://github.com/nabijaczleweli/cargo-update)")
        }
        (false, false) => packages = cargo_update::ops::intersect_packages(&packages, &opts.to_update, opts.install),
    }

    let registry = try!(cargo_update::ops::get_index_path(&opts.cargo_dir.1).map_err(|e| {
        println!("Couldn't get package repository: {}.", e);
        2
    }));
    let mut registry_repo = try!(Repository::open(&registry).map_err(|_| {
        println!("Failed to open registry repository at {}.", registry.display());
        2
    }));
    try!(cargo_update::ops::update_index(&mut registry_repo, "https://github.com/rust-lang/crates.io-index", &mut stdout()).map_err(|e| {
        println!("Failed to update index repository: {}.", e);
        2
    }));
    let latest_registry = try!(registry_repo.revparse_single("origin/master").map_err(|_| {
        println!("Failed to read master branch of registry repository at {}.", registry.display());
        2
    }));

    for package in &mut packages {
        package.pull_version(&latest_registry.as_commit().unwrap().tree().unwrap(), &registry_repo);
    }

    {
        let mut out = TabWriter::new(stdout());
        writeln!(out, "Package\tInstalled\tLatest\tNeeds update").unwrap();
        for (package, package_target_version) in
            packages.iter()
                .map(|p| (p, configuration.get(&p.name).and_then(|c| c.target_version.as_ref())))
                .sorted_by(|&(ref lhs, lhstv), &(ref rhs, rhstv)| (!lhs.needs_update(lhstv), &lhs.name).cmp(&(!rhs.needs_update(rhstv), &rhs.name))) {
            write!(out, "{}\t", package.name).unwrap();
            if let Some(ref v) = package.version {
                write!(out, "v{}", v).unwrap();
            }
            if let Some(tv) = package_target_version {
                write!(out, "\t{}", tv).unwrap();
            } else if let Some(upd_v) = package.update_to_version() {
                write!(out, "\tv{}", upd_v).unwrap();
            } else {
                write!(out, "\tN/A").unwrap();
            }
            writeln!(out,
                     "\t{}",
                     if package.needs_update(package_target_version) {
                         "Yes"
                     } else {
                         "No"
                     })
                .unwrap();
        }
        writeln!(out).unwrap();
        out.flush().unwrap();
    }

    if opts.update {
        if !opts.force {
            packages.retain(|p| p.needs_update(configuration.get(&p.name).and_then(|c| c.target_version.as_ref())));
        }

        packages.retain(|pkg| pkg.update_to_version().is_some());

        if !packages.is_empty() {
            let (success_n, errored, result): (usize, Vec<String>, Option<i32>) = packages.into_iter()
                .map(|package| -> Result<(), (i32, String)> {
                    println!("{} {}",
                             if package.version.is_some() {
                                 "Updating"
                             } else {
                                 "Installing"
                             },
                             package.name);

                    if cfg!(target_os = "windows") && package.version.is_some() && package.name == "cargo-update" {
                        save_cargo_update_exec(package.version.as_ref().unwrap());
                    }

                    let install_res = if let Some(cfg) = configuration.get(&package.name) {
                            Command::new("cargo")
                                .args(&cfg.cargo_args()[..])
                                .arg(&package.name)
                                .arg("--vers")
                                .arg(if let Some(tv) = cfg.target_version.as_ref() {
                                    tv.to_string()
                                } else {
                                    package.update_to_version().unwrap().to_string()
                                })
                                .status()
                        } else {
                            Command::new("cargo")
                                .arg("install")
                                .arg("-f")
                                .arg(&package.name)
                                .arg("--vers")
                                .arg(package.update_to_version().unwrap().to_string())
                                .status()
                        }
                        .unwrap();

                    println!();
                    if !install_res.success() {
                        if cfg!(target_os = "windows") && package.version.is_some() && package.name == "cargo-update" {
                            restore_cargo_update_exec(package.version.as_ref().unwrap());
                        }

                        Err((install_res.code().unwrap_or(-1), package.name))
                    } else {
                        Ok(())
                    }
                })
                .fold((0, vec![], None), |(s, mut e, r), p| match p {
                    Ok(()) => (s + 1, e, r),
                    Err((pr, pn)) => {
                        e.push(pn);
                        (s, e, r.or_else(|| Some(pr)))
                    }
                });

            println!();
            println!("Updated {} package{}.", success_n, if success_n == 1 { "" } else { "s" });
            if !errored.is_empty() && result.is_some() {
                print!("Failed to update ");
                for (i, e) in errored.iter().enumerate() {
                    if i != 0 {
                        print!(", ");
                    }
                    print!("{}", e);
                }
                println!(".");
                return Err(result.unwrap());
            }
        } else {
            println!("No packages need updating.");
        }
    }

    if opts.update_git {
        let mut packages = cargo_update::ops::installed_git_repo_packages(&crates_file);

        if !opts.filter.is_empty() {
            packages.retain(|p| configuration.get(&p.name).map(|p_cfg| opts.filter.iter().all(|f| f.matches(p_cfg))).unwrap_or(false));
        }
        if !opts.all {
            packages.retain(|p| opts.to_update.iter().any(|u| p.name == u.0));
        }

        for package in &mut packages {
            package.pull_version(&opts.temp_dir.1);
        }

        {
            let mut out = TabWriter::new(stdout());
            writeln!(out, "Package\tInstalled\tLatest\tNeeds update").unwrap();
            for package in packages.iter()
                .sorted_by(|lhs, rhs| (!lhs.needs_update(), &lhs.name).cmp(&(!rhs.needs_update(), &rhs.name))) {
                writeln!(out,
                         "{}\t{}\t{}\t{}",
                         package.name,
                         package.id,
                         package.newest_id.as_ref().unwrap(),
                         if package.needs_update() { "Yes" } else { "No" })
                    .unwrap();
            }
            writeln!(out).unwrap();
            out.flush().unwrap();
        }

        if opts.update {
            if !opts.force {
                packages.retain(cargo_update::ops::GitRepoPackage::needs_update);
            }

            if !packages.is_empty() {
                let (success_n, errored, result): (usize, Vec<String>, Option<i32>) = packages.into_iter()
                    .map(|package| -> Result<(), (i32, String)> {
                        println!("Updating {} from {}", package.name, package.url);

                        if cfg!(target_os = "windows") && package.name == "cargo-update" {
                            save_cargo_update_exec(&package.id.to_string());
                        }

                        let install_res = if let Some(cfg) = configuration.get(&package.name) {
                                let mut cmd = Command::new("cargo");
                                cmd.args(&cfg.cargo_args()[..])
                                    .arg("--git")
                                    .arg(&package.url)
                                    .arg(&package.name);
                                if let Some(ref b) = package.branch.as_ref() {
                                    cmd.arg("--branch").arg(b);
                                }
                                cmd.status()
                            } else {
                                let mut cmd = Command::new("cargo");
                                cmd.arg("install")
                                    .arg("-f")
                                    .arg("--git")
                                    .arg(&package.url)
                                    .arg(&package.name);
                                if let Some(ref b) = package.branch.as_ref() {
                                    cmd.arg("--branch").arg(b);
                                }
                                cmd.status()
                            }
                            .unwrap();

                        println!();
                        if !install_res.success() {
                            if cfg!(target_os = "windows") && package.name == "cargo-update" {
                                restore_cargo_update_exec(&package.id.to_string());
                            }

                            Err((install_res.code().unwrap_or(-1), package.name))
                        } else {
                            Ok(())
                        }
                    })
                    .fold((0, vec![], None), |(s, mut e, r), p| match p {
                        Ok(()) => (s + 1, e, r),
                        Err((pr, pn)) => {
                            e.push(pn);
                            (s, e, r.or_else(|| Some(pr)))
                        }
                    });

                println!();
                println!("Updated {} git package{}.", success_n, if success_n == 1 { "" } else { "s" });
                if !errored.is_empty() && result.is_some() {
                    print!("Failed to update ");
                    for (i, e) in errored.iter().enumerate() {
                        if i != 0 {
                            print!(", ");
                        }
                        print!("{}", e);
                    }
                    println!(".");
                    return Err(result.unwrap());
                }
            } else {
                println!("No git packages need updating.");
            }
        }
    }

    Ok(())
}

/// This way the past-current exec will be "replaced" and we'll get no dupes in .cargo.toml
#[cfg(target_os="windows")]
fn save_cargo_update_exec<D: Display>(version: &D) {
    save_cargo_update_exec_impl(format!("exe-v{}", version));
}

#[cfg(target_os="windows")]
fn save_cargo_update_exec_impl(extension: String) {
    let cur_exe = env::current_exe().unwrap();
    fs::rename(&cur_exe, cur_exe.with_extension(extension)).unwrap();
    File::create(cur_exe).unwrap();
}

#[cfg(target_os="windows")]
fn restore_cargo_update_exec<D: Display>(version: &D) {
    restore_cargo_update_exec_impl(format!("exe-v{}", version))
}

#[cfg(target_os="windows")]
fn restore_cargo_update_exec_impl(extension: String) {
    let cur_exe = env::current_exe().unwrap();
    fs::remove_file(&cur_exe).unwrap();
    fs::rename(cur_exe.with_extension(extension), cur_exe).unwrap();
}

#[cfg(not(target_os="windows"))]
fn save_cargo_update_exec<D: Display>(_: &D) {}

#[cfg(not(target_os="windows"))]
fn restore_cargo_update_exec<D: Display>(_: &D) {}
<commit_msg>Update git packages even if repo packages failed, and show a full summary at the end<commit_after>extern crate cargo_update;
extern crate tabwriter;
extern crate lazysort;
extern crate regex;
extern crate git2;

use std::process::{Command, exit};
use std::io::{Write, stdout};
use tabwriter::TabWriter;
use lazysort::SortedBy;
use std::fmt::Display;
use git2::Repository;
#[cfg(target_os="windows")]
use std::fs::File;
use regex::Regex;
use std::env;
use std::fs;

fn main() {
    let result = actual_main().err().unwrap_or(0);
    exit(result);
}

fn actual_main() -> Result<(), i32> {
    let opts = cargo_update::Options::parse();

    if cfg!(target_os = "windows") {
        let old_version_r = Regex::new(r"cargo-install-update\.exe-v.+").unwrap();
        for old_version in fs::read_dir(env::current_exe().unwrap().parent().unwrap().canonicalize().unwrap())
            .unwrap()
            .map(Result::unwrap)
            .filter(|f| old_version_r.is_match(&f.file_name().into_string().unwrap())) {
            fs::remove_file(old_version.path()).unwrap();
        }
    }

    let crates_file = cargo_update::ops::resolve_crates_file(opts.crates_file.1.clone());
    let configuration = try!(cargo_update::ops::PackageConfig::read(&crates_file.with_file_name(".install_config.toml")));
    let mut packages = cargo_update::ops::installed_main_repo_packages(&crates_file);

    if !opts.filter.is_empty() {
        packages.retain(|p| configuration.get(&p.name).map(|p_cfg| opts.filter.iter().all(|f| f.matches(p_cfg))).unwrap_or(false));
    }
    match (opts.all, opts.to_update.is_empty()) {
        (true, true) => {}
        (true, false) => {
            for pkg in cargo_update::ops::intersect_packages(&packages, &opts.to_update, opts.install).into_iter() {
                if packages.iter().find(|p| p.name == pkg.name).is_none() {
                    packages.push(pkg);
                }
            }
        }
        (false, true) => {
            panic!("No packages to update and -a not specified, this should've been caught by option parser (please report to \
                    http://github.com/nabijaczleweli/cargo-update)")
        }
        (false, false) => packages = cargo_update::ops::intersect_packages(&packages, &opts.to_update, opts.install),
    }

    let registry = try!(cargo_update::ops::get_index_path(&opts.cargo_dir.1).map_err(|e| {
        println!("Couldn't get package repository: {}.", e);
        2
    }));
    let mut registry_repo = try!(Repository::open(&registry).map_err(|_| {
        println!("Failed to open registry repository at {}.", registry.display());
        2
    }));
    try!(cargo_update::ops::update_index(&mut registry_repo, "https://github.com/rust-lang/crates.io-index", &mut stdout()).map_err(|e| {
        println!("Failed to update index repository: {}.", e);
        2
    }));
    let latest_registry = try!(registry_repo.revparse_single("origin/master").map_err(|_| {
        println!("Failed to read master branch of registry repository at {}.", registry.display());
        2
    }));

    for package in &mut packages {
        package.pull_version(&latest_registry.as_commit().unwrap().tree().unwrap(), &registry_repo);
    }

    {
        let mut out = TabWriter::new(stdout());
        writeln!(out, "Package\tInstalled\tLatest\tNeeds update").unwrap();
        for (package, package_target_version) in
            packages.iter()
                .map(|p| (p, configuration.get(&p.name).and_then(|c| c.target_version.as_ref())))
                .sorted_by(|&(ref lhs, lhstv), &(ref rhs, rhstv)| (!lhs.needs_update(lhstv), &lhs.name).cmp(&(!rhs.needs_update(rhstv), &rhs.name))) {
            write!(out, "{}\t", package.name).unwrap();
            if let Some(ref v) = package.version {
                write!(out, "v{}", v).unwrap();
            }
            if let Some(tv) = package_target_version {
                write!(out, "\t{}", tv).unwrap();
            } else if let Some(upd_v) = package.update_to_version() {
                write!(out, "\tv{}", upd_v).unwrap();
            } else {
                write!(out, "\tN/A").unwrap();
            }
            writeln!(out,
                     "\t{}",
                     if package.needs_update(package_target_version) {
                         "Yes"
                     } else {
                         "No"
                     })
                .unwrap();
        }
        writeln!(out).unwrap();
        out.flush().unwrap();
    }

    let mut success_n_global = 0usize;
    let mut errored_global = vec![];
    let mut result_global = None;

    if opts.update {
        if !opts.force {
            packages.retain(|p| p.needs_update(configuration.get(&p.name).and_then(|c| c.target_version.as_ref())));
        }

        packages.retain(|pkg| pkg.update_to_version().is_some());

        if !packages.is_empty() {
            let (success_n, errored, result): (usize, Vec<String>, Option<i32>) = packages.into_iter()
                .map(|package| -> Result<(), (i32, String)> {
                    println!("{} {}",
                             if package.version.is_some() {
                                 "Updating"
                             } else {
                                 "Installing"
                             },
                             package.name);

                    if cfg!(target_os = "windows") && package.version.is_some() && package.name == "cargo-update" {
                        save_cargo_update_exec(package.version.as_ref().unwrap());
                    }

                    let install_res = if let Some(cfg) = configuration.get(&package.name) {
                            Command::new("cargo")
                                .args(&cfg.cargo_args()[..])
                                .arg(&package.name)
                                .arg("--vers")
                                .arg(if let Some(tv) = cfg.target_version.as_ref() {
                                    tv.to_string()
                                } else {
                                    package.update_to_version().unwrap().to_string()
                                })
                                .status()
                        } else {
                            Command::new("cargo")
                                .arg("install")
                                .arg("-f")
                                .arg(&package.name)
                                .arg("--vers")
                                .arg(package.update_to_version().unwrap().to_string())
                                .status()
                        }
                        .unwrap();

                    println!();
                    if !install_res.success() {
                        if cfg!(target_os = "windows") && package.version.is_some() && package.name == "cargo-update" {
                            restore_cargo_update_exec(package.version.as_ref().unwrap());
                        }

                        Err((install_res.code().unwrap_or(-1), package.name))
                    } else {
                        Ok(())
                    }
                })
                .fold((0, vec![], None), |(s, mut e, r), p| match p {
                    Ok(()) => (s + 1, e, r),
                    Err((pr, pn)) => {
                        e.push(pn);
                        (s, e, r.or_else(|| Some(pr)))
                    }
                });

            success_n_global += success_n;

            println!();
            println!("Updated {} package{}.", success_n, if success_n == 1 { "" } else { "s" });
            if !errored.is_empty() && result.is_some() {
                print!("Failed to update ");
                for (i, e) in errored.iter().enumerate() {
                    if i != 0 {
                        print!(", ");
                    }
                    print!("{}", e);
                }
                println!(".");
                println!();

                if opts.update_git {
                    errored_global = errored;
                    result_global = result;
                } else {
                    return Err(result.unwrap());
                }
            }
        } else {
            println!("No packages need updating.");
        }
    }

    if opts.update_git {
        let mut packages = cargo_update::ops::installed_git_repo_packages(&crates_file);

        if !opts.filter.is_empty() {
            packages.retain(|p| configuration.get(&p.name).map(|p_cfg| opts.filter.iter().all(|f| f.matches(p_cfg))).unwrap_or(false));
        }
        if !opts.all {
            packages.retain(|p| opts.to_update.iter().any(|u| p.name == u.0));
        }

        for package in &mut packages {
            package.pull_version(&opts.temp_dir.1);
        }

        {
            let mut out = TabWriter::new(stdout());
            writeln!(out, "Package\tInstalled\tLatest\tNeeds update").unwrap();
            for package in packages.iter()
                .sorted_by(|lhs, rhs| (!lhs.needs_update(), &lhs.name).cmp(&(!rhs.needs_update(), &rhs.name))) {
                writeln!(out,
                         "{}\t{}\t{}\t{}",
                         package.name,
                         package.id,
                         package.newest_id.as_ref().unwrap(),
                         if package.needs_update() { "Yes" } else { "No" })
                    .unwrap();
            }
            writeln!(out).unwrap();
            out.flush().unwrap();
        }

        if opts.update {
            if !opts.force {
                packages.retain(cargo_update::ops::GitRepoPackage::needs_update);
            }

            if !packages.is_empty() {
                let (success_n, errored, result): (usize, Vec<String>, Option<i32>) = packages.into_iter()
                    .map(|package| -> Result<(), (i32, String)> {
                        println!("Updating {} from {}", package.name, package.url);

                        if cfg!(target_os = "windows") && package.name == "cargo-update" {
                            save_cargo_update_exec(&package.id.to_string());
                        }

                        let install_res = if let Some(cfg) = configuration.get(&package.name) {
                                let mut cmd = Command::new("cargo");
                                cmd.args(&cfg.cargo_args()[..])
                                    .arg("--git")
                                    .arg(&package.url)
                                    .arg(&package.name);
                                if let Some(ref b) = package.branch.as_ref() {
                                    cmd.arg("--branch").arg(b);
                                }
                                cmd.status()
                            } else {
                                let mut cmd = Command::new("cargo");
                                cmd.arg("install")
                                    .arg("-f")
                                    .arg("--git")
                                    .arg(&package.url)
                                    .arg(&package.name);
                                if let Some(ref b) = package.branch.as_ref() {
                                    cmd.arg("--branch").arg(b);
                                }
                                cmd.status()
                            }
                            .unwrap();

                        println!();
                        if !install_res.success() {
                            if cfg!(target_os = "windows") && package.name == "cargo-update" {
                                restore_cargo_update_exec(&package.id.to_string());
                            }

                            Err((install_res.code().unwrap_or(-1), package.name))
                        } else {
                            Ok(())
                        }
                    })
                    .fold((0, vec![], None), |(s, mut e, r), p| match p {
                        Ok(()) => (s + 1, e, r),
                        Err((pr, pn)) => {
                            e.push(pn);
                            (s, e, r.or_else(|| Some(pr)))
                        }
                    });

                success_n_global += success_n;

                println!();
                println!("Updated {} git package{}.", success_n, if success_n == 1 { "" } else { "s" });
                if !errored.is_empty() && result.is_some() {
                    print!("Failed to update ");
                    for (i, e) in errored.iter().enumerate() {
                        if i != 0 {
                            print!(", ");
                        }
                        print!("{}", e);
                    }
                    println!(".");
                    println!();

                    errored_global.extend(errored);

                    if result_global.is_none() {
                        return Err(result.unwrap());
                    }
                }
            } else {
                println!("No git packages need updating.");
            }
        }
    }

    if opts.update {
        println!("Overall updated {} package{}.", success_n_global, if success_n_global == 1 { "" } else { "s" });

        if !errored_global.is_empty() && result_global.is_some() {
            print!("Overall failed to update ");
            for (i, e) in errored_global.iter().enumerate() {
                if i != 0 {
                    print!(", ");
                }
                print!("{}", e);
            }
            println!(".");

            return Err(result_global.unwrap());
        }
    }

    Ok(())
}

/// This way the past-current exec will be "replaced" and we'll get no dupes in .cargo.toml
#[cfg(target_os="windows")]
fn save_cargo_update_exec<D: Display>(version: &D) {
    save_cargo_update_exec_impl(format!("exe-v{}", version));
}

#[cfg(target_os="windows")]
fn save_cargo_update_exec_impl(extension: String) {
    let cur_exe = env::current_exe().unwrap();
    fs::rename(&cur_exe, cur_exe.with_extension(extension)).unwrap();
    File::create(cur_exe).unwrap();
}

#[cfg(target_os="windows")]
fn restore_cargo_update_exec<D: Display>(version: &D) {
    restore_cargo_update_exec_impl(format!("exe-v{}", version))
}

#[cfg(target_os="windows")]
fn restore_cargo_update_exec_impl(extension: String) {
    let cur_exe = env::current_exe().unwrap();
    fs::remove_file(&cur_exe).unwrap();
    fs::rename(cur_exe.with_extension(extension), cur_exe).unwrap();
}

#[cfg(not(target_os="windows"))]
fn save_cargo_update_exec<D: Display>(_: &D) {}

#[cfg(not(target_os="windows"))]
fn restore_cargo_update_exec<D: Display>(_: &D) {}
<|endoftext|>


<commit_before>extern crate piston;
extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;

use piston::event_loop::Events;
use piston::input::{Event, Input, Button, Key};
use piston::window::WindowSettings;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};
use graphics::polygon::Polygon;
use graphics::{DrawState, Transformed};

struct Spaceship {
    x: f64,
    y: f64,
}

const SPACESHIP_POINTS: [[f64; 2]; 3] = [
    [5.0, 10.0],
    [-5.0, 10.0],
    [0.0, -10.0],
];

impl Spaceship {
    fn new() -> Spaceship {
        return Spaceship{x: 100.0, y: 100.0};
    }
}

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];

fn main() {
    let opengl = OpenGL::V3_2;

    let window = Window::new(
        WindowSettings::new(
            "vs-game",
            [200, 200],
        )
        .exit_on_esc(true)
    ).unwrap();

    let mut gl = GlGraphics::new(opengl);
    let ds = DrawState::new();

    let poly = Polygon::new(WHITE);
    let mut spaceship = Spaceship::new();

    for e in window.events() {
        use graphics::clear;

        match e {
            Event::Render(r) => gl.draw(r.viewport(), |c, gl| {
                clear(BLACK, gl);
                poly.draw(&SPACESHIP_POINTS, &ds, c.transform.trans(spaceship.x, spaceship.y), gl)
            }),
            Event::Input(Input::Press(Button::Keyboard(k))) =>
                match k {
                    Key::Up => spaceship.y -= 1.0,
                    Key::Down => spaceship.y += 1.0,
                    Key::Left => spaceship.x -= 1.0,
                    Key::Right => spaceship.x += 1.0,
                    Key::Q => return,
                    _ => (),
                },
            _ => (),
        }
    }
}
<commit_msg>Added rotation and velocity<commit_after>extern crate piston;
extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;

use piston::event_loop::Events;
use piston::input::{Event, Input, Button, Key};
use piston::window::WindowSettings;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};
use graphics::polygon::Polygon;
use graphics::{DrawState, Transformed};

struct Spaceship {
    x: f64,
    y: f64,
    dx: f64,
    dy: f64,
    theta: f64,
}

const SPACESHIP_POINTS: [[f64; 2]; 3] = [
    [5.0, 7.0],
    [-5.0, 7.0],
    [0.0, -13.0],
];

impl Spaceship {
    fn new() -> Spaceship {
        return Spaceship{
            x: 100.0,
            y: 100.0,
            dx: 0.0,
            dy: 0.0,
            theta: 0.0,
        };
    }

    fn accelerate(&mut self, ddv: f64) {
        self.dx += self.theta.sin()*ddv;
        self.dy -= self.theta.cos()*ddv;
    }
}

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];

fn main() {
    let opengl = OpenGL::V3_2;

    let window = Window::new(
        WindowSettings::new(
            "vs-game",
            [200, 200],
        )
        .exit_on_esc(true)
    ).unwrap();

    let mut gl = GlGraphics::new(opengl);
    let ds = DrawState::new();

    let poly = Polygon::new(WHITE);
    let mut spaceship = Spaceship::new();

    for e in window.events() {
        use graphics::clear;

        match e {
            Event::Update(u) => {
                spaceship.x = (spaceship.x + spaceship.dx*u.dt + 200.0) % 200.0;
                spaceship.y = (spaceship.y + spaceship.dy*u.dt + 200.0) % 200.0;
            },
            Event::Render(r) => gl.draw(r.viewport(), |c, gl| {
                clear(BLACK, gl);
                poly.draw(
                    &SPACESHIP_POINTS,
                    &ds,
                    c.transform
                        .trans(spaceship.x, spaceship.y)
                        .rot_rad(spaceship.theta),
                    gl,
                )
            }),
            Event::Input(Input::Press(Button::Keyboard(k))) =>
                match k {
                    Key::Up => spaceship.accelerate(2.0),
                    Key::Down => spaceship.accelerate(-2.0),
                    Key::Left => spaceship.theta -= 0.1,
                    Key::Right => spaceship.theta += 0.1,
                    Key::R => spaceship = Spaceship::new(),
                    Key::Q => return,
                    _ => (),
                },
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>#![deny(clippy::all)]

extern crate cargo_metadata;
extern crate clap;
extern crate semver;
extern crate tomllib;

mod config;
mod version;

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

use semver::Version;
use tomllib::types::{ParseResult, Value};
use tomllib::TOMLParser;

fn main() {
    let conf = config::get_config();
    let raw_data = read_file(&conf.manifest);
    let parser = TOMLParser::new();
    let (mut parser, result) = parser.parse(&raw_data);
    match result {
        ParseResult::Full => {}
        _ => panic!("couldn't parse Cargo.toml"),
    }

    let raw_value = parser
        .get_value("package.version")
        .expect("package.version missing");
    let mut version = match raw_value {
        Value::String(raw_version, _) => Version::parse(&raw_version).unwrap(),
        _ => panic!("version not a string"),
    };

    let old_version = version.clone();
    version::update_version(&mut version, conf.version);
    println!("Version {} -> {}", old_version, version);

    parser.set_value(
        "package.version",
        Value::basic_string(version.to_string()).unwrap(),
    );

    let mut f = OpenOptions::new()
        .write(true)
        .truncate(true)
        .open(&conf.manifest)
        .unwrap();
    f.write_all(format!("{}", parser).as_bytes()).unwrap();
}

fn read_file(file: &Path) -> String {
    let mut file = File::open(file).unwrap();
    let mut raw_data = String::new();
    file.read_to_string(&mut raw_data).unwrap();
    raw_data
}
<commit_msg>add basic test suite for input/output toml<commit_after>#![deny(clippy::all)]

extern crate cargo_metadata;
extern crate clap;
extern crate semver;
extern crate tomllib;

mod config;
mod version;

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

use semver::Version;
use tomllib::types::{ParseResult, Value};
use tomllib::TOMLParser;

fn main() {
    let conf = config::get_config();
    let raw_data = read_file(&conf.manifest);

    let output = update_toml_with_version(&raw_data, conf.version);

    let mut f = OpenOptions::new()
        .write(true)
        .truncate(true)
        .open(&conf.manifest)
        .unwrap();
    f.write_all(output.as_bytes()).unwrap();
}

fn read_file(file: &Path) -> String {
    let mut file = File::open(file).unwrap();
    let mut raw_data = String::new();
    file.read_to_string(&mut raw_data).unwrap();
    raw_data
}

fn update_toml_with_version(raw_data: &str, new_version: config::NewVersion) -> String {
    let parser = TOMLParser::new();
    let (mut parser, result) = parser.parse(raw_data);
    match result {
        ParseResult::Full => {}
        _ => panic!("couldn't parse Cargo.toml"),
    }

    let raw_value = parser
        .get_value("package.version")
        .expect("package.version missing");
    let mut version = match raw_value {
        Value::String(raw_version, _) => Version::parse(&raw_version).unwrap(),
        _ => panic!("version not a string"),
    };

    let old_version = version.clone();
    version::update_version(&mut version, new_version);
    println!("Version {} -> {}", old_version, version);

    parser.set_value(
        "package.version",
        Value::basic_string(version.to_string()).unwrap(),
    );
    format!("{}", parser)
}

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

    fn toml_test_wrapper(
        template: &str,
        version_modifier: config::NewVersion,
        start_version: &str,
        end_version: &str,
    ) {
        let input = template.replace("$VERSION", &format!("\"{}\"", start_version));
        let expected_output = template.replace("$VERSION", &format!("\"{}\"", end_version));
        let output = update_toml_with_version(&input, version_modifier);
        assert_eq!(
            output, expected_output,
            "toml output should be same with new version"
        );
    }

    #[test]
    fn toml_test_simple() {
        let input = "[package]
version = $VERSION";
        let version_modifier = "1.0.0".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.0");
        let version_modifier = "patch".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.1");
        let version_modifier = "minor".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.1.0");
        let version_modifier = "major".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "2.0.0");
    }

    #[test]
    fn toml_test_formatting_preserved_spaces() {
        let input = "  [package]
    version = $VERSION";
        let version_modifier = "1.0.0".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.1.0", "1.0.0");
        let version_modifier = "patch".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.1.0", "1.1.1");
        let version_modifier = "minor".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.1.0", "1.2.0");
        let version_modifier = "major".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.1.0", "2.0.0");

        let input = "  [  package   ]
    version= $VERSION";
        let version_modifier = "1.0.0".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.1.1", "1.0.0");
        let version_modifier = "patch".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.1.1", "1.1.2");
        let version_modifier = "minor".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.1.1", "1.2.0");
        let version_modifier = "major".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.1.1", "2.0.0");

        let input = "  [  package   ]

    version= $VERSION

    ";
        let version_modifier = "4.0.0".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "2.0.0", "4.0.0");
        let version_modifier = "patch".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "2.0.0", "2.0.1");
        let version_modifier = "minor".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "2.0.0", "2.1.0");
        let version_modifier = "major".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "2.0.0", "3.0.0");
    }

    #[test]
    fn toml_test_formatting_preserved_comments() {
        let input = "#before header
[package]
version = $VERSION";
        let version_modifier = "1.0.0".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.0");
        let version_modifier = "patch".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.1");
        let version_modifier = "minor".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.1.0");
        let version_modifier = "major".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "2.0.0");

        let input = "[package]# end of header
version= $VERSION";
        let version_modifier = "1.0.0".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.0");
        let version_modifier = "patch".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.1");
        let version_modifier = "minor".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.1.0");
        let version_modifier = "major".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "2.0.0");

        let input = "[package]
# version = \"2.0.0\"
version= $VERSION";
        let version_modifier = "1.0.0".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.0");
        let version_modifier = "patch".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.1");
        let version_modifier = "minor".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.1.0");
        let version_modifier = "major".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "2.0.0");
    }

    #[test]
    #[ignore = "tomllib can't handle having [a] and [a.b] in the same file"]
    fn toml_test_dotted_headers() {
        let input = "[package]
version = $VERSION

[a]
d = false

[a.b]
c = true";
        let version_modifier = "1.0.0".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.0");
        let version_modifier = "patch".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.0.1");
        let version_modifier = "minor".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "1.1.0");
        let version_modifier = "major".parse().expect("version modifier");
        toml_test_wrapper(input, version_modifier, "1.0.0", "2.0.0");
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

mod lib;
use lib::Petnames;

use std::collections::HashSet;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use std::process;
use std::str::FromStr;

use clap::{App, Arg};
use rand::seq::IteratorRandom;

fn main() {
    if let Err(e) = run() {
        eprintln!("Error: {}", e);
        process::exit(1);
    }
}

enum Error {
    IoError(io::Error),
    FileIoError(path::PathBuf, io::Error),
    CardinalityError(String),
    AlliterationError(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::IoError(ref e) => write!(f, "{}", e),
            Error::FileIoError(ref path, ref e) => write!(f, "{}: {}", e, path.display()),
            Error::CardinalityError(ref message) => write!(f, "cardinality is zero: {}", message),
            Error::AlliterationError(ref message) => write!(f, "cannot alliterate: {}", message),
        }
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error::IoError(error)
    }
}

fn run() -> Result<(), Error> {
    let matches = App::new("rust-petname")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generate human readable random names.")
        .after_help(concat!(
            "Based on Dustin Kirkland's petname project ",
            "<https://github.com/dustinkirkland/petname>."
        ))
        .arg(
            Arg::with_name("words")
                .short("w")
                .long("words")
                .value_name("WORDS")
                .default_value("2")
                .help("Number of words in name")
                .takes_value(true)
                .validator(can_be_parsed::<u8>),
        )
        .arg(
            Arg::with_name("separator")
                .short("s")
                .long("separator")
                .value_name("SEP")
                .default_value("-")
                .help("Separator between words")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("complexity")
                .short("c")
                .long("complexity")
                .value_name("COM")
                .possible_values(&["0", "1", "2"])
                .hide_possible_values(true)
                .default_value("0")
                .help("Use small words (0), medium words (1), or large words (2)")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("directory")
                .short("d")
                .long("dir")
                .value_name("DIR")
                .help("Directory containing adjectives.txt, adverbs.txt, names.txt")
                .conflicts_with("complexity")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("count")
                .long("count")
                .value_name("COUNT")
                .default_value("1")
                .help("Generate multiple names; pass 0 to produce infinite names!")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("letters")
                .short("l")
                .long("letters")
                .value_name("LETTERS")
                .default_value("0")
                .help("Maxiumum number of letters in each word; 0 for unlimited")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("alliterate")
                .short("a")
                .long("alliterate")
                .help("Generate names where each word begins with the same letter")
                .takes_value(false),
        )
        .arg(
            // For compatibility with upstream.
            Arg::with_name("ubuntu")
                .short("u")
                .long("ubuntu")
                .help("Alias; see --alliterate")
                .takes_value(false),
        )
        .get_matches();

    // Unwrapping is safe because these options have defaults.
    let opt_separator = matches.value_of("separator").unwrap();
    let opt_words = matches.value_of("words").unwrap();
    let opt_complexity = matches.value_of("complexity").unwrap();
    let opt_count = matches.value_of("count").unwrap();
    let opt_letters = matches.value_of("letters").unwrap();
    let opt_alliterate = matches.is_present("alliterate") || matches.is_present("ubuntu");

    // Optional arguments without defaults.
    let opt_directory = matches.value_of("directory");

    // Parse numbers. Validated so unwrapping is okay.
    let opt_words: u8 = opt_words.parse().unwrap();
    let opt_count: usize = opt_count.parse().unwrap();
    let opt_letters: usize = opt_letters.parse().unwrap();

    // Load custom word lists, if specified.
    let words = match opt_directory {
        Some(dirname) => Words::load(dirname)?,
        None => Words::Builtin,
    };

    // Select the appropriate word list.
    let mut petnames = match words {
        Words::Custom(ref adjectives, ref adverbs, ref names) => {
            Petnames::init(&adjectives, &adverbs, &names)
        }
        Words::Builtin => match opt_complexity {
            "0" => Petnames::small(),
            "1" => Petnames::medium(),
            "2" => Petnames::large(),
            _ => Petnames::small(),
        },
    };

    // If requested, limit the number of letters.
    if opt_letters != 0 {
        petnames.retain(|s| s.len() <= opt_letters);
    }

    // Check cardinality.
    if petnames.cardinality(opt_words) == 0 {
        return Err(Error::CardinalityError(
            "no petnames to choose from; try relaxing constraints".to_string(),
        ));
    }

    // We're going to need a source of randomness.
    let mut rng = rand::thread_rng();

    // If requested, choose a random letter then discard all words that do not
    // begin with that letter.
    if opt_alliterate {
        // We choose the first letter from the intersection of the first letters
        // of each word list in `petnames`.
        let firsts =
            common_first_letters(&petnames.adjectives, &[&petnames.adverbs, &petnames.names]);
        // Choose the first letter at random; fails if there are no letters.
        match firsts.iter().choose(&mut rng) {
            Some(c) => petnames.retain(|s| s.chars().next() == Some(*c)),
            None => {
                return Err(Error::AlliterationError(
                    "word lists have no initial letters in common".to_string(),
                ))
            }
        };
    }

    // Stream if count is 0.
    if opt_count == 0 {
        loop {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    } else {
        for _ in 1..=opt_count {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    }

    Ok(())
}

fn can_be_parsed<INTO>(value: String) -> Result<(), String>
where
    INTO: FromStr,
    <INTO as FromStr>::Err: std::fmt::Display,
{
    match value.parse::<INTO>() {
        Err(e) => Err(format!("{}", e)),
        Ok(_) => Ok(()),
    }
}

fn common_first_letters(init: &[&str], more: &[&[&str]]) -> HashSet<char> {
    let mut firsts = first_letters(init);
    let firsts_other: Vec<HashSet<char>> = more.iter().map(|list| first_letters(list)).collect();
    firsts.retain(|c| firsts_other.iter().all(|fs| fs.contains(c)));
    firsts
}

fn first_letters(names: &[&str]) -> HashSet<char> {
    names.iter().filter_map(|s| s.chars().next()).collect()
}

enum Words {
    Custom(String, String, String),
    Builtin,
}

impl Words {
    // Load word lists from the given directory. This function expects to find three
    // files in that directory: `adjectives.txt`, `adverbs.txt`, and `names.txt`.
    // Each should be valid UTF-8, and contain words separated by whitespace.
    fn load<T: AsRef<path::Path>>(dirname: T) -> Result<Self, Error> {
        let dirname = dirname.as_ref();
        Ok(Self::Custom(
            read_file_to_string(dirname.join("adjectives.txt"))?,
            read_file_to_string(dirname.join("adverbs.txt"))?,
            read_file_to_string(dirname.join("names.txt"))?,
        ))
    }
}

fn read_file_to_string<P: AsRef<path::Path>>(path: P) -> Result<String, Error> {
    fs::read_to_string(&path)
        .map_err(|error| Error::FileIoError(path.as_ref().to_path_buf(), error))
}
<commit_msg>Be explicit about what happens when exiting okay.<commit_after>#[macro_use]
extern crate clap;

mod lib;
use lib::Petnames;

use std::collections::HashSet;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use std::process;
use std::str::FromStr;

use clap::{App, Arg};
use rand::seq::IteratorRandom;

fn main() {
    match run() {
        Err(e) => {
            eprintln!("Error: {}", e);
            process::exit(1);
        }
        Ok(()) => {
            process::exit(0);
        }
    }
}

enum Error {
    IoError(io::Error),
    FileIoError(path::PathBuf, io::Error),
    CardinalityError(String),
    AlliterationError(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::IoError(ref e) => write!(f, "{}", e),
            Error::FileIoError(ref path, ref e) => write!(f, "{}: {}", e, path.display()),
            Error::CardinalityError(ref message) => write!(f, "cardinality is zero: {}", message),
            Error::AlliterationError(ref message) => write!(f, "cannot alliterate: {}", message),
        }
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error::IoError(error)
    }
}

fn run() -> Result<(), Error> {
    let matches = App::new("rust-petname")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generate human readable random names.")
        .after_help(concat!(
            "Based on Dustin Kirkland's petname project ",
            "<https://github.com/dustinkirkland/petname>."
        ))
        .arg(
            Arg::with_name("words")
                .short("w")
                .long("words")
                .value_name("WORDS")
                .default_value("2")
                .help("Number of words in name")
                .takes_value(true)
                .validator(can_be_parsed::<u8>),
        )
        .arg(
            Arg::with_name("separator")
                .short("s")
                .long("separator")
                .value_name("SEP")
                .default_value("-")
                .help("Separator between words")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("complexity")
                .short("c")
                .long("complexity")
                .value_name("COM")
                .possible_values(&["0", "1", "2"])
                .hide_possible_values(true)
                .default_value("0")
                .help("Use small words (0), medium words (1), or large words (2)")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("directory")
                .short("d")
                .long("dir")
                .value_name("DIR")
                .help("Directory containing adjectives.txt, adverbs.txt, names.txt")
                .conflicts_with("complexity")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("count")
                .long("count")
                .value_name("COUNT")
                .default_value("1")
                .help("Generate multiple names; pass 0 to produce infinite names!")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("letters")
                .short("l")
                .long("letters")
                .value_name("LETTERS")
                .default_value("0")
                .help("Maxiumum number of letters in each word; 0 for unlimited")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("alliterate")
                .short("a")
                .long("alliterate")
                .help("Generate names where each word begins with the same letter")
                .takes_value(false),
        )
        .arg(
            // For compatibility with upstream.
            Arg::with_name("ubuntu")
                .short("u")
                .long("ubuntu")
                .help("Alias; see --alliterate")
                .takes_value(false),
        )
        .get_matches();

    // Unwrapping is safe because these options have defaults.
    let opt_separator = matches.value_of("separator").unwrap();
    let opt_words = matches.value_of("words").unwrap();
    let opt_complexity = matches.value_of("complexity").unwrap();
    let opt_count = matches.value_of("count").unwrap();
    let opt_letters = matches.value_of("letters").unwrap();
    let opt_alliterate = matches.is_present("alliterate") || matches.is_present("ubuntu");

    // Optional arguments without defaults.
    let opt_directory = matches.value_of("directory");

    // Parse numbers. Validated so unwrapping is okay.
    let opt_words: u8 = opt_words.parse().unwrap();
    let opt_count: usize = opt_count.parse().unwrap();
    let opt_letters: usize = opt_letters.parse().unwrap();

    // Load custom word lists, if specified.
    let words = match opt_directory {
        Some(dirname) => Words::load(dirname)?,
        None => Words::Builtin,
    };

    // Select the appropriate word list.
    let mut petnames = match words {
        Words::Custom(ref adjectives, ref adverbs, ref names) => {
            Petnames::init(&adjectives, &adverbs, &names)
        }
        Words::Builtin => match opt_complexity {
            "0" => Petnames::small(),
            "1" => Petnames::medium(),
            "2" => Petnames::large(),
            _ => Petnames::small(),
        },
    };

    // If requested, limit the number of letters.
    if opt_letters != 0 {
        petnames.retain(|s| s.len() <= opt_letters);
    }

    // Check cardinality.
    if petnames.cardinality(opt_words) == 0 {
        return Err(Error::CardinalityError(
            "no petnames to choose from; try relaxing constraints".to_string(),
        ));
    }

    // We're going to need a source of randomness.
    let mut rng = rand::thread_rng();

    // If requested, choose a random letter then discard all words that do not
    // begin with that letter.
    if opt_alliterate {
        // We choose the first letter from the intersection of the first letters
        // of each word list in `petnames`.
        let firsts =
            common_first_letters(&petnames.adjectives, &[&petnames.adverbs, &petnames.names]);
        // Choose the first letter at random; fails if there are no letters.
        match firsts.iter().choose(&mut rng) {
            Some(c) => petnames.retain(|s| s.chars().next() == Some(*c)),
            None => {
                return Err(Error::AlliterationError(
                    "word lists have no initial letters in common".to_string(),
                ))
            }
        };
    }

    // Stream if count is 0.
    if opt_count == 0 {
        loop {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    } else {
        for _ in 1..=opt_count {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    }

    Ok(())
}

fn can_be_parsed<INTO>(value: String) -> Result<(), String>
where
    INTO: FromStr,
    <INTO as FromStr>::Err: std::fmt::Display,
{
    match value.parse::<INTO>() {
        Err(e) => Err(format!("{}", e)),
        Ok(_) => Ok(()),
    }
}

fn common_first_letters(init: &[&str], more: &[&[&str]]) -> HashSet<char> {
    let mut firsts = first_letters(init);
    let firsts_other: Vec<HashSet<char>> = more.iter().map(|list| first_letters(list)).collect();
    firsts.retain(|c| firsts_other.iter().all(|fs| fs.contains(c)));
    firsts
}

fn first_letters(names: &[&str]) -> HashSet<char> {
    names.iter().filter_map(|s| s.chars().next()).collect()
}

enum Words {
    Custom(String, String, String),
    Builtin,
}

impl Words {
    // Load word lists from the given directory. This function expects to find three
    // files in that directory: `adjectives.txt`, `adverbs.txt`, and `names.txt`.
    // Each should be valid UTF-8, and contain words separated by whitespace.
    fn load<T: AsRef<path::Path>>(dirname: T) -> Result<Self, Error> {
        let dirname = dirname.as_ref();
        Ok(Self::Custom(
            read_file_to_string(dirname.join("adjectives.txt"))?,
            read_file_to_string(dirname.join("adverbs.txt"))?,
            read_file_to_string(dirname.join("names.txt"))?,
        ))
    }
}

fn read_file_to_string<P: AsRef<path::Path>>(path: P) -> Result<String, Error> {
    fs::read_to_string(&path)
        .map_err(|error| Error::FileIoError(path.as_ref().to_path_buf(), error))
}
<|endoftext|>


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

extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate piston;

use std::env;

#[macro_use]
pub mod common;
pub mod dragon;
mod glwindow;

use dragon::DragonFractal;
use glwindow::{WindowHandler};

fn main() {
    let args = env::args().collect::<Vec<String>>();
    if args.len() <= 1 {
        panic!("You must provide an iteration number");
    }
    let iterations = args.get(1).unwrap().parse::<u64>().unwrap();

    let window = WindowHandler::new();
    let dr = DragonFractal::new(iterations).unwrap();
    window.run(&dr);
}
<commit_msg>Changing how the program is called to support other turtle programs.<commit_after>// Copyright (c) 2015 William (B.J.) Snow Orvis
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate piston;

use std::env;

#[macro_use]
pub mod common;
pub mod dragon;
mod glwindow;

use dragon::DragonFractal;
use glwindow::{WindowHandler};

// TODO: Implement a proper "usage"
fn validate_args(args: &Vec<String>) {
    if (*args).len() <= 1 {
        panic!("You must provide a fractal type and an iteration number");
    }
    if (*args).len() <= 2 {
        panic!("You must provide an iteration number");
    }
}

fn main() {
    let args = env::args().collect::<Vec<String>>();
    validate_args(&args);

    let program_name = args.get(1).unwrap();
    let iterations = args.get(2).unwrap().parse::<u64>().unwrap();

    let program = match program_name.as_ref() {
        "dragon" => DragonFractal::new(iterations).unwrap(),
        _ => panic!("Unknown program type")
    };

    let window = WindowHandler::new();
    window.run(&program);
}
<|endoftext|>


<commit_before>extern crate mqtt;
extern crate serde_json;
extern crate rs_jsonpath;
#[macro_use]
extern crate clap;
extern crate itertools;

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::sync::Arc;

use mqtt::{Encodable, Decodable};
use mqtt::packet::{ConnackPacket, Packet, PingrespPacket, PubackPacket, PublishPacket, PubrecPacket, PubcompPacket, SubackPacket, VariablePacket};
use mqtt::{TopicFilter, QualityOfService};
use mqtt::control::ConnectReturnCode;
use mqtt::packet::suback::SubscribeReturnCode;
use mqtt::packet::publish::QoSWithPacketIdentifier;

mod csv_data_processor;
use csv_data_processor::CsvDataProcessor;

macro_rules! log(
    ($($arg:tt)*) => { {
        let _ = writeln!(&mut ::std::io::stderr(), $($arg)*);
    } }
);

#[derive(Debug)]
enum ZinkError<'a> {
    Mqtt(mqtt::packet::VariablePacketError<'a>),
    NotMqttConnect,
}

impl<'a, P> From<mqtt::packet::PacketError<'a, P>> for ZinkError<'a>
    where P: mqtt::packet::Packet<'a>,
          mqtt::packet::VariablePacketError<'a>: std::convert::From<mqtt::packet::PacketError<'a, P>> {

    fn from(err: mqtt::packet::PacketError<'a, P>) -> ZinkError<'a> {
        ZinkError::Mqtt(mqtt::packet::VariablePacketError::from(err))
    }
}

fn main() {
    let matches = clap_app!(
        zink =>
            (version: "0.1.0")
            (@arg file: -f --file +takes_value "File to append result to. If not specified, send results to stdout")
            (@arg bind: -b --bind +takes_value default_value("0.0.0.0:1883") "Bind address and port")
            (@arg JSONPATH: +required +use_delimiter +multiple "JSON paths to use")
    ).get_matches();

    let jsonpaths: Vec<String> = matches.value_of("JSONPATH").unwrap().split(",").map(String::from).collect();

    let handle: Box<Write + Send> = if let Some(filepath) = matches.value_of("file") {
        Box::new(OpenOptions::new()
                 .append(true)
                 .create(true)
                 .open(filepath)
                 .unwrap()) as Box<Write + Send>
    } else {
        Box::new(io::stdout()) as Box<Write + Send>
    };

    let csv_data_processor = Arc::new(CsvDataProcessor::new(handle, jsonpaths));

    let bind_address = matches.value_of("bind").unwrap();
    let listener = TcpListener::bind(bind_address).unwrap();

    log!("Bound to {}", bind_address);

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let csv_data_processor = csv_data_processor.clone();
                thread::spawn(move || {
                    let res = handle_client(stream, &|ref x: &PublishPacket| {
                        csv_data_processor.process_publish(x);
                    });
                    log!("handle_client exited with {:?}", res);
                });
            }
            Err(e) => {
                log!("{}", e);
            }
        }
    }
}

fn sub_to_ack(&(_, qos): &(TopicFilter, QualityOfService)) -> SubscribeReturnCode {
    match qos {
        QualityOfService::Level0 => SubscribeReturnCode::MaximumQoSLevel0,
        QualityOfService::Level1 => SubscribeReturnCode::MaximumQoSLevel1,
        QualityOfService::Level2 => SubscribeReturnCode::MaximumQoSLevel1,
    }
}

fn handle_client<'a, 'b>(mut stream: TcpStream, process_publish: &'b Fn(&PublishPacket)) -> Result<(), ZinkError<'a>> {
    // This makes .read() call blocking.
    // Otherwise, mqtt decode consumes 100% CPU time.
    let _ = stream.set_read_timeout(None);

    log!("{:?}", stream);
    if let Ok(VariablePacket::ConnectPacket(x)) = VariablePacket::decode(&mut stream) {
        log!("{:?}", x);
        try!(ConnackPacket::new(false, ConnectReturnCode::ConnectionAccepted).encode(&mut stream));
    } else {
        return Err(ZinkError::NotMqttConnect);
    }

    loop {
        let parse_result = VariablePacket::decode(&mut stream);
        if let Ok(packet) = parse_result {
            log!("{:?}", packet);
            match packet {
                VariablePacket::SubscribePacket(x) => {
                    try!(SubackPacket::new(
                        x.packet_identifier(),
                        x.payload().subscribes().into_iter().map(sub_to_ack).collect()
                    ).encode(&mut stream));
                }
                VariablePacket::PingreqPacket(_) => {
                    try!(PingrespPacket::new()
                         .encode(&mut stream));
                }
                VariablePacket::PubrelPacket(x) => {
                    try!(PubcompPacket::new(x.packet_identifier())
                         .encode(&mut stream));
                }
                VariablePacket::PublishPacket(x) => {
                    match x.qos() {
                        QoSWithPacketIdentifier::Level0 => {
                            // No additional handling is required
                        }
                        QoSWithPacketIdentifier::Level1(pkid) => {
                            try!(PubackPacket::new(pkid)
                                 .encode(&mut stream));
                        }
                        QoSWithPacketIdentifier::Level2(pkid) => {
                            try!(PubrecPacket::new(pkid)
                                 .encode(&mut stream));
                        }
                    }

                    if let Ok(payload) = std::str::from_utf8(x.payload()) {
                        log!("{}: {}", x.topic_name(), payload);
                    } else {
                        log!("{}: {:?}", x.topic_name(), x.payload());
                    }

                    process_publish(&x);
                }
                _ => {
                }
            }
        }
    }
}
<commit_msg>Fix infinite loop when client disconnects<commit_after>extern crate mqtt;
extern crate serde_json;
extern crate rs_jsonpath;
#[macro_use]
extern crate clap;
extern crate itertools;

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::sync::Arc;

use mqtt::{Encodable, Decodable};
use mqtt::packet::{ConnackPacket, Packet, PingrespPacket, PubackPacket, PublishPacket, PubrecPacket, PubcompPacket, SubackPacket, VariablePacket};
use mqtt::{TopicFilter, QualityOfService};
use mqtt::control::ConnectReturnCode;
use mqtt::packet::suback::SubscribeReturnCode;
use mqtt::packet::publish::QoSWithPacketIdentifier;

mod csv_data_processor;
use csv_data_processor::CsvDataProcessor;

macro_rules! log(
    ($($arg:tt)*) => { {
        let _ = writeln!(&mut ::std::io::stderr(), $($arg)*);
    } }
);

#[derive(Debug)]
enum ZinkError<'a> {
    Mqtt(mqtt::packet::VariablePacketError<'a>),
    NotMqttConnect,
}

impl<'a, P> From<mqtt::packet::PacketError<'a, P>> for ZinkError<'a>
    where P: mqtt::packet::Packet<'a>,
          mqtt::packet::VariablePacketError<'a>: std::convert::From<mqtt::packet::PacketError<'a, P>> {

    fn from(err: mqtt::packet::PacketError<'a, P>) -> ZinkError<'a> {
        ZinkError::Mqtt(mqtt::packet::VariablePacketError::from(err))
    }
}

impl<'a> From<mqtt::packet::VariablePacketError<'a>> for ZinkError<'a> {
    fn from(err: mqtt::packet::VariablePacketError<'a>) -> ZinkError<'a> {
        ZinkError::Mqtt(err)
    }
}

fn main() {
    let matches = clap_app!(
        zink =>
            (version: "0.1.0")
            (@arg file: -f --file +takes_value "File to append result to. If not specified, send results to stdout")
            (@arg bind: -b --bind +takes_value default_value("0.0.0.0:1883") "Bind address and port")
            (@arg JSONPATH: +required +use_delimiter +multiple "JSON paths to use")
    ).get_matches();

    let jsonpaths: Vec<String> = matches.value_of("JSONPATH").unwrap().split(",").map(String::from).collect();

    let handle: Box<Write + Send> = if let Some(filepath) = matches.value_of("file") {
        Box::new(OpenOptions::new()
                 .append(true)
                 .create(true)
                 .open(filepath)
                 .unwrap()) as Box<Write + Send>
    } else {
        Box::new(io::stdout()) as Box<Write + Send>
    };

    let csv_data_processor = Arc::new(CsvDataProcessor::new(handle, jsonpaths));

    let bind_address = matches.value_of("bind").unwrap();
    let listener = TcpListener::bind(bind_address).unwrap();

    log!("Bound to {}", bind_address);

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let csv_data_processor = csv_data_processor.clone();
                thread::spawn(move || {
                    let res = handle_client(stream, &|ref x: &PublishPacket| {
                        csv_data_processor.process_publish(x);
                    });
                    log!("handle_client exited with {:?}", res);
                });
            }
            Err(e) => {
                log!("{}", e);
            }
        }
    }
}

fn sub_to_ack(&(_, qos): &(TopicFilter, QualityOfService)) -> SubscribeReturnCode {
    match qos {
        QualityOfService::Level0 => SubscribeReturnCode::MaximumQoSLevel0,
        QualityOfService::Level1 => SubscribeReturnCode::MaximumQoSLevel1,
        QualityOfService::Level2 => SubscribeReturnCode::MaximumQoSLevel1,
    }
}

fn handle_client<'a, 'b>(mut stream: TcpStream, process_publish: &'b Fn(&PublishPacket)) -> Result<(), ZinkError<'a>> {
    // This makes .read() call blocking.
    // Otherwise, mqtt decode consumes 100% CPU time.
    let _ = stream.set_read_timeout(None);

    log!("{:?}", stream);
    if let Ok(VariablePacket::ConnectPacket(x)) = VariablePacket::decode(&mut stream) {
        log!("{:?}", x);
        try!(ConnackPacket::new(false, ConnectReturnCode::ConnectionAccepted).encode(&mut stream));
    } else {
        return Err(ZinkError::NotMqttConnect);
    }

    loop {
        let packet = try!(VariablePacket::decode(&mut stream));
        log!("{:?}", packet);
        match packet {
            VariablePacket::SubscribePacket(x) => {
                try!(SubackPacket::new(
                    x.packet_identifier(),
                    x.payload().subscribes().into_iter().map(sub_to_ack).collect()
                ).encode(&mut stream));
            }
            VariablePacket::PingreqPacket(_) => {
                try!(PingrespPacket::new()
                     .encode(&mut stream));
            }
            VariablePacket::PubrelPacket(x) => {
                try!(PubcompPacket::new(x.packet_identifier())
                     .encode(&mut stream));
            }
            VariablePacket::PublishPacket(x) => {
                match x.qos() {
                    QoSWithPacketIdentifier::Level0 => {
                        // No additional handling is required
                    }
                    QoSWithPacketIdentifier::Level1(pkid) => {
                        try!(PubackPacket::new(pkid)
                             .encode(&mut stream));
                    }
                    QoSWithPacketIdentifier::Level2(pkid) => {
                        try!(PubrecPacket::new(pkid)
                             .encode(&mut stream));
                    }
                }

                if let Ok(payload) = std::str::from_utf8(x.payload()) {
                    log!("{}: {}", x.topic_name(), payload);
                } else {
                    log!("{}: {:?}", x.topic_name(), x.payload());
                }

                process_publish(&x);
            }
            _ => {
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Rustdoc's FileSystem abstraction module.
//!
//! On Windows this indirects IO into threads to work around performance issues
//! with Defender (and other similar virus scanners that do blocking operations).
//! On other platforms this is a thin shim to fs.
//!
//! Only calls needed to permit this workaround have been abstracted: thus
//! fs::read is still done directly via the fs module; if in future rustdoc
//! needs to read-after-write from a file, then it would be added to this
//! abstraction.

use std::fs;
use std::io;
use std::path::Path;
use std::string::ToString;
use std::sync::mpsc::Sender;

macro_rules! try_err {
    ($e:expr, $file:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => return Err(E::new(e, $file)),
        }
    };
}

pub trait PathError {
    fn new<S, P: AsRef<Path>>(e: S, path: P) -> Self
    where
        S: ToString + Sized;
}

pub struct DocFS {
    sync_only: bool,
    errors: Option<Sender<String>>,
}

impl DocFS {
    pub fn new(errors: Sender<String>) -> DocFS {
        DocFS { sync_only: false, errors: Some(errors) }
    }

    pub fn set_sync_only(&mut self, sync_only: bool) {
        self.sync_only = sync_only;
    }

    pub fn close(&mut self) {
        self.errors = None;
    }

    pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
        // For now, dir creation isn't a huge time consideration, do it
        // synchronously, which avoids needing ordering between write() actions
        // and directory creation.
        fs::create_dir_all(path)
    }

    pub fn write<P, C, E>(&self, path: P, contents: C) -> Result<(), E>
    where
        P: AsRef<Path>,
        C: AsRef<[u8]>,
        E: PathError,
    {
        if !self.sync_only && cfg!(windows) {
            // A possible future enhancement after more detailed profiling would
            // be to create the file sync so errors are reported eagerly.
            let path = path.as_ref().to_path_buf();
            let contents = contents.as_ref().to_vec();
            let sender = self.errors.clone().expect("can't write after closing");
            rayon::spawn(move || {
                fs::write(&path, contents).unwrap_or_else(|e| {
                    sender
                        .send(format!("\"{}\": {}", path.display(), e))
                        .expect(&format!("failed to send error on \"{}\"", path.display()));
                });
            });
            Ok(())
        } else {
            Ok(try_err!(fs::write(&path, contents), path))
        }
    }
}
<commit_msg>fix clippy::expect_fun_call: use unwrap_or_else to prevent panic message from always being evaluated<commit_after>//! Rustdoc's FileSystem abstraction module.
//!
//! On Windows this indirects IO into threads to work around performance issues
//! with Defender (and other similar virus scanners that do blocking operations).
//! On other platforms this is a thin shim to fs.
//!
//! Only calls needed to permit this workaround have been abstracted: thus
//! fs::read is still done directly via the fs module; if in future rustdoc
//! needs to read-after-write from a file, then it would be added to this
//! abstraction.

use std::fs;
use std::io;
use std::path::Path;
use std::string::ToString;
use std::sync::mpsc::Sender;

macro_rules! try_err {
    ($e:expr, $file:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => return Err(E::new(e, $file)),
        }
    };
}

pub trait PathError {
    fn new<S, P: AsRef<Path>>(e: S, path: P) -> Self
    where
        S: ToString + Sized;
}

pub struct DocFS {
    sync_only: bool,
    errors: Option<Sender<String>>,
}

impl DocFS {
    pub fn new(errors: Sender<String>) -> DocFS {
        DocFS { sync_only: false, errors: Some(errors) }
    }

    pub fn set_sync_only(&mut self, sync_only: bool) {
        self.sync_only = sync_only;
    }

    pub fn close(&mut self) {
        self.errors = None;
    }

    pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
        // For now, dir creation isn't a huge time consideration, do it
        // synchronously, which avoids needing ordering between write() actions
        // and directory creation.
        fs::create_dir_all(path)
    }

    pub fn write<P, C, E>(&self, path: P, contents: C) -> Result<(), E>
    where
        P: AsRef<Path>,
        C: AsRef<[u8]>,
        E: PathError,
    {
        if !self.sync_only && cfg!(windows) {
            // A possible future enhancement after more detailed profiling would
            // be to create the file sync so errors are reported eagerly.
            let path = path.as_ref().to_path_buf();
            let contents = contents.as_ref().to_vec();
            let sender = self.errors.clone().expect("can't write after closing");
            rayon::spawn(move || {
                fs::write(&path, contents).unwrap_or_else(|e| {
                    sender.send(format!("\"{}\": {}", path.display(), e)).unwrap_or_else(|_| {
                        panic!("failed to send error on \"{}\"", path.display())
                    })
                });
            });
            Ok(())
        } else {
            Ok(try_err!(fs::write(&path, contents), path))
        }
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate regex;
extern crate toml;

use self::chrono::{Duration, NaiveTime};
use self::regex::Regex;
use self::toml::Value as Toml;

use error::{CannotHappenError, CliError, ConfigError, ConfigErrorKind};
use lib::setting;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;

pub enum KeyKind {
    BurnAfter,
    SweepPeriod,
    SweepTime,
}

impl KeyKind {
    pub fn from<S: AsRef<str>>(key: S) -> Result<KeyKind, ConfigError> {
        match key.as_ref().trim() {
            "burn.after"   => Ok(KeyKind::BurnAfter),
            "sweep.period" => Ok(KeyKind::SweepPeriod),
            "sweep.time"   => Ok(KeyKind::SweepTime),
            _              => Err(ConfigError::new(ConfigErrorKind::InvalidKey)),
        }
    }

    fn to_str(&self) -> &str {
        match *self {
            KeyKind::BurnAfter   => "burn.after",
            KeyKind::SweepPeriod => "sweep.period",
            KeyKind::SweepTime   => "sweep.time",
        }
    }

    fn to_pair(&self) -> (&str, &str) {
        let key = self
            .to_str()
            .split('.')
            .collect::<Vec<&str>>();
        (key[0], key[1]) // unsafe!
    }
}

pub struct Config {
    toml: Toml,
}

impl Config {
    fn insert_deeply(table: &mut BTreeMap<String, BTreeMap<String, String>>, key: &KeyKind, value: String) {
        let (first, second) = key.to_pair();

        let second = second.to_string();

        if table.contains_key(first) {
            table.get_mut(first).map(|e| e.insert(second, value));
        } else {
            let mut entry = BTreeMap::new();
            entry.insert(second, value);

            table.insert(first.to_string(), entry);
        }
    }

    fn new(toml: Toml) -> Self {
        Config { toml: toml }
    }

    pub fn default() -> Self {
        let mut config = BTreeMap::new();
        Self::insert_deeply(&mut config, &KeyKind::BurnAfter  , "2 weeks".to_string());
        Self::insert_deeply(&mut config, &KeyKind::SweepPeriod, "daily"  .to_string());
        Self::insert_deeply(&mut config, &KeyKind::SweepTime  , "00:00"  .to_string());

        Self::new(toml::encode(&config))
    }

    pub fn read() -> Result<Self, CliError> {
        let mut f = try!(File::open(setting::config_file()));

        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));

        let result = contents
            .parse()
            .map_err(|e: Vec<_>| e.into_iter().find(|_| true));
        let toml = match result {
            Ok(toml)    => toml,
            Err(option) => match option {
                Some(e) => return Err(From::from(e)),
                None    => return Err(From::from(CannotHappenError)),
            },
        };

        Ok(Self::new(toml))
    }

    pub fn set(mut self, key: &KeyKind, value: String) -> Result<Self, ConfigError> {
        let mut config = match toml::decode::<BTreeMap<String, BTreeMap<String, String>>>(self.toml) {
            Some(decoded) => decoded,
            None          => return Err(ConfigError::new(ConfigErrorKind::Something)),
        };

        Self::insert_deeply(&mut config, key, value);

        self.toml = toml::encode(&config);

        Ok(self)
    }

    pub fn to_string(&self) -> String {
        toml::encode_str(&self.toml)
    }

    fn get(key: &KeyKind) -> Result<String, CliError> {
        let config = try!(Self::read()).toml;

        let result = config
            .lookup(key.to_str())
            .ok_or(ConfigError::new(match *key {
                KeyKind::BurnAfter   => ConfigErrorKind::NotFoundBurnAfter,
                KeyKind::SweepPeriod => unimplemented!(),
                KeyKind::SweepTime   => unimplemented!(),
            }));
        let value = try!(result).to_string(); // This to_string() is not documented.

        Ok(value)
    }

    pub fn validate<S: AsRef<str>>(key: &KeyKind, value: S) -> Result<String, CliError> {
        let value = value.as_ref().trim();

        match *key {
            KeyKind::BurnAfter => {
                let pair = try!(Regex::new(r"^(?P<num>\d+)\s?(?P<unit>days?|weeks?)$"))
                    .captures(value)
                    .map_or((None, None), |caps| (caps.name("num"), caps.name("unit")));
                let (num, unit) = match pair {
                    (Some(num), Some(unit)) => (num, unit),
                    _                       => return Err(From::from(ConfigError::new(ConfigErrorKind::BurnAfter))),
                };
                Ok(format!("{} {}", num, unit))
            },
            KeyKind::SweepPeriod => {
                match value {
                    "daily" | "weekly" => Ok(value.to_string()),
                    _                  => return Err(From::from(ConfigError::new(ConfigErrorKind::SweepPeriod))),
                }
            },
            KeyKind::SweepTime => {
                match NaiveTime::from_str(format!("{}:00", value).as_ref()) {
                    Ok(_)  => Ok(value.to_string()),
                    // should set Err(e) to Error::cause()
                    Err(_) => return Err(From::from(ConfigError::new(ConfigErrorKind::SweepTime))),
                }
            },
        }
    }

    pub fn extract_burn_after() -> Result<Duration, CliError> {
        let key = KeyKind::BurnAfter;

        let after       = try!(Self::get(&key));
        let after       = try!(Self::validate(&key, after));
        let after       = after.split(' ').collect::<Vec<&str>>();
        let (num, unit) = (after[0], after[1]);                    // unsafe!
        let num         = try!(num.parse::<u32>()) as i64;

        let duration = match unit {
            "day"  | "days"  => Duration::days(num),
            "week" | "weeks" => Duration::weeks(num),
            _                => return Err(From::from(CannotHappenError)),
        };

        Ok(duration)
    }
}
<commit_msg>[Bug] Fix incorrect regular expression<commit_after>extern crate chrono;
extern crate regex;
extern crate toml;

use self::chrono::{Duration, NaiveTime};
use self::regex::Regex;
use self::toml::Value as Toml;

use error::{CannotHappenError, CliError, ConfigError, ConfigErrorKind};
use lib::setting;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;

pub enum KeyKind {
    BurnAfter,
    SweepPeriod,
    SweepTime,
}

impl KeyKind {
    pub fn from<S: AsRef<str>>(key: S) -> Result<KeyKind, ConfigError> {
        match key.as_ref().trim() {
            "burn.after"   => Ok(KeyKind::BurnAfter),
            "sweep.period" => Ok(KeyKind::SweepPeriod),
            "sweep.time"   => Ok(KeyKind::SweepTime),
            _              => Err(ConfigError::new(ConfigErrorKind::InvalidKey)),
        }
    }

    fn to_str(&self) -> &str {
        match *self {
            KeyKind::BurnAfter   => "burn.after",
            KeyKind::SweepPeriod => "sweep.period",
            KeyKind::SweepTime   => "sweep.time",
        }
    }

    fn to_pair(&self) -> (&str, &str) {
        let key = self
            .to_str()
            .split('.')
            .collect::<Vec<&str>>();
        (key[0], key[1]) // unsafe!
    }
}

pub struct Config {
    toml: Toml,
}

impl Config {
    fn insert_deeply(table: &mut BTreeMap<String, BTreeMap<String, String>>, key: &KeyKind, value: String) {
        let (first, second) = key.to_pair();

        let second = second.to_string();

        if table.contains_key(first) {
            table.get_mut(first).map(|e| e.insert(second, value));
        } else {
            let mut entry = BTreeMap::new();
            entry.insert(second, value);

            table.insert(first.to_string(), entry);
        }
    }

    fn new(toml: Toml) -> Self {
        Config { toml: toml }
    }

    pub fn default() -> Self {
        let mut config = BTreeMap::new();
        Self::insert_deeply(&mut config, &KeyKind::BurnAfter  , "2 weeks".to_string());
        Self::insert_deeply(&mut config, &KeyKind::SweepPeriod, "daily"  .to_string());
        Self::insert_deeply(&mut config, &KeyKind::SweepTime  , "00:00"  .to_string());

        Self::new(toml::encode(&config))
    }

    pub fn read() -> Result<Self, CliError> {
        let mut f = try!(File::open(setting::config_file()));

        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));

        let result = contents
            .parse()
            .map_err(|e: Vec<_>| e.into_iter().find(|_| true));
        let toml = match result {
            Ok(toml)    => toml,
            Err(option) => match option {
                Some(e) => return Err(From::from(e)),
                None    => return Err(From::from(CannotHappenError)),
            },
        };

        Ok(Self::new(toml))
    }

    pub fn set(mut self, key: &KeyKind, value: String) -> Result<Self, ConfigError> {
        let mut config = match toml::decode::<BTreeMap<String, BTreeMap<String, String>>>(self.toml) {
            Some(decoded) => decoded,
            None          => return Err(ConfigError::new(ConfigErrorKind::Something)),
        };

        Self::insert_deeply(&mut config, key, value);

        self.toml = toml::encode(&config);

        Ok(self)
    }

    pub fn to_string(&self) -> String {
        toml::encode_str(&self.toml)
    }

    fn get(key: &KeyKind) -> Result<String, CliError> {
        let config = try!(Self::read()).toml;

        let result = config
            .lookup(key.to_str())
            .ok_or(ConfigError::new(match *key {
                KeyKind::BurnAfter   => ConfigErrorKind::NotFoundBurnAfter,
                KeyKind::SweepPeriod => unimplemented!(),
                KeyKind::SweepTime   => unimplemented!(),
            }));
        let value = try!(result).to_string(); // This to_string() is not documented.

        Ok(value)
    }

    pub fn validate<S: AsRef<str>>(key: &KeyKind, value: S) -> Result<String, CliError> {
        let value = value.as_ref().trim();

        match *key {
            KeyKind::BurnAfter => {
                let pair = try!(Regex::new(r"(?P<num>\d+)\s?(?P<unit>days?|weeks?)"))
                    .captures(value)
                    .map_or((None, None), |caps| (caps.name("num"), caps.name("unit")));
                let (num, unit) = match pair {
                    (Some(num), Some(unit)) => (num, unit),
                    _                       => return Err(From::from(ConfigError::new(ConfigErrorKind::BurnAfter))),
                };
                Ok(format!("{} {}", num, unit))
            },
            KeyKind::SweepPeriod => {
                match value {
                    "daily" | "weekly" => Ok(value.to_string()),
                    _                  => return Err(From::from(ConfigError::new(ConfigErrorKind::SweepPeriod))),
                }
            },
            KeyKind::SweepTime => {
                match NaiveTime::from_str(format!("{}:00", value).as_ref()) {
                    Ok(_)  => Ok(value.to_string()),
                    // should set Err(e) to Error::cause()
                    Err(_) => return Err(From::from(ConfigError::new(ConfigErrorKind::SweepTime))),
                }
            },
        }
    }

    pub fn extract_burn_after() -> Result<Duration, CliError> {
        let key = KeyKind::BurnAfter;

        let after       = try!(Self::get(&key));
        let after       = try!(Self::validate(&key, after));
        let after       = after.split(' ').collect::<Vec<&str>>();
        let (num, unit) = (after[0], after[1]);                    // unsafe!
        let num         = try!(num.parse::<u32>()) as i64;

        let duration = match unit {
            "day"  | "days"  => Duration::days(num),
            "week" | "weeks" => Duration::weeks(num),
            _                => return Err(From::from(CannotHappenError)),
        };

        Ok(duration)
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Container traits

use option::Option;

/// A trait to represent the abstract idea of a container. The only concrete
/// knowledge known is the number of elements contained within.
pub trait Container {
    /// Return the number of elements in the container
    fn len(&self) -> uint;

    /// Return true if the container contains no elements
    #[inline]
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

/// A trait to represent mutable containers
pub trait Mutable: Container {
    /// Clear the container, removing all values.
    fn clear(&mut self);
}

/// A map is a key-value store where values may be looked up by their keys. This
/// trait provides basic operations to operate on these stores.
pub trait Map<K, V>: Container {
    /// Return a reference to the value corresponding to the key
    fn find<'a>(&'a self, key: &K) -> Option<&'a V>;

    /// Return true if the map contains a value for the specified key
    fn contains_key(&self, key: &K) -> bool {
        self.find(key).is_some()
    }
}

/// This trait provides basic operations to modify the contents of a map.
pub trait MutableMap<K, V>: Map<K, V> + Mutable {
    /// Insert a key-value pair into the map. An existing value for a
    /// key is replaced by the new value. Return true if the key did
    /// not already exist in the map.
    #[inline]
    fn insert(&mut self, key: K, value: V) -> bool {
        self.swap(key, value).is_none()
    }

    /// Remove a key-value pair from the map. Return true if the key
    /// was present in the map, otherwise false.
    #[inline]
    fn remove(&mut self, key: &K) -> bool {
        self.pop(key).is_some()
    }

    /// Insert a key-value pair from the map. If the key already had a value
    /// present in the map, that value is returned. Otherwise None is returned.
    fn swap(&mut self, k: K, v: V) -> Option<V>;

    /// Removes a key from the map, returning the value at the key if the key
    /// was previously in the map.
    fn pop(&mut self, k: &K) -> Option<V>;

    /// Return a mutable reference to the value corresponding to the key
    fn find_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V>;
}

/// A set is a group of objects which are each distinct from one another. This
/// trait represents actions which can be performed on sets to iterate over
/// them.
pub trait Set<T>: Container {
    /// Return true if the set contains a value
    fn contains(&self, value: &T) -> bool;

    /// Return true if the set has no elements in common with `other`.
    /// This is equivalent to checking for an empty intersection.
    fn is_disjoint(&self, other: &Self) -> bool;

    /// Return true if the set is a subset of another
    fn is_subset(&self, other: &Self) -> bool;

    /// Return true if the set is a superset of another
    fn is_superset(&self, other: &Self) -> bool;

    // FIXME #8154: Add difference, sym. difference, intersection and union iterators
}

/// This trait represents actions which can be performed on sets to mutate
/// them.
pub trait MutableSet<T>: Set<T> + Mutable {
    /// Add a value to the set. Return true if the value was not already
    /// present in the set.
    fn insert(&mut self, value: T) -> bool;

    /// Remove a value from the set. Return true if the value was
    /// present in the set.
    fn remove(&mut self, value: &T) -> bool;
}
<commit_msg>container: inline contains_key default method<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Container traits

use option::Option;

/// A trait to represent the abstract idea of a container. The only concrete
/// knowledge known is the number of elements contained within.
pub trait Container {
    /// Return the number of elements in the container
    fn len(&self) -> uint;

    /// Return true if the container contains no elements
    #[inline]
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

/// A trait to represent mutable containers
pub trait Mutable: Container {
    /// Clear the container, removing all values.
    fn clear(&mut self);
}

/// A map is a key-value store where values may be looked up by their keys. This
/// trait provides basic operations to operate on these stores.
pub trait Map<K, V>: Container {
    /// Return a reference to the value corresponding to the key
    fn find<'a>(&'a self, key: &K) -> Option<&'a V>;

    /// Return true if the map contains a value for the specified key
    #[inline]
    fn contains_key(&self, key: &K) -> bool {
        self.find(key).is_some()
    }
}

/// This trait provides basic operations to modify the contents of a map.
pub trait MutableMap<K, V>: Map<K, V> + Mutable {
    /// Insert a key-value pair into the map. An existing value for a
    /// key is replaced by the new value. Return true if the key did
    /// not already exist in the map.
    #[inline]
    fn insert(&mut self, key: K, value: V) -> bool {
        self.swap(key, value).is_none()
    }

    /// Remove a key-value pair from the map. Return true if the key
    /// was present in the map, otherwise false.
    #[inline]
    fn remove(&mut self, key: &K) -> bool {
        self.pop(key).is_some()
    }

    /// Insert a key-value pair from the map. If the key already had a value
    /// present in the map, that value is returned. Otherwise None is returned.
    fn swap(&mut self, k: K, v: V) -> Option<V>;

    /// Removes a key from the map, returning the value at the key if the key
    /// was previously in the map.
    fn pop(&mut self, k: &K) -> Option<V>;

    /// Return a mutable reference to the value corresponding to the key
    fn find_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V>;
}

/// A set is a group of objects which are each distinct from one another. This
/// trait represents actions which can be performed on sets to iterate over
/// them.
pub trait Set<T>: Container {
    /// Return true if the set contains a value
    fn contains(&self, value: &T) -> bool;

    /// Return true if the set has no elements in common with `other`.
    /// This is equivalent to checking for an empty intersection.
    fn is_disjoint(&self, other: &Self) -> bool;

    /// Return true if the set is a subset of another
    fn is_subset(&self, other: &Self) -> bool;

    /// Return true if the set is a superset of another
    fn is_superset(&self, other: &Self) -> bool;

    // FIXME #8154: Add difference, sym. difference, intersection and union iterators
}

/// This trait represents actions which can be performed on sets to mutate
/// them.
pub trait MutableSet<T>: Set<T> + Mutable {
    /// Add a value to the set. Return true if the value was not already
    /// present in the set.
    fn insert(&mut self, value: T) -> bool;

    /// Remove a value from the set. Return true if the value was
    /// present in the set.
    fn remove(&mut self, value: &T) -> bool;
}
<|endoftext|>