<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    logger::stdout("Creating GitHub release");
    github::release(&config, &tag_name, &tag_message)
        .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn write_changelog(repository_path: &str, version: &Version, new_version: &str) {
    logger::stdout("Writing Changelog");
    changelog::write(repository_path, &version.to_string(), &new_version)
        .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn package_crate(config: &config::Config, repository_path: &str, new_version: &str) {
    if config.release_mode {
        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }
    }

    logger::stdout("Package crate");
    if !cargo::package(repository_path) {
        print_exit!("`cargo package` failed. See above for the cargo error message.");
    }

    git::commit_files(&config, &new_version)
        .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");

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

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch);

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    config_builder.repository_path(repository_path.to_owned());

    // extra scope scope to make sure borrow of `repo` is dropped
    {
        let signature = match git::get_signature(&repo) {
            Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
                process::exit(1);
            }
        };

        config_builder.signature(signature.to_owned());
    }

    // In case we are in write-mode AND release mode,
    // we will make sure we got all configuration settings
    if write_mode && release_mode {
        let remote_or_none = repo.find_remote("origin");
        match remote_or_none {
            Ok(remote) => {
                let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
                let (user, repo_name) = user_repo_from_url(&url)
                    .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));
                config_builder.user(user);
                config_builder.repository_name(repo_name);

                let gh_token = env::var("GH_TOKEN")
                    .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

                let cargo_token = env::var("CARGO_TOKEN")
                    .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));

                config_builder.gh_token(gh_token);
                config_builder.cargo_token(cargo_token);
            },
            Err(err) => {
                logger::warn(format!("Could not determine the origin remote url: {:?}", err));
                logger::warn("semantic-rs can't push changes or create a release on GitHub");
            }
        }
    }

    config_builder.repository(repo);
    let config = config_builder.build();

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if !config.write_mode {
        let changelog = generate_changelog(repository_path, &version, &new_version);
        print_changelog(&changelog);
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        write_changelog(&repository_path, &version, &new_version);
        package_crate(&config, &repository_path, &new_version);

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            push_to_github(&config, &tag_name);
            release_on_github(&config, &tag_message, &tag_name);
            release_on_cratesio(&config);
            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<commit_msg>refactor: move git committer error msg to constant<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

const COMMITTER_MESSAGE: &'static str = r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    logger::stdout("Creating GitHub release");
    github::release(&config, &tag_name, &tag_message)
        .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn write_changelog(repository_path: &str, version: &Version, new_version: &str) {
    logger::stdout("Writing Changelog");
    changelog::write(repository_path, &version.to_string(), &new_version)
        .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn package_crate(config: &config::Config, repository_path: &str, new_version: &str) {
    if config.release_mode {
        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }
    }

    logger::stdout("Package crate");
    if !cargo::package(repository_path) {
        print_exit!("`cargo package` failed. See above for the cargo error message.");
    }

    git::commit_files(&config, &new_version)
        .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");

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

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch);

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    config_builder.repository_path(repository_path.to_owned());

    // extra scope scope to make sure borrow of `repo` is dropped
    {
        let signature = match git::get_signature(&repo) {
            Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(COMMITTER_MESSAGE);
                process::exit(1);
            }
        };

        config_builder.signature(signature.to_owned());
    }

    // In case we are in write-mode AND release mode,
    // we will make sure we got all configuration settings
    if write_mode && release_mode {
        let remote_or_none = repo.find_remote("origin");
        match remote_or_none {
            Ok(remote) => {
                let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
                let (user, repo_name) = user_repo_from_url(&url)
                    .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));
                config_builder.user(user);
                config_builder.repository_name(repo_name);

                let gh_token = env::var("GH_TOKEN")
                    .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

                let cargo_token = env::var("CARGO_TOKEN")
                    .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));

                config_builder.gh_token(gh_token);
                config_builder.cargo_token(cargo_token);
            },
            Err(err) => {
                logger::warn(format!("Could not determine the origin remote url: {:?}", err));
                logger::warn("semantic-rs can't push changes or create a release on GitHub");
            }
        }
    }

    config_builder.repository(repo);
    let config = config_builder.build();

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if !config.write_mode {
        let changelog = generate_changelog(repository_path, &version, &new_version);
        print_changelog(&changelog);
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        write_changelog(&repository_path, &version, &new_version);
        package_crate(&config, &repository_path, &new_version);

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            push_to_github(&config, &tag_name);
            release_on_github(&config, &tag_message, &tag_name);
            release_on_cratesio(&config);
            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate ansi_term;
extern crate atty;
extern crate regex;
extern crate ignore;

pub mod lscolors;
pub mod fshelper;

use std::borrow::Cow;
use std::env;
use std::error::Error;
use std::io::Write;
use std::ops::Deref;
#[cfg(target_family = "unix")]
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, Component};
use std::process;
use std::sync::Arc;

use clap::{App, AppSettings, Arg};
use atty::Stream;
use regex::{Regex, RegexBuilder};
use ignore::WalkBuilder;

use lscolors::LsColors;

/// Defines how to display search result paths.
#[derive(PartialEq)]
enum PathDisplay {
    /// As an absolute path
    Absolute,

    /// As a relative path
    Relative
}

/// Configuration options for *fd*.
struct FdOptions {
    /// Determines whether the regex search is case-sensitive or case-insensitive.
    case_sensitive: bool,

    /// Whether to search within the full file path or just the base name (filename or directory
    /// name).
    search_full_path: bool,

    /// Whether to ignore hidden files and directories (or not).
    ignore_hidden: bool,

    /// Whether to respect VCS ignore files (`.gitignore`, `.ignore`, ..) or not.
    read_ignore: bool,

    /// Whether to follow symlinks or not.
    follow_links: bool,

    /// Whether elements of output should be separated by a null character
    null_separator: bool,

    /// The maximum search depth, or `None` if no maximum search depth should be set.
    ///
    /// A depth of `1` includes all files under the current directory, a depth of `2` also includes
    /// all files under subdirectories of the current directory, etc.
    max_depth: Option<usize>,

    /// Display results as relative or absolute path.
    path_display: PathDisplay,

    /// `None` if the output should not be colorized. Otherwise, a `LsColors` instance that defines
    /// how to style different filetypes.
    ls_colors: Option<LsColors>
}

/// Root directory
static ROOT_DIR : &'static str = "/";

/// Parent directory
static PARENT_DIR : &'static str = "..";

/// Print a search result to the console.
fn print_entry(base: &Path, entry: &Path, config: &FdOptions) {
    let path_full = base.join(entry);

    let path_str = entry.to_string_lossy();

    #[cfg(target_family = "unix")]
    let is_executable = |p: &std::path::PathBuf| {
        p.metadata()
         .ok()
         .map(|f| f.permissions().mode() & 0o111 != 0)
         .unwrap_or(false)
    };

    #[cfg(not(target_family = "unix"))]
    let is_executable =  |p: &std::path::PathBuf| {false};

    let stdout = std::io::stdout();
    let mut handle = stdout.lock();

    if let Some(ref ls_colors) = config.ls_colors {
        let default_style = ansi_term::Style::default();

        let mut component_path = base.to_path_buf();

        if config.path_display == PathDisplay::Absolute {
            print!("{}", ls_colors.directory.paint(ROOT_DIR));
        }

        // Traverse the path and colorize each component
        for component in entry.components() {
            let comp_str = match component {
                Component::Normal(p) => p.to_string_lossy(),
                Component::ParentDir => Cow::from(PARENT_DIR),
                _                    => error("Error: unexpected path component.")
            };

            component_path.push(Path::new(comp_str.deref()));

            let style =
                if component_path.symlink_metadata()
                                 .map(|md| md.file_type().is_symlink())
                                 .unwrap_or(false) {
                    &ls_colors.symlink
                } else if component_path.is_dir() {
                    &ls_colors.directory
                } else if is_executable(&component_path) {
                    &ls_colors.executable
                } else {
                    // Look up file name
                    let o_style =
                        component_path.file_name()
                                      .and_then(|n| n.to_str())
                                      .and_then(|n| ls_colors.filenames.get(n));

                    match o_style {
                        Some(s) => s,
                        None =>
                            // Look up file extension
                            component_path.extension()
                                          .and_then(|e| e.to_str())
                                          .and_then(|e| ls_colors.extensions.get(e))
                                          .unwrap_or(&default_style)
                    }
                };

            write!(handle, "{}", style.paint(comp_str));

            if component_path.is_dir() && component_path != path_full {
                let sep = std::path::MAIN_SEPARATOR.to_string();
                write!(handle, "{}", style.paint(sep));
            }
        }
        if config.null_separator {
          writeln!(handle, "{}", '\0');
        } else {
          writeln!(handle);
        }
    } else {
        // Uncolorized output

        let prefix = if config.path_display == PathDisplay::Absolute { ROOT_DIR } else { "" };
        let separator = if config.null_separator { "\0" } else { "\n" };

        let r = writeln!(&mut std::io::stdout(), "{}{}{}", prefix, path_str, separator);

        if r.is_err() {
            // Probably a broken pipe. Exit gracefully.
            process::exit(0);
        }
    }
}

/// Recursively scan the given search path and search for files / pathnames matching the pattern.
fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
    let walker = WalkBuilder::new(root)
                     .hidden(config.ignore_hidden)
                     .ignore(config.read_ignore)
                     .git_ignore(config.read_ignore)
                     .parents(config.read_ignore)
                     .git_global(config.read_ignore)
                     .git_exclude(config.read_ignore)
                     .follow_links(config.follow_links)
                     .max_depth(config.max_depth)
                     .build_parallel();

    walker.run(|| {
        let base = base.to_owned();
        let config = config.clone();
        let pattern = pattern.clone();

        Box::new(move |entry_o| {
            let entry = match entry_o {
                Ok(e) => e,
                Err(_) => return ignore::WalkState::Continue
            };

            let path_rel_buf = match fshelper::path_relative_from(entry.path(), &*base) {
                Some(p) => p,
                None => error("Error: could not get relative path for directory entry.")
            };
            let path_rel = path_rel_buf.as_path();

            let search_str_o =
                if config.search_full_path {
                    Some(path_rel.to_string_lossy())
                } else {
                    path_rel.file_name()
                            .map(|f| f.to_string_lossy())
                };

            if let Some(search_str) = search_str_o {
                pattern.find(&*search_str)
                          .map(|_| print_entry(&base, path_rel, &config));
            }

            ignore::WalkState::Continue
        })
    });
}

/// Print error message to stderr and exit with status `1`.
fn error(message: &str) -> ! {
    writeln!(&mut std::io::stderr(), "{}", message)
        .expect("Failed writing to stderr");
    process::exit(1);
}

fn main() {
    let matches =
        App::new("fd")
            .version(crate_version!())
            .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>]")
            .setting(AppSettings::ColoredHelp)
            .setting(AppSettings::DeriveDisplayOrder)
            .arg(Arg::with_name("case-sensitive")
                        .long("case-sensitive")
                        .short("s")
                        .help("Case-sensitive search (default: smart case)"))
            .arg(Arg::with_name("full-path")
                        .long("full-path")
                        .short("p")
                        .help("Search full path (default: file-/dirname only)"))
            .arg(Arg::with_name("hidden")
                        .long("hidden")
                        .short("H")
                        .help("Search hidden files and directories"))
            .arg(Arg::with_name("no-ignore")
                        .long("no-ignore")
                        .short("I")
                        .help("Do not respect .(git)ignore files"))
            .arg(Arg::with_name("follow")
                        .long("follow")
                        .short("f")
                        .help("Follow symlinks"))
            .arg(Arg::with_name("null_separator")
                        .long("print0")
                        .short("0")
                        .help("Separate results by the null character"))
            .arg(Arg::with_name("absolute-path")
                        .long("absolute-path")
                        .short("a")
                        .help("Show absolute instead of relative paths"))
            .arg(Arg::with_name("no-color")
                        .long("no-color")
                        .short("n")
                        .help("Do not colorize output"))
            .arg(Arg::with_name("depth")
                        .long("max-depth")
                        .short("d")
                        .takes_value(true)
                        .help("Set maximum search depth (default: none)"))
            .arg(Arg::with_name("pattern")
                        .help("the search pattern, a regular expression (optional)"))
            .arg(Arg::with_name("path")
                        .help("the root directory for the filesystem search (optional)"))
            .get_matches();

    // Get the search pattern
    let empty_pattern = String::new();
    let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern);

    // Get the current working directory
    let current_dir_buf = match env::current_dir() {
        Ok(cd) => cd,
        Err(_) => error("Error: could not get current directory.")
    };
    let current_dir = current_dir_buf.as_path();

    // Get the root directory for the search
    let mut root_dir_is_absolute = false;
    let root_dir_buf = if let Some(rd) = matches.value_of("path") {
        let path = Path::new(rd);

        root_dir_is_absolute = path.is_absolute();

        path.canonicalize().unwrap_or_else(
            |_| error(&format!("Error: could not find directory '{}'.", rd))
        )
    } else {
        current_dir_buf.clone()
    };

    if !root_dir_buf.is_dir() {
        error(&format!("Error: '{}' is not a directory.", root_dir_buf.to_string_lossy()));
    }

    let root_dir = root_dir_buf.as_path();

    // The search will be case-sensitive if the command line flag is set or
    // if the pattern has an uppercase character (smart case).
    let case_sensitive = matches.is_present("case-sensitive") ||
                         pattern.chars().any(char::is_uppercase);

    let colored_output = !matches.is_present("no-color") &&
                         atty::is(Stream::Stdout);

    let ls_colors =
        if colored_output {
            Some(
                env::var("LS_COLORS")
                    .ok()
                    .map(|val| LsColors::from_string(&val))
                    .unwrap_or_default()
            )
        } else {
            None
        };

    let config = FdOptions {
        case_sensitive:    case_sensitive,
        search_full_path:  matches.is_present("full-path"),
        ignore_hidden:     !matches.is_present("hidden"),
        read_ignore:       !matches.is_present("no-ignore"),
        follow_links:      matches.is_present("follow"),
        null_separator:    matches.is_present("null_separator"),
        max_depth:         matches.value_of("depth")
                                   .and_then(|ds| usize::from_str_radix(ds, 10).ok()),
        path_display:      if matches.is_present("absolute-path") || root_dir_is_absolute {
                               PathDisplay::Absolute
                           } else {
                               PathDisplay::Relative
                           },
        ls_colors:         ls_colors
    };

    let root = Path::new(ROOT_DIR);
    let base = match config.path_display {
        PathDisplay::Relative => current_dir,
        PathDisplay::Absolute => root
    };

    match RegexBuilder::new(pattern)
              .case_insensitive(!config.case_sensitive)
              .build() {
        Ok(re)   => scan(root_dir, Arc::new(re), base, Arc::new(config)),
        Err(err) => error(err.description())
    }
}
<commit_msg>Implement short-time buffering for sorted output<commit_after>#[macro_use]
extern crate clap;
extern crate ansi_term;
extern crate atty;
extern crate regex;
extern crate ignore;

pub mod lscolors;
pub mod fshelper;

use std::borrow::Cow;
use std::env;
use std::error::Error;
use std::io::Write;
use std::ops::Deref;
#[cfg(target_family = "unix")]
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf, Component};
use std::process;
use std::sync::Arc;
use std::sync::mpsc::channel;
use std::time;

use clap::{App, AppSettings, Arg};
use atty::Stream;
use regex::{Regex, RegexBuilder};
use ignore::WalkBuilder;

use lscolors::LsColors;

/// Defines how to display search result paths.
#[derive(PartialEq)]
enum PathDisplay {
    /// As an absolute path
    Absolute,

    /// As a relative path
    Relative
}

/// Configuration options for *fd*.
struct FdOptions {
    /// Determines whether the regex search is case-sensitive or case-insensitive.
    case_sensitive: bool,

    /// Whether to search within the full file path or just the base name (filename or directory
    /// name).
    search_full_path: bool,

    /// Whether to ignore hidden files and directories (or not).
    ignore_hidden: bool,

    /// Whether to respect VCS ignore files (`.gitignore`, `.ignore`, ..) or not.
    read_ignore: bool,

    /// Whether to follow symlinks or not.
    follow_links: bool,

    /// Whether elements of output should be separated by a null character
    null_separator: bool,

    /// The maximum search depth, or `None` if no maximum search depth should be set.
    ///
    /// A depth of `1` includes all files under the current directory, a depth of `2` also includes
    /// all files under subdirectories of the current directory, etc.
    max_depth: Option<usize>,

    /// The number of threads to use.
    threads: Option<usize>,

    /// Time to buffer results internally before streaming to the console. This is useful to
    /// provide a sorted output, in case the total execution time is shorter than
    /// `max_buffer_time`.
    max_buffer_time: Option<time::Duration>,

    /// Display results as relative or absolute path.
    path_display: PathDisplay,

    /// `None` if the output should not be colorized. Otherwise, a `LsColors` instance that defines
    /// how to style different filetypes.
    ls_colors: Option<LsColors>
}

/// Root directory
static ROOT_DIR : &'static str = "/";

/// Parent directory
static PARENT_DIR : &'static str = "..";

/// Print a search result to the console.
fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) {
    let path_full = base.join(entry);

    let path_str = entry.to_string_lossy();

    #[cfg(target_family = "unix")]
    let is_executable = |p: &std::path::PathBuf| {
        p.metadata()
         .ok()
         .map(|f| f.permissions().mode() & 0o111 != 0)
         .unwrap_or(false)
    };

    #[cfg(not(target_family = "unix"))]
    let is_executable =  |p: &std::path::PathBuf| {false};

    let stdout = std::io::stdout();
    let mut handle = stdout.lock();

    if let Some(ref ls_colors) = config.ls_colors {
        let default_style = ansi_term::Style::default();

        let mut component_path = base.to_path_buf();

        if config.path_display == PathDisplay::Absolute {
            print!("{}", ls_colors.directory.paint(ROOT_DIR));
        }

        // Traverse the path and colorize each component
        for component in entry.components() {
            let comp_str = match component {
                Component::Normal(p) => p.to_string_lossy(),
                Component::ParentDir => Cow::from(PARENT_DIR),
                _                    => error("Error: unexpected path component.")
            };

            component_path.push(Path::new(comp_str.deref()));

            let style =
                if component_path.symlink_metadata()
                                 .map(|md| md.file_type().is_symlink())
                                 .unwrap_or(false) {
                    &ls_colors.symlink
                } else if component_path.is_dir() {
                    &ls_colors.directory
                } else if is_executable(&component_path) {
                    &ls_colors.executable
                } else {
                    // Look up file name
                    let o_style =
                        component_path.file_name()
                                      .and_then(|n| n.to_str())
                                      .and_then(|n| ls_colors.filenames.get(n));

                    match o_style {
                        Some(s) => s,
                        None =>
                            // Look up file extension
                            component_path.extension()
                                          .and_then(|e| e.to_str())
                                          .and_then(|e| ls_colors.extensions.get(e))
                                          .unwrap_or(&default_style)
                    }
                };

            write!(handle, "{}", style.paint(comp_str)).ok();

            if component_path.is_dir() && component_path != path_full {
                let sep = std::path::MAIN_SEPARATOR.to_string();
                write!(handle, "{}", style.paint(sep)).ok();
            }
        }

        let r = if config.null_separator {
          write!(handle, "{}", '\0')
        } else {
          writeln!(handle, "")
        };
        if r.is_err() {
            // Probably a broken pipe. Exit gracefully.
            process::exit(0);
        }
    } else {
        // Uncolorized output

        let prefix = if config.path_display == PathDisplay::Absolute { ROOT_DIR } else { "" };
        let separator = if config.null_separator { "\0" } else { "\n" };

        let r = write!(&mut std::io::stdout(), "{}{}{}", prefix, path_str, separator);

        if r.is_err() {
            // Probably a broken pipe. Exit gracefully.
            process::exit(0);
        }
    }
}

/// Recursively scan the given search path and search for files / pathnames matching the pattern.
fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
    let (tx, rx) = channel();

    let walker = WalkBuilder::new(root)
                     .hidden(config.ignore_hidden)
                     .ignore(config.read_ignore)
                     .git_ignore(config.read_ignore)
                     .parents(config.read_ignore)
                     .git_global(config.read_ignore)
                     .git_exclude(config.read_ignore)
                     .follow_links(config.follow_links)
                     .max_depth(config.max_depth)
                     .threads(config.threads.unwrap_or(0))
                     .build_parallel();

    walker.run(|| {
        let base = base.to_owned();
        let config = config.clone();
        let pattern = pattern.clone();
        let tx_thread = tx.clone();

        Box::new(move |entry_o| {
            let entry = match entry_o {
                Ok(e) => e,
                Err(_) => return ignore::WalkState::Continue
            };

            let path_rel_buf = match fshelper::path_relative_from(entry.path(), &*base) {
                Some(p) => p,
                None => error("Error: could not get relative path for directory entry.")
            };
            let path_rel = path_rel_buf.as_path();

            let search_str_o =
                if config.search_full_path {
                    Some(path_rel.to_string_lossy())
                } else {
                    path_rel.file_name()
                            .map(|f| f.to_string_lossy())
                };

            if let Some(search_str) = search_str_o {
                // TODO: take care of the unwrap call
                pattern.find(&*search_str)
                       .map(|_| tx_thread.send(path_rel_buf.to_owned()).unwrap());
            }

            ignore::WalkState::Continue
        })
    });

    // Drop the initial sender. If we don't do this, the receiver will block even
    // if all threads have finished, since there is still one sender around.
    drop(tx);

    let start = time::Instant::now();

    let mut buffer = vec!();

    // Are we still buffering (or streaming to the console)?
    let mut buffering = true;

    // Maximum time to wait before we start streaming to the console.
    let max_buffer_time = config.max_buffer_time
                                .unwrap_or_else(|| time::Duration::from_millis(100));

    for value in rx {
        if buffering {
            buffer.push(value);

            // Have we reached the maximum time?
            if time::Instant::now() - start > max_buffer_time {
                // Flush the buffer
                for v in &buffer {
                    print_entry(base, v, &config);
                }
                buffer.clear();

                // Start streaming
                buffering = false;
            }
        } else {
            print_entry(base, &value, &config);
        }
    }

    // If we have finished fast enough (faster than max_buffer_time), we haven't streamed
    // anything to the console, yet. In this case, sort the output and print it:
    buffer.sort();
    for value in buffer {
        print_entry(base, &value, &config);
    }
}

/// Print error message to stderr and exit with status `1`.
fn error(message: &str) -> ! {
    writeln!(&mut std::io::stderr(), "{}", message)
        .expect("Failed writing to stderr");
    process::exit(1);
}

fn main() {
    let matches =
        App::new("fd")
            .version(crate_version!())
            .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>]")
            .setting(AppSettings::ColoredHelp)
            .setting(AppSettings::DeriveDisplayOrder)
            .arg(Arg::with_name("case-sensitive")
                        .long("case-sensitive")
                        .short("s")
                        .help("Case-sensitive search (default: smart case)"))
            .arg(Arg::with_name("full-path")
                        .long("full-path")
                        .short("p")
                        .help("Search full path (default: file-/dirname only)"))
            .arg(Arg::with_name("hidden")
                        .long("hidden")
                        .short("H")
                        .help("Search hidden files and directories"))
            .arg(Arg::with_name("no-ignore")
                        .long("no-ignore")
                        .short("I")
                        .help("Do not respect .(git)ignore files"))
            .arg(Arg::with_name("follow")
                        .long("follow")
                        .short("f")
                        .help("Follow symlinks"))
            .arg(Arg::with_name("null_separator")
                        .long("print0")
                        .short("0")
                        .help("Separate results by the null character"))
            .arg(Arg::with_name("absolute-path")
                        .long("absolute-path")
                        .short("a")
                        .help("Show absolute instead of relative paths"))
            .arg(Arg::with_name("no-color")
                        .long("no-color")
                        .short("n")
                        .help("Do not colorize output"))
            .arg(Arg::with_name("depth")
                        .long("max-depth")
                        .short("d")
                        .takes_value(true)
                        .help("Set maximum search depth (default: none)"))
            .arg(Arg::with_name("threads")
                        .long("threads")
                        .short("j")
                        .takes_value(true)
                        .help("the number of threads to use"))
            .arg(Arg::with_name("max-buffer-time")
                        .long("max-buffer-time")
                        .takes_value(true)
                        .help("the time to buffer in milliseconds before streaming to the console")
                        .hidden(true))
            .arg(Arg::with_name("pattern")
                        .help("the search pattern, a regular expression (optional)"))
            .arg(Arg::with_name("path")
                        .help("the root directory for the filesystem search (optional)"))
            .get_matches();

    // Get the search pattern
    let empty_pattern = String::new();
    let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern);

    // Get the current working directory
    let current_dir_buf = match env::current_dir() {
        Ok(cd) => cd,
        Err(_) => error("Error: could not get current directory.")
    };
    let current_dir = current_dir_buf.as_path();

    // Get the root directory for the search
    let mut root_dir_is_absolute = false;
    let root_dir_buf = if let Some(rd) = matches.value_of("path") {
        let path = Path::new(rd);

        root_dir_is_absolute = path.is_absolute();

        path.canonicalize().unwrap_or_else(
            |_| error(&format!("Error: could not find directory '{}'.", rd))
        )
    } else {
        current_dir_buf.clone()
    };

    if !root_dir_buf.is_dir() {
        error(&format!("Error: '{}' is not a directory.", root_dir_buf.to_string_lossy()));
    }

    let root_dir = root_dir_buf.as_path();

    // The search will be case-sensitive if the command line flag is set or
    // if the pattern has an uppercase character (smart case).
    let case_sensitive = matches.is_present("case-sensitive") ||
                         pattern.chars().any(char::is_uppercase);

    let colored_output = !matches.is_present("no-color") &&
                         atty::is(Stream::Stdout);

    let ls_colors =
        if colored_output {
            Some(
                env::var("LS_COLORS")
                    .ok()
                    .map(|val| LsColors::from_string(&val))
                    .unwrap_or_default()
            )
        } else {
            None
        };

    let config = FdOptions {
        case_sensitive:    case_sensitive,
        search_full_path:  matches.is_present("full-path"),
        ignore_hidden:     !matches.is_present("hidden"),
        read_ignore:       !matches.is_present("no-ignore"),
        follow_links:      matches.is_present("follow"),
        null_separator:    matches.is_present("null_separator"),
        max_depth:         matches.value_of("depth")
                                   .and_then(|n| usize::from_str_radix(n, 10).ok()),
        threads:           matches.value_of("threads")
                                   .and_then(|n| usize::from_str_radix(n, 10).ok()),
        max_buffer_time:   matches.value_of("max-buffer-time")
                                  .and_then(|n| u64::from_str_radix(n, 10).ok())
                                  .map(time::Duration::from_millis),
        path_display:      if matches.is_present("absolute-path") || root_dir_is_absolute {
                               PathDisplay::Absolute
                           } else {
                               PathDisplay::Relative
                           },
        ls_colors:         ls_colors
    };

    let root = Path::new(ROOT_DIR);
    let base = match config.path_display {
        PathDisplay::Relative => current_dir,
        PathDisplay::Absolute => root
    };

    match RegexBuilder::new(pattern)
              .case_insensitive(!config.case_sensitive)
              .build() {
        Ok(re)   => scan(root_dir, Arc::new(re), base, Arc::new(config)),
        Err(err) => error(err.description())
    }
}
<|endoftext|>


<commit_before>extern crate image;
extern crate rand;
extern crate docopt;
extern crate rustc_serialize;
extern crate num_cpus;
extern crate scoped_threadpool;
extern crate clock_ticks;
extern crate tray_rust;

use std::path::PathBuf;
use std::io::ErrorKind;

use docopt::Docopt;

use tray_rust::{scene, exec};
use tray_rust::exec::{Exec, distrib};

static USAGE: &'static str = "
Usage:
    tray_rust <scenefile> [-o <path>] [-n <number>] [--start-frame <number>] [--end-frame <number>]
    tray_rust <scenefile> --master <workers>... [-o <path>] [--start-frame <number>] [--end-frame <number>]
    tray_rust --worker [-n <number>]
    tray_rust (-h | --help)

Options:
  -o <path>               Specify the output file or directory to save the image or frames. Supported formats are
                          PNG, JPG and PPM. Default is 'frame<#>.png'.
  -n <number>             Specify the number of threads to use for rendering. Defaults to the number of cores
                          on the system.
  --start-frame <number>  Specify frame to start rendering at, specifies an inclusive range [start, end]
  --end-frame <number>    Specify frame to stop rendering at, specifies an inclusive range [start, end]
  --master                Start a master process to manage the worker nodes in <workers>... for distributed
                          rendering. The master collects results from workers and saves the image(s).
  <workers>...            Specify the list of worker nodes the master will connect too.
  --worker                Start a worker process that will listen for a master process to contact it and
                          instruct on what to start rendering. The worker will report its results back to
                          the master.
  -h, --help              Show this message.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_scenefile: String,
    flag_o: Option<String>,
    flag_n: Option<u32>,
    flag_start_frame: Option<usize>,
    flag_end_frame: Option<usize>,
    flag_master: Option<bool>,
    arg_workers: Vec<String>,
    flag_worker: Option<bool>,
}

fn single_node_render(args: Args) {
    let num_threads = match args.flag_n {
        Some(n) => n,
        None => num_cpus::get() as u32,
    };
    let out_path = match &args.flag_o {
        &Some(ref f) => {
            let p = PathBuf::from(f);
            // If we're writing to a directory make sure it exists
            if p.extension() == None {
                match std::fs::create_dir(p.as_path()) {
                    Err(e) => {
                        if e.kind() != ErrorKind::AlreadyExists {
                            panic!("Failed to create output directory");
                        }
                    },
                    Ok(_) => {},
                }
            }
            p
        },
        &None => PathBuf::from("./"),
    };

    let (mut scene, mut rt, spp, mut frame_info) = scene::Scene::load_file(&args.arg_scenefile[..]);
    let dim = rt.dimensions();

    frame_info.start = match args.flag_start_frame {
        Some(x) => x,
        _ => frame_info.start,
    };
    frame_info.end = match args.flag_end_frame {
        Some(x) => x,
        _ => frame_info.end,
    };
    let scene_start = clock_ticks::precise_time_s();
    let mut config = exec::Config::new(out_path, spp, num_threads, frame_info, (0, 0));
    let mut exec = exec::MultiThreaded::new(num_threads);
    for i in frame_info.start..frame_info.end + 1 {
        config.current_frame = i;
        exec.render(&mut scene, &mut rt, &config);

        let img = rt.get_render();
        let out_file = match config.out_path.extension() {
            Some(_) => config.out_path.clone(),
            None => config.out_path.join(PathBuf::from(format!("frame{:05}.png", i))),
        };
        match image::save_buffer(&out_file.as_path(), &img[..], dim.0 as u32, dim.1 as u32, image::RGB(8)) {
            Ok(_) => {},
            Err(e) => println!("Error saving image, {}", e),
        };
        rt.clear();
        println!("Frame {}: rendered to '{}'\n--------------------", i, out_file.display());
    }
    let time = clock_ticks::precise_time_s() - scene_start;
    println!("Rendering entire sequence took {}s", time);
}

fn master_node(args: Args) {
    let out_path = match &args.flag_o {
        &Some(ref f) => {
            let p = PathBuf::from(f);
            // If we're writing to a directory make sure it exists
            if p.extension() == None {
                match std::fs::create_dir(p.as_path()) {
                    Err(e) => {
                        if e.kind() != ErrorKind::AlreadyExists {
                            panic!("Failed to create output directory");
                        }
                    },
                    Ok(_) => {},
                }
            }
            p
        },
        &None => PathBuf::from("./"),
    };

    let (_, rt, spp, frame_info) = scene::Scene::load_file(&args.arg_scenefile[..]);

    let frame_subset = match (args.flag_start_frame, args.flag_end_frame) {
        (Some(x), Some(y)) => Some((x, y)),
        (Some(x), None) => Some((x, frame_info.end)),
        (None, Some(x)) => Some((frame_info.start, x)),
        _ => None,
    };
    let scene_start = clock_ticks::precise_time_s();
    let config = exec::Config::new(out_path, spp, 0, frame_info, (0, 0));
    let mut master = distrib::Master::start_workers(args.arg_workers, config, &args.arg_scenefile, rt, frame_subset);
    master.wait_for_results();
    let time = clock_ticks::precise_time_s() - scene_start;
    println!("Rendering entire sequence took {}s", time);
}

fn worker_node(args: Args) {
    let num_threads = match args.flag_n {
        Some(n) => n,
        None => num_cpus::get() as u32,
    };
    let mut exec = exec::MultiThreaded::new(num_threads);
    let mut worker = distrib::Worker::listen_for_master(num_threads);
    let scene_start = clock_ticks::precise_time_s();
    for i in worker.config.frame_info.start..worker.config.frame_info.end + 1 {
        worker.config.current_frame = i;
        exec.render(&mut worker.scene, &mut worker.render_target, &worker.config);
        worker.send_results();
        worker.render_target.clear();
        println!("Frame {}: rendered\n--------------------", i);
    }
    let time = clock_ticks::precise_time_s() - scene_start;
    println!("Rendering entire sequence took {}s", time);
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
    println!("Got args {:?}", args);

    if Some(true) == args.flag_master {
        // TODO: no workers in the list should be handled by Docopt right?
        master_node(args);
    } else if Some(true) == args.flag_worker {
        worker_node(args);
    } else {
        single_node_render(args);
    }
}

<commit_msg>Minor debug test, have worker save out the frame it worked on<commit_after>extern crate image;
extern crate rand;
extern crate docopt;
extern crate rustc_serialize;
extern crate num_cpus;
extern crate scoped_threadpool;
extern crate clock_ticks;
extern crate tray_rust;

use std::path::PathBuf;
use std::io::ErrorKind;

use docopt::Docopt;

use tray_rust::{scene, exec};
use tray_rust::exec::{Exec, distrib};

static USAGE: &'static str = "
Usage:
    tray_rust <scenefile> [-o <path>] [-n <number>] [--start-frame <number>] [--end-frame <number>]
    tray_rust <scenefile> --master <workers>... [-o <path>] [--start-frame <number>] [--end-frame <number>]
    tray_rust --worker [-n <number>]
    tray_rust (-h | --help)

Options:
  -o <path>               Specify the output file or directory to save the image or frames. Supported formats are
                          PNG, JPG and PPM. Default is 'frame<#>.png'.
  -n <number>             Specify the number of threads to use for rendering. Defaults to the number of cores
                          on the system.
  --start-frame <number>  Specify frame to start rendering at, specifies an inclusive range [start, end]
  --end-frame <number>    Specify frame to stop rendering at, specifies an inclusive range [start, end]
  --master                Start a master process to manage the worker nodes in <workers>... for distributed
                          rendering. The master collects results from workers and saves the image(s).
  <workers>...            Specify the list of worker nodes the master will connect too.
  --worker                Start a worker process that will listen for a master process to contact it and
                          instruct on what to start rendering. The worker will report its results back to
                          the master.
  -h, --help              Show this message.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_scenefile: String,
    flag_o: Option<String>,
    flag_n: Option<u32>,
    flag_start_frame: Option<usize>,
    flag_end_frame: Option<usize>,
    flag_master: Option<bool>,
    arg_workers: Vec<String>,
    flag_worker: Option<bool>,
}

fn single_node_render(args: Args) {
    let num_threads = match args.flag_n {
        Some(n) => n,
        None => num_cpus::get() as u32,
    };
    let out_path = match &args.flag_o {
        &Some(ref f) => {
            let p = PathBuf::from(f);
            // If we're writing to a directory make sure it exists
            if p.extension() == None {
                match std::fs::create_dir(p.as_path()) {
                    Err(e) => {
                        if e.kind() != ErrorKind::AlreadyExists {
                            panic!("Failed to create output directory");
                        }
                    },
                    Ok(_) => {},
                }
            }
            p
        },
        &None => PathBuf::from("./"),
    };

    let (mut scene, mut rt, spp, mut frame_info) = scene::Scene::load_file(&args.arg_scenefile[..]);
    let dim = rt.dimensions();

    frame_info.start = match args.flag_start_frame {
        Some(x) => x,
        _ => frame_info.start,
    };
    frame_info.end = match args.flag_end_frame {
        Some(x) => x,
        _ => frame_info.end,
    };
    let scene_start = clock_ticks::precise_time_s();
    let mut config = exec::Config::new(out_path, spp, num_threads, frame_info, (0, 0));
    let mut exec = exec::MultiThreaded::new(num_threads);
    for i in frame_info.start..frame_info.end + 1 {
        config.current_frame = i;
        exec.render(&mut scene, &mut rt, &config);

        let img = rt.get_render();
        let out_file = match config.out_path.extension() {
            Some(_) => config.out_path.clone(),
            None => config.out_path.join(PathBuf::from(format!("frame{:05}.png", i))),
        };
        match image::save_buffer(&out_file.as_path(), &img[..], dim.0 as u32, dim.1 as u32, image::RGB(8)) {
            Ok(_) => {},
            Err(e) => println!("Error saving image, {}", e),
        };
        rt.clear();
        println!("Frame {}: rendered to '{}'\n--------------------", i, out_file.display());
    }
    let time = clock_ticks::precise_time_s() - scene_start;
    println!("Rendering entire sequence took {}s", time);
}

fn master_node(args: Args) {
    let out_path = match &args.flag_o {
        &Some(ref f) => {
            let p = PathBuf::from(f);
            // If we're writing to a directory make sure it exists
            if p.extension() == None {
                match std::fs::create_dir(p.as_path()) {
                    Err(e) => {
                        if e.kind() != ErrorKind::AlreadyExists {
                            panic!("Failed to create output directory");
                        }
                    },
                    Ok(_) => {},
                }
            }
            p
        },
        &None => PathBuf::from("./"),
    };

    let (_, rt, spp, frame_info) = scene::Scene::load_file(&args.arg_scenefile[..]);

    let frame_subset = match (args.flag_start_frame, args.flag_end_frame) {
        (Some(x), Some(y)) => Some((x, y)),
        (Some(x), None) => Some((x, frame_info.end)),
        (None, Some(x)) => Some((frame_info.start, x)),
        _ => None,
    };
    let scene_start = clock_ticks::precise_time_s();
    let config = exec::Config::new(out_path, spp, 0, frame_info, (0, 0));
    let mut master = distrib::Master::start_workers(args.arg_workers, config, &args.arg_scenefile, rt, frame_subset);
    master.wait_for_results();
    let time = clock_ticks::precise_time_s() - scene_start;
    println!("Rendering entire sequence took {}s", time);
}

fn worker_node(args: Args) {
    let num_threads = match args.flag_n {
        Some(n) => n,
        None => num_cpus::get() as u32,
    };
    let mut exec = exec::MultiThreaded::new(num_threads);
    let mut worker = distrib::Worker::listen_for_master(num_threads);
    let scene_start = clock_ticks::precise_time_s();
    for i in worker.config.frame_info.start..worker.config.frame_info.end + 1 {
        worker.config.current_frame = i;
        exec.render(&mut worker.scene, &mut worker.render_target, &worker.config);
        worker.send_results();
        let dim = worker.render_target.dimensions();
        let img = worker.render_target.get_render();
        let out_file = match worker.config.out_path.extension() {
            Some(_) => worker.config.out_path.clone(),
            None => worker.config.out_path.join(PathBuf::from(format!("/wrk/frame{:05}.png", i))),
        };
        match image::save_buffer(&out_file.as_path(), &img[..], dim.0 as u32, dim.1 as u32, image::RGB(8)) {
            Ok(_) => {},
            Err(e) => println!("Error saving image, {}", e),
        };
        worker.render_target.clear();
        println!("Frame {}: rendered\n--------------------", i);
    }
    let time = clock_ticks::precise_time_s() - scene_start;
    println!("Rendering entire sequence took {}s", time);
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
    println!("Got args {:?}", args);

    if Some(true) == args.flag_master {
        // TODO: no workers in the list should be handled by Docopt right?
        master_node(args);
    } else if Some(true) == args.flag_worker {
        worker_node(args);
    } else {
        single_node_render(args);
    }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;
extern crate image;
extern crate nalgebra;

mod steve;

const VERT_PROG: &'static str = include_str!("vert.glsl");

const FRAG_PROG: &'static str = include_str!("frag.glsl");

use glium::{Surface, VertexBuffer, Frame, Program};
use glium::index::NoIndices;
use glium::texture::srgb_texture2d::SrgbTexture2d;
use glium::uniforms::MagnifySamplerFilter;
use glium::draw_parameters::DepthTest;
use glium::backend::glutin_backend::GlutinFacade;
use glium::index::{PrimitiveType, IndicesSource};
use glium::vertex::BufferCreationError;
use glium::glutin::{Event, ElementState, VirtualKeyCode};
use std::f32::consts::{FRAC_PI_2, PI};
use std::thread::sleep_ms;
use nalgebra::{Rot3, Iso3, Vec3, Persp3, ToHomogeneous};

enum NextAction {
    Quit,
}

fn draw_frame(target: &mut Frame, vertex_buffer: &VertexBuffer<steve::Vertex>, index_buffer: &NoIndices, shader_prog: &Program, texture: &SrgbTexture2d, t: f32) {
    let perspective = {
        let (width, height) = target.get_dimensions();
        let aspect_ratio = width as f32 / height as f32;
        //println!("Aspect ratio: {} ({} x {})", aspect_ratio, height, width);

        let fov: f32 = 3.141592 / 3.0;
        let zfar = 1024.0;
        let znear = 0.1;

        let pmat = Persp3::new(aspect_ratio, fov, znear, zfar).to_mat();
        pmat.as_array().clone()
    };

    let rot1 = Rot3::new(Vec3::new(-FRAC_PI_2, 0.0, 0.0));
    let rot2 = Rot3::new(Vec3::new(0.0, FRAC_PI_2, 0.0));
    let rot3 = Rot3::new(Vec3::new(0.0, t, 0.0));
    let model = Iso3::new_with_rotmat(Vec3::new(0.0, 10.0, 100.0), rot3 * rot2 * rot1)
        .to_homogeneous().as_array().clone();

    let uniforms = uniform!{
        model: model,
        view:  [
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0f32],
            ],
        projection:  perspective,
        tex: texture.sampled().magnify_filter(MagnifySamplerFilter::Nearest),
    };

    let params = glium::DrawParameters {
        depth: glium::Depth {
            test: DepthTest::IfLess,
            write: true,
            .. Default::default()
        },
        .. Default::default()
    };

    target.draw(vertex_buffer, index_buffer, shader_prog, &uniforms, &params).unwrap();
}

fn handle_input(turn_rate: &mut f32, state: ElementState, key_char: u8, vk_opt: &Option<VirtualKeyCode>) -> Option<NextAction> {
    let mut next_action = None;
    let new_turn_rate = match *vk_opt {
        Some(vk) => match (vk, state) {
            (VirtualKeyCode::Right, ElementState::Pressed)  => PI / 200.0,
            (VirtualKeyCode::Left, ElementState::Pressed)  => -PI / 200.0,
            (VirtualKeyCode::Right, ElementState::Released) => 0.0f32,
            (VirtualKeyCode::Left, ElementState::Released) => 0.0f32,
            (VirtualKeyCode::Q, ElementState::Released) => { next_action =  Some(NextAction::Quit); 0.0f32},
            _ => 0.0f32
        },
        None => 0.0f32
    };
    *turn_rate = new_turn_rate;
    next_action
}

pub struct ModelPiece {
    vbo: VertexBuffer<steve::Vertex>,
    prim: PrimitiveType,
}

impl ModelPiece {
    fn new(display: &GlutinFacade, verts: &[steve::Vertex], prim: PrimitiveType) -> Result<Self, BufferCreationError> {
        let vertex_buffer = match VertexBuffer::new(display, verts) {
            Ok(vbo) => vbo,
            Err(e) => return Err(e),
        };
        Ok(ModelPiece{vbo: vertex_buffer, prim: prim})
    }
}

fn mainloop(display: &GlutinFacade) {
    use std::io::Cursor;

    let head = ModelPiece::new(display, &steve::HEAD, PrimitiveType::TrianglesList).unwrap();
    let torso = ModelPiece::new(display, &steve::TORSO, PrimitiveType::TrianglesList).unwrap();

    let larm = ModelPiece::new(display, &steve::LARM, PrimitiveType::TrianglesList).unwrap();
    let rarm = ModelPiece::new(display, &steve::RARM, PrimitiveType::TrianglesList).unwrap();

    let lleg = ModelPiece::new(display, &steve::LLEG, PrimitiveType::TrianglesList).unwrap();
    let rleg = ModelPiece::new(display, &steve::RLEG, PrimitiveType::TrianglesList).unwrap();

    let shader_prog = Program::from_source(display, VERT_PROG, FRAG_PROG, None).unwrap();

    let image = image::load(Cursor::new(&include_bytes!("steve.png")[..]),
                            image::PNG).unwrap();
    let texture = SrgbTexture2d::new(display, image).unwrap();

    let mut t = 0.0f32;
    let mut turn_rate = 0.0f32;

    loop {
        t += turn_rate;

        for ev in display.poll_events() {
            match ev {
                Event::Closed => return,
                Event::KeyboardInput(state, key_char, vk_opt) => match handle_input(&mut turn_rate, state, key_char, &vk_opt) {
                    Some(NextAction::Quit) => return,
                    None => ()
                },
                _ => ()
            }
        }

        //let (width, height) = display.get_framebuffer_dimensions();
        //println!("Framebuffer dimensions: {} x {}", width, height);
        let mut target = display.draw();
        let indices = NoIndices(head.prim);
        target.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0);

        draw_frame(&mut target, &head.vbo, &indices, &shader_prog, &texture, t);
        draw_frame(&mut target, &torso.vbo, &indices, &shader_prog, &texture, t);

        draw_frame(&mut target, &larm.vbo, &indices, &shader_prog, &texture, t);
        draw_frame(&mut target, &rarm.vbo, &indices, &shader_prog, &texture, t);

        draw_frame(&mut target, &lleg.vbo, &indices, &shader_prog, &texture, t);
        draw_frame(&mut target, &rleg.vbo, &indices, &shader_prog, &texture, t);

        target.finish().unwrap();
        sleep_ms(16);
    }
}

fn main() {
    use glium::{DisplayBuild, GliumCreationError};
    use glium::glutin::{WindowBuilder, GlRequest, Api, GlProfile};
    let display_option = WindowBuilder::new()
        .with_gl(GlRequest::Specific(Api::OpenGl, (3, 3)))
        .with_gl_profile(GlProfile::Core)
        .with_depth_buffer(24)
        .with_vsync()
        .build_glium();
    println!("If there was a message about an error just now, ignore it.  I think the driver's on crack.");
    match display_option {
        Ok(display) => mainloop(&display),
        Err(creation_error) => match creation_error {
            GliumCreationError::BackendCreationError(_) => println!("Oh, crap!"),
            GliumCreationError::IncompatibleOpenGl(msg) => println!("Incompatible OpenGL: {}", msg)
        }
    }
}
<commit_msg>Cleanups<commit_after>#[macro_use]
extern crate glium;
extern crate image;
extern crate nalgebra;

mod steve;

const VERT_PROG: &'static str = include_str!("vert.glsl");

const FRAG_PROG: &'static str = include_str!("frag.glsl");

use glium::{Surface, VertexBuffer, Frame, Program};
use glium::index::NoIndices;
use glium::texture::srgb_texture2d::SrgbTexture2d;
use glium::uniforms::MagnifySamplerFilter;
use glium::draw_parameters::DepthTest;
use glium::backend::glutin_backend::GlutinFacade;
use glium::index::PrimitiveType;
use glium::vertex::BufferCreationError;
use glium::glutin::{Event, ElementState, VirtualKeyCode};
use std::f32::consts::{FRAC_PI_2, PI};
use std::thread::sleep_ms;
use nalgebra::{Rot3, Iso3, Vec3, Persp3, ToHomogeneous};

enum NextAction {
    Quit,
}

fn draw_frame(target: &mut Frame, vertex_buffer: &VertexBuffer<steve::Vertex>, index_buffer: &NoIndices, shader_prog: &Program, texture: &SrgbTexture2d, t: f32) {
    let perspective = {
        let (width, height) = target.get_dimensions();
        let aspect_ratio = width as f32 / height as f32;
        //println!("Aspect ratio: {} ({} x {})", aspect_ratio, height, width);

        let fov: f32 = 3.141592 / 3.0;
        let zfar = 1024.0;
        let znear = 0.1;

        let pmat = Persp3::new(aspect_ratio, fov, znear, zfar).to_mat();
        pmat.as_array().clone()
    };

    let rot1 = Rot3::new(Vec3::new(-FRAC_PI_2, 0.0, 0.0));
    let rot2 = Rot3::new(Vec3::new(0.0, FRAC_PI_2, 0.0));
    let rot3 = Rot3::new(Vec3::new(0.0, t, 0.0));
    let model = Iso3::new_with_rotmat(Vec3::new(0.0, 10.0, 100.0), rot3 * rot2 * rot1)
        .to_homogeneous().as_array().clone();

    let uniforms = uniform!{
        model: model,
        view:  [
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0f32],
            ],
        projection:  perspective,
        tex: texture.sampled().magnify_filter(MagnifySamplerFilter::Nearest),
    };

    let params = glium::DrawParameters {
        depth: glium::Depth {
            test: DepthTest::IfLess,
            write: true,
            .. Default::default()
        },
        .. Default::default()
    };

    target.draw(vertex_buffer, index_buffer, shader_prog, &uniforms, &params).unwrap();
}

fn handle_input(turn_rate: &mut f32, state: ElementState, vk_opt: &Option<VirtualKeyCode>) -> Option<NextAction> {
    let mut next_action = None;
    let new_turn_rate = match *vk_opt {
        Some(vk) => match (vk, state) {
            (VirtualKeyCode::Right, ElementState::Pressed)  => PI / 200.0,
            (VirtualKeyCode::Left, ElementState::Pressed)  => -PI / 200.0,
            (VirtualKeyCode::Right, ElementState::Released) => 0.0f32,
            (VirtualKeyCode::Left, ElementState::Released) => 0.0f32,
            (VirtualKeyCode::Q, ElementState::Released) => { next_action =  Some(NextAction::Quit); 0.0f32},
            _ => 0.0f32
        },
        None => 0.0f32
    };
    *turn_rate = new_turn_rate;
    next_action
}

pub struct ModelPiece {
    vbo: VertexBuffer<steve::Vertex>,
    prim: PrimitiveType,
}

impl ModelPiece {
    fn new(display: &GlutinFacade, verts: &[steve::Vertex], prim: PrimitiveType) -> Result<Self, BufferCreationError> {
        let vertex_buffer = match VertexBuffer::new(display, verts) {
            Ok(vbo) => vbo,
            Err(e) => return Err(e),
        };
        Ok(ModelPiece{vbo: vertex_buffer, prim: prim})
    }
}

fn mainloop(display: &GlutinFacade) {
    use std::io::Cursor;

    let head = ModelPiece::new(display, &steve::HEAD, PrimitiveType::TrianglesList).unwrap();
    let torso = ModelPiece::new(display, &steve::TORSO, PrimitiveType::TrianglesList).unwrap();

    let larm = ModelPiece::new(display, &steve::LARM, PrimitiveType::TrianglesList).unwrap();
    let rarm = ModelPiece::new(display, &steve::RARM, PrimitiveType::TrianglesList).unwrap();

    let lleg = ModelPiece::new(display, &steve::LLEG, PrimitiveType::TrianglesList).unwrap();
    let rleg = ModelPiece::new(display, &steve::RLEG, PrimitiveType::TrianglesList).unwrap();

    let shader_prog = Program::from_source(display, VERT_PROG, FRAG_PROG, None).unwrap();

    let image = image::load(Cursor::new(&include_bytes!("steve.png")[..]),
                            image::PNG).unwrap();
    let texture = SrgbTexture2d::new(display, image).unwrap();

    let mut t = 0.0f32;
    let mut turn_rate = 0.0f32;

    loop {
        t += turn_rate;

        for ev in display.poll_events() {
            match ev {
                Event::Closed => return,
                Event::KeyboardInput(state, _, vk_opt) => match handle_input(&mut turn_rate, state, &vk_opt) {
                    Some(NextAction::Quit) => return,
                    None => ()
                },
                _ => ()
            }
        }

        //let (width, height) = display.get_framebuffer_dimensions();
        //println!("Framebuffer dimensions: {} x {}", width, height);
        let mut target = display.draw();
        let indices = NoIndices(head.prim);
        target.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0);

        draw_frame(&mut target, &head.vbo, &indices, &shader_prog, &texture, t);
        draw_frame(&mut target, &torso.vbo, &indices, &shader_prog, &texture, t);

        draw_frame(&mut target, &larm.vbo, &indices, &shader_prog, &texture, t);
        draw_frame(&mut target, &rarm.vbo, &indices, &shader_prog, &texture, t);

        draw_frame(&mut target, &lleg.vbo, &indices, &shader_prog, &texture, t);
        draw_frame(&mut target, &rleg.vbo, &indices, &shader_prog, &texture, t);

        target.finish().unwrap();
        sleep_ms(16);
    }
}

fn main() {
    use glium::{DisplayBuild, GliumCreationError};
    use glium::glutin::{WindowBuilder, GlRequest, Api, GlProfile};
    let display_option = WindowBuilder::new()
        .with_gl(GlRequest::Specific(Api::OpenGl, (3, 3)))
        .with_gl_profile(GlProfile::Core)
        .with_depth_buffer(24)
        .with_vsync()
        .build_glium();
    println!("If there was a message about an error just now, ignore it.  I think the driver's on crack.");
    match display_option {
        Ok(display) => mainloop(&display),
        Err(creation_error) => match creation_error {
            GliumCreationError::BackendCreationError(_) => println!("Oh, crap!"),
            GliumCreationError::IncompatibleOpenGl(msg) => println!("Incompatible OpenGL: {}", msg)
        }
    }
}
<|endoftext|>


<commit_before>extern crate cgmath;
pub use self::cgmath::*;
<commit_msg>Add map_range, partial_max, min, clamp math functions<commit_after>extern crate cgmath;
pub use self::cgmath::*;

use self::cgmath::num_traits::{NumCast, One};
use std::ops::Add;

/// Map a value from a given range to a new given range.
pub fn map_range<X, Y>(val: X, in_min: X, in_max: X, out_min: Y, out_max: Y) -> Y
where
    X: NumCast,
    Y: NumCast,
{
    let val_f: f64 = NumCast::from(val).unwrap();
    let in_min_f: f64 = NumCast::from(in_min).unwrap();
    let in_max_f: f64 = NumCast::from(in_max).unwrap();
    let out_min_f: f64 = NumCast::from(out_min).unwrap();
    let out_max_f: f64 = NumCast::from(out_max).unwrap();
    NumCast::from(
        (val_f - in_min_f) / (in_max_f - in_min_f) * (out_max_f - out_min_f) + out_min_f
    ).unwrap()
}

/// The max between two partially ordered values.
pub fn partial_max<T>(a: T, b: T) -> T
where
    T: PartialOrd,
{
    if a >= b { a } else { b }
}

/// The min between two partially ordered values.
pub fn partial_min<T>(a: T, b: T) -> T
where
    T: PartialOrd,
{
    if a <= b { a } else { b }
}

/// Clamp a value between some range.
pub fn clamp<T>(n: T, start: T, end: T) -> T
where
    T: PartialOrd,
{
    if start <= end {
        if n < start { start } else if n > end { end } else { n }
    } else {
        if n < end { end } else if n > start { start } else { n }
    }
}

pub fn two<S>() -> S
where
    S: Add<Output=S> + One,
{
    S::one() + S::one()
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_derive;

extern crate serde;

#[macro_use]
extern crate serde_jsonrc;

use serde_jsonrc::{from_str, Value};

#[test]
fn test_trailing_comma_object() {
    let s = r#"
    {
        "key": "value",
    }"#;
    let value: Value = from_str(s).unwrap();
    assert_eq!(value, json!({"key": "value"}));
}

#[test]
fn test_trailing_comma_array() {
    let s = r#"
    {
        "key": [
            "one",
            "two",
            "three",
        ]
    }"#;
    let value: Value = from_str(s).unwrap();
    assert_eq!(value, json!({"key": ["one", "two", "three"]}));
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
enum Animal {
    Dog,
    Frog(String, Vec<isize>),
    Cat { age: usize, name: String },
    AntHive(Vec<String>),
}

#[test]
fn test_parse_enum_as_array_with_deny_unknown_fields() {
    let animal: Animal = from_str("{\"Cat\":[0, \"Kate\",]}").unwrap();
    assert_eq!(
        animal,
        Animal::Cat {
            age: 0,
            name: "Kate".to_string()
        }
    );
}

#[test]
fn test_parse_enum_as_object_with_deny_unknown_fields() {
    let animal: Animal = from_str("{\"Cat\":{\"age\": 2, \"name\": \"Kate\",}}").unwrap();
    assert_eq!(
        animal,
        Animal::Cat {
            age: 2,
            name: "Kate".to_string()
        }
    );
}
<commit_msg>Add tests to verify duplicate commas are rejected.<commit_after>#[macro_use]
extern crate serde_derive;

extern crate serde;

#[macro_use]
extern crate serde_jsonrc;

use serde_jsonrc::{from_str, Value};

#[test]
fn test_trailing_comma_object() {
    let s = r#"
    {
        "key": "value",
    }"#;
    let value: Value = from_str(s).unwrap();
    assert_eq!(value, json!({"key": "value"}));
}

#[test]
fn test_double_comma_object() {
    let s = r#"
    {
        "key1": "value1",,
        "key2": "value2"
    }"#;
    let actual = from_str::<Value>(s).unwrap_err().to_string();
    assert_eq!(actual, "key must be a string at line 3 column 26");
}

#[test]
fn test_double_trailing_comma_object() {
    let s = r#"
    {
        "key1": "value1",
        "key2": "value2",,
    }"#;
    let actual = from_str::<Value>(s).unwrap_err().to_string();
    assert_eq!(actual, "key must be a string at line 4 column 26");
}

#[test]
fn test_trailing_comma_array() {
    let s = r#"
    {
        "key": [
            "one",
            "two",
            "three",
        ]
    }"#;
    let value: Value = from_str(s).unwrap();
    assert_eq!(value, json!({"key": ["one", "two", "three"]}));
}

#[test]
fn test_double_comma_array() {
    let s = r#"
    {
        "key": [
            "one",,
            "two",
        ]
    }"#;
    let actual = from_str::<Value>(s).unwrap_err().to_string();
    assert_eq!(actual, "expected value at line 4 column 19");
}

#[test]
fn test_double_trailing_comma_array() {
    let s = r#"
    {
        "key": [
            "one",
            "two",,
        ]
    }"#;
    let actual = from_str::<Value>(s).unwrap_err().to_string();
    assert_eq!(actual, "expected value at line 5 column 19");
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
enum Animal {
    Dog,
    Frog(String, Vec<isize>),
    Cat { age: usize, name: String },
    AntHive(Vec<String>),
}

#[test]
fn test_parse_enum_as_array_with_deny_unknown_fields() {
    let animal: Animal = from_str("{\"Cat\":[0, \"Kate\",]}").unwrap();
    assert_eq!(
        animal,
        Animal::Cat {
            age: 0,
            name: "Kate".to_string()
        }
    );
}

#[test]
fn test_parse_enum_as_object_with_deny_unknown_fields() {
    let animal: Animal = from_str("{\"Cat\":{\"age\": 2, \"name\": \"Kate\",}}").unwrap();
    assert_eq!(
        animal,
        Animal::Cat {
            age: 2,
            name: "Kate".to_string()
        }
    );
}
<|endoftext|>