<commit_before>

extern crate cratesfyi;
extern crate clap;
extern crate log;
extern crate env_logger;
extern crate time;
extern crate rustwide;

use std::env;
use std::path::{Path, PathBuf};

use clap::{Arg, App, AppSettings, SubCommand};
use cratesfyi::{DocBuilder, RustwideBuilder, DocBuilderOptions, db};
use cratesfyi::utils::add_crate_to_queue;
use cratesfyi::start_web_server;
use cratesfyi::db::{add_path_into_database, connect_db};

pub fn main() {
    logger_init();

    let matches = App::new("cratesfyi")
        .version(cratesfyi::BUILD_VERSION)
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("build")
            .about("Builds documentation in a chroot environment")
            .setting(AppSettings::ArgRequiredElseHelp)
            .arg(Arg::with_name("PREFIX")
                .short("P")
                .long("prefix")
                .takes_value(true))
            .arg(Arg::with_name("CRATES_IO_INDEX_PATH")
                .long("crates-io-index-path")
                .help("Sets crates.io-index path")
                .takes_value(true))
            .arg(Arg::with_name("SKIP_IF_EXISTS")
                .short("s")
                .long("skip")
                .help("Skips building documentation if documentation exists"))
            .arg(Arg::with_name("SKIP_IF_LOG_EXISTS")
                .long("skip-if-log-exists")
                .help("Skips building documentation if build log exists"))
            .arg(Arg::with_name("KEEP_BUILD_DIRECTORY")
                .short("-k")
                .long("keep-build-directory")
                .help("Keeps build directory after build."))
            .subcommand(SubCommand::with_name("world")
                .about("Builds documentation of every crate"))
            .subcommand(SubCommand::with_name("crate")
                .about("Builds documentation for a crate")
                .arg(Arg::with_name("CRATE_NAME")
                    .index(1)
                    .required_unless("LOCAL")
                    .requires("CRATE_VERSION")
                    .help("Crate name"))
                .arg(Arg::with_name("CRATE_VERSION")
                    .index(2)
                    .help("Version of crate"))
                .arg(Arg::with_name("LOCAL")
                    .short("-l")
                    .long("--local")
                    .takes_value(true)
                    .conflicts_with_all(&["CRATE_NAME", "CRATE_VERSION"])
                    .help("Build a crate at a specific path")))
            .subcommand(SubCommand::with_name("update-toolchain")
                .about("update the curretntly installed rustup toolchain")
                .arg(Arg::with_name("ONLY_FIRST_TIME")
                     .long("only-first-time")
                     .help("update toolchain only if no toolchain is currently installed")))
            .subcommand(SubCommand::with_name("add-essential-files")
                .about("Adds essential files for the installed version of rustc"))
            .subcommand(SubCommand::with_name("lock")
                .about("Locks cratesfyi daemon to stop building new crates"))
            .subcommand(SubCommand::with_name("unlock")
                .about("Unlocks cratesfyi daemon to continue building new crates"))
            .subcommand(SubCommand::with_name("print-options")))
        .subcommand(SubCommand::with_name("blacklist")
            .about("Blacklist operations")
            .setting(AppSettings::ArgRequiredElseHelp)
            .subcommand(SubCommand::with_name("add")
                .about("Add a crate to the blacklist")
                .arg(Arg::with_name("CRATE_NAME")
                    .index(1)
                    .required(true)
                    .help("Crate name")))
            .subcommand(SubCommand::with_name("remove")
                .about("Remove a crate from the blacklist")
                .arg(Arg::with_name("CRATE_NAME")
                    .index(1)
                    .required(true)
                    .help("Crate name"))))
        .subcommand(SubCommand::with_name("start-web-server")
            .about("Starts web server")
            .arg(Arg::with_name("SOCKET_ADDR")
                .index(1)
                .required(false)
                .help("Socket address to listen to")))
        .subcommand(SubCommand::with_name("daemon")
            .about("Starts cratesfyi daemon")
            .arg(Arg::with_name("FOREGROUND")
                .short("-f")
                .long("foreground")
                .help("run the server in the foreground instead of detaching a child")))
        .subcommand(SubCommand::with_name("database")
            .about("Database operations")
            .setting(AppSettings::ArgRequiredElseHelp)
            .subcommand(SubCommand::with_name("move-to-s3"))
            .subcommand(SubCommand::with_name("migrate")
                .about("Run database migrations")
                .arg(Arg::with_name("VERSION")))
            .subcommand(SubCommand::with_name("update-github-fields")
                .about("Updates github stats for crates."))
            .subcommand(SubCommand::with_name("add-directory")
                .about("Adds a directory into database")
                .arg(Arg::with_name("DIRECTORY")
                    .index(1)
                    .required(true)
                    .help("Path of file or directory"))
                .arg(Arg::with_name("PREFIX")
                    .index(2)
                    .help("Prefix of files in database")))
            .subcommand(SubCommand::with_name("update-release-activity")
                .about("Updates monthly release activity chart"))
            .subcommand(SubCommand::with_name("update-search-index")
                .about("Updates search index"))
            .subcommand(SubCommand::with_name("delete-crate")
                .about("Removes a whole crate from the database")
                .arg(Arg::with_name("CRATE_NAME")
                    .takes_value(true)
                    .required(true)
                    .help("Name of the crate to delete"))))
        .subcommand(SubCommand::with_name("queue")
            .about("Interactions with the build queue")
            .setting(AppSettings::ArgRequiredElseHelp)
            .subcommand(SubCommand::with_name("add")
                .about("Add a crate to the build queue")
                .arg(Arg::with_name("CRATE_NAME")
                    .index(1)
                    .required(true)
                    .help("Name of crate to build"))
                .arg(Arg::with_name("CRATE_VERSION")
                    .index(2)
                    .required(true)
                    .help("Version of crate to build"))
                .arg(Arg::with_name("BUILD_PRIORITY")
                    .short("p")
                    .long("priority")
                    .help("Priority of build (default: 5) (new crate builds get priority 0)")
                    .takes_value(true))))
        .get_matches();

    if let Some(matches) = matches.subcommand_matches("build") {
        let docbuilder_opts = {
            let mut docbuilder_opts = if let Some(prefix) = matches.value_of("PREFIX") {
                DocBuilderOptions::from_prefix(PathBuf::from(prefix))
            } else if let Ok(prefix) = env::var("CRATESFYI_PREFIX") {
                DocBuilderOptions::from_prefix(PathBuf::from(prefix))
            } else {
                DocBuilderOptions::default()
            };

            if let Some(crates_io_index_path) = matches.value_of("CRATES_IO_INDEX_PATH") {
                docbuilder_opts.crates_io_index_path = PathBuf::from(crates_io_index_path);
            }

            docbuilder_opts.skip_if_exists = matches.is_present("SKIP_IF_EXISTS");
            docbuilder_opts.skip_if_log_exists = matches.is_present("SKIP_IF_LOG_EXISTS");
            docbuilder_opts.keep_build_directory = matches.is_present("KEEP_BUILD_DIRECTORY");

            docbuilder_opts.check_paths().unwrap();

            docbuilder_opts
        };

        let mut docbuilder = DocBuilder::new(docbuilder_opts);

        if let Some(_) = matches.subcommand_matches("world") {
            docbuilder.load_cache().expect("Failed to load cache");
            let mut builder = RustwideBuilder::init().unwrap();
            builder.build_world(&mut docbuilder).expect("Failed to build world");
            docbuilder.save_cache().expect("Failed to save cache");
        } else if let Some(matches) = matches.subcommand_matches("crate") {
            docbuilder.load_cache().expect("Failed to load cache");
            let mut builder = RustwideBuilder::init().expect("failed to initialize rustwide");
            match matches.value_of("LOCAL") {
                Some(path) => builder.build_local_package(&mut docbuilder, Path::new(path)),
                None => builder.build_package(&mut docbuilder,
                    matches.value_of("CRATE_NAME").unwrap(), matches.value_of("CRATE_VERSION").unwrap(), None),
            }.expect("Building documentation failed");
            docbuilder.save_cache().expect("Failed to save cache");
        } else if let Some(m) = matches.subcommand_matches("update-toolchain") {
            if m.is_present("ONLY_FIRST_TIME") {
                let conn = db::connect_db().unwrap();
                let res = conn.query("SELECT * FROM config WHERE name = 'rustc_version';", &[]).unwrap();
                if !res.is_empty() {
                    println!("update-toolchain was already called in the past, exiting");
                    return;
                }
            }
            let mut builder = RustwideBuilder::init().unwrap();
            builder.update_toolchain().expect("failed to update toolchain");
        } else if matches.subcommand_matches("add-essential-files").is_some() {
            let mut builder = RustwideBuilder::init().unwrap();
            builder.add_essential_files().expect("failed to add essential files");
        } else if let Some(_) = matches.subcommand_matches("lock") {
            docbuilder.lock().expect("Failed to lock");
        } else if let Some(_) = matches.subcommand_matches("unlock") {
            docbuilder.unlock().expect("Failed to unlock");
        } else if let Some(_) = matches.subcommand_matches("print-options") {
            println!("{:?}", docbuilder.options());
        }

    } else if let Some(matches) = matches.subcommand_matches("blacklist") {
        let conn = db::connect_db().expect("Failed to connect to the database");

        if let Some(matches) = matches.subcommand_matches("add") {
            let crate_name = matches.value_of("CRATE_NAME").expect("Verified by clap");
            db::blacklist::add_crate(&conn, crate_name).expect("Failed to add crate to blacklist");

        } else if let Some(matches) = matches.subcommand_matches("remove") {
            let crate_name = matches.value_of("CRATE_NAME").expect("Verified by clap");
            db::blacklist::remove_crate(&conn, crate_name).expect("Failed to remove crate from blacklist");
        }

    } else if let Some(matches) = matches.subcommand_matches("database") {
        if let Some(matches) = matches.subcommand_matches("migrate") {
            let version = matches.value_of("VERSION").map(|v| v.parse::<i64>()
                                                          .expect("Version should be an integer"));
            db::migrate(version, &connect_db().expect("failed to connect to the database"))
                .expect("Failed to run database migrations");
        } else if let Some(_) = matches.subcommand_matches("update-github-fields") {
            cratesfyi::utils::github_updater().expect("Failed to update github fields");
        } else if let Some(matches) = matches.subcommand_matches("add-directory") {
            add_path_into_database(&db::connect_db().unwrap(),
                                   matches.value_of("PREFIX").unwrap_or(""),
                                   matches.value_of("DIRECTORY").unwrap())
                .expect("Failed to add directory into database");
        } else if let Some(_) = matches.subcommand_matches("update-release-activity") {
            // FIXME: This is actually util command not database
            cratesfyi::utils::update_release_activity().expect("Failed to update release activity");
        } else if let Some(_) = matches.subcommand_matches("update-search-index") {
            let conn = db::connect_db().unwrap();
            db::update_search_index(&conn).expect("Failed to update search index");
        } else if let Some(_) = matches.subcommand_matches("move-to-s3") {
            let conn = db::connect_db().unwrap();
            let mut count = 1;
            let mut total = 0;
            while count != 0 {
                count = db::file::move_to_s3(&conn, 5_000).expect("Failed to upload batch to S3");
                total += count;
                eprintln!(
                    "moved {} rows to s3 in this batch, total moved so far: {}",
                    count, total
                );
            }
        } else if let Some(matches) = matches.subcommand_matches("delete-crate") {
            let name = matches.value_of("CRATE_NAME").expect("missing crate name");
            let conn = db::connect_db().expect("failed to connect to the database");
            db::delete_crate(&conn, &name).expect("failed to delete the crate");
        }

    } else if let Some(matches) = matches.subcommand_matches("start-web-server") {
        start_web_server(Some(matches.value_of("SOCKET_ADDR").unwrap_or("0.0.0.0:3000")));

    } else if let Some(_) = matches.subcommand_matches("daemon") {
        let foreground = matches.subcommand_matches("daemon")
            .map_or(false, |opts| opts.is_present("FOREGROUND"));
        cratesfyi::utils::start_daemon(!foreground);

    } else if let Some(matches) = matches.subcommand_matches("queue") {
        if let Some(matches) = matches.subcommand_matches("add") {
            let priority = matches.value_of("BUILD_PRIORITY").unwrap_or("5");
            let priority: i32 = priority.parse().expect("--priority was not a number");
            let conn = connect_db().expect("Could not connect to database");

            add_crate_to_queue(&conn,
                               matches.value_of("CRATE_NAME").unwrap(),
                               matches.value_of("CRATE_VERSION").unwrap(),
                               priority).expect("Could not add crate to queue");
        }
    }
}

fn logger_init() {
    use std::io::Write;

    let mut builder = env_logger::Builder::new();
    builder.format(|buf, record| {
        writeln!(buf, "{} [{}] {}: {}",
                time::now().strftime("%Y/%m/%d %H:%M:%S").unwrap(),
                record.level(),
                record.target(),
                record.args())
    });
    builder.parse(&env::var("RUST_LOG").unwrap_or("cratesfyi=info".to_owned()));

    rustwide::logging::init_with(builder.build());
}
<commit_msg>Remove .index(1) from Arg<commit_after>

extern crate cratesfyi;
extern crate clap;
extern crate log;
extern crate env_logger;
extern crate time;
extern crate rustwide;

use std::env;
use std::path::{Path, PathBuf};

use clap::{Arg, App, AppSettings, SubCommand};
use cratesfyi::{DocBuilder, RustwideBuilder, DocBuilderOptions, db};
use cratesfyi::utils::add_crate_to_queue;
use cratesfyi::start_web_server;
use cratesfyi::db::{add_path_into_database, connect_db};

pub fn main() {
    logger_init();

    let matches = App::new("cratesfyi")
        .version(cratesfyi::BUILD_VERSION)
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("build")
            .about("Builds documentation in a chroot environment")
            .setting(AppSettings::ArgRequiredElseHelp)
            .arg(Arg::with_name("PREFIX")
                .short("P")
                .long("prefix")
                .takes_value(true))
            .arg(Arg::with_name("CRATES_IO_INDEX_PATH")
                .long("crates-io-index-path")
                .help("Sets crates.io-index path")
                .takes_value(true))
            .arg(Arg::with_name("SKIP_IF_EXISTS")
                .short("s")
                .long("skip")
                .help("Skips building documentation if documentation exists"))
            .arg(Arg::with_name("SKIP_IF_LOG_EXISTS")
                .long("skip-if-log-exists")
                .help("Skips building documentation if build log exists"))
            .arg(Arg::with_name("KEEP_BUILD_DIRECTORY")
                .short("-k")
                .long("keep-build-directory")
                .help("Keeps build directory after build."))
            .subcommand(SubCommand::with_name("world")
                .about("Builds documentation of every crate"))
            .subcommand(SubCommand::with_name("crate")
                .about("Builds documentation for a crate")
                .arg(Arg::with_name("CRATE_NAME")
                    .index(1)
                    .required_unless("LOCAL")
                    .requires("CRATE_VERSION")
                    .help("Crate name"))
                .arg(Arg::with_name("CRATE_VERSION")
                    .index(2)
                    .help("Version of crate"))
                .arg(Arg::with_name("LOCAL")
                    .short("-l")
                    .long("--local")
                    .takes_value(true)
                    .conflicts_with_all(&["CRATE_NAME", "CRATE_VERSION"])
                    .help("Build a crate at a specific path")))
            .subcommand(SubCommand::with_name("update-toolchain")
                .about("update the curretntly installed rustup toolchain")
                .arg(Arg::with_name("ONLY_FIRST_TIME")
                     .long("only-first-time")
                     .help("update toolchain only if no toolchain is currently installed")))
            .subcommand(SubCommand::with_name("add-essential-files")
                .about("Adds essential files for the installed version of rustc"))
            .subcommand(SubCommand::with_name("lock")
                .about("Locks cratesfyi daemon to stop building new crates"))
            .subcommand(SubCommand::with_name("unlock")
                .about("Unlocks cratesfyi daemon to continue building new crates"))
            .subcommand(SubCommand::with_name("print-options")))
        .subcommand(SubCommand::with_name("blacklist")
            .about("Blacklist operations")
            .setting(AppSettings::ArgRequiredElseHelp)
            .subcommand(SubCommand::with_name("add")
                .about("Add a crate to the blacklist")
                .arg(Arg::with_name("CRATE_NAME")
                    .required(true)
                    .help("Crate name")))
            .subcommand(SubCommand::with_name("remove")
                .about("Remove a crate from the blacklist")
                .arg(Arg::with_name("CRATE_NAME")
                    .required(true)
                    .help("Crate name"))))
        .subcommand(SubCommand::with_name("start-web-server")
            .about("Starts web server")
            .arg(Arg::with_name("SOCKET_ADDR")
                .index(1)
                .required(false)
                .help("Socket address to listen to")))
        .subcommand(SubCommand::with_name("daemon")
            .about("Starts cratesfyi daemon")
            .arg(Arg::with_name("FOREGROUND")
                .short("-f")
                .long("foreground")
                .help("run the server in the foreground instead of detaching a child")))
        .subcommand(SubCommand::with_name("database")
            .about("Database operations")
            .setting(AppSettings::ArgRequiredElseHelp)
            .subcommand(SubCommand::with_name("move-to-s3"))
            .subcommand(SubCommand::with_name("migrate")
                .about("Run database migrations")
                .arg(Arg::with_name("VERSION")))
            .subcommand(SubCommand::with_name("update-github-fields")
                .about("Updates github stats for crates."))
            .subcommand(SubCommand::with_name("add-directory")
                .about("Adds a directory into database")
                .arg(Arg::with_name("DIRECTORY")
                    .index(1)
                    .required(true)
                    .help("Path of file or directory"))
                .arg(Arg::with_name("PREFIX")
                    .index(2)
                    .help("Prefix of files in database")))
            .subcommand(SubCommand::with_name("update-release-activity")
                .about("Updates monthly release activity chart"))
            .subcommand(SubCommand::with_name("update-search-index")
                .about("Updates search index"))
            .subcommand(SubCommand::with_name("delete-crate")
                .about("Removes a whole crate from the database")
                .arg(Arg::with_name("CRATE_NAME")
                    .takes_value(true)
                    .required(true)
                    .help("Name of the crate to delete"))))
        .subcommand(SubCommand::with_name("queue")
            .about("Interactions with the build queue")
            .setting(AppSettings::ArgRequiredElseHelp)
            .subcommand(SubCommand::with_name("add")
                .about("Add a crate to the build queue")
                .arg(Arg::with_name("CRATE_NAME")
                    .index(1)
                    .required(true)
                    .help("Name of crate to build"))
                .arg(Arg::with_name("CRATE_VERSION")
                    .index(2)
                    .required(true)
                    .help("Version of crate to build"))
                .arg(Arg::with_name("BUILD_PRIORITY")
                    .short("p")
                    .long("priority")
                    .help("Priority of build (default: 5) (new crate builds get priority 0)")
                    .takes_value(true))))
        .get_matches();

    if let Some(matches) = matches.subcommand_matches("build") {
        let docbuilder_opts = {
            let mut docbuilder_opts = if let Some(prefix) = matches.value_of("PREFIX") {
                DocBuilderOptions::from_prefix(PathBuf::from(prefix))
            } else if let Ok(prefix) = env::var("CRATESFYI_PREFIX") {
                DocBuilderOptions::from_prefix(PathBuf::from(prefix))
            } else {
                DocBuilderOptions::default()
            };

            if let Some(crates_io_index_path) = matches.value_of("CRATES_IO_INDEX_PATH") {
                docbuilder_opts.crates_io_index_path = PathBuf::from(crates_io_index_path);
            }

            docbuilder_opts.skip_if_exists = matches.is_present("SKIP_IF_EXISTS");
            docbuilder_opts.skip_if_log_exists = matches.is_present("SKIP_IF_LOG_EXISTS");
            docbuilder_opts.keep_build_directory = matches.is_present("KEEP_BUILD_DIRECTORY");

            docbuilder_opts.check_paths().unwrap();

            docbuilder_opts
        };

        let mut docbuilder = DocBuilder::new(docbuilder_opts);

        if let Some(_) = matches.subcommand_matches("world") {
            docbuilder.load_cache().expect("Failed to load cache");
            let mut builder = RustwideBuilder::init().unwrap();
            builder.build_world(&mut docbuilder).expect("Failed to build world");
            docbuilder.save_cache().expect("Failed to save cache");
        } else if let Some(matches) = matches.subcommand_matches("crate") {
            docbuilder.load_cache().expect("Failed to load cache");
            let mut builder = RustwideBuilder::init().expect("failed to initialize rustwide");
            match matches.value_of("LOCAL") {
                Some(path) => builder.build_local_package(&mut docbuilder, Path::new(path)),
                None => builder.build_package(&mut docbuilder,
                    matches.value_of("CRATE_NAME").unwrap(), matches.value_of("CRATE_VERSION").unwrap(), None),
            }.expect("Building documentation failed");
            docbuilder.save_cache().expect("Failed to save cache");
        } else if let Some(m) = matches.subcommand_matches("update-toolchain") {
            if m.is_present("ONLY_FIRST_TIME") {
                let conn = db::connect_db().unwrap();
                let res = conn.query("SELECT * FROM config WHERE name = 'rustc_version';", &[]).unwrap();
                if !res.is_empty() {
                    println!("update-toolchain was already called in the past, exiting");
                    return;
                }
            }
            let mut builder = RustwideBuilder::init().unwrap();
            builder.update_toolchain().expect("failed to update toolchain");
        } else if matches.subcommand_matches("add-essential-files").is_some() {
            let mut builder = RustwideBuilder::init().unwrap();
            builder.add_essential_files().expect("failed to add essential files");
        } else if let Some(_) = matches.subcommand_matches("lock") {
            docbuilder.lock().expect("Failed to lock");
        } else if let Some(_) = matches.subcommand_matches("unlock") {
            docbuilder.unlock().expect("Failed to unlock");
        } else if let Some(_) = matches.subcommand_matches("print-options") {
            println!("{:?}", docbuilder.options());
        }

    } else if let Some(matches) = matches.subcommand_matches("blacklist") {
        let conn = db::connect_db().expect("Failed to connect to the database");

        if let Some(matches) = matches.subcommand_matches("add") {
            let crate_name = matches.value_of("CRATE_NAME").expect("Verified by clap");
            db::blacklist::add_crate(&conn, crate_name).expect("Failed to add crate to blacklist");

        } else if let Some(matches) = matches.subcommand_matches("remove") {
            let crate_name = matches.value_of("CRATE_NAME").expect("Verified by clap");
            db::blacklist::remove_crate(&conn, crate_name).expect("Failed to remove crate from blacklist");
        }

    } else if let Some(matches) = matches.subcommand_matches("database") {
        if let Some(matches) = matches.subcommand_matches("migrate") {
            let version = matches.value_of("VERSION").map(|v| v.parse::<i64>()
                                                          .expect("Version should be an integer"));
            db::migrate(version, &connect_db().expect("failed to connect to the database"))
                .expect("Failed to run database migrations");
        } else if let Some(_) = matches.subcommand_matches("update-github-fields") {
            cratesfyi::utils::github_updater().expect("Failed to update github fields");
        } else if let Some(matches) = matches.subcommand_matches("add-directory") {
            add_path_into_database(&db::connect_db().unwrap(),
                                   matches.value_of("PREFIX").unwrap_or(""),
                                   matches.value_of("DIRECTORY").unwrap())
                .expect("Failed to add directory into database");
        } else if let Some(_) = matches.subcommand_matches("update-release-activity") {
            // FIXME: This is actually util command not database
            cratesfyi::utils::update_release_activity().expect("Failed to update release activity");
        } else if let Some(_) = matches.subcommand_matches("update-search-index") {
            let conn = db::connect_db().unwrap();
            db::update_search_index(&conn).expect("Failed to update search index");
        } else if let Some(_) = matches.subcommand_matches("move-to-s3") {
            let conn = db::connect_db().unwrap();
            let mut count = 1;
            let mut total = 0;
            while count != 0 {
                count = db::file::move_to_s3(&conn, 5_000).expect("Failed to upload batch to S3");
                total += count;
                eprintln!(
                    "moved {} rows to s3 in this batch, total moved so far: {}",
                    count, total
                );
            }
        } else if let Some(matches) = matches.subcommand_matches("delete-crate") {
            let name = matches.value_of("CRATE_NAME").expect("missing crate name");
            let conn = db::connect_db().expect("failed to connect to the database");
            db::delete_crate(&conn, &name).expect("failed to delete the crate");
        }

    } else if let Some(matches) = matches.subcommand_matches("start-web-server") {
        start_web_server(Some(matches.value_of("SOCKET_ADDR").unwrap_or("0.0.0.0:3000")));

    } else if let Some(_) = matches.subcommand_matches("daemon") {
        let foreground = matches.subcommand_matches("daemon")
            .map_or(false, |opts| opts.is_present("FOREGROUND"));
        cratesfyi::utils::start_daemon(!foreground);

    } else if let Some(matches) = matches.subcommand_matches("queue") {
        if let Some(matches) = matches.subcommand_matches("add") {
            let priority = matches.value_of("BUILD_PRIORITY").unwrap_or("5");
            let priority: i32 = priority.parse().expect("--priority was not a number");
            let conn = connect_db().expect("Could not connect to database");

            add_crate_to_queue(&conn,
                               matches.value_of("CRATE_NAME").unwrap(),
                               matches.value_of("CRATE_VERSION").unwrap(),
                               priority).expect("Could not add crate to queue");
        }
    }
}

fn logger_init() {
    use std::io::Write;

    let mut builder = env_logger::Builder::new();
    builder.format(|buf, record| {
        writeln!(buf, "{} [{}] {}: {}",
                time::now().strftime("%Y/%m/%d %H:%M:%S").unwrap(),
                record.level(),
                record.target(),
                record.args())
    });
    builder.parse(&env::var("RUST_LOG").unwrap_or("cratesfyi=info".to_owned()));

    rustwide::logging::init_with(builder.build());
}
<|endoftext|>


<commit_before>extern crate mpi;
extern crate num;

use mpi::traits::*;
use mpi::topology::Universe;
use num::pow;
use mpi::collective::SystemOperation;
use std::process;

const MAX_MSG_SIZE: usize = (1<<22);
const MAX_ALIGNMENT: usize = 16384;
const BUF_SIZE: usize = MAX_MSG_SIZE + MAX_ALIGNMENT;
const LARGE_MSG_SIZE: isize = 8192 - 1;
const LOOP_SMALL: isize = 10000;
const LOOP_LARGE: isize = 1000;
const SKIP_SMALL: isize = 100;
const SKIP_LARGE: isize = 10;

fn main() {
  let universe = mpi::initialize().unwrap();
  let world = universe.world();
  let size = world.size();
  let rank = world.rank();
  let pairs = size/2;

  if size < 2 {
    println!("ERROR: This test requires at least 2 processes");
    process::exit(1);
  }

  if rank == 0 {
    println!("# RUST OSU MPI Multi Latency Test v1.0");
    println!("# Size               Latency (us)");
  }

  world.barrier();
  multi_latency(rank, pairs, universe);
  world.barrier();
}

fn multi_latency(rank: i32, pairs: i32, universe: Universe) {
  let world = universe.world();
  let s_buf = vec![0;BUF_SIZE];
  let root_process = world.process_at_rank(0);

  for size in (0..1).chain((0..23).map(|x| pow(2, x))) {
    let mut t_start = 0.0f64;

    world.barrier();

    let (range, extra) = match size {
      x @ _ if x > LARGE_MSG_SIZE => (LOOP_LARGE, SKIP_LARGE),
      _ => (LOOP_SMALL, SKIP_SMALL)
    };

    match rank {
      _ if rank < pairs => {
        let peer_rank = rank + pairs;

        for iter in (0 .. range + extra) {
          if iter == extra {
            t_start = universe.get_time();
            world.barrier();
          }

          world.process_at_rank(peer_rank).send(&s_buf[0.. size as usize]);
          // XXX: This will probabaly hurt performance in comarison to C.
          let (_, _) = world.receive_vec::<u32>();
        }
      },
      _ => {
        let peer_rank = rank - pairs;

        for iter in (0 .. range + extra) {
          if iter == extra {
            t_start = universe.get_time();
            world.barrier();
          }

          // XXX: This will probabaly hurt performance in comarison to C.
          let (_, _) = world.receive_vec::<u32>();
          world.process_at_rank(peer_rank).send(&s_buf[0.. size as usize]);
        }
      }
    }

    let t_end = universe.get_time();
    let latency = ((t_end - t_start) * 1.0e6) / (2.0 * range as f64);

    let mut latency_sum = 0.0f64;
    root_process.reduce_into(&latency, Some(&mut latency_sum), SystemOperation::sum());

    if world.rank() == 0 {
      let avg_latency: f64 = latency_sum / (pairs * 2) as f64;
      println!("{:<28} {1:.2}", size, avg_latency);
    }
  }
}
<commit_msg>Make sure world doesn't get dropped early. The copy of world made in multi_latency got dropped early, which called MPI_Finalize.<commit_after>extern crate mpi;
extern crate num;

use mpi::traits::*;
use mpi::topology::{Universe, SystemCommunicator};
use num::pow;
use mpi::collective::SystemOperation;
use std::process;

const MAX_MSG_SIZE: usize = (1<<22);
const MAX_ALIGNMENT: usize = 16384;
const BUF_SIZE: usize = MAX_MSG_SIZE + MAX_ALIGNMENT;
const LARGE_MSG_SIZE: isize = 8192 - 1;
const LOOP_SMALL: isize = 10000;
const LOOP_LARGE: isize = 1000;
const SKIP_SMALL: isize = 100;
const SKIP_LARGE: isize = 10;

fn main() {
  let universe = mpi::initialize().unwrap();
  let world = universe.world();
  let size = world.size();
  let rank = world.rank();
  let pairs = size/2;

  if size < 2 {
    println!("ERROR: This test requires at least 2 processes");
    process::exit(1);
  }

  if rank == 0 {
    println!("# RUST OSU MPI Multi Latency Test v1.0");
    println!("# Size               Latency (us)");
  }

  world.barrier();
  multi_latency(rank, pairs, universe, world);
  world.barrier();
}

fn multi_latency(rank: i32, pairs: i32,
                 universe: Universe, world: SystemCommunicator) {
  let s_buf = vec![0;BUF_SIZE];
  let root_process = world.process_at_rank(0);

  for size in (0..1).chain((0..23).map(|x| pow(2, x))) {
    let mut t_start = 0.0f64;

    world.barrier();

    let (range, extra) = match size {
      x @ _ if x > LARGE_MSG_SIZE => (LOOP_LARGE, SKIP_LARGE),
      _ => (LOOP_SMALL, SKIP_SMALL)
    };

    match rank {
      _ if rank < pairs => {
        let peer_rank = rank + pairs;

        for iter in (0 .. range + extra) {
          if iter == extra {
            t_start = universe.get_time();
            world.barrier();
          }

          world.process_at_rank(peer_rank).send(&s_buf[0.. size as usize]);
          // XXX: This will probabaly hurt performance in comarison to C.
          let (_, _) = world.receive_vec::<u32>();
        }
      },
      _ => {
        let peer_rank = rank - pairs;

        for iter in (0 .. range + extra) {
          if iter == extra {
            t_start = universe.get_time();
            world.barrier();
          }

          // XXX: This will probabaly hurt performance in comarison to C.
          let (_, _) = world.receive_vec::<u32>();
          world.process_at_rank(peer_rank).send(&s_buf[0.. size as usize]);
        }
      }
    }

    let t_end = universe.get_time();
    let latency = ((t_end - t_start) * 1.0e6) / (2.0 * range as f64);

    let mut latency_sum = 0.0f64;
    root_process.reduce_into(&latency, Some(&mut latency_sum), SystemOperation::sum());

    if world.rank() == 0 {
      let avg_latency: f64 = latency_sum / (pairs * 2) as f64;
      println!("{:<28} {1:.2}", size, avg_latency);
    }
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

mod ivf;
use self::ivf::IvfMuxer;

mod mp4;
use mp4::Mp4Muxer;

mod y4m;
pub use self::y4m::write_y4m_frame;

#[cfg(feature = "avformat-sys")]
mod avformat;

use std::io;
use std::ffi::OsStr;
use std::path::Path;

pub trait Muxer {
  fn write_header(
    &mut self, width: usize, height: usize, framerate_num: usize,
    framerate_den: usize
  );

  fn write_frame(&mut self, pts: u64, data: &[u8]);

  fn flush(&mut self) -> io::Result<()>;
}

pub fn create_muxer(path: &str) -> Box<dyn Muxer> {
  if path == "-" {
    return IvfMuxer::open(path);
  }

  let ext = Path::new(path)
    .extension()
    .and_then(OsStr::to_str)
    .map(str::to_lowercase)
    .expect("no extension");

  match &ext[..] {
    "mp4" => {
      Mp4Muxer::open(path)
    }
    "ivf" => {
      IvfMuxer::open(path)
    }
    _e => {
      panic!("{} is not a supported extension, please change to .ivf", ext);
    }
  }
}
<commit_msg>Keep the default muxer as ivf<commit_after>// Copyright (c) 2017-2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

mod ivf;
use self::ivf::IvfMuxer;

mod mp4;
use mp4::Mp4Muxer;

mod y4m;
pub use self::y4m::write_y4m_frame;

#[cfg(feature = "avformat-sys")]
mod avformat;

use std::io;
use std::ffi::OsStr;
use std::path::Path;

pub trait Muxer {
  fn write_header(
    &mut self, width: usize, height: usize, framerate_num: usize,
    framerate_den: usize
  );

  fn write_frame(&mut self, pts: u64, data: &[u8]);

  fn flush(&mut self) -> io::Result<()>;
}

pub fn create_muxer(path: &str) -> Box<dyn Muxer> {
  if path == "-" {
    return IvfMuxer::open(path);
  }

  let ext = Path::new(path)
    .extension()
    .and_then(OsStr::to_str)
    .map(str::to_lowercase)
    .unwrap_or_else(|| "ivf".into());

  match &ext[..] {
    "mp4" => {
      Mp4Muxer::open(path)
    }
    "ivf" => {
      IvfMuxer::open(path)
    }
    _e => {
      panic!("{} is not a supported extension, please change to .ivf", ext);
    }
  }
}
<|endoftext|>


<commit_before>/*!
# ParView
*/
#![deny(non_camel_case_types)]
#![deny(unused_parens)]
#![deny(non_upper_case_globals)]
#![deny(unused_qualifications)]
#![deny(missing_docs)]
#![deny(unused_results)]

extern crate docopt;
extern crate parview;
extern crate serde;

use docopt::Docopt;
use serde::Deserialize;

use std::path::Path;

use parview::{misc, Color, Config, Frame, Palette, Parviewer, TomlConfig, EPSILON};
use std::f32::consts::PI;

// Write the Docopt usage string.
const USAGE: &'static str = "
Usage: parview [options] [--] [<file>]

Options:
    -h, --help              Help and usage
    -g, --generate          Generate test_frames.json
    -p, --palette FILE      Use palette file (toml file), instead of default.
    -c, --config FILE       Use config file (toml file), instead of default.

Arguments:
    <file>      json file representing the frames. json.gz also accepted,
                if the extension is \".gz\".
";

#[derive(Debug, Deserialize)]
struct Args {
    flag_palette: Option<String>,
    flag_config: Option<String>,
    arg_file: Option<String>,
}

fn run() -> Result<(), Box<std::error::Error>> {
    let args: Args = Docopt::new(USAGE)
        .and_then(|dopt| dopt.parse())
        .unwrap_or_else(|e| e.exit());
    let toml_config: TomlConfig = match args.flag_config {
        None => Default::default(),
        Some(ref fname) => {
            let path: &Path = Path::new(&fname[..]);
            try!(misc::load_toml::<TomlConfig>(path))
        }
    };

    let config: Config = toml_config.to_parviewer_config();

    let fname: &str = match args.arg_file {
        Some(ref s) => s,
        None => "test_frame.json",
    };
    let path: &Path = Path::new(fname);

    let (frames, palette): (Vec<Frame>, Palette) = if args.flag_generate {
        let frames = try!(misc::generate_frame(path));
        let palette = match args.flag_palette {
            None => Default::default(),
            Some(fname) => {
                let palette_path: &Path = Path::new(&fname[..]);
                try!(misc::generate_palette(palette_path))
            }
        };
        (frames, palette)
    } else {
        let frames = try!(misc::deserialize_by_ext(path));
        let palette = match args.flag_palette {
            None => Default::default(),
            Some(fname) => {
                let palette_path: &Path = Path::new(&fname[..]);
                try!(misc::load_toml::<Palette>(palette_path))
            }
        };
        (frames, palette)
    };

    // println!("config: {:?}", config);

    let mut viewer = try!(Parviewer::new(frames, palette, config));
    let _ = viewer.timer.at_least(toml_config.fps);
    let text_color = Color(255, 255, 255);

    viewer.run(|viewer, _| {
        if toml_config.rotate.abs() > EPSILON {
            let new_yaw = viewer.camera.yaw() + (toml_config.rotate * PI / 180.);
            viewer.camera.set_yaw(new_yaw);
        }

        viewer.draw_frame_text(0., 0., text_color);

        let dt = viewer.timer.get_dt();
        let dt_text = if dt >= 0.6 || dt.abs() < 1e-6 || dt <= -0.6 {
            format!("{}", dt)
        } else if dt > 0. {
            format!("1/{}", 1. / dt)
        } else {
            format!("-1/{}", -1. / dt)
        };

        let text = format!(
            "t:{:6.2}, dt:{}, coloring: {}",
            viewer.timer.get_time(),
            dt_text,
            viewer.palette.partials_string()
        );

        viewer.draw_text(&*text, 0., 1., text_color);
    });
    Ok(())
}

/// The main entry point.
pub fn main() {
    if let Err(err) = run() {
        println!("ERROR.");

        misc::err_print(&*err);
    }
}
<commit_msg>parviewer bin builds, but there's a runtime error<commit_after>/*!
# ParView
*/
#![deny(non_camel_case_types)]
#![deny(unused_parens)]
#![deny(non_upper_case_globals)]
#![deny(unused_qualifications)]
#![deny(missing_docs)]
#![deny(unused_results)]

extern crate docopt;
extern crate parview;
extern crate serde;

use docopt::Docopt;
use serde::Deserialize;

use std::path::Path;

use parview::{misc, Color, Config, Frame, Palette, Parviewer, TomlConfig, EPSILON};
use std::f32::consts::PI;

// Write the Docopt usage string.
const USAGE: &'static str = "
Usage: parview [options] [--] [<file>]

Options:
    -h, --help              Help and usage
    -g, --generate          Generate test_frames.json
    -p, --palette FILE      Use palette file (toml file), instead of default.
    -c, --config FILE       Use config file (toml file), instead of default.

Arguments:
    <file>      json file representing the frames. json.gz also accepted,
                if the extension is \".gz\".
";

#[derive(Debug, Deserialize)]
struct Args {
    flag_palette: Option<String>,
    flag_config: Option<String>,
    flag_generate: bool,
    arg_file: Option<String>,
}

fn run() -> Result<(), Box<std::error::Error>> {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());
    let toml_config: TomlConfig = match args.flag_config {
        None => Default::default(),
        Some(ref fname) => {
            let path: &Path = Path::new(&fname[..]);
            try!(misc::load_toml::<TomlConfig>(path))
        }
    };

    let config: Config = toml_config.to_parviewer_config();

    let fname: &str = match args.arg_file {
        Some(ref s) => s,
        None => "test_frame.json",
    };
    let path: &Path = Path::new(fname);

    let (frames, palette): (Vec<Frame>, Palette) = if args.flag_generate {
        let frames = try!(misc::generate_frame(path));
        let palette = match args.flag_palette {
            None => Default::default(),
            Some(fname) => {
                let palette_path: &Path = Path::new(&fname[..]);
                try!(misc::generate_palette(palette_path))
            }
        };
        (frames, palette)
    } else {
        let frames = try!(misc::deserialize_by_ext(path));
        let palette = match args.flag_palette {
            None => Default::default(),
            Some(fname) => {
                let palette_path: &Path = Path::new(&fname[..]);
                try!(misc::load_toml::<Palette>(palette_path))
            }
        };
        (frames, palette)
    };

    // println!("config: {:?}", config);

    let mut viewer = try!(Parviewer::new(frames, palette, config));
    let _ = viewer.timer.at_least(toml_config.fps);
    let text_color = Color(255, 255, 255);

    viewer.run(|viewer, _| {
        if toml_config.rotate.abs() > EPSILON {
            let new_yaw = viewer.camera.yaw() + (toml_config.rotate * PI / 180.);
            viewer.camera.set_yaw(new_yaw);
        }

        viewer.draw_frame_text(0., 0., text_color);

        let dt = viewer.timer.get_dt();
        let dt_text = if dt >= 0.6 || dt.abs() < 1e-6 || dt <= -0.6 {
            format!("{}", dt)
        } else if dt > 0. {
            format!("1/{}", 1. / dt)
        } else {
            format!("-1/{}", -1. / dt)
        };

        let text = format!(
            "t:{:6.2}, dt:{}, coloring: {}",
            viewer.timer.get_time(),
            dt_text,
            viewer.palette.partials_string()
        );

        viewer.draw_text(&*text, 0., 1., text_color);
    });
    Ok(())
}

/// The main entry point.
pub fn main() {
    if let Err(err) = run() {
        println!("ERROR.");

        misc::err_print(&*err);
    }
}
<|endoftext|>


<commit_before>#[rustc::attr(not(nightly), ignore)]
#[test]
fn ui() {
    let t = trybuild::TestCases::new();
    t.compile_fail("tests/ui/**/*.rs");
}
<commit_msg>Disable ui tests on emscripten<commit_after>#[cfg(not(target_os = "emscripten"))]
#[rustc::attr(not(nightly), ignore)]
#[test]
fn ui() {
    let t = trybuild::TestCases::new();
    t.compile_fail("tests/ui/**/*.rs");
}
<|endoftext|>


<commit_before>//! Procedurally generating the game world.

use math::*;
use world::{Chunk, ChunkIndex, ChunkProvider, HeightType, HexPillar};
use world::{CHUNK_SIZE, GroundMaterial, PILLAR_STEP_HEIGHT, PillarSection, Prop, PropType};
use rand::{Rand, Rng};
use gen::PlantGenerator;
use noise::{PermutationTable, open_simplex2, open_simplex3};
use gen::seeded_rng;

// pub const LANDSCAPE_FLAT: f32 = 0.01;
// pub const LANDSCAPE_MODERATE: f32 = 0.05;
// pub const LANDSCAPE_HILLY: f32 = 0.1125;

/// Land "fill noise" scaling in x, y, and z direction.
const LAND_NOISE_SCALE: (f32, f32, f32) = (0.03, 0.03, 0.05);

/// Main type to generate the game world. Implements the `ChunkProvider` trait
/// (TODO, see #8).
pub struct WorldGenerator {
    seed: u64,
    terrain_table: PermutationTable,
    plant_table: PermutationTable,
}

impl WorldGenerator {
    /// Creates the generator with the given seed.
    pub fn with_seed(seed: u64) -> Self {
        let mut terrain_rng = seeded_rng(seed, 0, ());
        let mut plant_rng = seeded_rng(seed, 1, ());

        WorldGenerator {
            seed: seed,
            terrain_table: PermutationTable::rand(&mut terrain_rng),
            plant_table: PermutationTable::rand(&mut plant_rng),
        }
    }

    /// Returns the seed of this world generator.
    pub fn seed(&self) -> u64 {
        self.seed
    }
}

impl ChunkProvider for WorldGenerator {
    fn load_chunk(&self, index: ChunkIndex) -> Option<Chunk> {
        const WORLDGEN_HEIGHT: usize = 128;
        // Create a 3D-Array of booleans indicating which pillar sections to fill
        // (Map height is unlimited in theory, but we'll limit worldgen to 64 height
        // units)
        let mut fill = [[[false; WORLDGEN_HEIGHT]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize];

        // Chunk start position in absolute axial coords
        let chunk_start = index.0 * CHUNK_SIZE as i32;
        for q in 0..CHUNK_SIZE {
            for r in 0..CHUNK_SIZE {
                for i in 0..WORLDGEN_HEIGHT {
                    let real_pos = (chunk_start + AxialVector::new(q as i32, r as i32)).to_real();
                    let x = real_pos.x;
                    let y = real_pos.y;
                    let z = i as f32 * PILLAR_STEP_HEIGHT;
                    let fill_noise = open_simplex3::<f32>(&self.terrain_table,
                                                          &[x * LAND_NOISE_SCALE.0,
                                                            y * LAND_NOISE_SCALE.1,
                                                            z * LAND_NOISE_SCALE.2]);

                    // Offset the noise, since it's in range -1 .. 1 (or something like that)
                    let fill_noise = fill_noise + 1.0;

                    fill[q as usize][r as usize][i as usize] =
                        fill_noise > 1.7 * (i as f32 / WORLDGEN_HEIGHT as f32);
                }
            }
        }

        Some(Chunk::with_pillars(index, |pos| {
            let real_pos = pos.to_real();
            let x = real_pos.x;
            let y = real_pos.y;

            // Pillar pos relative to first pillar
            let rel_pos = pos - index.0 * CHUNK_SIZE as i32;
            let column = &fill[rel_pos.q as usize][rel_pos.r as usize];

            // Create sections for all connected `true`s in the array
            let mut sections = Vec::new();
            let mut low = 0;
            let mut height = None;
            for i in 0..WORLDGEN_HEIGHT {
                match (height, column[i]) {
                    (Some(h), true) => {
                        // Next one's still solid, increase section height
                        height = Some(h + 1);
                    }
                    (Some(h), false) => {
                        // Create a section of height `h` and start over
                        sections.push(PillarSection::new(GroundMaterial::Dirt,
                                                         HeightType::from_units(low),
                                                         HeightType::from_units(low + h)));
                        height = None;
                    }
                    (None, true) => {
                        low = i as u16;
                        height = Some(1);
                    }
                    (None, false) => {}
                };
            }

            if let Some(h) = height {
                // Create the topmost pillar
                sections.push(PillarSection::new(GroundMaterial::Dirt,
                                                 HeightType::from_units(low),
                                                 HeightType::from_units(low + h)));
            }

            let mut props = Vec::new();

            let plant_noise = open_simplex2::<f32>(&self.plant_table,
                                                   &[(x as f32) * 0.08, (y as f32) * 0.08]);

            // Place a test plant every few blocks
            if plant_noise > 0.4 {
                let mut rng = super::seeded_rng(self.seed, "TREE", (pos.q, pos.r));
                if rng.next_f32() < plant_noise {
                    let gen = PlantGenerator::rand(&mut rng);

                    // put the tree at the highest position
                    let height = match sections.last() {
                        Some(section) => section.top,
                        None => HeightType::from_units(0),
                    };

                    props.push(Prop {
                        baseline: height,
                        prop: PropType::Plant(gen.generate(&mut rng)),
                    });
                }
            }

            HexPillar::new(sections, props)
        }))
    }

    fn is_chunk_loadable(&self, _: ChunkIndex) -> bool {
        // All chunks can be generated from nothing
        true
    }
}
<commit_msg>use a sigmoid function to calculate the threshold<commit_after>//! Procedurally generating the game world.

use math::*;
use world::{Chunk, ChunkIndex, ChunkProvider, HeightType, HexPillar};
use world::{CHUNK_SIZE, GroundMaterial, PILLAR_STEP_HEIGHT, PillarSection, Prop, PropType};
use rand::{Rand, Rng};
use gen::PlantGenerator;
use noise::{PermutationTable, open_simplex2, open_simplex3};
use gen::seeded_rng;

// pub const LANDSCAPE_FLAT: f32 = 0.01;
// pub const LANDSCAPE_MODERATE: f32 = 0.05;
// pub const LANDSCAPE_HILLY: f32 = 0.1125;

/// Land "fill noise" scaling in x, y, and z direction.
const LAND_NOISE_SCALE: (f32, f32, f32) = (0.03, 0.03, 0.05);

/// Main type to generate the game world. Implements the `ChunkProvider` trait
/// (TODO, see #8).
pub struct WorldGenerator {
    seed: u64,
    terrain_table: PermutationTable,
    plant_table: PermutationTable,
}

impl WorldGenerator {
    /// Creates the generator with the given seed.
    pub fn with_seed(seed: u64) -> Self {
        let mut terrain_rng = seeded_rng(seed, 0, ());
        let mut plant_rng = seeded_rng(seed, 1, ());

        WorldGenerator {
            seed: seed,
            terrain_table: PermutationTable::rand(&mut terrain_rng),
            plant_table: PermutationTable::rand(&mut plant_rng),
        }
    }

    /// Returns the seed of this world generator.
    pub fn seed(&self) -> u64 {
        self.seed
    }
}

impl ChunkProvider for WorldGenerator {
    fn load_chunk(&self, index: ChunkIndex) -> Option<Chunk> {
        const WORLDGEN_HEIGHT: usize = 256;
        // Create a 3D-Array of booleans indicating which pillar sections to fill
        // (Map height is unlimited in theory, but we'll limit worldgen to 64 height
        // units)
        let mut fill = [[[false; WORLDGEN_HEIGHT]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize];

        // Chunk start position in absolute axial coords
        let chunk_start = index.0 * CHUNK_SIZE as i32;
        for q in 0..CHUNK_SIZE {
            for r in 0..CHUNK_SIZE {
                for i in 0..WORLDGEN_HEIGHT {
                    let real_pos = (chunk_start + AxialVector::new(q as i32, r as i32)).to_real();
                    let x = real_pos.x;
                    let y = real_pos.y;
                    let z = i as f32 * PILLAR_STEP_HEIGHT;
                    let fill_noise = open_simplex3::<f32>(&self.terrain_table,
                                                          &[x * LAND_NOISE_SCALE.0,
                                                            y * LAND_NOISE_SCALE.1,
                                                            z * LAND_NOISE_SCALE.2]);

                    // The noise is (theoretically) in the range -1..1
                    // Map the noise to a range of 0..1
                    let fill_noise = (fill_noise + 1.0) / 2.0;

                    // Calculate threshold to fill this "block". The lower the threshold, the more
                    // likely this voxel is filled, so it should increase with height.
                    let height_pct = i as f32 / WORLDGEN_HEIGHT as f32;

                    // The threshold is calculated using a sigmoid function. These are the
                    // parameters used:

                    /// Minimum threshold to prevent threshold to reach 0,
                    /// needed to have any caves at all
                    const MIN_THRESH: f32 = 0.6;
                    /// "Steepness" of the sigmoid function.
                    const THRESH_STEEPNESS: f32 = 30.0;
                    /// Threshold at half value (max. steepness, avg. terrain
                    /// height)
                    const THRESH_MID: f32 = 0.5;

                    let sig_thresh =
                        1.0 / (1.0 + f32::exp(-THRESH_STEEPNESS * (height_pct - THRESH_MID)));

                    let threshold = (sig_thresh + MIN_THRESH) / (1.0 + MIN_THRESH);

                    fill[q as usize][r as usize][i as usize] = fill_noise > threshold;
                }
            }
        }

        Some(Chunk::with_pillars(index, |pos| {
            let real_pos = pos.to_real();
            let x = real_pos.x;
            let y = real_pos.y;

            // Pillar pos relative to first pillar
            let rel_pos = pos - index.0 * CHUNK_SIZE as i32;
            let column = &fill[rel_pos.q as usize][rel_pos.r as usize];

            // Create sections for all connected `true`s in the array
            let mut sections = Vec::new();
            let mut low = 0;
            let mut height = None;
            for i in 0..WORLDGEN_HEIGHT {
                match (height, column[i]) {
                    (Some(h), true) => {
                        // Next one's still solid, increase section height
                        height = Some(h + 1);
                    }
                    (Some(h), false) => {
                        // Create a section of height `h` and start over
                        sections.push(PillarSection::new(GroundMaterial::Dirt,
                                                         HeightType::from_units(low),
                                                         HeightType::from_units(low + h)));
                        height = None;
                    }
                    (None, true) => {
                        low = i as u16;
                        height = Some(1);
                    }
                    (None, false) => {}
                };
            }

            if let Some(h) = height {
                // Create the topmost pillar
                sections.push(PillarSection::new(GroundMaterial::Dirt,
                                                 HeightType::from_units(low),
                                                 HeightType::from_units(low + h)));
            }

            let mut props = Vec::new();

            let plant_noise = open_simplex2::<f32>(&self.plant_table,
                                                   &[(x as f32) * 0.08, (y as f32) * 0.08]);

            // Place a test plant every few blocks
            if plant_noise > 0.4 {
                let mut rng = super::seeded_rng(self.seed, "TREE", (pos.q, pos.r));
                if rng.next_f32() < plant_noise {
                    let gen = PlantGenerator::rand(&mut rng);

                    // put the tree at the highest position
                    let height = match sections.last() {
                        Some(section) => section.top,
                        None => HeightType::from_units(0),
                    };

                    props.push(Prop {
                        baseline: height,
                        prop: PropType::Plant(gen.generate(&mut rng)),
                    });
                }
            }

            HexPillar::new(sections, props)
        }))
    }

    fn is_chunk_loadable(&self, _: ChunkIndex) -> bool {
        // All chunks can be generated from nothing
        true
    }
}
<|endoftext|>


<commit_before>//! Bounds-checked buffer cursor type.
use std::ops::Deref;
use std::rc::Rc;
use std::cell::RefCell;
use buffer::{Position, GapBuffer};

/// Read-only wrapper for a `Position`, to allow field level access to a
/// buffer's cursor while simultaneously enforcing bounds-checking when
/// updating its value.
#[derive(Clone)]
pub struct Cursor {
    pub data: Rc<RefCell<GapBuffer>>,
    pub position: Position,
    sticky_offset: usize,
}

impl Deref for Cursor {
    type Target = Position;

    fn deref(&self) -> &Position {
        &self.position
    }
}

/// Initializes a cursor bound to the specified gap buffer, at the specified position.
pub fn new(data: Rc<RefCell<GapBuffer>>, position: Position) -> Cursor {
    Cursor{
        data: data,
        position: position,
        sticky_offset: position.offset
    }
}

impl Cursor {
    /// Moves the cursor to the specified location. The location is
    /// bounds-checked against the data and the cursor will not be
    /// updated if it is out-of-bounds.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// let in_bounds = scribe::buffer::Position{ line: 0, offset: 2 };
    /// let out_of_bounds = scribe::buffer::Position{ line: 2, offset: 2 };
    /// buffer.insert("scribe");
    ///
    /// buffer.cursor.move_to(in_bounds);
    /// assert_eq!(buffer.cursor.line, 0);
    /// assert_eq!(buffer.cursor.offset, 2);
    ///
    /// buffer.cursor.move_to(out_of_bounds);
    /// assert_eq!(buffer.cursor.line, 0);
    /// assert_eq!(buffer.cursor.offset, 2);
    /// ```
    pub fn move_to(&mut self, position: Position) -> bool {
        if self.data.borrow().in_bounds(&position) {
            self.position = position;

            // Remember this offset so that we can try
            // to maintain it when moving across lines.
            self.sticky_offset = position.offset;

            return true
        }
        false
    }

    /// Decrements the cursor line. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_up(&mut self) {
        // Don't bother if we are already at the top.
        if self.line == 0 { return; }

        let target_line = self.line-1;
        let new_position = Position{ line: target_line, offset: self.sticky_offset };

        // Try moving to the same offset on the line above, falling back to its EOL.
        if self.move_to(new_position) == false {
            let mut target_offset = 0;
            for (line_number, line) in self.data.borrow().to_string().lines().enumerate() {
                if line_number == target_line {
                    target_offset = line.chars().count();
                }
            }
            self.move_to(Position{ line: target_line, offset: target_offset });

            // Moving the position successfully updates the sticky offset, but we
            // haven't actually moved to where we really wanted to go (offset-wise).
            // Restore the original desired offset; it might be available on the next try.
            self.sticky_offset = new_position.offset;
        }
    }

    /// Increments the cursor line. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_down(&mut self) {
        let target_line = self.line+1;
        let new_position = Position{ line: target_line, offset: self.sticky_offset };

        // Try moving to the same offset on the line below, falling back to its EOL.
        if self.move_to(new_position) == false {
            let mut target_offset = 0;
            for (line_number, line) in self.data.borrow().to_string().lines().enumerate() {
                if line_number == target_line {
                    target_offset = line.chars().count();
                }
            }
            self.move_to(Position{ line: target_line, offset: target_offset });

            // Moving the position successfully updates the sticky offset, but we
            // haven't actually moved to where we really wanted to go (offset-wise).
            // Restore the original desired offset; it might be available on the next try.
            self.sticky_offset = new_position.offset;
        }
    }

    /// Decrements the cursor offset. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_left(&mut self) {
        // Don't bother if we are already at the left edge.
        if self.offset == 0 { return; }

        let new_position = Position{ line: self.line, offset: self.offset-1 };
        self.move_to(new_position);
    }

    /// Increments the cursor offset. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_right(&mut self) {
        let new_position = Position{ line: self.line, offset: self.offset+1 };
        self.move_to(new_position);
    }

    /// Sets the cursor offset to 0: the start of the current line.
    pub fn move_to_start_of_line(&mut self) {
        let new_position = Position{ line: self.line, offset: 0 };
        self.move_to(new_position);
    }

    /// Moves the cursor offset to after the last character on the current line.
    pub fn move_to_end_of_line(&mut self) {
        let data = self.data.borrow().to_string();
        let current_line = data.lines().nth(self.line);
        match current_line {
            Some(line) => {
                let new_position = Position{ line: self.line, offset: line.chars().count() };
                self.move_to(new_position);
            },
            None => (),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::new;
    use buffer::gap_buffer;
    use buffer::Position;
    use std::rc::Rc;
    use std::cell::RefCell;

    #[test]
    fn move_up_goes_to_eol_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.\nAnother line that is longer.".to_string())));
        let mut cursor = new(buffer, Position{ line: 1, offset: 20 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_down_goes_to_eol_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("Another line that is longer.\nThis is a test.".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_down();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_up_persists_offset_across_shorter_lines() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = new(buffer, Position{ line: 2, offset: 20 });
        cursor.move_up();
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 20);
    }

    #[test]
    fn move_down_persists_offset_across_shorter_lines() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_down();
        cursor.move_down();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 20);
    }

    #[test]
    fn move_to_sets_persisted_offset() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_to(Position{ line: 1, offset: 5 });
        cursor.move_down();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 5);
    }

    #[test]
    fn move_to_start_of_line_sets_offset_to_zero() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.\nAnother line.".to_string())));
        let mut cursor = new(buffer, Position{ line: 1, offset: 5 });
        cursor.move_to_start_of_line();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_end_of_line_sets_offset_the_line_length() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.\nAnother line.".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 5 });
        cursor.move_to_end_of_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_up_does_nothing_if_at_the_start_of_line() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_left_does_nothing_if_at_the_start_of_line() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_left();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 0);
    }
}
<commit_msg>Add move_to_end_of_buffer cursor method.<commit_after>//! Bounds-checked buffer cursor type.
use std::ops::Deref;
use std::rc::Rc;
use std::cell::RefCell;
use buffer::{Position, GapBuffer};

/// Read-only wrapper for a `Position`, to allow field level access to a
/// buffer's cursor while simultaneously enforcing bounds-checking when
/// updating its value.
#[derive(Clone)]
pub struct Cursor {
    pub data: Rc<RefCell<GapBuffer>>,
    pub position: Position,
    sticky_offset: usize,
}

impl Deref for Cursor {
    type Target = Position;

    fn deref(&self) -> &Position {
        &self.position
    }
}

/// Initializes a cursor bound to the specified gap buffer, at the specified position.
pub fn new(data: Rc<RefCell<GapBuffer>>, position: Position) -> Cursor {
    Cursor{
        data: data,
        position: position,
        sticky_offset: position.offset
    }
}

impl Cursor {
    /// Moves the cursor to the specified location. The location is
    /// bounds-checked against the data and the cursor will not be
    /// updated if it is out-of-bounds.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// let in_bounds = scribe::buffer::Position{ line: 0, offset: 2 };
    /// let out_of_bounds = scribe::buffer::Position{ line: 2, offset: 2 };
    /// buffer.insert("scribe");
    ///
    /// buffer.cursor.move_to(in_bounds);
    /// assert_eq!(buffer.cursor.line, 0);
    /// assert_eq!(buffer.cursor.offset, 2);
    ///
    /// buffer.cursor.move_to(out_of_bounds);
    /// assert_eq!(buffer.cursor.line, 0);
    /// assert_eq!(buffer.cursor.offset, 2);
    /// ```
    pub fn move_to(&mut self, position: Position) -> bool {
        if self.data.borrow().in_bounds(&position) {
            self.position = position;

            // Remember this offset so that we can try
            // to maintain it when moving across lines.
            self.sticky_offset = position.offset;

            return true
        }
        false
    }

    /// Decrements the cursor line. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_up(&mut self) {
        // Don't bother if we are already at the top.
        if self.line == 0 { return; }

        let target_line = self.line-1;
        let new_position = Position{ line: target_line, offset: self.sticky_offset };

        // Try moving to the same offset on the line above, falling back to its EOL.
        if self.move_to(new_position) == false {
            let mut target_offset = 0;
            for (line_number, line) in self.data.borrow().to_string().lines().enumerate() {
                if line_number == target_line {
                    target_offset = line.chars().count();
                }
            }
            self.move_to(Position{ line: target_line, offset: target_offset });

            // Moving the position successfully updates the sticky offset, but we
            // haven't actually moved to where we really wanted to go (offset-wise).
            // Restore the original desired offset; it might be available on the next try.
            self.sticky_offset = new_position.offset;
        }
    }

    /// Increments the cursor line. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_down(&mut self) {
        let target_line = self.line+1;
        let new_position = Position{ line: target_line, offset: self.sticky_offset };

        // Try moving to the same offset on the line below, falling back to its EOL.
        if self.move_to(new_position) == false {
            let mut target_offset = 0;
            for (line_number, line) in self.data.borrow().to_string().lines().enumerate() {
                if line_number == target_line {
                    target_offset = line.chars().count();
                }
            }
            self.move_to(Position{ line: target_line, offset: target_offset });

            // Moving the position successfully updates the sticky offset, but we
            // haven't actually moved to where we really wanted to go (offset-wise).
            // Restore the original desired offset; it might be available on the next try.
            self.sticky_offset = new_position.offset;
        }
    }

    /// Decrements the cursor offset. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_left(&mut self) {
        // Don't bother if we are already at the left edge.
        if self.offset == 0 { return; }

        let new_position = Position{ line: self.line, offset: self.offset-1 };
        self.move_to(new_position);
    }

    /// Increments the cursor offset. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_right(&mut self) {
        let new_position = Position{ line: self.line, offset: self.offset+1 };
        self.move_to(new_position);
    }

    /// Sets the cursor offset to 0: the start of the current line.
    pub fn move_to_start_of_line(&mut self) {
        let new_position = Position{ line: self.line, offset: 0 };
        self.move_to(new_position);
    }

    /// Moves the cursor offset to after the last character on the current line.
    pub fn move_to_end_of_line(&mut self) {
        let data = self.data.borrow().to_string();
        let current_line = data.lines().nth(self.line);
        match current_line {
            Some(line) => {
                let new_position = Position{ line: self.line, offset: line.chars().count() };
                self.move_to(new_position);
            },
            None => (),
        }
    }

    /// Moves the cursor to the last line in the buffer.
    pub fn move_to_end_of_buffer(&mut self) {
        // Figure out the number and length of the last line.
        let mut line = 0;
        let mut length = 0;
        for c in self.data.borrow().to_string().chars() {
            if c == '\n' {
                line += 1;
                length = 0;
            } else {
                length += 1;
            }
        }

        let target_position =
            if length < self.sticky_offset {
                // Current offset is beyond the last line's length; move to the end of it.
                Position{ line: line, offset: length }
            } else {
                // Current offset is available on the last line; go there.
                Position{ line: line, offset: self.sticky_offset }
            };
        self.move_to(target_position);
    }
}

#[cfg(test)]
mod tests {
    use super::new;
    use buffer::gap_buffer;
    use buffer::Position;
    use std::rc::Rc;
    use std::cell::RefCell;

    #[test]
    fn move_up_goes_to_eol_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.\nAnother line that is longer.".to_string())));
        let mut cursor = new(buffer, Position{ line: 1, offset: 20 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_down_goes_to_eol_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("Another line that is longer.\nThis is a test.".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_down();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_up_persists_offset_across_shorter_lines() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = new(buffer, Position{ line: 2, offset: 20 });
        cursor.move_up();
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 20);
    }

    #[test]
    fn move_down_persists_offset_across_shorter_lines() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_down();
        cursor.move_down();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 20);
    }

    #[test]
    fn move_to_sets_persisted_offset() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_to(Position{ line: 1, offset: 5 });
        cursor.move_down();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 5);
    }

    #[test]
    fn move_to_start_of_line_sets_offset_to_zero() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.\nAnother line.".to_string())));
        let mut cursor = new(buffer, Position{ line: 1, offset: 5 });
        cursor.move_to_start_of_line();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_end_of_line_sets_offset_the_line_length() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.\nAnother line.".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 5 });
        cursor.move_to_end_of_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_up_does_nothing_if_at_the_start_of_line() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_left_does_nothing_if_at_the_start_of_line() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("This is a test.".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_left();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_end_of_buffer_moves_to_same_offset_on_last_line() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 2 });
        cursor.move_to_end_of_buffer();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 2);
    }

    #[test]
    fn move_to_end_of_buffer_moves_to_end_of_last_line_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 5 });
        cursor.move_to_end_of_buffer();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 4);
    }

    #[test]
    fn move_to_end_of_buffer_moves_last_line_when_it_is_a_trailing_newline() {
        let buffer = Rc::new(RefCell::new(gap_buffer::new("first\nsecond\nlast\n".to_string())));
        let mut cursor = new(buffer, Position{ line: 0, offset: 2 });
        cursor.move_to_end_of_buffer();
        assert_eq!(cursor.line, 3);
        assert_eq!(cursor.offset, 0);
    }
}
<|endoftext|>


<commit_before>use std::mem;
use stdweb::unstable::TryInto;
use stdweb::web::error::Error as WebError;
use rand_core::{Error, ErrorKind};
use super::OsRngImpl;

#[derive(Clone, Debug)]
enum OsRngMethod {
    Browser,
    Node
}

#[derive(Clone, Debug)]
pub struct OsRng(OsRngMethod);

impl OsRngImpl for OsRng {
    fn new() -> Result<OsRng, Error> {
        let result = js! {
            try {
                if (
                    typeof self === "object" &&
                    typeof self.crypto === "object" &&
                    typeof self.crypto.getRandomValues === "function"
                ) {
                    return { success: true, ty: 1 };
                }

                if (typeof require("crypto").randomBytes === "function") {
                    return { success: true, ty: 2 };
                }

                return { success: false, error: new Error("not supported") };
            } catch(err) {
                return { success: false, error: err };
            }
        };

        if js!{ return @{ result.as_ref() }.success } == true {
            let ty = js!{ return @{ result }.ty };

            if ty == 1 { Ok(OsRng(OsRngMethod::Browser)) }
            else if ty == 2 { Ok(OsRng(OsRngMethod::Node)) }
            else { panic!("unexpected ty value: {:?}", ty) }
        } else {
            let err: WebError = js!{ return @{ result }.error }.try_into().unwrap();
            Err(Error::with_cause(ErrorKind::Unavailable, "WASM Error", err))
        }
    }

    fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        assert_eq!(mem::size_of::<usize>(), 4);

        let len = dest.len() as u32;
        let ptr = dest.as_mut_ptr() as i32;

        let result = match self.0 {
            OsRngMethod::Browser => js! {
                try {
                    let array = new Uint8Array(@{ len });
                    self.crypto.getRandomValues(array);
                    HEAPU8.set(array, @{ ptr });

                    return { success: true };
                } catch(err) {
                    return { success: false, error: err };
                }
            },
            OsRngMethod::Node => js! {
                try {
                    let bytes = require("crypto").randomBytes(@{ len });
                    HEAPU8.set(new Uint8Array(bytes), @{ ptr });

                    return { success: true };
                } catch(err) {
                    return { success: false, error: err };
                }
            }
        };

        if js!{ return @{ result.as_ref() }.success } == true {
            Ok(())
        } else {
            let err: WebError = js!{ return @{ result }.error }.try_into().unwrap();
            Err(Error::with_cause(ErrorKind::Unexpected, "WASM Error", err))
        }
    }

    fn max_chunk_size(&self) -> usize { 65536 }

    fn method_str(&self) -> &'static str {
        match self.0 {
            OsRngMethod::Browser => "Crypto.getRandomValues",
            OsRngMethod::Node => "crypto.randomBytes",
        }
    }
}<commit_msg>return unreachable<commit_after>use std::mem;
use stdweb::unstable::TryInto;
use stdweb::web::error::Error as WebError;
use rand_core::{Error, ErrorKind};
use super::OsRngImpl;

#[derive(Clone, Debug)]
enum OsRngMethod {
    Browser,
    Node
}

#[derive(Clone, Debug)]
pub struct OsRng(OsRngMethod);

impl OsRngImpl for OsRng {
    fn new() -> Result<OsRng, Error> {
        let result = js! {
            try {
                if (
                    typeof self === "object" &&
                    typeof self.crypto === "object" &&
                    typeof self.crypto.getRandomValues === "function"
                ) {
                    return { success: true, ty: 1 };
                }

                if (typeof require("crypto").randomBytes === "function") {
                    return { success: true, ty: 2 };
                }

                return { success: false, error: new Error("not supported") };
            } catch(err) {
                return { success: false, error: err };
            }
        };

        if js!{ return @{ result.as_ref() }.success } == true {
            let ty = js!{ return @{ result }.ty };

            if ty == 1 { Ok(OsRng(OsRngMethod::Browser)) }
            else if ty == 2 { Ok(OsRng(OsRngMethod::Node)) }
            else { unreachable!() }
        } else {
            let err: WebError = js!{ return @{ result }.error }.try_into().unwrap();
            Err(Error::with_cause(ErrorKind::Unavailable, "WASM Error", err))
        }
    }

    fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        assert_eq!(mem::size_of::<usize>(), 4);

        let len = dest.len() as u32;
        let ptr = dest.as_mut_ptr() as i32;

        let result = match self.0 {
            OsRngMethod::Browser => js! {
                try {
                    let array = new Uint8Array(@{ len });
                    self.crypto.getRandomValues(array);
                    HEAPU8.set(array, @{ ptr });

                    return { success: true };
                } catch(err) {
                    return { success: false, error: err };
                }
            },
            OsRngMethod::Node => js! {
                try {
                    let bytes = require("crypto").randomBytes(@{ len });
                    HEAPU8.set(new Uint8Array(bytes), @{ ptr });

                    return { success: true };
                } catch(err) {
                    return { success: false, error: err };
                }
            }
        };

        if js!{ return @{ result.as_ref() }.success } == true {
            Ok(())
        } else {
            let err: WebError = js!{ return @{ result }.error }.try_into().unwrap();
            Err(Error::with_cause(ErrorKind::Unexpected, "WASM Error", err))
        }
    }

    fn max_chunk_size(&self) -> usize { 65536 }

    fn method_str(&self) -> &'static str {
        match self.0 {
            OsRngMethod::Browser => "Crypto.getRandomValues",
            OsRngMethod::Node => "crypto.randomBytes",
        }
    }
}<|endoftext|>


<commit_before><commit_msg>Minor cleanup<commit_after><|endoftext|>