<commit_before>#![deny(warnings)]

extern crate bufstream;
extern crate cargo_registry;
extern crate conduit;
extern crate conduit_middleware;
extern crate conduit_test;
extern crate curl;
extern crate dotenv;
extern crate git2;
extern crate postgres;
extern crate rustc_serialize;
extern crate semver;
extern crate time;
extern crate url;

use std::collections::HashMap;
use std::process::Command;
use std::env;
use std::sync::{Once, ONCE_INIT, Arc};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use rustc_serialize::json::{self, Json};

use conduit::{Request, Method};
use conduit_test::MockRequest;
use cargo_registry::app::App;
use cargo_registry::db::{self, RequestTransaction};
use cargo_registry::dependency::Kind;
use cargo_registry::{User, Crate, Version, Keyword, Dependency, Category, Model};
use cargo_registry::upload as u;

macro_rules! t {
    ($e:expr) => (
        match $e {
            Ok(e) => e,
            Err(m) => panic!("{} failed with: {}", stringify!($e), m),
        }
    )
}

macro_rules! t_resp { ($e:expr) => (t!($e)) }

macro_rules! ok_resp {
    ($e:expr) => ({
        let resp = t_resp!($e);
        if !::ok_resp(&resp) { panic!("bad response: {:?}", resp.status); }
        resp
    })
}

macro_rules! bad_resp {
    ($e:expr) => ({
        let mut resp = t_resp!($e);
        match ::bad_resp(&mut resp) {
            None => panic!("ok response: {:?}", resp.status),
            Some(b) => b,
        }
    })
}

#[derive(RustcDecodable, Debug)]
struct Error { detail: String }
#[derive(RustcDecodable)]
struct Bad { errors: Vec<Error> }

mod category;
mod keyword;
mod krate;
mod user;
mod record;
mod git;
mod version;
mod team;

fn app() -> (record::Bomb, Arc<App>, conduit_middleware::MiddlewareBuilder) {
    dotenv::dotenv().ok();
    static INIT: Once = ONCE_INIT;
    git::init();

    let (proxy, bomb) = record::proxy();
    let config = cargo_registry::Config {
        s3_bucket: env::var("S3_BUCKET").unwrap_or(String::new()),
        s3_access_key: env::var("S3_ACCESS_KEY").unwrap_or(String::new()),
        s3_secret_key: env::var("S3_SECRET_KEY").unwrap_or(String::new()),
        s3_region: env::var("S3_REGION").ok(),
        s3_proxy: Some(proxy),
        session_key: "test".to_string(),
        git_repo_checkout: git::checkout(),
        gh_client_id: env::var("GH_CLIENT_ID").unwrap_or(String::new()),
        gh_client_secret: env::var("GH_CLIENT_SECRET").unwrap_or(String::new()),
        db_url: env("TEST_DATABASE_URL"),
        env: cargo_registry::Env::Test,
        max_upload_size: 1000,
        mirror: false,
    };
    INIT.call_once(|| db_setup(&config.db_url));
    let app = App::new(&config);
    let app = Arc::new(app);
    let middleware = cargo_registry::middleware(app.clone());
    return (bomb, app, middleware);

    fn db_setup(db: &str) {
        let mut me = t!(env::current_exe());
        me.pop();
        if me.ends_with("deps") {
            me.pop();
        }
        me.push("migrate");
        assert!(t!(Command::new(&me).env("DATABASE_URL", db)
                           .status()).success());
    }
}

fn env(s: &str) -> String {
    match env::var(s).ok() {
        Some(s) => s,
        None => panic!("must have `{}` defined", s),
    }
}

fn req(app: Arc<App>, method: conduit::Method, path: &str) -> MockRequest {
    let mut req = MockRequest::new(method, path);
    req.mut_extensions().insert(db::Transaction::new(app));
    return req;
}

fn ok_resp(r: &conduit::Response) -> bool {
    r.status.0 == 200
}

fn bad_resp(r: &mut conduit::Response) -> Option<Bad> {
    let bad = json::<Bad>(r);
    if bad.errors.len() == 0 { return None }
    Some(bad)
}

fn json<T: rustc_serialize::Decodable>(r: &mut conduit::Response) -> T {
    let mut data = Vec::new();
    r.body.read_to_end(&mut data).unwrap();
    let s = std::str::from_utf8(&data).unwrap();
    let j = match Json::from_str(s) {
        Ok(t) => t,
        Err(e) => panic!("failed to decode: {:?}\n{}", e, s),
    };
    let j = fixup(j);
    let s = j.to_string();
    return match json::decode(&s) {
        Ok(t) => t,
        Err(e) => panic!("failed to decode: {:?}\n{}", e, s),
    };

    fn fixup(json: Json) -> Json {
        match json {
            Json::Object(object) => {
                Json::Object(object.into_iter().map(|(k, v)| {
                    let k = if k == "crate" {
                        "krate".to_string()
                    } else {
                        k
                    };
                    (k, fixup(v))
                }).collect())
            }
            Json::Array(list) => {
                Json::Array(list.into_iter().map(fixup).collect())
            }
            j => j,
        }
    }
}

static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;

fn user(login: &str) -> User {
    User {
        id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
        gh_id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
        gh_login: login.to_string(),
        email: None,
        name: None,
        avatar: None,
        gh_access_token: User::new_api_token(), // just randomize it
        api_token: User::new_api_token(),
    }
}

fn krate(name: &str) -> Crate {
    cargo_registry::krate::Crate {
        id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
        name: name.to_string(),
        updated_at: time::now().to_timespec(),
        created_at: time::now().to_timespec(),
        downloads: 10,
        max_version: semver::Version::parse("0.0.0").unwrap(),
        documentation: None,
        homepage: None,
        description: None,
        readme: None,
        license: None,
        repository: None,
        max_upload_size: None,
    }
}

fn mock_user(req: &mut Request, u: User) -> User {
    let u = User::find_or_insert(req.tx().unwrap(),
                                 u.gh_id,
                                 &u.gh_login,
                                 u.email.as_ref().map(|s| &s[..]),
                                 u.name.as_ref().map(|s| &s[..]),
                                 u.avatar.as_ref().map(|s| &s[..]),
                                 &u.gh_access_token,
                                 &u.api_token).unwrap();
    req.mut_extensions().insert(u.clone());
    return u;
}

fn mock_crate(req: &mut Request, krate: Crate) -> (Crate, Version) {
    mock_crate_vers(req, krate, &semver::Version::parse("1.0.0").unwrap())
}

fn mock_crate_vers(req: &mut Request, krate: Crate, v: &semver::Version)
                   -> (Crate, Version) {
    let user = req.extensions().find::<User>().unwrap();
    let mut krate = Crate::find_or_insert(req.tx().unwrap(), &krate.name,
                                          user.id, &krate.description,
                                          &krate.homepage,
                                          &krate.documentation,
                                          &krate.readme,
                                          &krate.repository,
                                          &krate.license,
                                          &None,
                                          krate.max_upload_size).unwrap();
    let v = krate.add_version(req.tx().unwrap(), v, &HashMap::new(), &[]);
    (krate, v.unwrap())
}

fn mock_dep(req: &mut Request, version: &Version, krate: &Crate,
            target: Option<&str>) -> Dependency {
    Dependency::insert(req.tx().unwrap(),
                       version.id,
                       krate.id,
                       &semver::VersionReq::parse(">= 0").unwrap(),
                       Kind::Normal,
                       false, true, &[],
                       &target.map(|s| s.to_string())).unwrap()
}

fn mock_keyword(req: &mut Request, name: &str) -> Keyword {
    Keyword::find_or_insert(req.tx().unwrap(), name).unwrap()
}

fn mock_category(req: &mut Request, name: &str) -> Category {
    let conn = req.tx().unwrap();
    let stmt = conn.prepare(" \
        INSERT INTO categories (category) \
        VALUES ($1) \
        RETURNING *").unwrap();
    let rows = stmt.query(&[&name]).unwrap();
    Model::from_row(&rows.iter().next().unwrap())
}

fn logout(req: &mut Request) {
    req.mut_extensions().pop::<User>();
}

fn new_req(app: Arc<App>, krate: &str, version: &str) -> MockRequest {
    new_req_full(app, ::krate(krate), version, Vec::new())
}

fn new_req_full(app: Arc<App>, krate: Crate, version: &str,
                deps: Vec<u::CrateDependency>) -> MockRequest {
    let mut req = ::req(app, Method::Put, "/api/v1/crates/new");
    req.with_body(&new_req_body(krate, version, deps, Vec::new()));
    return req;
}

fn new_req_with_keywords(app: Arc<App>, krate: Crate, version: &str,
                         kws: Vec<String>) -> MockRequest {
    let mut req = ::req(app, Method::Put, "/api/v1/crates/new");
    req.with_body(&new_req_body(krate, version, Vec::new(), kws));
    return req;
}

fn new_req_body(krate: Crate, version: &str, deps: Vec<u::CrateDependency>,
                kws: Vec<String>) -> Vec<u8> {
    let kws = kws.into_iter().map(u::Keyword).collect();
    new_crate_to_body(&u::NewCrate {
        name: u::CrateName(krate.name),
        vers: u::CrateVersion(semver::Version::parse(version).unwrap()),
        features: HashMap::new(),
        deps: deps,
        authors: vec!["foo".to_string()],
        description: Some("description".to_string()),
        homepage: krate.homepage,
        documentation: krate.documentation,
        readme: krate.readme,
        keywords: Some(u::KeywordList(kws)),
        license: Some("MIT".to_string()),
        license_file: None,
        repository: krate.repository,
    }, &[])
}

fn new_crate_to_body(new_crate: &u::NewCrate, krate: &[u8]) -> Vec<u8> {
    let json = json::encode(&new_crate).unwrap();
    let mut body = Vec::new();
    body.extend([
        (json.len() >>  0) as u8,
        (json.len() >>  8) as u8,
        (json.len() >> 16) as u8,
        (json.len() >> 24) as u8,
    ].iter().cloned());
    body.extend(json.as_bytes().iter().cloned());
    body.extend(&[
        (krate.len() >>  0) as u8,
        (krate.len() >>  8) as u8,
        (krate.len() >> 16) as u8,
        (krate.len() >> 24) as u8,
    ]);
    body.extend(krate);
    body
}
<commit_msg>Alphabetize mods<commit_after>#![deny(warnings)]

extern crate bufstream;
extern crate cargo_registry;
extern crate conduit;
extern crate conduit_middleware;
extern crate conduit_test;
extern crate curl;
extern crate dotenv;
extern crate git2;
extern crate postgres;
extern crate rustc_serialize;
extern crate semver;
extern crate time;
extern crate url;

use std::collections::HashMap;
use std::process::Command;
use std::env;
use std::sync::{Once, ONCE_INIT, Arc};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use rustc_serialize::json::{self, Json};

use conduit::{Request, Method};
use conduit_test::MockRequest;
use cargo_registry::app::App;
use cargo_registry::db::{self, RequestTransaction};
use cargo_registry::dependency::Kind;
use cargo_registry::{User, Crate, Version, Keyword, Dependency, Category, Model};
use cargo_registry::upload as u;

macro_rules! t {
    ($e:expr) => (
        match $e {
            Ok(e) => e,
            Err(m) => panic!("{} failed with: {}", stringify!($e), m),
        }
    )
}

macro_rules! t_resp { ($e:expr) => (t!($e)) }

macro_rules! ok_resp {
    ($e:expr) => ({
        let resp = t_resp!($e);
        if !::ok_resp(&resp) { panic!("bad response: {:?}", resp.status); }
        resp
    })
}

macro_rules! bad_resp {
    ($e:expr) => ({
        let mut resp = t_resp!($e);
        match ::bad_resp(&mut resp) {
            None => panic!("ok response: {:?}", resp.status),
            Some(b) => b,
        }
    })
}

#[derive(RustcDecodable, Debug)]
struct Error { detail: String }
#[derive(RustcDecodable)]
struct Bad { errors: Vec<Error> }

mod category;
mod git;
mod keyword;
mod krate;
mod record;
mod team;
mod user;
mod version;

fn app() -> (record::Bomb, Arc<App>, conduit_middleware::MiddlewareBuilder) {
    dotenv::dotenv().ok();
    static INIT: Once = ONCE_INIT;
    git::init();

    let (proxy, bomb) = record::proxy();
    let config = cargo_registry::Config {
        s3_bucket: env::var("S3_BUCKET").unwrap_or(String::new()),
        s3_access_key: env::var("S3_ACCESS_KEY").unwrap_or(String::new()),
        s3_secret_key: env::var("S3_SECRET_KEY").unwrap_or(String::new()),
        s3_region: env::var("S3_REGION").ok(),
        s3_proxy: Some(proxy),
        session_key: "test".to_string(),
        git_repo_checkout: git::checkout(),
        gh_client_id: env::var("GH_CLIENT_ID").unwrap_or(String::new()),
        gh_client_secret: env::var("GH_CLIENT_SECRET").unwrap_or(String::new()),
        db_url: env("TEST_DATABASE_URL"),
        env: cargo_registry::Env::Test,
        max_upload_size: 1000,
        mirror: false,
    };
    INIT.call_once(|| db_setup(&config.db_url));
    let app = App::new(&config);
    let app = Arc::new(app);
    let middleware = cargo_registry::middleware(app.clone());
    return (bomb, app, middleware);

    fn db_setup(db: &str) {
        let mut me = t!(env::current_exe());
        me.pop();
        if me.ends_with("deps") {
            me.pop();
        }
        me.push("migrate");
        assert!(t!(Command::new(&me).env("DATABASE_URL", db)
                           .status()).success());
    }
}

fn env(s: &str) -> String {
    match env::var(s).ok() {
        Some(s) => s,
        None => panic!("must have `{}` defined", s),
    }
}

fn req(app: Arc<App>, method: conduit::Method, path: &str) -> MockRequest {
    let mut req = MockRequest::new(method, path);
    req.mut_extensions().insert(db::Transaction::new(app));
    return req;
}

fn ok_resp(r: &conduit::Response) -> bool {
    r.status.0 == 200
}

fn bad_resp(r: &mut conduit::Response) -> Option<Bad> {
    let bad = json::<Bad>(r);
    if bad.errors.len() == 0 { return None }
    Some(bad)
}

fn json<T: rustc_serialize::Decodable>(r: &mut conduit::Response) -> T {
    let mut data = Vec::new();
    r.body.read_to_end(&mut data).unwrap();
    let s = std::str::from_utf8(&data).unwrap();
    let j = match Json::from_str(s) {
        Ok(t) => t,
        Err(e) => panic!("failed to decode: {:?}\n{}", e, s),
    };
    let j = fixup(j);
    let s = j.to_string();
    return match json::decode(&s) {
        Ok(t) => t,
        Err(e) => panic!("failed to decode: {:?}\n{}", e, s),
    };

    fn fixup(json: Json) -> Json {
        match json {
            Json::Object(object) => {
                Json::Object(object.into_iter().map(|(k, v)| {
                    let k = if k == "crate" {
                        "krate".to_string()
                    } else {
                        k
                    };
                    (k, fixup(v))
                }).collect())
            }
            Json::Array(list) => {
                Json::Array(list.into_iter().map(fixup).collect())
            }
            j => j,
        }
    }
}

static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;

fn user(login: &str) -> User {
    User {
        id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
        gh_id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
        gh_login: login.to_string(),
        email: None,
        name: None,
        avatar: None,
        gh_access_token: User::new_api_token(), // just randomize it
        api_token: User::new_api_token(),
    }
}

fn krate(name: &str) -> Crate {
    cargo_registry::krate::Crate {
        id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
        name: name.to_string(),
        updated_at: time::now().to_timespec(),
        created_at: time::now().to_timespec(),
        downloads: 10,
        max_version: semver::Version::parse("0.0.0").unwrap(),
        documentation: None,
        homepage: None,
        description: None,
        readme: None,
        license: None,
        repository: None,
        max_upload_size: None,
    }
}

fn mock_user(req: &mut Request, u: User) -> User {
    let u = User::find_or_insert(req.tx().unwrap(),
                                 u.gh_id,
                                 &u.gh_login,
                                 u.email.as_ref().map(|s| &s[..]),
                                 u.name.as_ref().map(|s| &s[..]),
                                 u.avatar.as_ref().map(|s| &s[..]),
                                 &u.gh_access_token,
                                 &u.api_token).unwrap();
    req.mut_extensions().insert(u.clone());
    return u;
}

fn mock_crate(req: &mut Request, krate: Crate) -> (Crate, Version) {
    mock_crate_vers(req, krate, &semver::Version::parse("1.0.0").unwrap())
}

fn mock_crate_vers(req: &mut Request, krate: Crate, v: &semver::Version)
                   -> (Crate, Version) {
    let user = req.extensions().find::<User>().unwrap();
    let mut krate = Crate::find_or_insert(req.tx().unwrap(), &krate.name,
                                          user.id, &krate.description,
                                          &krate.homepage,
                                          &krate.documentation,
                                          &krate.readme,
                                          &krate.repository,
                                          &krate.license,
                                          &None,
                                          krate.max_upload_size).unwrap();
    let v = krate.add_version(req.tx().unwrap(), v, &HashMap::new(), &[]);
    (krate, v.unwrap())
}

fn mock_dep(req: &mut Request, version: &Version, krate: &Crate,
            target: Option<&str>) -> Dependency {
    Dependency::insert(req.tx().unwrap(),
                       version.id,
                       krate.id,
                       &semver::VersionReq::parse(">= 0").unwrap(),
                       Kind::Normal,
                       false, true, &[],
                       &target.map(|s| s.to_string())).unwrap()
}

fn mock_keyword(req: &mut Request, name: &str) -> Keyword {
    Keyword::find_or_insert(req.tx().unwrap(), name).unwrap()
}

fn mock_category(req: &mut Request, name: &str) -> Category {
    let conn = req.tx().unwrap();
    let stmt = conn.prepare(" \
        INSERT INTO categories (category) \
        VALUES ($1) \
        RETURNING *").unwrap();
    let rows = stmt.query(&[&name]).unwrap();
    Model::from_row(&rows.iter().next().unwrap())
}

fn logout(req: &mut Request) {
    req.mut_extensions().pop::<User>();
}

fn new_req(app: Arc<App>, krate: &str, version: &str) -> MockRequest {
    new_req_full(app, ::krate(krate), version, Vec::new())
}

fn new_req_full(app: Arc<App>, krate: Crate, version: &str,
                deps: Vec<u::CrateDependency>) -> MockRequest {
    let mut req = ::req(app, Method::Put, "/api/v1/crates/new");
    req.with_body(&new_req_body(krate, version, deps, Vec::new()));
    return req;
}

fn new_req_with_keywords(app: Arc<App>, krate: Crate, version: &str,
                         kws: Vec<String>) -> MockRequest {
    let mut req = ::req(app, Method::Put, "/api/v1/crates/new");
    req.with_body(&new_req_body(krate, version, Vec::new(), kws));
    return req;
}

fn new_req_body(krate: Crate, version: &str, deps: Vec<u::CrateDependency>,
                kws: Vec<String>) -> Vec<u8> {
    let kws = kws.into_iter().map(u::Keyword).collect();
    new_crate_to_body(&u::NewCrate {
        name: u::CrateName(krate.name),
        vers: u::CrateVersion(semver::Version::parse(version).unwrap()),
        features: HashMap::new(),
        deps: deps,
        authors: vec!["foo".to_string()],
        description: Some("description".to_string()),
        homepage: krate.homepage,
        documentation: krate.documentation,
        readme: krate.readme,
        keywords: Some(u::KeywordList(kws)),
        license: Some("MIT".to_string()),
        license_file: None,
        repository: krate.repository,
    }, &[])
}

fn new_crate_to_body(new_crate: &u::NewCrate, krate: &[u8]) -> Vec<u8> {
    let json = json::encode(&new_crate).unwrap();
    let mut body = Vec::new();
    body.extend([
        (json.len() >>  0) as u8,
        (json.len() >>  8) as u8,
        (json.len() >> 16) as u8,
        (json.len() >> 24) as u8,
    ].iter().cloned());
    body.extend(json.as_bytes().iter().cloned());
    body.extend(&[
        (krate.len() >>  0) as u8,
        (krate.len() >>  8) as u8,
        (krate.len() >> 16) as u8,
        (krate.len() >> 24) as u8,
    ]);
    body.extend(krate);
    body
}
<|endoftext|>


<commit_before>use piston_window::{Context, G2d};
use piston_window::types::Color;
use rand::{Rng, thread_rng};

use colors::*;
use game::{Renderer, render_square_in_grid};
use settings::WIDTH_IN_BLOCKS;

pub fn create_random_piece() -> Piece {
    let all_tetrominos = [&I, &J, &L, &O, &S, &T, &Z];
    let ptype = thread_rng().choose(&all_tetrominos).unwrap();
    Piece::create(*ptype)
}

pub static I: Tetromino = Tetromino {
    configurations: [
        [Block{x:0, y:1}, Block{x:1, y:1}, Block{x:2, y:1}, Block{x:3, y:1}],
        [Block{x:2, y:0}, Block{x:2, y:1}, Block{x:2, y:2}, Block{x:2, y:3}],
        [Block{x:0, y:2}, Block{x:1, y:2}, Block{x:2, y:2}, Block{x:3, y:2}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}, Block{x:1, y:3}]
    ],
    color: CYAN
};

pub static J: Tetromino = Tetromino {
    configurations: [
        [Block{x:0, y:0}, Block{x:0, y:1}, Block{x:1, y:1}, Block{x:2, y:1}],
        [Block{x:2, y:0}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}],
        [Block{x:2, y:2}, Block{x:2, y:1}, Block{x:1, y:1}, Block{x:0, y:1}],
        [Block{x:0, y:2}, Block{x:1, y:2}, Block{x:1, y:1}, Block{x:1, y:0}]
    ],
    color: BLUE
};

pub static L: Tetromino = Tetromino {
    configurations: [
        [Block{x:2, y:0}, Block{x:2, y:1}, Block{x:1, y:1}, Block{x:0, y:1}],
        [Block{x:2, y:2}, Block{x:1, y:2}, Block{x:1, y:1}, Block{x:1, y:0}],
        [Block{x:0, y:2}, Block{x:0, y:1}, Block{x:1, y:1}, Block{x:2, y:1}],
        [Block{x:0, y:0}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}]
    ],
    color: ORANGE
};

pub static O: Tetromino = Tetromino {
    configurations: [
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:0}, Block{x:2, y:1}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:0}, Block{x:2, y:1}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:0}, Block{x:2, y:1}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:0}, Block{x:2, y:1}]
    ],
    color: YELLOW
};

pub static S: Tetromino = Tetromino {
    configurations: [
        [Block{x:0, y:1}, Block{x:1, y:1}, Block{x:1, y:0}, Block{x:2, y:0}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:1}, Block{x:2, y:2}],
        [Block{x:2, y:1}, Block{x:1, y:1}, Block{x:1, y:2}, Block{x:0, y:2}],
        [Block{x:1, y:2}, Block{x:1, y:1}, Block{x:0, y:1}, Block{x:0, y:0}]
    ],
    color: LIME
};

pub static T: Tetromino = Tetromino {
    configurations: [
        [Block{x:1, y:0}, Block{x:0, y:1}, Block{x:1, y:1}, Block{x:2, y:1}],
        [Block{x:2, y:1}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}],
        [Block{x:1, y:2}, Block{x:2, y:1}, Block{x:1, y:1}, Block{x:0, y:1}],
        [Block{x:0, y:1}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}]
    ],
    color: PURPLE
};

pub static Z: Tetromino = Tetromino {
    configurations: [
        [Block{x:0, y:0}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:1}],
        [Block{x:2, y:0}, Block{x:2, y:1}, Block{x:1, y:1}, Block{x:1, y:2}],
        [Block{x:2, y:2}, Block{x:1, y:2}, Block{x:1, y:1}, Block{x:0, y:1}],
        [Block{x:0, y:2}, Block{x:0, y:1}, Block{x:1, y:1}, Block{x:1, y:0}]
    ],
    color: RED
};

pub type Configuration = [Block; 4];
type Rotation = usize;

pub struct Piece {
    // TODO: Replace with position
    // TODO: Make this private again
    pub x: i32,
    pub y: i32,
    ptype: &'static Tetromino,
    rotation: Rotation
}
impl Piece {
    pub fn create(ptype: &'static Tetromino) -> Piece {
        let x = (WIDTH_IN_BLOCKS as i32 / 2) - 2;
        Piece::new(x, 0, ptype, 0)
    }

    fn new(x: i32, y: i32, ptype: &'static Tetromino, rotation: Rotation) -> Piece {
        Piece {
            x: x,
            y: y,
            ptype: ptype,
            rotation: rotation
        }
    }

    pub fn get_blocks(&self) -> &Configuration {
        // TODO: Remove this in favor of blocks_iter
        self.ptype.get_configuration(self.rotation)
    }

    pub fn blocks_iter(&self) -> BlockIterator {
        let configuration = self.ptype.get_configuration(self.rotation);
        BlockIterator::new(self.x, self.y, configuration)
    }

    pub fn rotated(&self) -> Self {
        let rotation = (self.rotation + 1) % self.ptype.configurations.len();
        Self::new(self.x, self.y, self.ptype, rotation)
    }

    pub fn moved(&self, direction: Direction) -> Self {
        // TODO: Add direction Down and have it move here
        let translated_x = match direction {
            Direction::Left => self.x - 1,
            Direction::Right => self.x + 1
        };
        Self::new(translated_x, self.y, self.ptype, self.rotation)
    }

    pub fn is_out_of_bounds(&self) -> bool {
        self.blocks_iter().any(|block| {
            block.x < 0 || block.x >= WIDTH_IN_BLOCKS as i32
        })
    }

    pub fn wall_kick(&self) -> Self {
        // TODO: Refactor to use blocks_iter when test is written
        let mut translation = 0;
        let min_block = self.get_blocks().iter().min_by_key(|block| self.x + block.x).unwrap();
        let max_block = self.get_blocks().iter().max_by_key(|block| self.x + block.x).unwrap();
        if self.x + min_block.x < 0 {
            translation = self.x + min_block.x;
        } else if self.x + max_block.x >= WIDTH_IN_BLOCKS as i32 {
            translation = (self.x + max_block.x) - (WIDTH_IN_BLOCKS - 1) as i32;
        }
        Self::new(self.x - translation, self.y, self.ptype, self.rotation)
    }

    fn get_color(&self) -> Color {
        self.ptype.color
    }
}
impl Renderer for Piece {
    fn render(&self, context: Context, graphics: &mut G2d) {
        for block in self.blocks_iter() {
            render_square_in_grid(block.x, block.y, self.get_color(), context, graphics);
        }
    }
}

pub struct BlockIterator {
    x: i32,
    y: i32,
    index: usize,
    blocks: &'static Configuration
}
impl BlockIterator {
    pub fn new(x: i32, y: i32, blocks: &'static Configuration) -> BlockIterator {
        BlockIterator {
            x: x,
            y: y,
            index: 0,
            blocks: blocks
        }
    }
}
impl Iterator for BlockIterator {
    type Item = Block;
    fn next(&mut self) -> Option<Block> {
        if self.index >= self.blocks.len() {
            return None;
        }
        let ref block = self.blocks[self.index];
        self.index += 1;
        let translated_x = self.x + block.x;
        let translated_y = self.y + block.y;
        Some(Block{x: translated_x, y: translated_y})
    }
}

pub enum Direction {
    Left,
    Right
}

#[derive(Debug)]
pub struct Tetromino {
    configurations: [Configuration; 4],
    color: Color
}
impl Tetromino {
    fn get_configuration(&self, rotation: Rotation) -> &Configuration {
        &self.configurations[rotation]
    }
}

#[derive(Debug, PartialEq)]
pub struct Block {
    pub x: i32,
    pub y: i32
}

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

    #[test]
    fn test_piece_rotated() {
        let mut p = Piece::create(&I);
        assert_eq!(p.rotation, 0);
        p = p.rotated();
        assert_eq!(p.rotation, 1);
        p = p.rotated();
        assert_eq!(p.rotation, 2);
        p = p.rotated();
        assert_eq!(p.rotation, 3);
        p = p.rotated();
        assert_eq!(p.rotation, 0);
    }

    #[test]
    fn test_piece_moved_left() {
        let mut p = Piece::create(&Z);
        let result = p.moved(Direction::Left);
        assert_eq!(result.x, p.x - 1);
    }

    #[test]
    fn test_piece_moved_right() {
        let mut p = Piece::create(&Z);
        let result = p.moved(Direction::Right);
        assert_eq!(result.x, p.x + 1);
    }

    #[test]
    fn test_piece_out_of_bounds() {
        let mut p = Piece::create(&I);
        p.x = 6;
        assert_eq!(p.is_out_of_bounds(), false);
        p.x = 7;
        assert_eq!(p.is_out_of_bounds(), true);
        p.x = 0;
        assert_eq!(p.is_out_of_bounds(), false);
        p.x = -1;
        assert_eq!(p.is_out_of_bounds(), true);
    }

    #[test]
    fn test_wall_kick_in_bounds() {
        let p = Piece::create(&I);
        let kicked = p.wall_kick();
        assert_eq!(kicked.x, p.x);
        assert_eq!(kicked.y, p.y);
    }

    #[test]
    fn test_wall_kick_out_of_bounds() {
        let mut p = Piece::create(&I);
        p.x = 7;
        let kicked = p.wall_kick();
        assert_eq!(kicked.x, 6);
        assert_eq!(kicked.y, p.y);
    }

    #[test]
    fn test_block_iterator() {
        let mut block_iter = BlockIterator::new(2, 2, &I.configurations[0]);
        assert_eq!(block_iter.next(), Some(Block{x: 2, y: 3}));
        assert_eq!(block_iter.next(), Some(Block{x: 3, y: 3}));
        assert_eq!(block_iter.next(), Some(Block{x: 4, y: 3}));
        assert_eq!(block_iter.next(), Some(Block{x: 5, y: 3}));
        assert_eq!(block_iter.next(), None);
    }
}
<commit_msg>update the wall_kick code to use the new blocks_iter() function<commit_after>use piston_window::{Context, G2d};
use piston_window::types::Color;
use rand::{Rng, thread_rng};

use colors::*;
use game::{Renderer, render_square_in_grid};
use settings::WIDTH_IN_BLOCKS;

pub fn create_random_piece() -> Piece {
    let all_tetrominos = [&I, &J, &L, &O, &S, &T, &Z];
    let ptype = thread_rng().choose(&all_tetrominos).unwrap();
    Piece::create(*ptype)
}

pub static I: Tetromino = Tetromino {
    configurations: [
        [Block{x:0, y:1}, Block{x:1, y:1}, Block{x:2, y:1}, Block{x:3, y:1}],
        [Block{x:2, y:0}, Block{x:2, y:1}, Block{x:2, y:2}, Block{x:2, y:3}],
        [Block{x:0, y:2}, Block{x:1, y:2}, Block{x:2, y:2}, Block{x:3, y:2}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}, Block{x:1, y:3}]
    ],
    color: CYAN
};

pub static J: Tetromino = Tetromino {
    configurations: [
        [Block{x:0, y:0}, Block{x:0, y:1}, Block{x:1, y:1}, Block{x:2, y:1}],
        [Block{x:2, y:0}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}],
        [Block{x:2, y:2}, Block{x:2, y:1}, Block{x:1, y:1}, Block{x:0, y:1}],
        [Block{x:0, y:2}, Block{x:1, y:2}, Block{x:1, y:1}, Block{x:1, y:0}]
    ],
    color: BLUE
};

pub static L: Tetromino = Tetromino {
    configurations: [
        [Block{x:2, y:0}, Block{x:2, y:1}, Block{x:1, y:1}, Block{x:0, y:1}],
        [Block{x:2, y:2}, Block{x:1, y:2}, Block{x:1, y:1}, Block{x:1, y:0}],
        [Block{x:0, y:2}, Block{x:0, y:1}, Block{x:1, y:1}, Block{x:2, y:1}],
        [Block{x:0, y:0}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}]
    ],
    color: ORANGE
};

pub static O: Tetromino = Tetromino {
    configurations: [
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:0}, Block{x:2, y:1}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:0}, Block{x:2, y:1}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:0}, Block{x:2, y:1}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:0}, Block{x:2, y:1}]
    ],
    color: YELLOW
};

pub static S: Tetromino = Tetromino {
    configurations: [
        [Block{x:0, y:1}, Block{x:1, y:1}, Block{x:1, y:0}, Block{x:2, y:0}],
        [Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:1}, Block{x:2, y:2}],
        [Block{x:2, y:1}, Block{x:1, y:1}, Block{x:1, y:2}, Block{x:0, y:2}],
        [Block{x:1, y:2}, Block{x:1, y:1}, Block{x:0, y:1}, Block{x:0, y:0}]
    ],
    color: LIME
};

pub static T: Tetromino = Tetromino {
    configurations: [
        [Block{x:1, y:0}, Block{x:0, y:1}, Block{x:1, y:1}, Block{x:2, y:1}],
        [Block{x:2, y:1}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}],
        [Block{x:1, y:2}, Block{x:2, y:1}, Block{x:1, y:1}, Block{x:0, y:1}],
        [Block{x:0, y:1}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:1, y:2}]
    ],
    color: PURPLE
};

pub static Z: Tetromino = Tetromino {
    configurations: [
        [Block{x:0, y:0}, Block{x:1, y:0}, Block{x:1, y:1}, Block{x:2, y:1}],
        [Block{x:2, y:0}, Block{x:2, y:1}, Block{x:1, y:1}, Block{x:1, y:2}],
        [Block{x:2, y:2}, Block{x:1, y:2}, Block{x:1, y:1}, Block{x:0, y:1}],
        [Block{x:0, y:2}, Block{x:0, y:1}, Block{x:1, y:1}, Block{x:1, y:0}]
    ],
    color: RED
};

pub type Configuration = [Block; 4];
type Rotation = usize;

pub struct Piece {
    // TODO: Replace with position
    // TODO: Make this private again
    pub x: i32,
    pub y: i32,
    ptype: &'static Tetromino,
    rotation: Rotation
}
impl Piece {
    pub fn create(ptype: &'static Tetromino) -> Piece {
        let x = (WIDTH_IN_BLOCKS as i32 / 2) - 2;
        Piece::new(x, 0, ptype, 0)
    }

    fn new(x: i32, y: i32, ptype: &'static Tetromino, rotation: Rotation) -> Piece {
        Piece {
            x: x,
            y: y,
            ptype: ptype,
            rotation: rotation
        }
    }

    pub fn get_blocks(&self) -> &Configuration {
        // TODO: Remove this in favor of blocks_iter
        self.ptype.get_configuration(self.rotation)
    }

    pub fn blocks_iter(&self) -> BlockIterator {
        let configuration = self.ptype.get_configuration(self.rotation);
        BlockIterator::new(self.x, self.y, configuration)
    }

    pub fn rotated(&self) -> Self {
        let rotation = (self.rotation + 1) % self.ptype.configurations.len();
        Self::new(self.x, self.y, self.ptype, rotation)
    }

    pub fn moved(&self, direction: Direction) -> Self {
        // TODO: Add direction Down and have it move here
        let translated_x = match direction {
            Direction::Left => self.x - 1,
            Direction::Right => self.x + 1
        };
        Self::new(translated_x, self.y, self.ptype, self.rotation)
    }

    pub fn is_out_of_bounds(&self) -> bool {
        self.blocks_iter().any(|block| {
            block.x < 0 || block.x >= WIDTH_IN_BLOCKS as i32
        })
    }

    pub fn wall_kick(&self) -> Self {
        // TODO: Refactor to use blocks_iter when test is written
        let mut translation = 0;
        let min_block = self.blocks_iter().min_by_key(|block| block.x).unwrap();
        let max_block = self.blocks_iter().max_by_key(|block| block.x).unwrap();
        if min_block.x < 0 {
            translation = min_block.x;
        } else if max_block.x >= WIDTH_IN_BLOCKS as i32 {
            translation = max_block.x - (WIDTH_IN_BLOCKS - 1) as i32;
        }
        Self::new(self.x - translation, self.y, self.ptype, self.rotation)
    }

    fn get_color(&self) -> Color {
        self.ptype.color
    }
}
impl Renderer for Piece {
    fn render(&self, context: Context, graphics: &mut G2d) {
        for block in self.blocks_iter() {
            render_square_in_grid(block.x, block.y, self.get_color(), context, graphics);
        }
    }
}

pub struct BlockIterator {
    x: i32,
    y: i32,
    index: usize,
    blocks: &'static Configuration
}
impl BlockIterator {
    pub fn new(x: i32, y: i32, blocks: &'static Configuration) -> BlockIterator {
        BlockIterator {
            x: x,
            y: y,
            index: 0,
            blocks: blocks
        }
    }
}
impl Iterator for BlockIterator {
    type Item = Block;
    fn next(&mut self) -> Option<Block> {
        if self.index >= self.blocks.len() {
            return None;
        }
        let ref block = self.blocks[self.index];
        self.index += 1;
        let translated_x = self.x + block.x;
        let translated_y = self.y + block.y;
        Some(Block{x: translated_x, y: translated_y})
    }
}

pub enum Direction {
    Left,
    Right
}

#[derive(Debug)]
pub struct Tetromino {
    configurations: [Configuration; 4],
    color: Color
}
impl Tetromino {
    fn get_configuration(&self, rotation: Rotation) -> &Configuration {
        &self.configurations[rotation]
    }
}

#[derive(Debug, PartialEq)]
pub struct Block {
    pub x: i32,
    pub y: i32
}

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

    #[test]
    fn test_piece_rotated() {
        let mut p = Piece::create(&I);
        assert_eq!(p.rotation, 0);
        p = p.rotated();
        assert_eq!(p.rotation, 1);
        p = p.rotated();
        assert_eq!(p.rotation, 2);
        p = p.rotated();
        assert_eq!(p.rotation, 3);
        p = p.rotated();
        assert_eq!(p.rotation, 0);
    }

    #[test]
    fn test_piece_moved_left() {
        let mut p = Piece::create(&Z);
        let result = p.moved(Direction::Left);
        assert_eq!(result.x, p.x - 1);
    }

    #[test]
    fn test_piece_moved_right() {
        let mut p = Piece::create(&Z);
        let result = p.moved(Direction::Right);
        assert_eq!(result.x, p.x + 1);
    }

    #[test]
    fn test_piece_out_of_bounds() {
        let mut p = Piece::create(&I);
        p.x = 6;
        assert_eq!(p.is_out_of_bounds(), false);
        p.x = 7;
        assert_eq!(p.is_out_of_bounds(), true);
        p.x = 0;
        assert_eq!(p.is_out_of_bounds(), false);
        p.x = -1;
        assert_eq!(p.is_out_of_bounds(), true);
    }

    #[test]
    fn test_wall_kick_in_bounds() {
        let p = Piece::create(&I);
        let kicked = p.wall_kick();
        assert_eq!(kicked.x, p.x);
        assert_eq!(kicked.y, p.y);
    }

    #[test]
    fn test_wall_kick_out_of_bounds() {
        let mut p = Piece::create(&I);
        p.x = 7;
        let kicked = p.wall_kick();
        assert_eq!(kicked.x, 6);
        assert_eq!(kicked.y, p.y);
    }

    #[test]
    fn test_block_iterator() {
        let mut block_iter = BlockIterator::new(2, 2, &I.configurations[0]);
        assert_eq!(block_iter.next(), Some(Block{x: 2, y: 3}));
        assert_eq!(block_iter.next(), Some(Block{x: 3, y: 3}));
        assert_eq!(block_iter.next(), Some(Block{x: 4, y: 3}));
        assert_eq!(block_iter.next(), Some(Block{x: 5, y: 3}));
        assert_eq!(block_iter.next(), None);
    }
}
<|endoftext|>


<commit_before>// External modules:
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
use error::{Result, ResultExt};
use image;
use image::imageops::resize;
use image::{GenericImage, FilterType};

// Internal modules:
use config::PanolutionConfig;
use optimizer::{Solution, ImageArrangement};

// Std modules:
use std::path::PathBuf;

fn is_supported_format(entry: &DirEntry) -> bool {
    if entry.file_type().is_file() {
        if let Some(file_name) = entry.file_name().to_str() {
            let extension = file_name.split(".").last().unwrap_or("").to_lowercase();

            let supported: Vec<String> = vec!["jpg", "jpeg", "gif", "png", "tif", "tiff"].iter().map(|s| s.to_string()).collect();

            supported.contains(&extension)
        } else {
            false
        }
    } else {
        false
    }
}

fn create_image_arrangement(path: &PathBuf) -> Result<ImageArrangement> {
    info!("Loading image file: '{:?}'", path);
    let img = image::open(&path).chain_err(|| format!("can't open image: '{:?}", path))?;
    let width = img.width();
    let height = img.height();

    Ok(ImageArrangement{
        file_name: path.to_str().unwrap().to_string(),
        w: width,
        h: height,
        x: 0.5,
        y: 0.5,
        angle: 0.0
    })
}

pub fn create_thumbnails(config: &PanolutionConfig) -> Result<Vec<Solution>> {
    let mut all_image_paths: Vec<PathBuf> = Vec::new();
    let mut result = Vec::new();
    let walker = WalkDir::new(&config.input_path).into_iter();

    for entry in walker.filter_entry(|e| is_supported_format(e)) {
        let entry = entry.chain_err(|| "error in WalkDir")?;
        let path = entry.path();

        if let Some(file_name) = path.file_name() {
            if let Some(file_name) = file_name.to_str() {
                if file_name.starts_with("thumb_") {
                    info!("Ignore thumbnail file: {:?}", path);
                } else {
                    all_image_paths.push(path.to_path_buf());
                }
            } else {
                info!("Could not converto to str: {:?}", file_name);
            }
        } else {
            info!("Could not get file_name: {:?}", path)
        }
    }

    for scale_factor in &config.scale_factors {
        let mut arrangement = Vec::new();

        for path in &all_image_paths {
            if *scale_factor >= 1.0 {
                info!("Add original image");

                arrangement.push(create_image_arrangement(&path)?);
            } else {
                let base_path = path.parent().unwrap();
                let file_name = path.file_name().unwrap(); // Save to unwrap since we checked above
                let thumb_path = base_path.join(format!("thumb_{}_{}", scale_factor, file_name.to_str().unwrap()));

                if thumb_path.exists() {
                    info!("Thumbnail was already generated, using old one: '{:?}'", &thumb_path);
                    arrangement.push(create_image_arrangement(&thumb_path)?);
                } else {
                    info!("Loading image file: '{:?}'", path);
                    let orig_img = image::open(&path).chain_err(|| format!("can't open image: '{:?}", path))?;
                    let width = orig_img.width();
                    let height = orig_img.height();
                    info!("No thumbnail found for scale factor '{}', generating new one", scale_factor);
                    let (orig_w, orig_h) = orig_img.dimensions();
                    let (thumb_w, thumb_h) = (((orig_w as f64) * scale_factor) as u32, ((orig_h as f64) * scale_factor) as u32);
                    info!("image sizes: ({}, {}) -> ({}, {})", orig_w, orig_h, thumb_w, thumb_h);
                    let thumb_img = resize(&orig_img, thumb_w, thumb_h, FilterType::Nearest);
                    let _ = thumb_img.save(&thumb_path).chain_err(|| "can't save thumbnail image")?;

                    arrangement.push(
                        ImageArrangement{
                            file_name: thumb_path.to_str().unwrap().to_string(),
                            w: thumb_w,
                            h: thumb_h,
                            x: 0.5,
                            y: 0.5,
                            angle: 0.0
                        }
                    );
                }
            }
        }

        result.push(Solution{arrangement: arrangement});
    }

    Ok(result)
}
<commit_msg>fix file filter<commit_after>// External modules:
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
use error::{Result, ResultExt};
use image;
use image::imageops::resize;
use image::{GenericImage, FilterType};

// Internal modules:
use config::PanolutionConfig;
use optimizer::{Solution, ImageArrangement};

// Std modules:
use std::path::PathBuf;

fn valid_image_file(file_name: &str) -> bool {
    let extension = file_name.split(".").last().unwrap_or("").to_lowercase();

    let supported: Vec<String> = vec!["jpg", "jpeg", "gif", "png", "tif", "tiff"].iter().map(|s| s.to_string()).collect();

    supported.contains(&extension)
}

fn create_image_arrangement(path: &PathBuf) -> Result<ImageArrangement> {
    info!("Loading image file: '{:?}'", path);
    let img = image::open(&path).chain_err(|| format!("can't open image: '{:?}", path))?;
    let width = img.width();
    let height = img.height();

    Ok(ImageArrangement{
        file_name: path.to_str().unwrap().to_string(),
        w: width,
        h: height,
        x: 0.5,
        y: 0.5,
        angle: 0.0
    })
}

pub fn create_thumbnails(config: &PanolutionConfig) -> Result<Vec<Solution>> {
    let mut all_image_paths: Vec<PathBuf> = Vec::new();
    let mut result = Vec::new();

    for entry in WalkDir::new(&config.input_path) {
        let entry = entry.chain_err(|| "error in WalkDir")?;

        if entry.file_type().is_file() {
            if let Some(file_name) = entry.file_name().to_str() {
                if valid_image_file(file_name) {
                    if file_name.starts_with("thumb_") {
                        info!("Ignore thumbnail file: {:?}", file_name);
                    } else {
                        all_image_paths.push(entry.path().to_path_buf());
                    }
                } else {
                    info!("Image format currently not supported: {}", file_name);
                }
            } else {
                info!("Could not convert file name to str: {:?}", entry);
            }
        } else {
            info!("Ignore non-file: {:?}", entry);
        }
    }

    for scale_factor in &config.scale_factors {
        let mut arrangement = Vec::new();

        for path in &all_image_paths {
            if *scale_factor >= 1.0 {
                info!("Add original image");

                arrangement.push(create_image_arrangement(&path)?);
            } else {
                let base_path = path.parent().unwrap();
                let file_name = path.file_name().unwrap(); // Save to unwrap since we checked above
                let thumb_path = base_path.join(format!("thumb_{}_{}", scale_factor, file_name.to_str().unwrap()));

                if thumb_path.exists() {
                    info!("Thumbnail was already generated, using old one: '{:?}'", &thumb_path);
                    arrangement.push(create_image_arrangement(&thumb_path)?);
                } else {
                    info!("Loading image file: '{:?}'", path);
                    let orig_img = image::open(&path).chain_err(|| format!("can't open image: '{:?}", path))?;
                    let width = orig_img.width();
                    let height = orig_img.height();
                    info!("No thumbnail found for scale factor '{}', generating new one", scale_factor);
                    let (orig_w, orig_h) = orig_img.dimensions();
                    let (thumb_w, thumb_h) = (((orig_w as f64) * scale_factor) as u32, ((orig_h as f64) * scale_factor) as u32);
                    info!("image sizes: ({}, {}) -> ({}, {})", orig_w, orig_h, thumb_w, thumb_h);
                    let thumb_img = resize(&orig_img, thumb_w, thumb_h, FilterType::Nearest);
                    let _ = thumb_img.save(&thumb_path).chain_err(|| "can't save thumbnail image")?;

                    arrangement.push(
                        ImageArrangement{
                            file_name: thumb_path.to_str().unwrap().to_string(),
                            w: thumb_w,
                            h: thumb_h,
                            x: 0.5,
                            y: 0.5,
                            angle: 0.0
                        }
                    );
                }
            }
        }

        result.push(Solution{arrangement: arrangement});
    }

    Ok(result)
}
<|endoftext|>


<commit_before>use std::fmt;
use std::iter::Map;
use std::slice::Iter;

use regex::Regex;

type SliceVec = Vec<(usize, usize)>;

#[derive(Debug)]
pub enum Pattern {
    Template
}

impl fmt::Display for Pattern {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let result = match *self {
            Pattern::Template => r"(\{%.*?%\}|\{\{.*?\}\}?|\{\{|\{%)"
        };

        write!(f, "{}", result)
    }
}

impl Pattern {
    pub fn to_regex(&self) -> Regex {
        let pattern = format!("{}", self);
        Regex::new(&pattern).unwrap()
    }
}

pub struct Tokenizer<'t> {
    source: &'t str
}

impl<'t> Tokenizer<'t> {
    pub fn new<'a>(source: &'a str) -> Tokenizer<'a> {
        Tokenizer { source: source }
    }

    pub fn tokenize<'a>(&'a self, pattern: &'a Regex) -> Vec<&'a str> {
        let slices = self.matched_slices(pattern);
        slices.iter().map(|&(start, end)| &self.source[start..end]).collect()
    }

    fn matched_slices(&self, pattern: &Regex) -> SliceVec {
        let mut slices = pattern.find_iter(self.source).collect::<Vec<_>>();
        let missing = self.find_missing_slices(&slices);

        slices.extend(&missing);
        slices.sort();
        slices
    }

    fn find_missing_slices(&self, slices: &SliceVec) -> SliceVec {
        if slices.is_empty() { return vec![(0, self.source.len())]; }

        let mut missing = self.missing_middle_slices(slices);
        let first       = self.missing_first_slice(slices);
        let last        = self.missing_last_slice(slices);

        if first.is_some() { missing.push(first.unwrap()) }
        if last.is_some()  { missing.push(last.unwrap()) }

        missing
    }

    fn missing_first_slice(&self, slices: &SliceVec) -> Option<(usize, usize)> {
        let pos = slices[0].0;
        if pos == 0 { return None; }

        Some((0, pos))
    }

    fn missing_last_slice(&self, slices: &SliceVec) -> Option<(usize, usize)> {
        let last = slices.last().unwrap();
        let len  = self.source.len();
        if last.1 == len { return None; }

        Some((last.1, len))
    }

    fn missing_middle_slices(&self, slices: &SliceVec) -> SliceVec {
        let mut missing: SliceVec = Vec::new();
        let last_index            = slices.len() - 1;

        for (index, &(_, right)) in slices.iter().enumerate() {
            if index == last_index { continue; }

            let edge = slices[index + 1].0;
            if right != edge {
                missing.push((right, edge));
            }
        }

        missing
    }
}

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

    fn assert_tokens(tokenizer: &Tokenizer, expected: Vec<&str>) {
        let re     = Pattern::Template.to_regex();
        let actual = tokenizer.tokenize(&re);

        assert_eq!(expected, actual);
    }

    #[test]
    fn tokenize_string_with_no_matches() {
        let tokenizer = Tokenizer::new("hello world");
        assert_tokens(&tokenizer, vec!["hello world"]);
    }

    #[test]
    fn tokenize_single_variable() {
        let tokenizer = Tokenizer::new("{{funk}}");
        assert_tokens(&tokenizer, vec!["{{funk}}"]);
    }

    #[test]
    fn tokenize_single_variable_surrounded_by_whitespace() {
        let tokenizer = Tokenizer::new(" {{funk}} ");
        assert_tokens(&tokenizer, vec![" ", "{{funk}}", " "]);
    }

    #[test]
    fn tokenize_multiple_variables() {
        let tokenizer = Tokenizer::new(" {{funk}} {{so}} {{brutha}} ");
        let expected = vec![
            " ",
            "{{funk}}",
            " ",
            "{{so}}",
            " ",
            "{{brutha}}",
            " "
        ];

        assert_tokens(&tokenizer, expected);
    }
}
<commit_msg>adding a few more tests<commit_after>use std::fmt;
use std::iter::Map;
use std::slice::Iter;

use regex::Regex;

type SliceVec = Vec<(usize, usize)>;

#[derive(Debug)]
pub enum Pattern {
    Template
}

impl fmt::Display for Pattern {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let result = match *self {
            Pattern::Template => r"(\{%.*?%\}|\{\{.*?\}\}?|\{\{|\{%)"
        };

        write!(f, "{}", result)
    }
}

impl Pattern {
    pub fn to_regex(&self) -> Regex {
        let pattern = format!("{}", self);
        Regex::new(&pattern).unwrap()
    }
}

pub struct Tokenizer<'t> {
    source: &'t str
}

impl<'t> Tokenizer<'t> {
    pub fn new<'a>(source: &'a str) -> Tokenizer<'a> {
        Tokenizer { source: source }
    }

    pub fn tokenize<'a>(&'a self, pattern: &'a Regex) -> Vec<&'a str> {
        let slices = self.matched_slices(pattern);
        slices.iter().map(|&(start, end)| &self.source[start..end]).collect()
    }

    fn matched_slices(&self, pattern: &Regex) -> SliceVec {
        let mut slices = pattern.find_iter(self.source).collect::<Vec<_>>();
        let missing = self.find_missing_slices(&slices);

        slices.extend(&missing);
        slices.sort();
        slices
    }

    fn find_missing_slices(&self, slices: &SliceVec) -> SliceVec {
        if slices.is_empty() { return vec![(0, self.source.len())]; }

        let mut missing = self.missing_middle_slices(slices);
        let first       = self.missing_first_slice(slices);
        let last        = self.missing_last_slice(slices);

        if first.is_some() { missing.push(first.unwrap()) }
        if last.is_some()  { missing.push(last.unwrap()) }

        missing
    }

    fn missing_first_slice(&self, slices: &SliceVec) -> Option<(usize, usize)> {
        let pos = slices[0].0;
        if pos == 0 { return None; }

        Some((0, pos))
    }

    fn missing_last_slice(&self, slices: &SliceVec) -> Option<(usize, usize)> {
        let last = slices.last().unwrap();
        let len  = self.source.len();
        if last.1 == len { return None; }

        Some((last.1, len))
    }

    fn missing_middle_slices(&self, slices: &SliceVec) -> SliceVec {
        let mut missing: SliceVec = Vec::new();
        let last_index            = slices.len() - 1;

        for (index, &(_, right)) in slices.iter().enumerate() {
            if index == last_index { continue; }

            let edge = slices[index + 1].0;
            if right != edge {
                missing.push((right, edge));
            }
        }

        missing
    }
}

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

    fn assert_tokens(tokenizer: &Tokenizer, expected: Vec<&str>) {
        let re     = Pattern::Template.to_regex();
        let actual = tokenizer.tokenize(&re);

        assert_eq!(expected, actual);
    }

    #[test]
    fn tokenize_blank_string() {
        let tokenizer = Tokenizer::new("");
        assert_tokens(&tokenizer, vec![""]);
    }

    #[test]
    fn tokenize_whitespace_only_string() {
        let tokenizer = Tokenizer::new("  ");
        assert_tokens(&tokenizer, vec!["  "]);
    }

    #[test]
    fn tokenize_string_with_no_matches() {
        let tokenizer = Tokenizer::new("hello world");
        assert_tokens(&tokenizer, vec!["hello world"]);
    }

    #[test]
    fn tokenize_single_variable() {
        let tokenizer = Tokenizer::new("{{funk}}");
        assert_tokens(&tokenizer, vec!["{{funk}}"]);
    }

    #[test]
    fn tokenize_single_variable_surrounded_by_whitespace() {
        let tokenizer = Tokenizer::new(" {{funk}} ");
        assert_tokens(&tokenizer, vec![" ", "{{funk}}", " "]);
    }

    #[test]
    fn tokenize_multiple_variables() {
        let tokenizer = Tokenizer::new(" {{funk}} {{so}} {{brutha}} ");
        let expected = vec![
            " ",
            "{{funk}}",
            " ",
            "{{so}}",
            " ",
            "{{brutha}}",
            " "
        ];

        assert_tokens(&tokenizer, expected);
    }

    #[test]
    fn tokenize_single_block() {
        let tokenizer = Tokenizer::new(" {%comment%} ");
        assert_tokens(&tokenizer, vec![" ", "{%comment%}", " "]);
    }

    #[test]
    fn tokenize_empty_block_tag() {
        let tokenizer = Tokenizer::new(" {% thing %} {% comment %} My comment here {% endcomment %} ");

        assert_tokens(&tokenizer, vec![
            " ",
            "{% thing %}",
            " ",
            "{% comment %}",
            " My comment here ",
            "{% endcomment %}",
            " "
        ]);
    }
}
<|endoftext|>


<commit_before>//! Loads the configuration and runs the game workflow.
//! This file not sourced for the wasm32 (wasm) target.
#![cfg(not(target_arch = "wasm32"))]

extern crate ascii_hangman_backend;
extern crate crossterm;
extern crate rand;
extern crate thiserror;
use ascii_hangman_backend::game::State;
use ascii_hangman_backend::Backend;
use ascii_hangman_backend::HangmanBackend;
use ascii_hangman_backend::{AUTHOR, CONF_TEMPLATE, TITLE, VERSION};
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Write;
use std::path::PathBuf;
use std::process;

use crossterm::cursor::MoveTo;
use crossterm::cursor::MoveToNextLine;
use crossterm::queue;
use crossterm::style::Color;
use crossterm::style::Print;
#[cfg(not(target_os = "windows"))]
use crossterm::style::ResetColor;
use crossterm::style::SetForegroundColor;
use crossterm::terminal::Clear;
use crossterm::terminal::ClearType;
use std::io::stdout;

/// Text to show as command-line help --help
const COMMANDLINE_HELP: &str = r#"
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
ASCII-ART HANGMAN FOR KIDS

         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (")
    `-j                |
      `-._(           /
         |_\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lifes:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: ascii-hangman
        ascii-hangman [FILE]...
        ascii-hangman -h|--help
        asciiart-ascii-hangman-for-kids -V|--version

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to `ascii-hangman-words.txt`. In
case no `[FILE]` is found, a template configuration file `ascii-hangman-words.txt` is
written into the current working directory. Multiple `[FILE]`s are concatenated.

`[FILE]` is a UTF-8 file containing 4 different line-types:

- lines starting with a letter, a digit or '-' are secret strings. At the
  beginning of the game one line is randomly chosen and all characters are
  hidden. If you want to give an additional hint, enclose some characters
  with `_`.  The enclosed is then displayed in clear when the game starts.
  For example the configuration line:

      Guess _me_

  is shown in the game as:

      _ _ _ _ _ _ m e

- lines starting with `#` are ignored. This can be used for comments.

- lines starting with `|` are part of an optional custom ASCII-Art image shown
  progressively in the course of the game. If not defined here, built in
  ASCII-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:

   `:success-rewarding`       Every guessed character shows a bit more of
                              the image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                              image. Choose this mode together with a
                              traditional gallows image (not built in).

The following shows an example of a custom image (copy it left-aligned
into the config-file):

        |  ,~~--~~-.
        | +      | |\
        | || |~ |`,/-\
        | *\_) \_) `-'

If you prefer a traditional gallows image, add the following:

        :traditional-rewarding
        |  ______
        |  |    |
        |  |    O
        |  |   /|\
        |  |    |
        |  |   / \
        |__|_____
        ||      |___
        ||_________|

"#;

/// Default configuration filename when no filename is given at the command-line.
const PATHSTR: &str = "ascii-hangman-words.txt";
/// Fallback sample configuration when no configuration file can be found.

/// Fallback secret when no configuration file can be found.
const CONF_DEMO: &str = "- _Demo: add own words to config file and start a_gain_!";

// ------------------ MAIN ---------------------------------------------

/// Reads the configuration file.
pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
    let mut f = File::open(pathstr)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

/// Writes a sample configuration file on disk. Called when no configuration file can be found.
pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

/// Starts the game.
/// This is the terminal application
#[allow(unused_labels)]
fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            eprintln!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(ref a) if a == "-V" || a == "--version" => {
            eprintln!("{}", VERSION.unwrap());
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths = env::args()
        .skip(1)
        .map(PathBuf::from)
        .collect::<Vec<PathBuf>>();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.is_empty() {
        conf_file_paths.push(PathBuf::from(PATHSTR))
    };

    // read and concatenate all config files given on command line
    let cwd = env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {
        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s) => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        eprintln!(
                            "As no config-file :\n\
                             \t{:?}\n\
                             was found a template file is written in the \
                             current working directory.\n\
                             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                            path, cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        eprintln!(
                            "Couldn't write ascii-hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            why.to_string(),
                            cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE THE GAME

    let mut app = match Backend::new(&config) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("ERROR IN CONFIGURATION FILE\n{}", e);

            // wait for [Enter] key
            let s = &mut String::new();
            io::stdin().read_line(s).unwrap();
            process::exit(1);
        }
    };

    app.render();

    // PLAY

    'playing: loop {
        // Read user input
        io::stdout().flush().unwrap();
        // Read next char and send it
        let key = &mut String::new();
        io::stdin().read_line(key).unwrap();

        app.process_user_input(key);
        app.render();
        io::stdout().flush().unwrap();
        let state = app.get_state();
        if state != State::Ongoing {
            if state != State::VictoryGameOver {
                queue!(
                    stdout(),
                    MoveToNextLine(1),
                    Print("Press any key to continue or [Ctrl+C] to quit.")
                )
                .unwrap();
                stdout().flush().unwrap();
            } else {
                break;
            };
        };
    }

    queue!(stdout(), MoveToNextLine(1), Print(AUTHOR)).unwrap();
    stdout().flush().unwrap();
    let key = &mut String::new();
    io::stdin().read_line(key).unwrap();
}

/// Trait to render the text user interface (TUI)
trait Render {
    /// Renders and prints the TUI on the terminal.
    fn render(&self) {}
}

impl Render for Backend {
    fn render(&self) {
        // Disclose parts of the image.

        // Clear all lines in terminal;
        queue!(stdout(), Clear(ClearType::All), MoveTo(0, 0)).unwrap();

        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();

        queue!(
            stdout(),
            Print(&TITLE),
            MoveToNextLine(1),
            SetForegroundColor(Color::DarkYellow),
        )
        .unwrap();

        // Print image.
        queue!(stdout(), Print(self.render_image()), MoveToNextLine(1)).unwrap();

        // Print game status.
        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();
        queue!(stdout(), Print(self.render_game_lifes()), Print("\t")).unwrap();
        queue!(
            stdout(),
            Print(self.render_game_last_guess()),
            MoveToNextLine(2)
        )
        .unwrap();

        // Print secret.
        #[cfg(not(windows))]
        queue!(stdout(), SetForegroundColor(Color::DarkGreen),).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::White),).unwrap();
        queue!(stdout(), Print(self.render_secret()), MoveToNextLine(1)).unwrap();

        // Print instructions.
        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();

        queue!(stdout(), Print(self.render_instructions()), Print(" ")).unwrap();
        // Print queued.
        stdout().flush().unwrap();
    }
}
<commit_msg>doc: update documentation<commit_after>//! Loads the configuration and runs the game workflow.
//! This file not sourced for the wasm32 (wasm) target.
#![cfg(not(target_arch = "wasm32"))]

extern crate ascii_hangman_backend;
extern crate crossterm;
extern crate rand;
extern crate thiserror;
use ascii_hangman_backend::game::State;
use ascii_hangman_backend::Backend;
use ascii_hangman_backend::HangmanBackend;
use ascii_hangman_backend::{AUTHOR, CONF_TEMPLATE, TITLE, VERSION};
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Write;
use std::path::PathBuf;
use std::process;

use crossterm::cursor::MoveTo;
use crossterm::cursor::MoveToNextLine;
use crossterm::queue;
use crossterm::style::Color;
use crossterm::style::Print;
#[cfg(not(target_os = "windows"))]
use crossterm::style::ResetColor;
use crossterm::style::SetForegroundColor;
use crossterm::terminal::Clear;
use crossterm::terminal::ClearType;
use std::io::stdout;

/// Text to show as command-line help --help
const COMMANDLINE_HELP: &str = r#"
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
ASCII-ART HANGMAN FOR KIDS

         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (")
    `-j                |
      `-._(           /
         |_\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lifes:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: ascii-hangman
        ascii-hangman [FILE]...
        ascii-hangman -h|--help
        asciiart-ascii-hangman-for-kids -V|--version

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to `ascii-hangman-words.txt`. In
case no `[FILE]` is found, a template configuration file `ascii-hangman-words.txt` is
written into the current working directory. Multiple `[FILE]`s are concatenated.

`[FILE]` is a UTF-8 file containing 4 different line-types:

- lines starting with a letter, a digit or '-' are secret strings. At the
  beginning of the game one line (secret) is randomly chosen and all characters
  are hidden. If you want to give an additional hint, enclose some characters
  with `_`.  The enclosed is then displayed in clear when the game starts.
  Furthermore, the `|` acts as a hard line break and is not printed.
  For example the configuration line:

      Guess _me_

  is shown in the game as:

      _ _ _ _ _ _ m e

- lines starting with `#` are ignored. This can be used for comments.

- lines starting with `|` are part of an optional custom ASCII-Art image shown
  progressively in the course of the game. If not defined here, built in
  ASCII-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:

   `:success-rewarding`       Every guessed character shows a bit more of
                              the image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                              image. Choose this mode together with a
                              traditional gallows image (not built in).

The following shows an example of a custom image (copy it left-aligned
into the config-file):

        |  ,~~--~~-.
        | +      | |\
        | || |~ |`,/-\
        | *\_) \_) `-'

If you prefer a traditional gallows image, add the following:

        :traditional-rewarding
        |  ______
        |  |    |
        |  |    O
        |  |   /|\
        |  |    |
        |  |   / \
        |__|_____
        ||      |___
        ||_________|

"#;

/// Default configuration filename when no filename is given at the command-line.
const PATHSTR: &str = "ascii-hangman-words.txt";
/// Fallback sample configuration when no configuration file can be found.

/// Fallback secret when no configuration file can be found.
const CONF_DEMO: &str = "- _Demo: add own words to config file and start a_gain_!";

// ------------------ MAIN ---------------------------------------------

/// Reads the configuration file.
pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
    let mut f = File::open(pathstr)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

/// Writes a sample configuration file on disk. Called when no configuration file can be found.
pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

/// Starts the game.
/// This is the terminal application
#[allow(unused_labels)]
fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            eprintln!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(ref a) if a == "-V" || a == "--version" => {
            eprintln!("{}", VERSION.unwrap());
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths = env::args()
        .skip(1)
        .map(PathBuf::from)
        .collect::<Vec<PathBuf>>();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.is_empty() {
        conf_file_paths.push(PathBuf::from(PATHSTR))
    };

    // read and concatenate all config files given on command line
    let cwd = env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {
        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s) => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        eprintln!(
                            "As no config-file :\n\
                             \t{:?}\n\
                             was found a template file is written in the \
                             current working directory.\n\
                             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                            path, cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        eprintln!(
                            "Couldn't write ascii-hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            why.to_string(),
                            cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE THE GAME

    let mut app = match Backend::new(&config) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("ERROR IN CONFIGURATION FILE\n{}", e);

            // wait for [Enter] key
            let s = &mut String::new();
            io::stdin().read_line(s).unwrap();
            process::exit(1);
        }
    };

    app.render();

    // PLAY

    'playing: loop {
        // Read user input
        io::stdout().flush().unwrap();
        // Read next char and send it
        let key = &mut String::new();
        io::stdin().read_line(key).unwrap();

        app.process_user_input(key);
        app.render();
        io::stdout().flush().unwrap();
        let state = app.get_state();
        if state != State::Ongoing {
            if state != State::VictoryGameOver {
                queue!(
                    stdout(),
                    MoveToNextLine(1),
                    Print("Press any key to continue or [Ctrl+C] to quit.")
                )
                .unwrap();
                stdout().flush().unwrap();
            } else {
                break;
            };
        };
    }

    queue!(stdout(), MoveToNextLine(1), Print(AUTHOR)).unwrap();
    stdout().flush().unwrap();
    let key = &mut String::new();
    io::stdin().read_line(key).unwrap();
}

/// Trait to render the text user interface (TUI)
trait Render {
    /// Renders and prints the TUI on the terminal.
    fn render(&self) {}
}

impl Render for Backend {
    fn render(&self) {
        // Disclose parts of the image.

        // Clear all lines in terminal;
        queue!(stdout(), Clear(ClearType::All), MoveTo(0, 0)).unwrap();

        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();

        queue!(
            stdout(),
            Print(&TITLE),
            MoveToNextLine(1),
            SetForegroundColor(Color::DarkYellow),
        )
        .unwrap();

        // Print image.
        queue!(stdout(), Print(self.render_image()), MoveToNextLine(1)).unwrap();

        // Print game status.
        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();
        queue!(stdout(), Print(self.render_game_lifes()), Print("\t")).unwrap();
        queue!(
            stdout(),
            Print(self.render_game_last_guess()),
            MoveToNextLine(2)
        )
        .unwrap();

        // Print secret.
        #[cfg(not(windows))]
        queue!(stdout(), SetForegroundColor(Color::DarkGreen),).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::White),).unwrap();
        queue!(stdout(), Print(self.render_secret()), MoveToNextLine(1)).unwrap();

        // Print instructions.
        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();

        queue!(stdout(), Print(self.render_instructions()), Print(" ")).unwrap();
        // Print queued.
        stdout().flush().unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate rustty;

use rustty::{
    Terminal,
    Event,
};

use rustty::ui::{
    Painter,
    Dialog,
    DialogResult,
    Alignable,
    HorizontalAlign,
    VerticalAlign,
    ButtonLayout
};

fn create_maindlg() -> Dialog {
    // Create a dialog window 60 units wide and 12 units high
    let mut maindlg = Dialog::new(60, 12);

    // Text and alignment data to be used for displaying to dialog
    let s = "Hello! This is a showcase of the ui module!";
    let s2 = "Here's a vertical layout configuration.";
    let x = maindlg.window().halign_line(s, HorizontalAlign::Middle, 1);
    let x2 = maindlg.window().halign_line(s2, HorizontalAlign::Middle, 0);
    maindlg.window_mut().printline(x, 2, s);
    maindlg.window_mut().printline(x2, 3, s2);

    // Create the left column of buttons, these are buttons 0, 1, 2
    maindlg.add_button("Foo", 'f', DialogResult::Custom(1));
    maindlg.add_button("Bar", 'b', DialogResult::Custom(2));
    maindlg.add_button("Quit", 'q', DialogResult::Ok);
    maindlg.draw_buttons_subset(0, 3, ButtonLayout::Vertical(HorizontalAlign::Left));

    // Create the middle column of buttons, these are buttons 3, 4, 5
    maindlg.add_button("Juu", 'j', DialogResult::Custom(3));
    maindlg.add_button("Too", 't', DialogResult::Custom(4));
    maindlg.add_button("Boo", 'b', DialogResult::Custom(5));
    maindlg.draw_buttons_subset(3, 6, ButtonLayout::Vertical(HorizontalAlign::Middle));

    // Create the right column of buttons, these are buttons 6, 7, 8
    maindlg.add_button("Yhh", 'y', DialogResult::Custom(6));
    maindlg.add_button("Vpp", 'v', DialogResult::Custom(7));
    maindlg.add_button("Wgg", 'w', DialogResult::Custom(8));
    maindlg.draw_buttons_subset(6, 9, ButtonLayout::Vertical(HorizontalAlign::Right));

    // Draw the outline for the dialog
    maindlg.window_mut().draw_box();
    maindlg
}

fn create_hdlg(cols: usize) -> Dialog {
    // Create a dialog window that's as wide as the terminal, and 7 units high
    let mut hdlg = Dialog::new(cols, 7);

    // Text and alignment data to be used for displaying to dialog
    let s = "Here's a horizontal layout configuration.";
    let x = hdlg.window().halign_line(s, HorizontalAlign::Middle, 1);
    hdlg.window_mut().printline(x, 2, s);

    // Create a row of buttons to be displayed
    hdlg.add_button("Lo", 'l', DialogResult::Ok);
    hdlg.add_button("Yo", 'y', DialogResult::Ok);
    hdlg.add_button("Ho", 'h', DialogResult::Ok);
    hdlg.add_button("Uo", 'u', DialogResult::Ok);

    // Draw all buttons horizontally, and draw outline of dialog
    hdlg.draw_buttons(ButtonLayout::Horizontal(2));
    hdlg.window_mut().draw_box();
    hdlg
}

fn main() {
    let mut term = Terminal::new().unwrap();
    let mut maindlg = create_maindlg();
    let mut hdlg = create_hdlg(term.cols());
    // Align main dialog window with the middle of the screen, and hdlg with the bottom
    maindlg.window_mut().align(&term, HorizontalAlign::Middle, VerticalAlign::Middle, 0);
    hdlg.window_mut().align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 0);
    'main: loop {
        while let Some(Event::Key(ch)) = term.get_event(0).unwrap() {
            match maindlg.result_for_key(ch) {
                Some(DialogResult::Ok) => break 'main,
                Some(DialogResult::Custom(i)) => {
                    let msg = match i {
                        1   =>  "Foo!",
                        2   =>  "Bar!",
                        3   =>  "Juu!",
                        4   =>  "Too!",
                        5   =>  "Boo!",
                        6   =>  "Yhh!",
                        7   =>  "Vpp!",
                        8   =>  "Wgg!",
                        _   =>  ""
                    };
                    let w = maindlg.window_mut();
                    let x = w.halign_line(msg, HorizontalAlign::Middle, 1);
                    let y = w.valign_line(msg, VerticalAlign::Middle, 1);
                    w.printline(x, y, msg);
                },
                _ => {},
            }
        }

        // Draw widgets to screen
        maindlg.window().draw_into(&mut term);
        hdlg.window().draw_into(&mut term);
        term.swap_buffers().unwrap();
    }
}

<commit_msg>Refactor buttonlayout example to use new API<commit_after>extern crate rustty;

use rustty::{
    Terminal,
    Event,
};

use rustty::ui::core::{
    Painter,
    Widget,
    HorizontalAlign,
    VerticalAlign,
    ButtonResult,
    Button
};

use rustty::ui::{
    Dialog,
    StdButton,
    VerticalLayout,
    HorizontalLayout
};

fn boxify(vec: Vec<StdButton>) -> Vec<Box<Button>> {
    vec.into_iter().map(Box::new).map(|x| x as Box<Button>).collect()
}

fn create_maindlg() -> Dialog {
    // Create a dialog window 60 units wide and 12 units high
    let mut maindlg = Dialog::new(60, 12);

    // Text and alignment data to be used for displaying to dialog
    let s = "Hello! This is a showcase of the ui module!";
    let s2 = "Here's a horizontal layout configuration.";
    let x = maindlg.window().halign_line(s, HorizontalAlign::Middle, 1);
    let x2 = maindlg.window().halign_line(s2, HorizontalAlign::Middle, 0);
    maindlg.window_mut().printline(x, 2, s);
    maindlg.window_mut().printline(x2, 3, s2);

    let b1 = StdButton::new("Quit", 'q', ButtonResult::Ok);
    let b2 = StdButton::new("Foo!", 'f', ButtonResult::Custom(1));
    let b3 = StdButton::new("Bar!", 'b', ButtonResult::Custom(2));
    let b4 = StdButton::new("Juu!", 'j', ButtonResult::Custom(3));
    let b5 = StdButton::new("Tuu!", 't', ButtonResult::Custom(4));
    let b6 = StdButton::new("Boo!", 'b', ButtonResult::Custom(5));

    let mut hlayout1 = HorizontalLayout::from_vec(boxify(vec![b1, b2, b3]), 1);
    hlayout1.pack(&maindlg, HorizontalAlign::Middle, VerticalAlign::Bottom, (0, 2));
    maindlg.add_layout(hlayout1);

    let mut hlayout2 = HorizontalLayout::from_vec(boxify(vec![b4, b5, b6]), 1);
    hlayout2.pack(&maindlg, HorizontalAlign::Middle, VerticalAlign::Bottom, (0, 3));
    maindlg.add_layout(hlayout2);

    // Draw the outline for the dialog
    maindlg.window_mut().draw_box();
    maindlg
}

fn create_hdlg(rows: usize) -> Dialog {
    // Create a dialog window that's as wide as the terminal, and 7 units high
    let mut hdlg = Dialog::new(20, rows/4);

    // Text and alignment data to be used for displaying to dialog
    let s = "Vertical layout";
    let x = hdlg.window().halign_line(s, HorizontalAlign::Middle, 1);
    hdlg.window_mut().printline(x, 2, s);

    let b1 = StdButton::new("Yhh!", 'y', ButtonResult::Custom(1));
    let b2 = StdButton::new("Vpp!", 'v', ButtonResult::Custom(2));
    let b3 = StdButton::new("Wgg!", 'w', ButtonResult::Custom(3));

    let mut vlayout = VerticalLayout::from_vec(boxify(vec![b1, b2, b3]), 0);
    vlayout.pack(&hdlg, HorizontalAlign::Middle, VerticalAlign::Bottom, (0, 2));
    hdlg.add_layout(vlayout);

    hdlg.window_mut().draw_box();
    hdlg
}

fn main() {
    let mut term = Terminal::new().unwrap();
    let mut maindlg = create_maindlg();
    let mut hdlg = create_hdlg(term.rows());
    // Align main dialog window with the middle of the screen, and hdlg with the bottom
    maindlg.pack(&term, HorizontalAlign::Middle, VerticalAlign::Middle, (0,0));
    hdlg.pack(&term, HorizontalAlign::Left, VerticalAlign::Middle, (0,0));
    'main: loop {
        while let Some(Event::Key(ch)) = term.get_event(0).unwrap() {
            match maindlg.result_for_key(ch) {
                Some(ButtonResult::Ok) => break 'main,
                Some(ButtonResult::Custom(i)) => {
                    let msg = match i {
                        1   =>  "Foo!",
                        2   =>  "Bar!",
                        3   =>  "Juu!",
                        4   =>  "Too!",
                        5   =>  "Boo!",
                        6   =>  "Yhh!",
                        7   =>  "Vpp!",
                        8   =>  "Wgg!",
                        _   =>  ""
                    };
                    let w = maindlg.window_mut();
                    let x = w.halign_line(msg, HorizontalAlign::Middle, 1);
                    let y = w.valign_line(msg, VerticalAlign::Middle, 1);
                    w.printline(x, y, msg);
                },
                _ => {},
            }

            match hdlg.result_for_key(ch) {
                Some(ButtonResult::Custom(i)) => {
                    let msg = match i {
                        1 => "Yhh!",
                        2 => "Vpp!",
                        3 => "Wgg!",
                        _ => ""
                    };
                    let w = hdlg.window_mut();
                    let x = w.halign_line(msg, HorizontalAlign::Middle, 1);
                    let y = w.valign_line(msg, VerticalAlign::Middle, 1);
                    w.printline(x, y, msg);
                },
                _ => {},
            }
        }

        // Draw widgets to screen
        maindlg.window().draw_into(&mut term);
        hdlg.window().draw_into(&mut term);
        term.swap_buffers().unwrap();
    }
}

<|endoftext|>


<commit_before>use crate::{
    message::BasicReturnMessage, pinky_swear::PinkySwear, returned_messages::ReturnedMessages,
};
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

// FIXME: If dropped without data ready, register in ReturnedMessages
#[derive(Debug)]
pub struct PublisherConfirm {
    inner: PinkySwear<Confirmation>,
    returned_messages: ReturnedMessages,
    used: bool,
}

#[derive(Clone, Debug, PartialEq)]
pub enum Confirmation {
    Ack,
    Nack(BasicReturnMessage),
    NotRequested,
}

impl PublisherConfirm {
    pub(crate) fn new(
        inner: PinkySwear<Confirmation>,
        returned_messages: ReturnedMessages,
    ) -> Self {
        Self {
            inner,
            returned_messages,
            used: false,
        }
    }

    pub(crate) fn not_requested(returned_messages: ReturnedMessages) -> Self {
        Self {
            inner: PinkySwear::new_with_data(Confirmation::NotRequested),
            returned_messages,
            used: false,
        }
    }

    pub fn try_wait(&mut self) -> Option<Confirmation> {
        let confirmation = self.inner.try_wait()?;
        self.used = true;
        Some(confirmation)
    }

    pub fn wait(&mut self) -> Confirmation {
        self.used = true;
        self.inner.wait()
    }
}

impl Future for PublisherConfirm {
    type Output = Confirmation;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut this = self.as_mut();
        this.used = true;
        Pin::new(&mut this.inner).poll(cx)
    }
}
<commit_msg>Properly register PublisherConfirm when dropped unused<commit_after>use crate::{
    message::BasicReturnMessage, pinky_swear::PinkySwear, returned_messages::ReturnedMessages,
};
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

#[derive(Debug)]
pub struct PublisherConfirm {
    inner: Option<PinkySwear<Confirmation>>,
    returned_messages: ReturnedMessages,
    used: bool,
}

#[derive(Clone, Debug, PartialEq)]
pub enum Confirmation {
    Ack,
    Nack(BasicReturnMessage),
    NotRequested,
}

impl PublisherConfirm {
    pub(crate) fn new(
        inner: PinkySwear<Confirmation>,
        returned_messages: ReturnedMessages,
    ) -> Self {
        Self {
            inner: Some(inner),
            returned_messages,
            used: false,
        }
    }

    pub(crate) fn not_requested(returned_messages: ReturnedMessages) -> Self {
        Self {
            inner: Some(PinkySwear::new_with_data(Confirmation::NotRequested)),
            returned_messages,
            used: false,
        }
    }

    pub fn try_wait(&mut self) -> Option<Confirmation> {
        let confirmation = self.inner.as_ref().expect("inner should only be None after Drop").try_wait()?;
        self.used = true;
        Some(confirmation)
    }

    pub fn wait(&mut self) -> Confirmation {
        self.used = true;
        self.inner.as_ref().expect("inner should only be None after Drop").wait()
    }
}

impl Future for PublisherConfirm {
    type Output = Confirmation;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut this = self.as_mut();
        this.used = true;
        Pin::new(&mut this.inner.as_mut().expect("inner should only be None ater Drop")).poll(cx)
    }
}

impl Drop for PublisherConfirm {
    fn drop(&mut self) {
        if !self.used {
            let promise = PublisherConfirm::new(self.inner.take().expect("inner should only be None after Drop"), self.returned_messages.clone());
            self.returned_messages.register_dropped_confirm(promise);
        }
    }
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]

//! Implementation of a Micro Transport Protocol library,
//! as well as a small client and server.
//!
//! http://www.bittorrent.org/beps/bep_0029.html

extern crate utp;
use std::io::net::ip::{Ipv4Addr, SocketAddr};

fn usage() {
    println!("Usage: utp [-s|-c] <address> <port>");
}

fn main() {
    use utp::{UtpStream, UtpSocket};
    use std::from_str::FromStr;

    // Defaults
    static BUF_SIZE: uint = 4096;
    let mut addr = SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 8080 };

    let args = std::os::args();

	if args.len() != 2 && args.len() != 4 {
		usage();
		return;
	}

    if args.len() == 4 {
        addr = SocketAddr {
            ip:   FromStr::from_str(args.get(2).as_slice()).unwrap(),
            port: FromStr::from_str(args.get(3).as_slice()).unwrap(),
        };
    }

    match args.get(1).as_slice() {
        "-s" => {
            let mut buf = [0, ..BUF_SIZE];
            let mut sock = UtpSocket::bind(addr).unwrap();
            println!("Serving on {}", addr);

            loop {
                match sock.recvfrom(buf) {
                    Ok((_read, _src)) => {}
                    Err(e) => fail!("{}", e),
                }
            }
        }
        "-c" => {
            let mut stream = UtpStream::connect(addr);

            let buf = [0xF, ..BUF_SIZE];
            match stream.write(buf) {
                Ok(_) => {},
                Err(e) => fail!("{}", e),
            }

            let mut buf = [0u8, ..BUF_SIZE];
            match stream.read(buf) {
                Ok(n) => println!("{}", Vec::from_slice(buf.slice(0, n))),
                Err(e) => println!("{}", e),
            }

            match stream.close() {
                Ok(_) => {},
                Err(e) => fail!("{}", e),
            }
            drop(stream);
        }
        _ => usage(),
    }
}
<commit_msg>Use indexing syntax for Vec access.<commit_after>#![feature(macro_rules)]

//! Implementation of a Micro Transport Protocol library,
//! as well as a small client and server.
//!
//! http://www.bittorrent.org/beps/bep_0029.html

extern crate utp;
use std::io::net::ip::{Ipv4Addr, SocketAddr};

fn usage() {
    println!("Usage: utp [-s|-c] <address> <port>");
}

fn main() {
    use utp::{UtpStream, UtpSocket};
    use std::from_str::FromStr;

    // Defaults
    static BUF_SIZE: uint = 4096;
    let mut addr = SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 8080 };

    let args = std::os::args();

	if args.len() != 2 && args.len() != 4 {
		usage();
		return;
	}

    if args.len() == 4 {
        addr = SocketAddr {
            ip:   FromStr::from_str(args[2].as_slice()).unwrap(),
            port: FromStr::from_str(args[3].as_slice()).unwrap(),
        };
    }

    match args[1].as_slice() {
        "-s" => {
            let mut buf = [0, ..BUF_SIZE];
            let mut sock = UtpSocket::bind(addr).unwrap();
            println!("Serving on {}", addr);

            loop {
                match sock.recvfrom(buf) {
                    Ok((_read, _src)) => {}
                    Err(e) => fail!("{}", e),
                }
            }
        }
        "-c" => {
            let mut stream = UtpStream::connect(addr);

            let buf = [0xF, ..BUF_SIZE];
            match stream.write(buf) {
                Ok(_) => {},
                Err(e) => fail!("{}", e),
            }

            let mut buf = [0u8, ..BUF_SIZE];
            match stream.read(buf) {
                Ok(n) => println!("{}", Vec::from_slice(buf.slice(0, n))),
                Err(e) => println!("{}", e),
            }

            match stream.close() {
                Ok(_) => {},
                Err(e) => fail!("{}", e),
            }
            drop(stream);
        }
        _ => usage(),
    }
}
<|endoftext|>