<commit_before>extern crate env_logger;
#[macro_use]
extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate trust_dns;

use hyper::Client;
use hyper::header::Connection;

use serde_json::value::*;

use std::env;
use std::io::Read;

use trust_dns::rr::dns_class::DNSClass;
use trust_dns::rr::record_type::RecordType;
use trust_dns::rr::domain;
use trust_dns::rr::record_data::RData;
use trust_dns::udp::client::Client as DnsClient;

header! { (XAuthKey, "X-Auth-Key") => [String] }
header! { (XAuthEmail, "X-Auth-Email") => [String] }

// TODO(colemickens): none of the implementations handle paging properly

fn cloudflare_api(client: &hyper::client::Client, url: &str, body: Option<&str>) -> Result<Value, String> {
    let cloudflare_apikey = env::var("CLOUDFLARE_APIKEY").expect("missing apikey");
    let cloudflare_email = env::var("CLOUDFLARE_EMAIL").expect("missing email");

    let builder = match body {
        Some(body) => { client.put(url).body(body) }
        None => { client.get(url) }
    };

    let mut response = builder
        .header(Connection::close())
        .header(XAuthKey(cloudflare_apikey.to_owned()))
        .header(XAuthEmail(cloudflare_email.to_owned()))
        .send().unwrap();

    if response.status != hyper::status::StatusCode::Ok {
        println!("she's gonna blow! {}", response.status);
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        println!("{}", body);
        println!("exit early"); // TODO(colemickens): fix
    }

    let response_json: Value = serde_json::from_iter(response.by_ref().bytes()).unwrap();

    let status = response_json
        .as_object().unwrap()
        .get("success").unwrap()
        .as_boolean().unwrap();
    if !status {
        println!("request sent 200, but cloudflare reported !success"); // TODO(colemickens): this is inaccurate due to above "fix" todo
    }

    Ok(response_json)
}

fn get_current_ip() -> Result<String, ()> {
    let client = DnsClient::new(("8.8.8.8").parse().unwrap()).unwrap();

    let name = domain::Name::with_labels(vec![
        "o-o".to_string(),
        "myaddr".to_string(),
        "l".to_string(),
        "google".to_string(),
        "com".to_string()]);
    let response = client.query(name.clone(), DNSClass::IN, RecordType::TXT).unwrap();

    let record = &response.get_answers()[0];
    if let &RData::TXT{ ref txt_data } = record.get_rdata() {
        return Ok(txt_data[0].to_string());
    } else {
        return Err(())
    }
}

fn main() {
    env_logger::init().unwrap();

    let current_ip = get_current_ip().expect("must have current ip");
    let client = Client::new();
    let cloudflare_records_env = env::var("CLOUDFLARE_RECORDS").expect("missing records");
    let cloudflare_records: Vec<&str> = cloudflare_records_env.split(|c: char| c == ',').collect();

    let zones_url = "https://api.cloudflare.com/client/v4/zones";
    let zones_json = cloudflare_api(&client, zones_url, None).unwrap();

    let zone_ids = zones_json
        .as_object().unwrap()
        .get("result").unwrap()
        .as_array().unwrap()
        .iter()
        .map(|ref zone_node|
            zone_node.find("id").unwrap()
            .as_string().unwrap());

    for zone_id in zone_ids {
        let records_url = format!("https://api.cloudflare.com/client/v4/zones/{}/dns_records", zone_id);
        let records_json = cloudflare_api(&client, &*records_url, None).unwrap();

        let records = records_json
            .as_object().unwrap()
            .get("result").unwrap()
            .as_array().unwrap()
            .iter();

        for record in records {
            let record_id = record.find("id").unwrap().as_string().unwrap();
            let record_type = record.find("type").unwrap().as_string().unwrap();
            let record_name = record.find("name").unwrap().as_string().unwrap();
            let record_content = record.find("content").unwrap().as_string().unwrap();

            if record_type == "A" && cloudflare_records.contains(&record_name) {
                if record_content == current_ip
                {
                    println!("{} skipped, up to date", record_name);
                    continue;
                } else {
                    println!("{} needs update... ({} != {})", record_name, record_content, current_ip);

                    let record_url = format!("https://api.cloudflare.com/client/v4/zones/{}/dns_records/{}", zone_id, record_id);
                    let record_update_body = format!(
                        "{{ \"id\": \"{}\", \"name\": \"{}\", \"content\": \"{}\", \"type\": \"{}\" }}",
                        record_id,
                        record_name,
                        current_ip,
                        record_type);
                    println!("{}", record_update_body);
                    cloudflare_api(&client, &*record_url, Some(&*record_update_body)).unwrap();

                    println!("{} updated!", record_name);
                }
            }
        }
    }
}
<commit_msg>cleanup<commit_after>#[macro_use]
extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate trust_dns;

use hyper::Client;
use hyper::header::Connection;

use serde_json::value::*;

use std::env;
use std::io;
use std::io::prelude::*;

use trust_dns::rr::dns_class::DNSClass;
use trust_dns::rr::record_type::RecordType;
use trust_dns::rr::domain;
use trust_dns::rr::record_data::RData;
use trust_dns::udp::client::Client as DnsClient;

header! { (XAuthKey, "X-Auth-Key") => [String] }
header! { (XAuthEmail, "X-Auth-Email") => [String] }

// TODO(colemickens): none of the implementations handle paging properly

// overloaded function. no body is treated as a get, body is treated as a put
fn cloudflare_api(client: &hyper::client::Client, url: &str, body: Option<&str>) -> Result<Value, String> {
    let cloudflare_apikey = env::var("CLOUDFLARE_APIKEY").expect("missing apikey");
    let cloudflare_email = env::var("CLOUDFLARE_EMAIL").expect("missing email");

    let builder = match body {
        Some(body) => { client.put(url).body(body) }
        None => { client.get(url) }
    };

    let mut response = builder
        .header(Connection::close())
        .header(XAuthKey(cloudflare_apikey.to_owned()))
        .header(XAuthEmail(cloudflare_email.to_owned()))
        .send().unwrap();

    if response.status != hyper::status::StatusCode::Ok {
        println!("she's gonna blow! {}", response.status);
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        return Err(body);
    }

    let response_json: Value = serde_json::from_iter(response.by_ref().bytes()).unwrap();

    let success = response_json
        .as_object().unwrap()
        .get("success").unwrap()
        .as_boolean().unwrap();
    if !success {
        println!("response status={}, but cloudflare success={}", response.status, success);
    }

    Ok(response_json)
}

fn get_current_ip() -> Result<String, ()> {
    let client = DnsClient::new(("8.8.8.8").parse().unwrap()).unwrap();

    let name = domain::Name::with_labels(vec![
        "o-o".to_string(),
        "myaddr".to_string(),
        "l".to_string(),
        "google".to_string(),
        "com".to_string()]);
    let response = client.query(name.clone(), DNSClass::IN, RecordType::TXT).unwrap();

    let record = &response.get_answers()[0];
    if let &RData::TXT{ ref txt_data } = record.get_rdata() {
        return Ok(txt_data[0].to_string());
    } else {
        return Err(())
    }
}

fn main() {
    let current_ip = get_current_ip().expect("must have current ip");
    let client = Client::new();
    let cloudflare_records_env = env::var("CLOUDFLARE_RECORDS").expect("missing records");
    let cloudflare_records: Vec<&str> = cloudflare_records_env.split(|c: char| c == ',').collect();

    let zones_url = "https://api.cloudflare.com/client/v4/zones";
    let zones_json = cloudflare_api(&client, zones_url, None).unwrap();

    let zone_ids = zones_json
        .as_object().unwrap()
        .get("result").unwrap()
        .as_array().unwrap()
        .iter()
        .map(|ref zone_node|
            zone_node.find("id").unwrap()
            .as_string().unwrap());

    for zone_id in zone_ids {
        let records_url = format!("https://api.cloudflare.com/client/v4/zones/{}/dns_records", zone_id);
        let records_json = cloudflare_api(&client, &*records_url, None).unwrap();

        let records = records_json
            .as_object().unwrap()
            .get("result").unwrap()
            .as_array().unwrap()
            .iter();

        for record in records {
            let record_id = record.find("id").unwrap().as_string().unwrap();
            let record_type = record.find("type").unwrap().as_string().unwrap();
            let record_name = record.find("name").unwrap().as_string().unwrap();
            let record_content = record.find("content").unwrap().as_string().unwrap();

            if record_content == current_ip
            {
                println!("{} skipped, up to date", record_name);
                continue;
            }

            if record_type == "A" && cloudflare_records.contains(&record_name) {
                print!("{} ({} -> {})... ", record_name, record_content, current_ip);
                io::stdout().flush().ok();

                let record_url = format!(
                    "https://api.cloudflare.com/client/v4/zones/{}/dns_records/{}",
                    zone_id,
                    record_id);
                let record_update_body = format!(
                    r#"{{ "id": "{}", "name": "{}", "content": "{}", "type": "{}" }}"#,
                    record_id,
                    record_name,
                    current_ip,
                    record_type);
                cloudflare_api(&client, &*record_url, Some(&*record_update_body)).unwrap();

                println!("done")
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate clap;
extern crate ini;
extern crate sha1;

use std::ffi::{OsStr, OsString};
use std::fs::{self, File};
use std::io::{self, Read, Write, BufWriter};
use std::iter::FromIterator;
use std::path::{Path, PathBuf};

use ini::Ini;
use ini::ini::Error as IniError;

type Sha1DigestBytes = [u8; 20];
type ModList = Vec<String>;
type MediaSet = Vec<Asset>;

struct Asset {
	path: PathBuf,
	hash: Sha1DigestBytes,
}

impl Asset {
	pub fn new(pb: PathBuf, h: Sha1DigestBytes) -> Self {
		Asset {
			path: pb,
			hash: h,
		}
	}
}

impl PartialEq for Asset {
	fn eq(&self, other: &Self) -> bool {
		self.hash == other.hash
	}
}

enum AssetCopyMode {
	Symlink,
	Hardlink,
	Copy,
	None,
}

fn to_hex(input: &[u8]) -> String {
	String::from_iter(input.iter().map(|b| format!("{:02x}", b)))
}

fn make_absolute(path: &Path) -> PathBuf {
	if path.is_absolute() {
		path.to_path_buf()
	} else {
		std::env::current_dir()
			.and_then(|cd| Ok(cd.join(path)))
			.or_else(|_err| -> io::Result<_> {
				Ok(path.to_path_buf())
			})
			.unwrap()
	}
}

fn hash_file(path: &Path) -> io::Result<Sha1DigestBytes> {
	let mut buf = [0u8; 8192];
	let mut hash = sha1::Sha1::new();
	let mut file = File::open(&path)?;
	loop {
		match file.read(&mut buf) {
			Ok(0) => break,
			Ok(len) => hash.update(&buf[..len]),
			Err(e) => return Err(e),
		}
	}
	Ok(hash.digest().bytes())
}

fn search_media_dir(ms: &mut MediaSet, path: &Path) -> io::Result<()> {
	for entry in path.read_dir()? {
		let pb = entry?.path();
		if pb.is_file() {
			let h = hash_file(pb.as_path())?;
			ms.push(Asset::new(pb, h));
		}
	}
	Ok(())
}

fn search_mod_dir(ms: &mut MediaSet, path: &Path) -> io::Result<()> {
	static MEDIA_DIRS: &'static [&'static str] = &["textures", "models", "sounds"];
	for media_dir in MEDIA_DIRS {
		let media_pb = path.join(media_dir);
		if media_pb.is_dir() {
			search_media_dir(ms, media_pb.as_path())?;
		}
	}
	Ok(())
}

fn search_modpack_dir(ms: &mut MediaSet, path: &Path, mods: Option<&ModList>) -> io::Result<()> {
	for entry in path.read_dir()? {
		let entry_path = entry?.path();
		if !entry_path.is_dir() {
			continue;
		} else if entry_path.join("modpack.txt").exists() {
			search_modpack_dir(ms, entry_path.as_path(), mods)?;
		} else if entry_path.join("init.lua").exists() {
			if let Some(mod_list) = mods {
				let mod_name = &entry_path.file_name()
					.expect("Mod directory has no name!")
					.to_str()
					.expect("Mod directory name is not valid Unicode")
					.to_string();
				if !mod_list.contains(mod_name) {
					continue;
				}
			}
			search_mod_dir(ms, entry_path.as_path())?;
		}
		// Otherwise it's probably a VCS directory or something similar
	}
	Ok(())
}

fn write_index(ms: &MediaSet, path: &Path) -> io::Result<()> {
	let file = File::create(&path)?;
	let mut writer = BufWriter::new(file);
	writer.write_all(b"MTHS\x00\x01")?;
	for asset in ms {
		writer.write_all(&asset.hash)?;
	}
	Ok(())
}

fn copy_assets(ms: &MediaSet, path: &Path, mode: AssetCopyMode) -> io::Result<()> {
	fn copy_no_result<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		fs::copy(src, dst).map(|_| ())
	}

	#[cfg(unix)]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		std::os::unix::fs::symlink(src, dst)
	}

	#[cfg(windows)]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		std::os::windows::fs::symlink_file(src, dst)
	}

	#[cfg(not(any(unix, windows)))]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		Err(io::Error::new(io::ErrorKind::Other,
				"Symlinking not supported on this platform!"))
	}

	let copy_func = match mode {
		AssetCopyMode::Symlink => symlink_file,
		AssetCopyMode::Hardlink => fs::hard_link,
		AssetCopyMode::Copy => copy_no_result,
		AssetCopyMode::None => return Ok(()),
	};

	for asset in ms {
		copy_func(&asset.path, path.join(to_hex(&asset.hash)))?;
	}
	Ok(())
}

fn get_mod_list(path: &Path) -> Result<ModList, IniError> {
	let world_mt = Ini::load_from_file(path.join("world.mt"))?;
	let main_sec = world_mt.general_section();

	let mut list: ModList = vec![];
	for (key, value) in main_sec {
		if !key.starts_with("load_mod_") || value != "true" {
			continue;
		}
		let (_, mod_name) = key.split_at(9);
		list.push(mod_name.to_string());
	}
	Ok(list)
}

fn get_args<'a>() -> clap::ArgMatches<'a> {
	use clap::{App, Arg, ArgGroup};

	fn check_dir(s: &OsStr) -> Result<(), OsString> {
		let p = make_absolute(Path::new(&s));
		if p.is_dir() {
			return Ok(())
		}
		if let Some(parent) = p.parent() {
			if parent.is_dir() {
				return Ok(())
			}
		}
		return Err("Invalid path.".into())
	}

	let app = clap_app! { @app (app_from_crate!())
		(version_short: "v")

		(@arg mod_paths: [PATHS] ... validator_os(check_dir) "Additional mod paths to search.")

		(@arg out:   -o --out   <PATH> validator_os(check_dir) "Path to the output directory.")
		(@arg world: -w --world <PATH> validator_os(check_dir) "Path to the world directory.")
		(@arg game:  -g --game  <PATH> validator_os(check_dir) "Path to the game directory.")

		// Group these together with display_order
		(@arg copy: -c --copy display_order(1000) "Copy assets to output folder.")
		// Symlink added below if applicable
		(@arg hardlink: -l --hardlink display_order(1000) "Hard link assets to output folder.")
	};

	// Add symlink option if supported
	#[cfg(not(any(unix, windows)))]
	let add_symlink_arg = |app| app;

	#[cfg(any(unix, windows))]
	fn add_symlink_arg<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
		app.arg(Arg::with_name("symlink")
			.short("s")
			.long("symlink")
			.display_order(1000)
			.help("Symbolically link assets to output folder."))
	}

	add_symlink_arg(app)
		// Link group has to be added manually because the
		// symlink argument is added conditionally.
		.group(ArgGroup::with_name("link")
			.args(&["copy", "symlink", "hardlink"]))

		.get_matches()
}

fn run(args: clap::ArgMatches) -> i32 {
	macro_rules! handle_result {
		( $x:expr, $m:expr ) => {
			match $x {
				Ok(val) => val,
				Err(e) => {
					println!($m, e);
					return 1;
				}
			};
		}
	}

	// These unwraps are safe since the values are required
	// and clap will exit if the value is missing.
	let out_opt = args.value_of_os("out").unwrap();
	let out_path = Path::new(&out_opt);
	let world_opt = args.value_of_os("world").unwrap();
	let world_path = Path::new(&world_opt);
	let game_opt = args.value_of_os("game").unwrap();
	let game_path = Path::new(&game_opt);

	let copy_type = if args.is_present("copy") {
			AssetCopyMode::Copy
		} else if args.is_present("symlink") {
			AssetCopyMode::Symlink
		} else if args.is_present("hardlink") {
			AssetCopyMode::Hardlink
		} else {
			AssetCopyMode::None
		};

	let mut ms = MediaSet::new();
	let mods = handle_result!(get_mod_list(world_path), "Error getting mod list: {}");

	// Search world mods.
	let worldmods_path = world_path.join("worldmods");
	if worldmods_path.exists() {
		handle_result!(search_modpack_dir(&mut ms, worldmods_path.as_path(), Some(&mods)),
				"Error searching world mods: {}");
	}

	// Search game mods.
	// Note: Game mods can not currently be disabled.
	handle_result!(search_modpack_dir(&mut ms, game_path.join("mods").as_path(), None),
			"Error searching game mods: {}");

	if let Some(mod_paths) = args.values_of_os("mod_paths") {
		for mod_path in mod_paths {
			handle_result!(search_modpack_dir(&mut ms,
					Path::new(&mod_path),
					Some(&mods)),
					"Error searching other mods: {}");
		}
	}

	// Deduplicate list.  Otherwise linking will fail and the index will
	// be unnecessarily large.
	ms.sort_by(|a, b| a.hash.cmp(&b.hash));
	ms.dedup();

	if !out_path.exists() {
		handle_result!(fs::create_dir(out_path), "{}");
	}

	handle_result!(write_index(&ms, out_path.join("index.mth").as_path()),
			"Error writing asset index: {}");

	handle_result!(copy_assets(&ms, out_path, copy_type),
			"Error linking assets: {}");

	0
}

fn main() {
	// Rust (stable) only supports setting the exit code by calling this
	// function, but it doesn't do cleanup, so it can't be run until all
	// resources have already been destroyed.
	std::process::exit(run(get_args()))
}
<commit_msg>Discriminate between new and existing directories in arguments<commit_after>#[macro_use] extern crate clap;
extern crate ini;
extern crate sha1;

use std::ffi::{OsStr, OsString};
use std::fs::{self, File};
use std::io::{self, Read, Write, BufWriter};
use std::iter::FromIterator;
use std::path::{Path, PathBuf};

use ini::Ini;
use ini::ini::Error as IniError;

type Sha1DigestBytes = [u8; 20];
type ModList = Vec<String>;
type MediaSet = Vec<Asset>;

struct Asset {
	path: PathBuf,
	hash: Sha1DigestBytes,
}

impl Asset {
	pub fn new(pb: PathBuf, h: Sha1DigestBytes) -> Self {
		Asset {
			path: pb,
			hash: h,
		}
	}
}

impl PartialEq for Asset {
	fn eq(&self, other: &Self) -> bool {
		self.hash == other.hash
	}
}

enum AssetCopyMode {
	Symlink,
	Hardlink,
	Copy,
	None,
}

fn to_hex(input: &[u8]) -> String {
	String::from_iter(input.iter().map(|b| format!("{:02x}", b)))
}

fn make_absolute(path: &Path) -> PathBuf {
	if path.is_absolute() {
		path.to_path_buf()
	} else {
		std::env::current_dir()
			.and_then(|cd| Ok(cd.join(path)))
			.or_else(|_err| -> io::Result<_> {
				Ok(path.to_path_buf())
			})
			.unwrap()
	}
}

fn hash_file(path: &Path) -> io::Result<Sha1DigestBytes> {
	let mut buf = [0u8; 8192];
	let mut hash = sha1::Sha1::new();
	let mut file = File::open(&path)?;
	loop {
		match file.read(&mut buf) {
			Ok(0) => break,
			Ok(len) => hash.update(&buf[..len]),
			Err(e) => return Err(e),
		}
	}
	Ok(hash.digest().bytes())
}

fn search_media_dir(ms: &mut MediaSet, path: &Path) -> io::Result<()> {
	for entry in path.read_dir()? {
		let pb = entry?.path();
		if pb.is_file() {
			let h = hash_file(pb.as_path())?;
			ms.push(Asset::new(pb, h));
		}
	}
	Ok(())
}

fn search_mod_dir(ms: &mut MediaSet, path: &Path) -> io::Result<()> {
	static MEDIA_DIRS: &'static [&'static str] = &["textures", "models", "sounds"];
	for media_dir in MEDIA_DIRS {
		let media_pb = path.join(media_dir);
		if media_pb.is_dir() {
			search_media_dir(ms, media_pb.as_path())?;
		}
	}
	Ok(())
}

fn search_modpack_dir(ms: &mut MediaSet, path: &Path, mods: Option<&ModList>) -> io::Result<()> {
	for entry in path.read_dir()? {
		let entry_path = entry?.path();
		if !entry_path.is_dir() {
			continue;
		} else if entry_path.join("modpack.txt").exists() {
			search_modpack_dir(ms, entry_path.as_path(), mods)?;
		} else if entry_path.join("init.lua").exists() {
			if let Some(mod_list) = mods {
				let mod_name = &entry_path.file_name()
					.expect("Mod directory has no name!")
					.to_str()
					.expect("Mod directory name is not valid Unicode")
					.to_string();
				if !mod_list.contains(mod_name) {
					continue;
				}
			}
			search_mod_dir(ms, entry_path.as_path())?;
		}
		// Otherwise it's probably a VCS directory or something similar
	}
	Ok(())
}

fn write_index(ms: &MediaSet, path: &Path) -> io::Result<()> {
	let file = File::create(&path)?;
	let mut writer = BufWriter::new(file);
	writer.write_all(b"MTHS\x00\x01")?;
	for asset in ms {
		writer.write_all(&asset.hash)?;
	}
	Ok(())
}

fn copy_assets(ms: &MediaSet, path: &Path, mode: AssetCopyMode) -> io::Result<()> {
	fn copy_no_result<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		fs::copy(src, dst).map(|_| ())
	}

	#[cfg(unix)]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		std::os::unix::fs::symlink(src, dst)
	}

	#[cfg(windows)]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		std::os::windows::fs::symlink_file(src, dst)
	}

	#[cfg(not(any(unix, windows)))]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		Err(io::Error::new(io::ErrorKind::Other,
				"Symlinking not supported on this platform!"))
	}

	let copy_func = match mode {
		AssetCopyMode::Symlink => symlink_file,
		AssetCopyMode::Hardlink => fs::hard_link,
		AssetCopyMode::Copy => copy_no_result,
		AssetCopyMode::None => return Ok(()),
	};

	for asset in ms {
		copy_func(&asset.path, path.join(to_hex(&asset.hash)))?;
	}
	Ok(())
}

fn get_mod_list(path: &Path) -> Result<ModList, IniError> {
	let world_mt = Ini::load_from_file(path.join("world.mt"))?;
	let main_sec = world_mt.general_section();

	let mut list: ModList = vec![];
	for (key, value) in main_sec {
		if !key.starts_with("load_mod_") || value != "true" {
			continue;
		}
		let (_, mod_name) = key.split_at(9);
		list.push(mod_name.to_string());
	}
	Ok(list)
}

fn get_args<'a>() -> clap::ArgMatches<'a> {
	use clap::{App, Arg, ArgGroup};

	fn check_new_dir(s: &OsStr) -> Result<(), OsString> {
		let p = make_absolute(Path::new(&s));
		if p.is_dir() {
			return Ok(())
		}
		if let Some(parent) = p.parent() {
			if parent.is_dir() {
				return Ok(())
			}
		}
		return Err("Invalid path.".into())
	}

	fn check_existing_dir(s: &OsStr) -> Result<(), OsString> {
		if make_absolute(Path::new(&s)).is_dir() {
			Ok(())
		} else {
			Err("Invalid path.".into())
		}
	}

	let app = clap_app! { @app (app_from_crate!())
		(version_short: "v")

		(@arg mod_paths: [PATHS] ... validator_os(check_existing_dir) "Additional mod paths to search.")

		(@arg out:   -o --out   <PATH> validator_os(check_new_dir) "Path to the output directory.")
		(@arg world: -w --world <PATH> validator_os(check_existing_dir) "Path to the world directory.")
		(@arg game:  -g --game  <PATH> validator_os(check_existing_dir) "Path to the game directory.")

		// Group these together with display_order
		(@arg copy: -c --copy display_order(1000) "Copy assets to output folder.")
		// Symlink added below if applicable
		(@arg hardlink: -l --hardlink display_order(1000) "Hard link assets to output folder.")
	};

	// Add symlink option if supported
	#[cfg(not(any(unix, windows)))]
	let add_symlink_arg = |app| app;

	#[cfg(any(unix, windows))]
	fn add_symlink_arg<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
		app.arg(Arg::with_name("symlink")
			.short("s")
			.long("symlink")
			.display_order(1000)
			.help("Symbolically link assets to output folder."))
	}

	add_symlink_arg(app)
		// Link group has to be added manually because the
		// symlink argument is added conditionally.
		.group(ArgGroup::with_name("link")
			.args(&["copy", "symlink", "hardlink"]))

		.get_matches()
}

fn run(args: clap::ArgMatches) -> i32 {
	macro_rules! handle_result {
		( $x:expr, $m:expr ) => {
			match $x {
				Ok(val) => val,
				Err(e) => {
					println!($m, e);
					return 1;
				}
			};
		}
	}

	// These unwraps are safe since the values are required
	// and clap will exit if the value is missing.
	let out_opt = args.value_of_os("out").unwrap();
	let out_path = Path::new(&out_opt);
	let world_opt = args.value_of_os("world").unwrap();
	let world_path = Path::new(&world_opt);
	let game_opt = args.value_of_os("game").unwrap();
	let game_path = Path::new(&game_opt);

	let copy_type = if args.is_present("copy") {
			AssetCopyMode::Copy
		} else if args.is_present("symlink") {
			AssetCopyMode::Symlink
		} else if args.is_present("hardlink") {
			AssetCopyMode::Hardlink
		} else {
			AssetCopyMode::None
		};

	let mut ms = MediaSet::new();
	let mods = handle_result!(get_mod_list(world_path), "Error getting mod list: {}");

	// Search world mods.
	let worldmods_path = world_path.join("worldmods");
	if worldmods_path.exists() {
		handle_result!(search_modpack_dir(&mut ms, worldmods_path.as_path(), Some(&mods)),
				"Error searching world mods: {}");
	}

	// Search game mods.
	// Note: Game mods can not currently be disabled.
	handle_result!(search_modpack_dir(&mut ms, game_path.join("mods").as_path(), None),
			"Error searching game mods: {}");

	if let Some(mod_paths) = args.values_of_os("mod_paths") {
		for mod_path in mod_paths {
			handle_result!(search_modpack_dir(&mut ms,
					Path::new(&mod_path),
					Some(&mods)),
					"Error searching other mods: {}");
		}
	}

	// Deduplicate list.  Otherwise linking will fail and the index will
	// be unnecessarily large.
	ms.sort_by(|a, b| a.hash.cmp(&b.hash));
	ms.dedup();

	if !out_path.exists() {
		handle_result!(fs::create_dir(out_path), "{}");
	}

	handle_result!(write_index(&ms, out_path.join("index.mth").as_path()),
			"Error writing asset index: {}");

	handle_result!(copy_assets(&ms, out_path, copy_type),
			"Error linking assets: {}");

	0
}

fn main() {
	// Rust (stable) only supports setting the exit code by calling this
	// function, but it doesn't do cleanup, so it can't be run until all
	// resources have already been destroyed.
	std::process::exit(run(get_args()))
}
<|endoftext|>


<commit_before>#[crate_id = "rboy"];

extern crate native;
extern crate green;
extern crate extra;
extern crate getopts;
extern crate sync;
extern crate time;
extern crate sdl;

use cpu::CPU;
use sync::DuplexStream;
use sync::RWArc;

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;

static SCALE: int = 4;

#[start]
fn start(argc: int, argv: **u8) -> int { green::start(argc, argv, main) }

fn main() {
	let args: ~[~str] = std::os::args();
	let program = args[0].clone() + " <filename>";

	let opts = ~[ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f.to_err_msg()); return }
	};

	let filename: ~str = if !matches.free.is_empty() {
		matches.free[0].clone()
	} else {
		println!("{}", getopts::usage(program, opts));
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE, 144*SCALE, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = DuplexStream::new();
	let rawscreen = ~[0x00u8,.. 160*144*3];
	let arc = RWArc::new(rawscreen);
	let arc2 = arc.clone();
	native::task::spawn(proc() cpuloop(&cpustream, arc2, filename, &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			std::comm::Disconnected => { break 'main },
			std::comm::Data(_) => recalculate_screen(screen, &arc),
			std::comm::Empty => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &RWArc<~[u8]>) {
	arc.read(|data|
		for y in range(0, 144) {
			for x in range(0, 160) {
				screen.fill_rect(
					Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
					sdl::video::RGB(data[y*160*3 + x*3 + 0],
					                data[y*160*3 + x*3 + 1],
					                data[y*160*3 + x*3 + 2])
				);
			}
		}
	);
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: RWArc<~[u8]>, filename: ~str, matches: &getopts::Matches) {
	let mut c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let mut periodic = timer.periodic(8);

	let waitticks = (4194.304 * 8.0) as uint;

	let mut ticks = 0;
	loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				arc.write(|data|
					for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				);
				channel.try_send(0);
			}
		}
		ticks -= waitticks;
		periodic.recv();

		match channel.try_recv() {
			std::comm::Data(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => periodic = timer.periodic(1),
				SlowDown => periodic = timer.periodic(8),
			},
			std::comm::Empty => {},
			std::comm::Disconnected => { break },
		};
	}
}
<commit_msg>Slowed down the emulator a bit<commit_after>#[crate_id = "rboy"];

extern crate native;
extern crate green;
extern crate extra;
extern crate getopts;
extern crate sync;
extern crate time;
extern crate sdl;

use cpu::CPU;
use sync::DuplexStream;
use sync::RWArc;

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;

static SCALE: int = 4;

#[start]
fn start(argc: int, argv: **u8) -> int { green::start(argc, argv, main) }

fn main() {
	let args: ~[~str] = std::os::args();
	let program = args[0].clone() + " <filename>";

	let opts = ~[ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f.to_err_msg()); return }
	};

	let filename: ~str = if !matches.free.is_empty() {
		matches.free[0].clone()
	} else {
		println!("{}", getopts::usage(program, opts));
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE, 144*SCALE, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = DuplexStream::new();
	let rawscreen = ~[0x00u8,.. 160*144*3];
	let arc = RWArc::new(rawscreen);
	let arc2 = arc.clone();
	native::task::spawn(proc() cpuloop(&cpustream, arc2, filename, &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			std::comm::Disconnected => { break 'main },
			std::comm::Data(_) => recalculate_screen(screen, &arc),
			std::comm::Empty => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &RWArc<~[u8]>) {
	arc.read(|data|
		for y in range(0, 144) {
			for x in range(0, 160) {
				screen.fill_rect(
					Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
					sdl::video::RGB(data[y*160*3 + x*3 + 0],
					                data[y*160*3 + x*3 + 1],
					                data[y*160*3 + x*3 + 2])
				);
			}
		}
	);
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: RWArc<~[u8]>, filename: ~str, matches: &getopts::Matches) {
	let mut c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let mut periodic = timer.periodic(8);

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				arc.write(|data|
					for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				);
				channel.try_send(0);
			}
		}
		ticks -= waitticks;
		periodic.recv();

		match channel.try_recv() {
			std::comm::Data(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => periodic = timer.periodic(1),
				SlowDown => periodic = timer.periodic(8),
			},
			std::comm::Empty => {},
			std::comm::Disconnected => { break },
		};
	}
}
<|endoftext|>


<commit_before>#![feature(convert)]

extern crate byteorder;

use std::fs::File;
use std::io::Read;
use std::io::{Result, Error};
use std::io::ErrorKind::Other;

use byteorder::{LittleEndian, BigEndian, ByteOrder};

const LABEL_SCAN_SECTORS: usize = 4;
const MYPATH: &'static str = "/dev/mapper/luks-291e9250-200e-4c36-8e5e-66aa257669ed";

#[derive(Debug)]
struct Label {
    id: [u8; 8],
    sector: u64,
    crc: u32,
    offset: u32,
    type_: [u8; 8],
}

fn array_from_slice(slc: &[u8]) -> [u8; 8] {
    let mut array = [0u8; 8];
    for (&x, p) in slc.iter().zip(array.iter_mut()) {
        *p = x;
    }
    array
}

fn find_label() -> Result<Label> {

    let mut f = try!(File::open(MYPATH));

    let mut buf = vec![0; LABEL_SCAN_SECTORS * 512];

    try!(f.read(&mut buf));

    let label: Vec<u8> = "LABELONE".bytes().collect();

    for x in 0..LABEL_SCAN_SECTORS {
        if &buf[x*512..x*512+8] == label.as_slice() {
            let start = x*512;

            return Ok(Label{
                id: array_from_slice(&buf[start..start+8]),
                sector: LittleEndian::read_u64(&buf[start+8..start+16]),
                crc: LittleEndian::read_u32(&buf[start+16..start+20]),
                offset: LittleEndian::read_u32(&buf[start+20..start+24]),
                type_: array_from_slice(&buf[start+24..start+32]),
            })
        }
    }

    Err(Error::new(Other, "bad"))
}

fn main() {

    println!("{:?}", find_label());

}
<commit_msg>parse PV header<commit_after>
extern crate byteorder;

use std::fs::File;
use std::io::Read;
use std::io::{Result, Error};
use std::io::ErrorKind::Other;

use byteorder::{LittleEndian, ByteOrder};

const LABEL_SCAN_SECTORS: usize = 4;
const MYPATH: &'static str = "/dev/mapper/luks-291e9250-200e-4c36-8e5e-66aa257669ed";
const ID_LEN: usize = 32;

#[derive(Debug)]
struct Label {
    id: String,
    sector: u64,
    crc: u32,
    offset: u32,
    type_: String,
}

#[derive(Debug)]
struct DataArea {
    offset: u64,
    size: u64,
}

#[derive(Debug)]
struct MetadataArea {
    offset: u64,
    size: u64,
}

#[derive(Debug)]
struct BootloaderArea {
    offset: u64,
    size: u64,
}

#[derive(Debug)]
struct PvHeader {
    uuid: String,
    size: u64, // in bytes
    ext_version: u32,
    ext_flags: u32,
    data_areas: Vec<DataArea>,
    metadata_areas: Vec<MetadataArea>,
    bootloader_areas: Vec<BootloaderArea>,
}

fn get_label(buf: &[u8]) -> Result<Label> {
    for x in 0..LABEL_SCAN_SECTORS {
        if &buf[x*512..x*512+8] == b"LABELONE" {
            let start = x*512;

            return Ok(Label{
                id: String::from_utf8_lossy(&buf[start..start+8]).into_owned(),
                sector: LittleEndian::read_u64(&buf[start+8..start+16]),
                crc: LittleEndian::read_u32(&buf[start+16..start+20]),
                offset: LittleEndian::read_u32(&buf[start+20..start+24]) + start as u32,
                type_: String::from_utf8_lossy(&buf[start+24..start+32]).into_owned(),
            })
        }
    }

    Err(Error::new(Other, "bad"))
}

fn get_pv_header(buf: &[u8]) -> Result<PvHeader> {

    let mut da_buf = &buf[ID_LEN+8..];
    let mut da_vec = Vec::new();

    loop {
        let da_off = LittleEndian::read_u64(&da_buf[..8]);
        if da_off == 0 { break; }

        da_vec.push(DataArea {
            offset: da_off,
            size: LittleEndian::read_u64(&da_buf[8..16]),
        });

        da_buf = &da_buf[16..];
    }

    let mut md_vec = Vec::new();

    // metadata list is after a null entry for the data areas
    da_buf = &da_buf[16..];

    loop {
        let da_off = LittleEndian::read_u64(&da_buf[..8]);
        if da_off == 0 { break; }

        md_vec.push(MetadataArea {
            offset: da_off,
            size: LittleEndian::read_u64(&da_buf[8..16]),
        });

        da_buf = &da_buf[16..];
    }

    // pv extension is after a null entry for the metadata areas
    da_buf = &da_buf[16..];

    let ext_version = LittleEndian::read_u32(&da_buf[..4]);
    let mut ext_flags = 0;
    let mut ba_vec = Vec::new();

    if ext_version != 0 {
        ext_flags = LittleEndian::read_u32(&da_buf[4..8]);

        da_buf = &da_buf[8..];

        loop {
            let da_off = LittleEndian::read_u64(&da_buf[..8]);
            if da_off == 0 { break; }

            ba_vec.push(BootloaderArea {
                offset: da_off,
                size: LittleEndian::read_u64(&da_buf[8..16]),
            });

            da_buf = &da_buf[16..];
        }
    }

    Ok(PvHeader{
        uuid: String::from_utf8_lossy(&buf[..ID_LEN]).into_owned(),
        size: LittleEndian::read_u64(&buf[ID_LEN..ID_LEN+8]),
        ext_version: ext_version,
        ext_flags: ext_flags,
        data_areas: da_vec,
        metadata_areas: md_vec,
        bootloader_areas: ba_vec,
    })
}

fn find_stuff(path: &str) -> Result<Label> {

    let mut f = try!(File::open(path));

    let mut buf = vec![0; LABEL_SCAN_SECTORS * 512];

    try!(f.read(&mut buf));

    let label = try!(get_label(&buf));

    let pvheader = try!(get_pv_header(&buf[label.offset as usize..]));

    println!("pvheader {:?}", pvheader);

    return Ok(label);
}

fn main() {

    let label = find_stuff(MYPATH).unwrap();

    println!("{:?}", label);

}
<|endoftext|>


<commit_before>#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct Locale {
    rank: u8,
    file: u8
}

impl Locale {
    fn bit_index(&self) -> u32 {
        (8u32 * self.rank as u32) + self.file as u32
    }

    fn pinpoint(&self) -> Bitboard {
        Bitboard(2u64.pow(self.bit_index()))
    }
}

#[derive(Eq,PartialEq,Debug)]
struct Bitboard(u64);

impl Bitboard {
    fn union(&self, other: Bitboard) -> Bitboard {
        let Bitboard(our_bits) = *self;
        let Bitboard(their_bits) = other;
        Bitboard(our_bits | their_bits)
    }

    fn intersection(&self, other: Bitboard) -> Bitboard {
        let Bitboard(our_bits) = *self;
        let Bitboard(their_bits) = other;
        Bitboard(our_bits & their_bits)
    }

    fn invert(&self) -> Bitboard {
        let Bitboard(our_bits) = *self;
        Bitboard(our_bits ^ our_bits)
    }

    fn alight(&self, station: Locale) -> Bitboard {
        // classic `|= (1 << n)` would probably be more efficient, huh &c.
        self.union(station.pinpoint())
    }

    fn quench(&self, station: Locale) -> Bitboard {
        self.intersection(station.pinpoint().invert())
    }

    fn query(&self, station: Locale) -> bool {
        let Bitboard(our_bits) = *self;
        let Bitboard(beacon_bits) = station.pinpoint();
        (our_bits & beacon_bits) != 0
    }
}

enum Teams { Blue, Orange }

struct GameState {
    blue_servants: Bitboard,
    blue_ponies: Bitboard,
    blue_scholars: Bitboard,
    blue_cops: Bitboard,
    blue_princesses: Bitboard,
    blue_figurehead: Bitboard,
    orange_servants: Bitboard,
    orange_ponies: Bitboard,
    orange_scholars: Bitboard,
    orange_cops: Bitboard,
    orange_princesses: Bitboard,
    orange_figurehead: Bitboard,
}

#[test]
fn test_locale() {
    let a1 = Locale { rank: 0, file: 0 };
    assert_eq!(Bitboard(1u64), a1.pinpoint());

    let c4 = Locale { rank: 2, file: 3 };
    assert_eq!(Bitboard(524288u64), c4.pinpoint());
}

#[test]
fn test_empty_board_is_empty() {
    let empty_board = Bitboard(0);
    for rank in 0..8 {
        for file in 0..8 {
            assert!(!empty_board.query(Locale { rank: rank, file: file }));
        }
    }
}

#[test]
fn test_alight_and_quench() {
    let mut stage = Bitboard(0);
    let b5 = Locale { rank: 1, file: 4 };
    stage = stage.alight(b5);
    assert!(stage.query(b5));
    stage = stage.quench(b5);
    assert!(!stage.query(b5));
}
<commit_msg>put tests in a module; Bitboard::new()<commit_after>#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct Locale {
    rank: u8,
    file: u8
}

impl Locale {
    pub fn bit_index(&self) -> u32 {
        (8u32 * self.rank as u32) + self.file as u32
    }

    pub fn pinpoint(&self) -> Bitboard {
        Bitboard(2u64.pow(self.bit_index()))
    }
}

#[derive(Eq,PartialEq,Debug)]
struct Bitboard(u64);

impl Bitboard {
    pub fn new() -> Bitboard {
        Bitboard(0)
    }

    pub fn union(&self, other: Bitboard) -> Bitboard {
        let Bitboard(our_bits) = *self;
        let Bitboard(their_bits) = other;
        Bitboard(our_bits | their_bits)
    }

    pub fn intersection(&self, other: Bitboard) -> Bitboard {
        let Bitboard(our_bits) = *self;
        let Bitboard(their_bits) = other;
        Bitboard(our_bits & their_bits)
    }

    pub fn invert(&self) -> Bitboard {
        let Bitboard(our_bits) = *self;
        Bitboard(our_bits ^ our_bits)
    }

    pub fn alight(&self, station: Locale) -> Bitboard {
        // classic `|= (1 << n)` would probably be more efficient, huh &c.
        self.union(station.pinpoint())
    }

    pub fn quench(&self, station: Locale) -> Bitboard {
        self.intersection(station.pinpoint().invert())
    }

    pub fn query(&self, station: Locale) -> bool {
        let Bitboard(our_bits) = *self;
        let Bitboard(beacon_bits) = station.pinpoint();
        (our_bits & beacon_bits) != 0
    }
}

enum Teams { Blue, Orange }

#[derive(Eq,PartialEq,Debug)]
struct GameState {

    blue_servants: Bitboard,
    blue_ponies: Bitboard,
    blue_scholars: Bitboard,
    blue_cops: Bitboard,
    blue_princesses: Bitboard,
    blue_figurehead: Bitboard,
    orange_servants: Bitboard,
    orange_ponies: Bitboard,
    orange_scholars: Bitboard,
    orange_cops: Bitboard,
    orange_princesses: Bitboard,
    orange_figurehead: Bitboard,
}

#[cfg(test)]
mod test {
    use super::{Locale, Bitboard, GameState};

    #[test]
    fn test_locale() {
        let a1 = Locale { rank: 0, file: 0 };
        assert_eq!(Bitboard(1u64), a1.pinpoint());

        let c4 = Locale { rank: 2, file: 3 };
        assert_eq!(Bitboard(524288u64), c4.pinpoint());
    }

    #[test]
    fn test_empty_board_is_empty() {
        let empty_board = Bitboard::new();
        for rank in 0..8 {
            for file in 0..8 {
                assert!(!empty_board.query(Locale { rank: rank, file: file }));
            }
        }
    }

    #[test]
    fn test_alight_and_quench() {
        let mut stage = Bitboard(0);
        let b5 = Locale { rank: 1, file: 4 };
        stage = stage.alight(b5);
        assert!(stage.query(b5));
        stage = stage.quench(b5);
        assert!(!stage.query(b5));
    }
}
<|endoftext|>


<commit_before>// Copyright(C) 2016 Chris Liebert
#[macro_use]
extern crate glium;
extern crate nalgebra;
extern crate quick_3d;

use glium::glutin;
use glium::glutin::Event;
use glium::glutin::ElementState;
use glium::glutin::VirtualKeyCode;
use glium::Program;
use glium::Surface;
use glium::DisplayBuild;
use quick_3d::common;
use quick_3d::loader;

use nalgebra::{Matrix4, PerspectiveMatrix3};

fn main() {
    let screen_width = 800;
    let screen_height = 600;

    let db_file: &str = "test.db";

    let scene = loader::load_db(db_file);

    let display = glutin::WindowBuilder::new()
        //.resizable()
        //.with_vsync()
        //with_gl_debug_flag(true)
        .with_visibility(false)
        .with_dimensions(screen_width, screen_height)
        .build_glium()
        .unwrap();

    let mut vertex_buffers: Vec<glium::vertex::VertexBuffer<common::Vertex8f32>> = Vec::new();

    for i in 0..scene.meshes.len() {
        vertex_buffers.push(glium::vertex::VertexBuffer::new(&display, &scene.meshes[i].vertices).unwrap());
    }

    let index_buffer: glium::index::NoIndices =
        glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    if scene.shaders.len() == 0 {
        println!("No shaders loaded");
        return;
    }

    // Load the first shader in the database by default
    // TODO: load from config settings table?
    let shader_index = 0;

    // println!("Using glsl version {}", glsl_version);

    // TODO: wrap program! macro in another macros

    let program: Program = program!(&display,
    	/*
			110 => {
				vertex: &scene.shaders[shader_index].vertex_source_110,
				fragment: &scene.shaders[shader_index].fragment_source_110,
			},
			120 => {
				vertex: &scene.shaders[shader_index].vertex_source_120,
				fragment: &scene.shaders[shader_index].fragment_source_120,
			},
			130 => {
				vertex: &scene.shaders[shader_index].vertex_source_130,
				fragment: &scene.shaders[shader_index].fragment_source_130,
			},
			*/
			140 => {
				vertex: &scene.shaders[shader_index].vertex_source_140,
				fragment: &scene.shaders[shader_index].fragment_source_140,
			},
			/*
			150 => {
				vertex: &scene.shaders[shader_index].vertex_source_150,
				fragment: &scene.shaders[shader_index].fragment_source_150,
			},
			300 => {
				vertex: &scene.shaders[shader_index].vertex_source_300,
				fragment: &scene.shaders[shader_index].fragment_source_300,
			},
			330 => {
				vertex: &scene.shaders[shader_index].vertex_source_330,
				fragment: &scene.shaders[shader_index].fragment_source_330,
			},
			400 => {
				vertex: &scene.shaders[shader_index].vertex_source_400,
				fragment: &scene.shaders[shader_index].fragment_source_400,
			},
			410 => {
				vertex: &scene.shaders[shader_index].vertex_source_410,
				fragment: &scene.shaders[shader_index].fragment_source_410,
			},
			420 => {
				vertex: &scene.shaders[shader_index].vertex_source_420,
				fragment: &scene.shaders[shader_index].fragment_source_420,
			},
			430 => {
				vertex: &scene.shaders[shader_index].vertex_source_430,
				fragment: &scene.shaders[shader_index].fragment_source_430,
			},
			100 es => {
				vertex: &scene.shaders[shader_index].vertex_source_100es,
				fragment: &scene.shaders[shader_index].fragment_source_100es,
			},
			300 es => {
				vertex: &scene.shaders[shader_index].vertex_source_300es,
				fragment: &scene.shaders[shader_index].fragment_source_300es,
			}, */
		)
        .unwrap();

    // Shader compilation no longer required
    display.release_shader_compiler();

    // Set up camera
    let perspective = PerspectiveMatrix3::new(screen_width as f32 / screen_height as f32,
                                              45.0,
                                              0.1,
                                              1000.0);
    let perspective_matrix: Matrix4<f32> = perspective.to_matrix();

    let modelview_matrix_array = [[1.0f32, -0.0f32, -0.0f32, 0.0f32],
                                  [0.0f32, 1.0f32, -0.0f32, 0.0f32],
                                  [0.0f32, 0.0f32, 1.0f32, 0.0f32],
                                  [-5.0f32, -3.0f32, -12.0f32, 1.0f32]];

    let mut running = true;

    // Show the window once the data is loaded
    match display.get_window() {
        Some(x) => x.show(),
        None => {
            running = false;
            println!("Error retrieving window");
        }
    }

    while running {

        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);

        for i in 0..vertex_buffers.len() {
            let mesh: common::Mesh = scene.meshes[i as usize].clone();
            let material_id: i32 = mesh.material_id;
            let material: common::Material = scene.materials[material_id as usize].clone();

            let uniforms = uniform! {
		        projection: *perspective_matrix.as_ref(),
		        modelview: modelview_matrix_array,
		        light_position_worldspace: [2.0, 10.0, 1.0f32],
		        diffuse: material.diffuse,
		    };

            target.draw(&vertex_buffers[i],
                      &index_buffer,
                      &program,
                      &uniforms,
                      &Default::default())
                .unwrap();
        }

        target.finish().unwrap();

        for event in display.poll_events() {
            match event {
                Event::Closed => running = false,
                Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape)) => {
                    running = false
                }
                _ => (),
            }
        }
    }

}
<commit_msg>Added diffuse textures to the example program<commit_after>// Copyright(C) 2016 Chris Liebert
#[macro_use]
extern crate glium;
extern crate image;
extern crate nalgebra;
extern crate quick_3d;

use std::collections::HashMap;

use glium::glutin;
use glium::glutin::Event;
use glium::glutin::ElementState;
use glium::glutin::VirtualKeyCode;
use glium::Program;
use glium::Surface;
use glium::DisplayBuild;
use quick_3d::common;
use quick_3d::loader;

use nalgebra::{Matrix4, PerspectiveMatrix3};

fn main() {
    let screen_width = 400;
    let screen_height = 300;
    let db_file: &str = "test.db";

    let scene = loader::load_db(db_file);

    let display = glutin::WindowBuilder::new()
        //.resizable()
        //.with_vsync()
        //with_gl_debug_flag(true)
        .with_visibility(false) // Window is shown when scene finishes loading.
        .with_dimensions(screen_width, screen_height)
        .build_glium()
        .unwrap();

    let mut vertex_buffers: Vec<glium::vertex::VertexBuffer<common::Vertex8f32>> = Vec::with_capacity(scene.meshes.len());

    for i in 0..scene.meshes.len() {
        vertex_buffers.push(glium::vertex::VertexBuffer::new(&display, &scene.meshes[i].vertices).unwrap());
    }

    let index_buffer: glium::index::NoIndices =
        glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    if scene.shaders.len() == 0 {
        println!("No shaders loaded");
        return;
    }

	let mut textures: HashMap<String, glium::texture::CompressedSrgbTexture2d> = HashMap::new();

	for i in 0..scene.images.len() {
		//TODO: determine image format by name extension if nessisary
		//let image = image::load_from_memory_with_format(&scene.images[i].image, image::PNG).unwrap().to_rgba();
		let image = image::load_from_memory(&scene.images[i].image).unwrap().to_rgba();

	    let image_dimensions = image.dimensions();
	    let image = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(),
	                                                                   image_dimensions);
	    let opengl_texture: glium::texture::CompressedSrgbTexture2d = glium::texture::CompressedSrgbTexture2d::new(&display, image).unwrap();
		textures.insert(scene.images[i].name.clone(), opengl_texture);
	}

	// TODO: generate this texture instead of loading from sqlite
	let default_blank_texture = &textures["DEFAULT_BLANK_TEXTURE.png"];
    // Load the first shader in the database by default
    // TODO: load from config settings table?
    let shader_index = 0;

    // println!("Using glsl version {}", glsl_version);

    let program: Program = program!(&display,
    	/*
			110 => {
				vertex: &scene.shaders[shader_index].vertex_source_110,
				fragment: &scene.shaders[shader_index].fragment_source_110,
			},
			120 => {
				vertex: &scene.shaders[shader_index].vertex_source_120,
				fragment: &scene.shaders[shader_index].fragment_source_120,
			},
			130 => {
				vertex: &scene.shaders[shader_index].vertex_source_130,
				fragment: &scene.shaders[shader_index].fragment_source_130,
			},
			*/
			140 => {
				vertex: &scene.shaders[shader_index].vertex_source_140,
				fragment: &scene.shaders[shader_index].fragment_source_140,
			},
			/*
			150 => {
				vertex: &scene.shaders[shader_index].vertex_source_150,
				fragment: &scene.shaders[shader_index].fragment_source_150,
			},
			300 => {
				vertex: &scene.shaders[shader_index].vertex_source_300,
				fragment: &scene.shaders[shader_index].fragment_source_300,
			},
			330 => {
				vertex: &scene.shaders[shader_index].vertex_source_330,
				fragment: &scene.shaders[shader_index].fragment_source_330,
			},
			400 => {
				vertex: &scene.shaders[shader_index].vertex_source_400,
				fragment: &scene.shaders[shader_index].fragment_source_400,
			},
			410 => {
				vertex: &scene.shaders[shader_index].vertex_source_410,
				fragment: &scene.shaders[shader_index].fragment_source_410,
			},
			420 => {
				vertex: &scene.shaders[shader_index].vertex_source_420,
				fragment: &scene.shaders[shader_index].fragment_source_420,
			},
			430 => {
				vertex: &scene.shaders[shader_index].vertex_source_430,
				fragment: &scene.shaders[shader_index].fragment_source_430,
			},
			100 es => {
				vertex: &scene.shaders[shader_index].vertex_source_100es,
				fragment: &scene.shaders[shader_index].fragment_source_100es,
			},
			300 es => {
				vertex: &scene.shaders[shader_index].vertex_source_300es,
				fragment: &scene.shaders[shader_index].fragment_source_300es,
			}, */
		)
        .unwrap();

    // Shader compilation no longer required
    display.release_shader_compiler();

    // Set up camera
    let perspective = PerspectiveMatrix3::new(screen_width as f32 / screen_height as f32,
                                              45.0,
                                              0.1,
                                              1000.0);
    let perspective_matrix: Matrix4<f32> = perspective.to_matrix();

    let modelview_matrix_array = [[1.0f32, -0.0f32, -0.0f32, 0.0f32],
                                  [0.0f32, 1.0f32, -0.0f32, 0.0f32],
                                  [0.0f32, 0.0f32, 1.0f32, 0.0f32],
                                  [-5.0f32, -3.0f32, -12.0f32, 1.0f32]];

    let mut running = true;

    // Show the window once the data is loaded
    match display.get_window() {
        Some(x) => x.show(),
        None => {
            running = false;
            println!("Error retrieving window");
        }
    }

    while running {
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);

        for i in 0..vertex_buffers.len() {
            let material_id: i32  = scene.meshes[i as usize].material_id.clone();
			let diffuse = scene.materials[material_id as usize].diffuse.clone();
			let diffuse_texname: String = scene.materials[material_id as usize].diffuse_texname.clone();
			let opengl_texture: &glium::texture::CompressedSrgbTexture2d;
			if diffuse_texname.len() == 0 {
				opengl_texture = default_blank_texture;
			} else {
				opengl_texture = &textures[&diffuse_texname];
			}
            let uniforms = uniform! {
		        projection: *perspective_matrix.as_ref(),
		        modelview: modelview_matrix_array,
		        light_position_worldspace: [2.0, 10.0, 1.0f32],
		        diffuse: diffuse,
		        diffuse_texture: opengl_texture,
		    };

            target.draw(&vertex_buffers[i],
                      &index_buffer,
                      &program,
                      &uniforms,
                      &Default::default())
                .unwrap();
        }

        target.finish().unwrap();

        for event in display.poll_events() {
            match event {
                Event::Closed => running = false,
                Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape)) => {
                    running = false
                }
                _ => (),
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;

use std::io::{Write, BufRead};
use docopt::Docopt;

static LF: char = '\n';
static NULL: char = '\0';
static VERSION: &'static str = "0.0.1";
static USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

#[derive(Debug)]
enum ProcessingError {
    IO(std::io::Error),
    Utf8(std::str::Utf8Error),
}

impl std::fmt::Display for ProcessingError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            ProcessingError::IO(ref e) => {
                write!(f, "{}", e)
            },
            ProcessingError::Utf8(ref e) => {
                write!(f, "{}", e)
            },
        }
    }
}

impl From<std::io::Error> for ProcessingError {
    fn from(e: std::io::Error) -> ProcessingError {
        ProcessingError::IO(e)
    }
}
impl From<std::str::Utf8Error> for ProcessingError {
    fn from(e: std::str::Utf8Error) -> ProcessingError {
        ProcessingError::Utf8(e)
    }
}

#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

type FileInfoResult = Result<FileInfo, ProcessingError>;

fn process_reader<T: std::io::Read>(reader: T) -> FileInfoResult {
    let mut info = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    // TODO: Only to as much processing as is absolutely necessary to
    // provide the data we will end up printing.
    let mut rbuf = std::io::BufReader::new(reader);
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            // TODO: Handle files which are not utf8-encoded. Right
            // now we get an error here.
            let line = try!(std::str::from_utf8(&lbuf));
            let size = line.chars().count();
            info.max_line_length = match line.chars().last() {
                Some(c) => {
                    if c == LF {
                        info.lines += 1;
                        std::cmp::max(info.max_line_length, size - 1)
                    } else {
                        std::cmp::max(info.max_line_length, size)
                    }
                },
                None => std::cmp::max(info.max_line_length, size),
            };
            info.chars += size;
            let mut words: Vec<&str> = line
                .split(|c: char| c.is_whitespace())
                .collect();
            words.retain(|s: &&str| s.len() > 0);
            info.words += words.len();
        }
        lbuf.clear()
    }
    Ok(info)
}

macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

type NullDelimitedFileResult<'a> = Result<Vec<String>, ProcessingError>;

fn split_file_on_nulls(filename: &str) -> NullDelimitedFileResult {
    let file = try!(std::fs::File::open(filename));
    let mut result = Vec::new();
    let mut rbuf = std::io::BufReader::new(file);
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(NULL as u8, &mut lbuf));
        if size == 0 {
            break;
        }
        {
            let line = try!(std::str::from_utf8(&lbuf));
            result.push(line.trim_right_matches(NULL).to_string());
        }
        lbuf.clear()
    }
    Ok(result)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        std::process::exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        println_stderr!("wc: file operands cannot be combined with --files0-from");
        println_stderr!("Try 'wc --help' for more information");
        std::process::exit(1);
    }

    let mut files: Vec<String> = Vec::new();
    if args.flag_files0_from.len() != 0 {
        match split_file_on_nulls(&args.flag_files0_from) {
            Ok(parts) => {
                files.extend(parts);
            },
            Err(e) => {
                println_stderr!("wc: cannot open {} for reading: {}",
                                args.flag_files0_from, e);
                std::process::exit(1);
            },
        }
    } else {
        files.extend(args.arg_FILE);
    };

    let mut results = Vec::new();
    let mut totals = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    for filename in files {
        let result = match filename.as_ref() {
            "-" => process_reader(std::io::stdin()),
            _ => {
                let file = std::fs::File::open(filename.to_string());
                match file {
                    Ok(f) => process_reader(f),
                    Err(e) => Err(ProcessingError::IO(e)),
                }
            }
        };
        match result {
            Ok(ref r) => {
                totals.chars += r.chars;
                totals.lines += r.lines;
                totals.bytes += r.bytes;
                totals.words += r.words;
                totals.max_line_length = std::cmp::max(totals.max_line_length,
                                                       r.max_line_length);
            },
            Err(_) => {},
        }
        results.push((filename, result));
    }

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = totals.bytes.to_string().len();

    if results.len() > 1 {
        results.push(("total".to_string(), Ok(totals)));
    }

    let mut errors_encountered = false;

    // Present the results
    for data in results.iter() {
        let (ref filename, ref result) = *data;
        match *result {
            Ok(ref r) => {
                // let mut parts = Vec::new();
                let mut requested_field = false;
                if args.flag_lines {
                    print!("{:1$} ", r.lines, field_size);
                    requested_field = true;
                }
                if args.flag_words {
                    print!("{:1$} ", r.words, field_size);
                    requested_field = true;
                }
                if args.flag_bytes {
                    print!("{:1$} ", r.bytes, field_size);
                    requested_field = true;
                }
                if args.flag_chars {
                    print!("{:1$} ", r.chars, field_size);
                    requested_field = true;
                }
                if args.flag_max_line_length {
                    print!("{:1$} ", r.max_line_length, field_size);
                    requested_field = true;
                }
                if !requested_field {
                    print!("{:1$} ", r.lines, field_size);
                    print!("{:1$} ", r.words, field_size);
                    print!("{:1$} ", r.bytes, field_size);
                }
                println!("{}", filename);
            },
            Err(ref e) => {
                errors_encountered = true;
                println_stderr!("wc: {}: {}", filename, e);
            },
        }
    }

    if errors_encountered {
        std::process::exit(1);
    }
}
<commit_msg>Accept stdin for files0-from<commit_after>extern crate rustc_serialize;
extern crate docopt;

use std::io::{Write, BufRead};
use docopt::Docopt;

static LF: char = '\n';
static NULL: char = '\0';
static VERSION: &'static str = "0.0.1";
static USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

#[derive(Debug)]
enum ProcessingError {
    IO(std::io::Error),
    Utf8(std::str::Utf8Error),
}

impl std::fmt::Display for ProcessingError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            ProcessingError::IO(ref e) => {
                write!(f, "{}", e)
            },
            ProcessingError::Utf8(ref e) => {
                write!(f, "{}", e)
            },
        }
    }
}

impl From<std::io::Error> for ProcessingError {
    fn from(e: std::io::Error) -> ProcessingError {
        ProcessingError::IO(e)
    }
}
impl From<std::str::Utf8Error> for ProcessingError {
    fn from(e: std::str::Utf8Error) -> ProcessingError {
        ProcessingError::Utf8(e)
    }
}

#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

type FileInfoResult = Result<FileInfo, ProcessingError>;

fn process_reader<T: std::io::Read>(reader: T) -> FileInfoResult {
    let mut info = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    // TODO: Only to as much processing as is absolutely necessary to
    // provide the data we will end up printing.
    let mut rbuf = std::io::BufReader::new(reader);
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            // TODO: Handle files which are not utf8-encoded. Right
            // now we get an error here.
            let line = try!(std::str::from_utf8(&lbuf));
            let size = line.chars().count();
            info.max_line_length = match line.chars().last() {
                Some(c) => {
                    if c == LF {
                        info.lines += 1;
                        std::cmp::max(info.max_line_length, size - 1)
                    } else {
                        std::cmp::max(info.max_line_length, size)
                    }
                },
                None => std::cmp::max(info.max_line_length, size),
            };
            info.chars += size;
            let mut words: Vec<&str> = line
                .split(|c: char| c.is_whitespace())
                .collect();
            words.retain(|s: &&str| s.len() > 0);
            info.words += words.len();
        }
        lbuf.clear()
    }
    Ok(info)
}

macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
        )
        );

// type NullDelimitedFileResult<'a> = Result<Vec<String>, ProcessingError>;
type NullDelimitedFileResult = Result<Vec<String>, ProcessingError>;

fn split_file_on_nulls<T: std::io::Read>(file: T) -> NullDelimitedFileResult {
    let mut rbuf = std::io::BufReader::new(file);
    let mut result = Vec::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(NULL as u8, &mut lbuf));
        if size == 0 {
            break;
        }
        {
            let line = try!(std::str::from_utf8(&lbuf));
            result.push(line.trim_right_matches(NULL).to_string());
        }
        lbuf.clear()
    }
    Ok(result)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        std::process::exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        println_stderr!("wc: file operands cannot be combined with --files0-from");
        println_stderr!("Try 'wc --help' for more information");
        std::process::exit(1);
    }

    let mut files: Vec<String> = Vec::new();
    if args.flag_files0_from.len() != 0 {
        let split_results = match args.flag_files0_from.as_ref() {
            "-" => {
                split_file_on_nulls(std::io::stdin())
            },
            _ => {
                match std::fs::File::open(&args.flag_files0_from) {
                    Ok(f) => split_file_on_nulls(f),
                    Err(e) => {
                        println_stderr!("wc: cannot open {} for reading: {}",
                                        args.flag_files0_from, e);
                        std::process::exit(1);
                    },
                }
            },
        };
        match split_results {
            Ok(parts) => {
                files.extend(parts);
            },
            Err(e) => {
                println_stderr!("wc: error reading {}: {}",
                                args.flag_files0_from, e);
                std::process::exit(1);
            },
        };
    } else {
        files.extend(args.arg_FILE);
    };

    let mut results = Vec::new();
    let mut totals = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    for filename in files {
        let result = match filename.as_ref() {
            "-" => process_reader(std::io::stdin()),
            _ => {
                let file = std::fs::File::open(filename.to_string());
                match file {
                    Ok(f) => process_reader(f),
                    Err(e) => Err(ProcessingError::IO(e)),
                }
            }
        };
        match result {
            Ok(ref r) => {
                totals.chars += r.chars;
                totals.lines += r.lines;
                totals.bytes += r.bytes;
                totals.words += r.words;
                totals.max_line_length = std::cmp::max(totals.max_line_length,
                                                       r.max_line_length);
            },
            Err(_) => {},
        }
        results.push((filename, result));
    }

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = totals.bytes.to_string().len();

    if results.len() > 1 {
        results.push(("total".to_string(), Ok(totals)));
    }

    let mut errors_encountered = false;

    // Present the results
    for data in results.iter() {
        let (ref filename, ref result) = *data;
        match *result {
            Ok(ref r) => {
                // let mut parts = Vec::new();
                let mut requested_field = false;
                if args.flag_lines {
                    print!("{:1$} ", r.lines, field_size);
                    requested_field = true;
                }
                if args.flag_words {
                    print!("{:1$} ", r.words, field_size);
                    requested_field = true;
                }
                if args.flag_bytes {
                    print!("{:1$} ", r.bytes, field_size);
                    requested_field = true;
                }
                if args.flag_chars {
                    print!("{:1$} ", r.chars, field_size);
                    requested_field = true;
                }
                if args.flag_max_line_length {
                    print!("{:1$} ", r.max_line_length, field_size);
                    requested_field = true;
                }
                if !requested_field {
                    print!("{:1$} ", r.lines, field_size);
                    print!("{:1$} ", r.words, field_size);
                    print!("{:1$} ", r.bytes, field_size);
                }
                println!("{}", filename);
            },
            Err(ref e) => {
                errors_encountered = true;
                println_stderr!("wc: {}: {}", filename, e);
            },
        }
    }

    if errors_encountered {
        std::process::exit(1);
    }
}
<|endoftext|>


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

extern crate konane;
use konane::{Game, Occupancy, Position};

#[macro_use]
extern crate error_chain;

extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};

#[macro_use]
extern crate clap;

extern crate rand;
use rand::{Rng, StdRng};

const OPENGL_VERSIONS_STR: [&'static str; 12] = ["2.0", "2.1", "3.0", "3.1", "3.2", "3.3", "4.0", "4.1",
                                                 "4.2", "4.3", "4.4", "4.5"];
const OPENGL_VERSIONS_ENUM: [OpenGL; 12] = [OpenGL::V2_0,
                                            OpenGL::V2_1,
                                            OpenGL::V3_0,
                                            OpenGL::V3_1,
                                            OpenGL::V3_2,
                                            OpenGL::V3_3,
                                            OpenGL::V4_0,
                                            OpenGL::V4_1,
                                            OpenGL::V4_2,
                                            OpenGL::V4_3,
                                            OpenGL::V4_4,
                                            OpenGL::V4_5];
fn opengl_version_str_to_enum(value: &str) -> OpenGL {
  OPENGL_VERSIONS_ENUM[OPENGL_VERSIONS_STR.binary_search(&value).expect("impossible")]
}
fn opengl_version_enum_to_str(value: OpenGL) -> &'static str {
  OPENGL_VERSIONS_STR[OPENGL_VERSIONS_ENUM.binary_search(&value).expect("impossible")]
}
const OPENGL_VERSION_DEFAULT: OpenGL = OpenGL::V3_2;

fn main() {
  let matches = clap::App::new("kōnane")
    .version(crate_version!())
    .author("Alexander Ronald Altman <alexanderaltman@me.com>")
    .about("The ancient polynesian game of kōnane")
    .arg(clap::Arg::with_name("OpenGL version")
      .long("opengl")
      .short("g")
      .default_value(opengl_version_enum_to_str(OPENGL_VERSION_DEFAULT))
      .help("The version of OpenGL to use")
      .possible_values(&OPENGL_VERSIONS_STR))
    .setting(clap::AppSettings::ColoredHelp)
    .get_matches();
  let gl_version = opengl_version_str_to_enum(matches.value_of("OpenGL version").expect("impossible"));
  setup(matches, gl_version).expect("kōnane encountered an error");
}

mod errors {
  error_chain! {
    types {
      Error, ErrorKind, ChainErr, Result;
    }
    links {
      ::konane::errors::Error, ::konane::errors::ErrorKind, Game;
    }
    foreign_links {
      ::clap::Error, Clap, "clap error";
      ::std::io::Error, IO, "I/O error";
    }
    errors {
      OpenGL(inner: ::opengl_graphics::error::Error) {
        description("OpenGL graphics error")
        display("OpenGL graphics error: {}", inner)
      }
    }
  }

  impl From<::opengl_graphics::error::Error> for Error {
    fn from(inner: ::opengl_graphics::error::Error) -> Error { ErrorKind::OpenGL(inner).into() }
  }
}

struct Context<'a> {
  args: clap::ArgMatches<'a>,
  gl_version: OpenGL,
  window: &'a mut Window,
  gl: &'a mut GlGraphics,
  game: &'a mut Game,
  rng: &'a mut StdRng,
}

fn setup(matches: clap::ArgMatches, gl_version: OpenGL) -> errors::Result<()> {
  let rng = &mut try!(StdRng::new());
  let cxt = Context {
    args: matches,
    gl_version: gl_version,
    window: &mut try!(WindowSettings::new("kōnane", [1000, 1000])
      .opengl(gl_version)
      .exit_on_esc(true)
      .build()),
    gl: &mut GlGraphics::new(gl_version),
    game: &mut if rng.gen() { Game::new_white() } else { Game::new_black() },
    rng: rng,
  };
  run(cxt)
}

fn run(cxt: Context) -> errors::Result<()> {
  let mut events = cxt.window.events();
  while let Some(event) = events.next(cxt.window) {
    match event {
      Event::Render(params) => (),
      Event::AfterRender(params) => (),
      Event::Update(params) => (),
      Event::Idle(params) => (),
      Event::Input(params) => (),
    }
  }
  Ok(())
}
<commit_msg>Event handling improvements<commit_after>// main.rs
// Copyright 2016 Alexander Altman
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate konane;
use konane::{Game, Occupancy, Position};

#[macro_use]
extern crate error_chain;

extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};

#[macro_use]
extern crate clap;

extern crate rand;
use rand::{Rng, StdRng};

const OPENGL_VERSIONS_STR: [&'static str; 12] = ["2.0", "2.1", "3.0", "3.1", "3.2", "3.3", "4.0", "4.1",
                                                 "4.2", "4.3", "4.4", "4.5"];
const OPENGL_VERSIONS_ENUM: [OpenGL; 12] = [OpenGL::V2_0,
                                            OpenGL::V2_1,
                                            OpenGL::V3_0,
                                            OpenGL::V3_1,
                                            OpenGL::V3_2,
                                            OpenGL::V3_3,
                                            OpenGL::V4_0,
                                            OpenGL::V4_1,
                                            OpenGL::V4_2,
                                            OpenGL::V4_3,
                                            OpenGL::V4_4,
                                            OpenGL::V4_5];
fn opengl_version_str_to_enum(value: &str) -> OpenGL {
  OPENGL_VERSIONS_ENUM[OPENGL_VERSIONS_STR.binary_search(&value).expect("impossible")]
}
fn opengl_version_enum_to_str(value: OpenGL) -> &'static str {
  OPENGL_VERSIONS_STR[OPENGL_VERSIONS_ENUM.binary_search(&value).expect("impossible")]
}
const OPENGL_VERSION_DEFAULT: OpenGL = OpenGL::V3_2;

fn main() {
  let matches = clap::App::new("kōnane")
    .version(crate_version!())
    .author("Alexander Ronald Altman <alexanderaltman@me.com>")
    .about("The ancient polynesian game of kōnane")
    .arg(clap::Arg::with_name("OpenGL version")
      .long("opengl")
      .short("g")
      .default_value(opengl_version_enum_to_str(OPENGL_VERSION_DEFAULT))
      .help("The version of OpenGL to use")
      .possible_values(&OPENGL_VERSIONS_STR))
    .setting(clap::AppSettings::ColoredHelp)
    .get_matches();
  let gl_version = opengl_version_str_to_enum(matches.value_of("OpenGL version").expect("impossible"));
  setup(matches, gl_version).expect("kōnane encountered an error");
}

mod errors {
  error_chain! {
    types {
      Error, ErrorKind, ChainErr, Result;
    }
    links {
      ::konane::errors::Error, ::konane::errors::ErrorKind, Game;
    }
    foreign_links {
      ::clap::Error, Clap, "clap error";
      ::std::io::Error, IO, "I/O error";
    }
    errors {
      OpenGL(inner: ::opengl_graphics::error::Error) {
        description("OpenGL graphics error")
        display("OpenGL graphics error: {}", inner)
      }
    }
  }

  impl From<::opengl_graphics::error::Error> for Error {
    fn from(inner: ::opengl_graphics::error::Error) -> Error { ErrorKind::OpenGL(inner).into() }
  }
}

struct Context<'a> {
  args: clap::ArgMatches<'a>,
  gl_version: OpenGL,
  window: &'a mut Window,
  gl: &'a mut GlGraphics,
  game: &'a mut Game,
  rng: &'a mut StdRng,
}

fn setup(matches: clap::ArgMatches, gl_version: OpenGL) -> errors::Result<()> {
  let rng = &mut try!(StdRng::new());
  let cxt = Context {
    args: matches,
    gl_version: gl_version,
    window: &mut try!(WindowSettings::new("kōnane", [1000, 1000])
      .opengl(gl_version)
      .exit_on_esc(true)
      .build()),
    gl: &mut GlGraphics::new(gl_version),
    game: &mut if rng.gen() { Game::new_white() } else { Game::new_black() },
    rng: rng,
  };
  run(cxt)
}

fn run(cxt: Context) -> errors::Result<()> {
  let mut events = cxt.window.events();
  while let Some(event) = events.next(cxt.window) {
    match event {
      Event::Render(params) => (),
      Event::AfterRender(AfterRenderArgs) => (),
      Event::Update(params) => (),
      Event::Idle(params) => (),
      Event::Input(Input::Press(button)) => (),
      Event::Input(Input::Release(button)) => (),
      Event::Input(Input::Move(motion)) => (),
      Event::Input(Input::Text(string)) => (),
      Event::Input(Input::Resize(width, height)) => (),
      Event::Input(Input::Focus(on_window)) => (),
      Event::Input(Input::Cursor(on_window)) => (),
    }
  }
  Ok(())
}
<|endoftext|>