<commit_before>

extern crate cratesfyi;
extern crate clap;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate time;

use std::env;
use std::process;
use std::path::PathBuf;

use clap::{Arg, App, SubCommand};
use cratesfyi::{DocBuilder, DocBuilderOptions, db};
use cratesfyi::utils::build_doc;
use cratesfyi::start_web_server;
use cratesfyi::db::add_path_into_database;

pub fn main() {
    logger_init();

    let matches = App::new("cratesfyi")
                      .version(cratesfyi::BUILD_VERSION)
                      .about(env!("CARGO_PKG_DESCRIPTION"))
                      .subcommand(SubCommand::with_name("doc")
                                      .about("Builds documentation of a crate")
                                      .arg(Arg::with_name("CRATE_NAME")
                                               .index(1)
                                               .required(true)
                                               .help("Crate name"))
                                      .arg(Arg::with_name("CRATE_VERSION")
                                               .index(2)
                                               .required(false)
                                               .help("Crate version"))
                                      .arg(Arg::with_name("TARGET")
                                               .index(3)
                                               .required(false)
                                               .help("The target platform to compile for")))
                      .subcommand(SubCommand::with_name("build")
                                      .about("Builds documentation in a chroot environment")
                                      .arg(Arg::with_name("PREFIX")
                                               .short("P")
                                               .long("prefix")
                                               .takes_value(true))
                                      .arg(Arg::with_name("DESTINATION")
                                               .short("d")
                                               .long("destination")
                                               .help("Sets destination path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_PATH")
                                               .short("c")
                                               .long("chroot")
                                               .help("Sets chroot path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_USER")
                                               .short("u")
                                               .long("chroot-user")
                                               .help("Sets chroot user name")
                                               .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(true)
                                                               .help("Crate name"))
                                                      .arg(Arg::with_name("CRATE_VERSION")
                                                               .index(2)
                                                               .required(true)
                                                               .help("Version of crate")))
                                      .subcommand(SubCommand::with_name("add-essential-files")
                                                      .about("Adds essential files for 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("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"))
                      .subcommand(SubCommand::with_name("database")
                                      .about("Database operations")
                                      .subcommand(SubCommand::with_name("init")
                                                      .about("Initialize database. Currently \
                                                              only creates tables in database."))
                                      .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")))
                      .get_matches();

    // doc subcommand
    if let Some(matches) = matches.subcommand_matches("doc") {
        let name = matches.value_of("CRATE_NAME").unwrap();
        let version = matches.value_of("CRATE_VERSION");
        let target = matches.value_of("TARGET");
        if let Err(e) = build_doc(name, version, target) {
            panic!("{:#?}", e);
        }
    } else 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()
            };

            // set options
            if let Some(destination) = matches.value_of("DESTINATION") {
                docbuilder_opts.destination = PathBuf::from(destination);
            }

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

            if let Some(chroot_user) = matches.value_of("CHROOT_USER") {
                docbuilder_opts.chroot_user = chroot_user.to_string();
            }

            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");
            docbuilder.build_world().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");
            docbuilder.build_package(matches.value_of("CRATE_NAME").unwrap(),
                                     matches.value_of("CRATE_VERSION").unwrap())
                      .expect("Building documentation failed");
            docbuilder.save_cache().expect("Failed to save cache");
        } else if let Some(_) = matches.subcommand_matches("add-essential-files") {
            docbuilder.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) = matches.subcommand_matches("database") {
        if let Some(_) = matches.subcommand_matches("init") {
            use std::io::Write;
            use std::io;
            let conn = db::connect_db().unwrap();
            if let Err(err) = db::create_tables(&conn) {
                writeln!(&mut io::stderr(), "Failed to initialize database: {}", err).unwrap();
                process::exit(1);
            }
        } 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())
                .unwrap();
        } 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) = 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") {
        cratesfyi::utils::start_daemon();
    } else {
        println!("{}", matches.usage());
    }
}

fn logger_init() {
    let format = |record: &log::LogRecord| {
        format!("{} [{}] {}: {}",
                time::now().strftime("%Y/%m/%d %H:%M:%S").unwrap(),
                record.level(), record.target(), record.args())
    };

    let mut builder = env_logger::LogBuilder::new();
    builder.format(format);
    builder.parse(&env::var("RUST_LOG").unwrap_or("cratesfyi=info".to_owned()));
    builder.init().unwrap();
}
<commit_msg>Add update-search-index subcommand to database<commit_after>

extern crate cratesfyi;
extern crate clap;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate time;

use std::env;
use std::process;
use std::path::PathBuf;

use clap::{Arg, App, SubCommand};
use cratesfyi::{DocBuilder, DocBuilderOptions, db};
use cratesfyi::utils::build_doc;
use cratesfyi::start_web_server;
use cratesfyi::db::add_path_into_database;

pub fn main() {
    logger_init();

    let matches = App::new("cratesfyi")
                      .version(cratesfyi::BUILD_VERSION)
                      .about(env!("CARGO_PKG_DESCRIPTION"))
                      .subcommand(SubCommand::with_name("doc")
                                      .about("Builds documentation of a crate")
                                      .arg(Arg::with_name("CRATE_NAME")
                                               .index(1)
                                               .required(true)
                                               .help("Crate name"))
                                      .arg(Arg::with_name("CRATE_VERSION")
                                               .index(2)
                                               .required(false)
                                               .help("Crate version"))
                                      .arg(Arg::with_name("TARGET")
                                               .index(3)
                                               .required(false)
                                               .help("The target platform to compile for")))
                      .subcommand(SubCommand::with_name("build")
                                      .about("Builds documentation in a chroot environment")
                                      .arg(Arg::with_name("PREFIX")
                                               .short("P")
                                               .long("prefix")
                                               .takes_value(true))
                                      .arg(Arg::with_name("DESTINATION")
                                               .short("d")
                                               .long("destination")
                                               .help("Sets destination path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_PATH")
                                               .short("c")
                                               .long("chroot")
                                               .help("Sets chroot path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_USER")
                                               .short("u")
                                               .long("chroot-user")
                                               .help("Sets chroot user name")
                                               .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(true)
                                                               .help("Crate name"))
                                                      .arg(Arg::with_name("CRATE_VERSION")
                                                               .index(2)
                                                               .required(true)
                                                               .help("Version of crate")))
                                      .subcommand(SubCommand::with_name("add-essential-files")
                                                      .about("Adds essential files for 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("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"))
                      .subcommand(SubCommand::with_name("database")
                                      .about("Database operations")
                                      .subcommand(SubCommand::with_name("init")
                                                      .about("Initialize database. Currently \
                                                              only creates tables in database."))
                                      .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 montly release activity \
                                                              chart")
                                      .subcommand(SubCommand::with_name("update-search-index"))
                                                      .about("Updates search index"))
                      .get_matches();

    // doc subcommand
    if let Some(matches) = matches.subcommand_matches("doc") {
        let name = matches.value_of("CRATE_NAME").unwrap();
        let version = matches.value_of("CRATE_VERSION");
        let target = matches.value_of("TARGET");
        if let Err(e) = build_doc(name, version, target) {
            panic!("{:#?}", e);
        }
    } else 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()
            };

            // set options
            if let Some(destination) = matches.value_of("DESTINATION") {
                docbuilder_opts.destination = PathBuf::from(destination);
            }

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

            if let Some(chroot_user) = matches.value_of("CHROOT_USER") {
                docbuilder_opts.chroot_user = chroot_user.to_string();
            }

            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");
            docbuilder.build_world().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");
            docbuilder.build_package(matches.value_of("CRATE_NAME").unwrap(),
                                     matches.value_of("CRATE_VERSION").unwrap())
                      .expect("Building documentation failed");
            docbuilder.save_cache().expect("Failed to save cache");
        } else if let Some(_) = matches.subcommand_matches("add-essential-files") {
            docbuilder.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) = matches.subcommand_matches("database") {
        if let Some(_) = matches.subcommand_matches("init") {
            use std::io::Write;
            use std::io;
            let conn = db::connect_db().unwrap();
            if let Err(err) = db::create_tables(&conn) {
                writeln!(&mut io::stderr(), "Failed to initialize database: {}", err).unwrap();
                process::exit(1);
            }
        } 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())
                .unwrap();
        } 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) = 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") {
        cratesfyi::utils::start_daemon();
    } else {
        println!("{}", matches.usage());
    }
}

fn logger_init() {
    let format = |record: &log::LogRecord| {
        format!("{} [{}] {}: {}",
                time::now().strftime("%Y/%m/%d %H:%M:%S").unwrap(),
                record.level(), record.target(), record.args())
    };

    let mut builder = env_logger::LogBuilder::new();
    builder.format(format);
    builder.parse(&env::var("RUST_LOG").unwrap_or("cratesfyi=info".to_owned()));
    builder.init().unwrap();
}
<|endoftext|>


<commit_before>

extern crate cratesfyi;
extern crate clap;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate time;

use std::env;
use std::process;
use std::path::PathBuf;

use clap::{Arg, App, SubCommand};
use cratesfyi::{DocBuilder, DocBuilderOptions, db};
use cratesfyi::utils::build_doc;
use cratesfyi::start_web_server;
use cratesfyi::db::add_path_into_database;

pub fn main() {
    logger_init();

    let matches = App::new("cratesfyi")
                      .version(cratesfyi::BUILD_VERSION)
                      .about(env!("CARGO_PKG_DESCRIPTION"))
                      .subcommand(SubCommand::with_name("doc")
                                      .about("Builds documentation of a crate")
                                      .arg(Arg::with_name("CRATE_NAME")
                                               .index(1)
                                               .required(true)
                                               .help("Crate name"))
                                      .arg(Arg::with_name("CRATE_VERSION")
                                               .index(2)
                                               .required(false)
                                               .help("Crate version"))
                                      .arg(Arg::with_name("TARGET")
                                               .index(3)
                                               .required(false)
                                               .help("The target platform to compile for")))
                      .subcommand(SubCommand::with_name("build")
                                      .about("Builds documentation in a chroot environment")
                                      .arg(Arg::with_name("PREFIX")
                                               .short("P")
                                               .long("prefix")
                                               .takes_value(true))
                                      .arg(Arg::with_name("DESTINATION")
                                               .short("d")
                                               .long("destination")
                                               .help("Sets destination path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_PATH")
                                               .short("c")
                                               .long("chroot")
                                               .help("Sets chroot path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_USER")
                                               .short("u")
                                               .long("chroot-user")
                                               .help("Sets chroot user name")
                                               .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(true)
                                                               .help("Crate name"))
                                                      .arg(Arg::with_name("CRATE_VERSION")
                                                               .index(2)
                                                               .required(true)
                                                               .help("Version of crate")))
                                      .subcommand(SubCommand::with_name("add-essential-files")
                                                      .about("Adds essential files for rustc")))
                      .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"))
                      .subcommand(SubCommand::with_name("database")
                                      .about("Database operations")
                                      .subcommand(SubCommand::with_name("init")
                                                      .about("Initialize database. Currently \
                                                              only creates tables in database."))
                                      .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")))
                      .get_matches();

    // doc subcommand
    if let Some(matches) = matches.subcommand_matches("doc") {
        let name = matches.value_of("CRATE_NAME").unwrap();
        let version = matches.value_of("CRATE_VERSION");
        let target = matches.value_of("TARGET");
        if let Err(e) = build_doc(name, version, target) {
            panic!("{:#?}", e);
        }
    } else 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()
            };

            // set options
            if let Some(destination) = matches.value_of("DESTINATION") {
                docbuilder_opts.destination = PathBuf::from(destination);
            }

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

            if let Some(chroot_user) = matches.value_of("CHROOT_USER") {
                docbuilder_opts.chroot_user = chroot_user.to_string();
            }

            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);

        docbuilder.load_cache().expect("Failed to load cache");

        if let Some(_) = matches.subcommand_matches("world") {
            docbuilder.build_world().expect("Failed to build world");
        } else if let Some(matches) = matches.subcommand_matches("crate") {
            docbuilder.build_package(matches.value_of("CRATE_NAME").unwrap(),
                                     matches.value_of("CRATE_VERSION").unwrap())
                      .expect("Building documentation failed");
        } else if let Some(_) = matches.subcommand_matches("add-essential-files") {
            docbuilder.add_essential_files().expect("Failed to add essential files");
        }

        docbuilder.save_cache().expect("Failed to save cache");
    } else if let Some(matches) = matches.subcommand_matches("database") {
        if let Some(_) = matches.subcommand_matches("init") {
            use std::io::Write;
            use std::io;
            let conn = db::connect_db().unwrap();
            if let Err(err) = db::create_tables(&conn) {
                writeln!(&mut io::stderr(), "Failed to initialize database: {}", err).unwrap();
                process::exit(1);
            }
        } 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())
                .unwrap();
        } 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) = matches.subcommand_matches("start-web-server") {
        start_web_server(matches.value_of("SOCKET_ADDR"));
    } else if let Some(_) = matches.subcommand_matches("daemon") {
        cratesfyi::utils::start_daemon();
    } else {
        println!("{}", matches.usage());
    }
}

fn logger_init() {
    let format = |record: &log::LogRecord| {
        format!("{} [{}] {}: {}",
                time::now().strftime("%Y/%m/%d %H:%M:%S").unwrap(),
                record.level(), record.target(), record.args())
    };

    let mut builder = env_logger::LogBuilder::new();
    builder.format(format);
    builder.parse(&env::var("RUST_LOG").unwrap_or("cratesfyi=info".to_owned()));
    builder.init().unwrap();
}
<commit_msg>Listen 0.0.0.0:3000 by default on start-web-server<commit_after>

extern crate cratesfyi;
extern crate clap;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate time;

use std::env;
use std::process;
use std::path::PathBuf;

use clap::{Arg, App, SubCommand};
use cratesfyi::{DocBuilder, DocBuilderOptions, db};
use cratesfyi::utils::build_doc;
use cratesfyi::start_web_server;
use cratesfyi::db::add_path_into_database;

pub fn main() {
    logger_init();

    let matches = App::new("cratesfyi")
                      .version(cratesfyi::BUILD_VERSION)
                      .about(env!("CARGO_PKG_DESCRIPTION"))
                      .subcommand(SubCommand::with_name("doc")
                                      .about("Builds documentation of a crate")
                                      .arg(Arg::with_name("CRATE_NAME")
                                               .index(1)
                                               .required(true)
                                               .help("Crate name"))
                                      .arg(Arg::with_name("CRATE_VERSION")
                                               .index(2)
                                               .required(false)
                                               .help("Crate version"))
                                      .arg(Arg::with_name("TARGET")
                                               .index(3)
                                               .required(false)
                                               .help("The target platform to compile for")))
                      .subcommand(SubCommand::with_name("build")
                                      .about("Builds documentation in a chroot environment")
                                      .arg(Arg::with_name("PREFIX")
                                               .short("P")
                                               .long("prefix")
                                               .takes_value(true))
                                      .arg(Arg::with_name("DESTINATION")
                                               .short("d")
                                               .long("destination")
                                               .help("Sets destination path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_PATH")
                                               .short("c")
                                               .long("chroot")
                                               .help("Sets chroot path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_USER")
                                               .short("u")
                                               .long("chroot-user")
                                               .help("Sets chroot user name")
                                               .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(true)
                                                               .help("Crate name"))
                                                      .arg(Arg::with_name("CRATE_VERSION")
                                                               .index(2)
                                                               .required(true)
                                                               .help("Version of crate")))
                                      .subcommand(SubCommand::with_name("add-essential-files")
                                                      .about("Adds essential files for rustc")))
                      .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"))
                      .subcommand(SubCommand::with_name("database")
                                      .about("Database operations")
                                      .subcommand(SubCommand::with_name("init")
                                                      .about("Initialize database. Currently \
                                                              only creates tables in database."))
                                      .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")))
                      .get_matches();

    // doc subcommand
    if let Some(matches) = matches.subcommand_matches("doc") {
        let name = matches.value_of("CRATE_NAME").unwrap();
        let version = matches.value_of("CRATE_VERSION");
        let target = matches.value_of("TARGET");
        if let Err(e) = build_doc(name, version, target) {
            panic!("{:#?}", e);
        }
    } else 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()
            };

            // set options
            if let Some(destination) = matches.value_of("DESTINATION") {
                docbuilder_opts.destination = PathBuf::from(destination);
            }

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

            if let Some(chroot_user) = matches.value_of("CHROOT_USER") {
                docbuilder_opts.chroot_user = chroot_user.to_string();
            }

            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);

        docbuilder.load_cache().expect("Failed to load cache");

        if let Some(_) = matches.subcommand_matches("world") {
            docbuilder.build_world().expect("Failed to build world");
        } else if let Some(matches) = matches.subcommand_matches("crate") {
            docbuilder.build_package(matches.value_of("CRATE_NAME").unwrap(),
                                     matches.value_of("CRATE_VERSION").unwrap())
                      .expect("Building documentation failed");
        } else if let Some(_) = matches.subcommand_matches("add-essential-files") {
            docbuilder.add_essential_files().expect("Failed to add essential files");
        }

        docbuilder.save_cache().expect("Failed to save cache");
    } else if let Some(matches) = matches.subcommand_matches("database") {
        if let Some(_) = matches.subcommand_matches("init") {
            use std::io::Write;
            use std::io;
            let conn = db::connect_db().unwrap();
            if let Err(err) = db::create_tables(&conn) {
                writeln!(&mut io::stderr(), "Failed to initialize database: {}", err).unwrap();
                process::exit(1);
            }
        } 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())
                .unwrap();
        } 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) = 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") {
        cratesfyi::utils::start_daemon();
    } else {
        println!("{}", matches.usage());
    }
}

fn logger_init() {
    let format = |record: &log::LogRecord| {
        format!("{} [{}] {}: {}",
                time::now().strftime("%Y/%m/%d %H:%M:%S").unwrap(),
                record.level(), record.target(), record.args())
    };

    let mut builder = env_logger::LogBuilder::new();
    builder.format(format);
    builder.parse(&env::var("RUST_LOG").unwrap_or("cratesfyi=info".to_owned()));
    builder.init().unwrap();
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use nix::sys::wait::*;
use nix::sys::signal;
use nix::unistd::Pid;
use tracer::*;
use breakpoint::*;
use ptrace_control::*;
use nix::sys::ptrace::ptrace::*;
use config::Config;
///
/// So we are either:
///     Waiting for an signal
///     Handling a stop
///     Handling an exit
///     Handling being signalled
///     Handling ptrace events
///     Handling an error
///     Continuing execution
///     Handling ptrace error
///
/// So we wait -> other thing -> continuing
///
/// Cannot do wait -> wait (unless non-polling, timeout?)
///
/// Or wait -> other thing -> other thing -> continue
///
/// Or wait -> other thing -> continue -> continue

/// Possible states when executing an inferior process. This is an attempt at
/// a platform agnostic abstracting to provide the potential of future
/// implementations for other operating systems and provides the implementation
/// of the test running state machine
/// T is data used to store the necessary process information to enable tracing
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TestState {
    /// Start state. Wait for test to appear and track time to enable timeout
    Start {
        start_time: u64,
    },
    /// Initialise: once test process appears instrument
    Initialise ,
    /// Waiting for breakpoint to be hit or test to end
    Waiting {
        start_time: u64,
    },
    /// Test process stopped, check coverage
    Stopped,
    /// Process timed out
    Timeout,
    /// Unrecoverable error occurred
    Unrecoverable,
    /// Test exited normally
    End,
}

impl TestState {
    /// Convenience function used to check if the test has finished or errored
    pub fn is_finished(self) -> bool {
        match self {
            TestState::End | TestState::Unrecoverable => true,
            _ => false,
        }
    }
}

/// Trait for state machines to implement
pub trait StateMachine<T> where T:StateData {
    /// Update the states
    fn step(self, data: &mut T, config: &Config) -> TestState;
}

/// Tracing a process on an OS will have platform specific code.
/// Structs containing the platform specific datastructures should
/// provide this trait with an implementation of the handling of
/// the given states.
pub trait StateData {
    /// Starts the tracing. Returns None while waiting for
    /// start. Statemachine then checks timeout
    fn start(&mut self) -> Option<TestState>;
    /// Initialises test for tracing returns next state
    fn init(&mut self) -> TestState;
    /// Waits for notification from test executable that there's
    /// something to do. Selects the next appropriate state if there's
    /// something to do otherwise None
    fn wait(&mut self) -> Option<TestState>;
    /// Handle a stop in the test executable. Coverage data will
    /// be collected here as well as other OS specific functions
    fn stop(&mut self) -> TestState;
    /// Cleanup the system state - killing processes etc
    fn cleanup(&mut self);
}

impl <T> StateMachine<T> for TestState where T:StateData {

    fn step(self, data: &mut T, config: &Config) -> TestState {
        match self {
            org @ TestState::Start{..} => {
                if let Some(s) = data.start() {
                    s
                } else {
                    org
                }
            },
            TestState::Initialise => {
                data.init()
            },
            org @ TestState::Waiting{..} => {
                if let Some(s) =data.wait() {
                    s
                } else {
                    org
                }
            },
            TestState::Stopped => {
                data.stop()
            },
            TestState::Timeout => {
                data.cleanup();
                TestState::End
            },
            TestState::Unrecoverable => {
                data.cleanup();
                TestState::End
            },
            _ => {
                TestState::End
            }
        }
    }
}

pub fn create_state_machine<'a>(test: Pid,
                                traces: &'a mut Vec<TracerData>,
                                config: &'a Config) -> (TestState, LinuxData<'a>) {
    let mut data = LinuxData::new(traces, config);
    data.parent = test;
    (TestState::Start{start_time:0}, data)
}

/// Handle to linux process state
#[derive(Debug)]
pub struct LinuxData<'a> {
    /// Recent result from waitpid to be handled by statemachine
    wait: WaitStatus,
    /// Current Pid to process
    current: Pid,
    /// Parent PID of test process
    parent: Pid,
    /// Map of addresses to breakpoints
    breakpoints: HashMap<u64, Breakpoint>,
    /// Instrumentation points in code with associated coverage data
    traces: &'a mut Vec<TracerData>,
    /// Program config
    config: &'a Config,
    /// Used to store error for user in the event something goes wrong
    error_message: Option<String>,
}

impl <'a> StateData for LinuxData<'a> {

    fn start(&mut self) -> Option<TestState> {
        match waitpid(self.current, Some(WNOHANG)) {
            Ok(WaitStatus::StillAlive) => None,
            Ok(sig @ WaitStatus::Stopped(_, signal::SIGTRAP)) => {
                if let WaitStatus::Stopped(child, _) = sig {
                    self.current = child;
                }
                self.wait = sig;
                Some(TestState::Initialise)
            },
            Ok(s) => {
                println!("Unexpected signal when starting test");
                None
            },
            Err(e) => {
                println!("Error when starting test: {}", e);
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn init(&mut self) -> TestState {
        if let Err(c) = trace_children(self.current) {
            println!("Failed to trace child threads");
        }
        for trace in self.traces.iter() {
            if let Some(addr) = trace.address {
                match Breakpoint::new(self.current, addr) {
                    Ok(bp) => {
                        let _ = self.breakpoints.insert(addr, bp);
                    },
                    Err(e) => {
                        println!("Failed to instrument");
                    },
                }
            }
        }
        if let Ok(_) = continue_exec(self.parent, None) {
            TestState::Waiting{start_time:0}
        } else {
            TestState::Unrecoverable
        }
    }

    fn wait(&mut self) -> Option<TestState> {
        let wait = waitpid(Pid::from_raw(-1), Some(WNOHANG | __WALL));
        match wait {
            Ok(WaitStatus::StillAlive) => {
                self.wait = WaitStatus::StillAlive;
                None
            },
            Ok(s) => {
                self.wait = s;
                Some(TestState::Stopped)
            },
            Err(e) => {
                println!("An error occurred");
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn stop(&mut self) -> TestState {
        match self.wait {
            WaitStatus::PtraceEvent(_,_,_) => {
                self.handle_ptrace_event()
            },
            WaitStatus::Stopped(c,signal::SIGTRAP) => {
                self.current = c;
                self.collect_coverage_data()
            },
            WaitStatus::Stopped(child, signal::SIGSTOP) => {
                let _ =continue_exec(child, None);
                TestState::Waiting{start_time:0}
            },
            WaitStatus::Stopped(_, signal::SIGSEGV) => TestState::Unrecoverable,
            WaitStatus::Stopped(c, s) => {
                let sig = if self.config.forward_signals {
                    Some(s)
                } else {
                    None
                };
                let _ = continue_exec(c, sig);
                TestState::Waiting{start_time:0}
            },
            WaitStatus::Signaled(_,_,_) => {
                self.handle_signaled()
            },
            WaitStatus::Exited(child, sig) => {
                if child == self.parent {
                    TestState::End
                } else {
                    let _ = continue_exec(self.parent, None);
                    TestState::Waiting{start_time:0}
                }
            },
            _ => TestState::Unrecoverable,
        }
    }

    fn cleanup(&mut self)  {

    }
}

impl <'a>LinuxData<'a> {
    pub fn new(traces: &'a mut Vec<TracerData>, config: &'a Config) -> LinuxData<'a> {
        LinuxData {
            wait: WaitStatus::StillAlive,
            current: Pid::from_raw(0),
            parent: Pid::from_raw(0),
            breakpoints: HashMap::new(),
            traces: traces,
            config: config,
            error_message:None
        }
    }

    fn handle_ptrace_event(&mut self) -> TestState {
        match self.wait {
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_FORK) => {
                continue_exec(child, None);
                TestState::Waiting{start_time:0}
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_VFORK) => {
                continue_exec(child, None);
                TestState::Waiting{start_time:0}
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXEC) => {
                detach_child(child);
                TestState::Waiting{start_time:0}
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXIT) => {
                continue_exec(child, None);
                TestState::Waiting{start_time:0}
            },
            _ => TestState::Unrecoverable,
        }
    }

    fn collect_coverage_data(&mut self) -> TestState {
        let thread_count = 1;
        let mut unwarned = true;
        if let Ok(rip) = current_instruction_pointer(self.current) {
            let rip = (rip - 1) as u64;
            if  self.breakpoints.contains_key(&rip) {
                let bp = &mut self.breakpoints.get_mut(&rip).unwrap();
                let enable = (!self.config.no_count) && (thread_count < 2);
                if !enable && unwarned {
                    println!("Code is mulithreaded, disabling hit count");
                    unwarned = false;
                }
                // Don't reenable if multithreaded as can't yet sort out segfault issue
                let updated = if let Ok(x) = bp.process(self.current, enable) {
                     x
                } else {
                    false
                };
                if updated {
                    for t in self.traces.iter_mut()
                                        .filter(|x| x.address == Some(rip)) {
                        (*t).hits += 1;
                    }
                }
            } else {
                continue_exec(self.current, None);
            }
        } else {
            continue_exec(self.current, None);
        }
        TestState::Waiting{start_time:0}
    }

    fn handle_signaled(&mut self) -> TestState {
        match self.wait {
            WaitStatus::Signaled(child, signal::SIGTRAP, true) => {
                continue_exec(child, None);
                TestState::Waiting{start_time:0}
            },
            _ => {
                println!("Unexpected stop");
                TestState::Unrecoverable
            },
        }
    }
}
<commit_msg>Started populating error message value.<commit_after>use std::collections::HashMap;
use nix::sys::ptrace::ptrace::*;
use nix::sys::wait::*;
use nix::sys::signal;
use nix::Result;
use nix::unistd::Pid;
use tracer::*;
use breakpoint::*;
use ptrace_control::*;
use config::Config;
///
/// So we are either:
///     Waiting for an signal
///     Handling a stop
///     Handling an exit
///     Handling being signalled
///     Handling ptrace events
///     Handling an error
///     Continuing execution
///     Handling ptrace error
///
/// So we wait -> other thing -> continuing
///
/// Cannot do wait -> wait (unless non-polling, timeout?)
///
/// Or wait -> other thing -> other thing -> continue
///
/// Or wait -> other thing -> continue -> continue

/// Possible states when executing an inferior process. This is an attempt at
/// a platform agnostic abstracting to provide the potential of future
/// implementations for other operating systems and provides the implementation
/// of the test running state machine
/// T is data used to store the necessary process information to enable tracing
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TestState {
    /// Start state. Wait for test to appear and track time to enable timeout
    Start {
        start_time: u64,
    },
    /// Initialise: once test process appears instrument
    Initialise ,
    /// Waiting for breakpoint to be hit or test to end
    Waiting {
        start_time: u64,
    },
    /// Test process stopped, check coverage
    Stopped,
    /// Process timed out
    Timeout,
    /// Unrecoverable error occurred
    Unrecoverable,
    /// Test exited normally
    End,
}

impl TestState {
    /// Convenience function used to check if the test has finished or errored
    pub fn is_finished(self) -> bool {
        match self {
            TestState::End | TestState::Unrecoverable => true,
            _ => false,
        }
    }
}

/// Trait for state machines to implement
pub trait StateMachine<T> where T:StateData {
    /// Update the states
    fn step(self, data: &mut T, config: &Config) -> TestState;
}

/// Tracing a process on an OS will have platform specific code.
/// Structs containing the platform specific datastructures should
/// provide this trait with an implementation of the handling of
/// the given states.
pub trait StateData {
    /// Starts the tracing. Returns None while waiting for
    /// start. Statemachine then checks timeout
    fn start(&mut self) -> Option<TestState>;
    /// Initialises test for tracing returns next state
    fn init(&mut self) -> TestState;
    /// Waits for notification from test executable that there's
    /// something to do. Selects the next appropriate state if there's
    /// something to do otherwise None
    fn wait(&mut self) -> Option<TestState>;
    /// Handle a stop in the test executable. Coverage data will
    /// be collected here as well as other OS specific functions
    fn stop(&mut self) -> TestState;
    /// Cleanup the system state - killing processes etc
    fn cleanup(&mut self);
}

impl <T> StateMachine<T> for TestState where T:StateData {

    fn step(self, data: &mut T, config: &Config) -> TestState {
        match self {
            org @ TestState::Start{..} => {
                if let Some(s) = data.start() {
                    s
                } else {
                    org
                }
            },
            TestState::Initialise => {
                data.init()
            },
            org @ TestState::Waiting{..} => {
                if let Some(s) =data.wait() {
                    s
                } else {
                    org
                }
            },
            TestState::Stopped => {
                data.stop()
            },
            TestState::Timeout => {
                data.cleanup();
                TestState::End
            },
            TestState::Unrecoverable => {
                data.cleanup();
                TestState::End
            },
            _ => {
                TestState::End
            }
        }
    }
}

pub fn create_state_machine<'a>(test: Pid,
                                traces: &'a mut Vec<TracerData>,
                                config: &'a Config) -> (TestState, LinuxData<'a>) {
    let mut data = LinuxData::new(traces, config);
    data.parent = test;
    (TestState::Start{start_time:0}, data)
}

/// Handle to linux process state
#[derive(Debug)]
pub struct LinuxData<'a> {
    /// Recent result from waitpid to be handled by statemachine
    wait: WaitStatus,
    /// Current Pid to process
    current: Pid,
    /// Parent PID of test process
    parent: Pid,
    /// Map of addresses to breakpoints
    breakpoints: HashMap<u64, Breakpoint>,
    /// Instrumentation points in code with associated coverage data
    traces: &'a mut Vec<TracerData>,
    /// Program config
    config: &'a Config,
    /// Used to store error for user in the event something goes wrong
    error_message: Option<String>,
}

impl <'a> StateData for LinuxData<'a> {

    fn start(&mut self) -> Option<TestState> {
        match waitpid(self.current, Some(WNOHANG)) {
            Ok(WaitStatus::StillAlive) => None,
            Ok(sig @ WaitStatus::Stopped(_, signal::SIGTRAP)) => {
                if let WaitStatus::Stopped(child, _) = sig {
                    self.current = child;
                }
                self.wait = sig;
                Some(TestState::Initialise)
            },
            Ok(s) => {
                println!("Unexpected signal when starting test");
                None
            },
            Err(e) => {
                println!("Error when starting test: {}", e);
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn init(&mut self) -> TestState {
        if let Err(c) = trace_children(self.current) {
            println!("Failed to trace child threads");
        }
        for trace in self.traces.iter() {
            if let Some(addr) = trace.address {
                match Breakpoint::new(self.current, addr) {
                    Ok(bp) => {
                        let _ = self.breakpoints.insert(addr, bp);
                    },
                    Err(e) => {
                        self.error_message = Some("Failed to instrument test executable".to_string());
                    },
                }
            }
        }
        if let Ok(_) = continue_exec(self.parent, None) {
            TestState::Waiting{start_time:0}
        } else {
            TestState::Unrecoverable
        }
    }

    fn wait(&mut self) -> Option<TestState> {
        let wait = waitpid(Pid::from_raw(-1), Some(WNOHANG | __WALL));
        match wait {
            Ok(WaitStatus::StillAlive) => {
                self.wait = WaitStatus::StillAlive;
                None
            },
            Ok(s) => {
                self.wait = s;
                Some(TestState::Stopped)
            },
            Err(e) => {
                self.error_message = Some("An error occurred while waiting for response from test".to_string());
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn stop(&mut self) -> TestState {
        match self.wait {
            WaitStatus::PtraceEvent(_,_,_) => {
                match self.handle_ptrace_event() {
                    Ok(s) => s,
                    Err(e) => {
                        let msg = format!("Error occurred when handling ptrace event: {}", e);
                        self.error_message = Some(msg);
                        TestState::Unrecoverable
                    },
                }
            },
            WaitStatus::Stopped(c,signal::SIGTRAP) => {
                self.current = c;
                match self.collect_coverage_data() {
                    Ok(s) => s,
                    Err(e) => {
                        self.error_message = Some(format!("Error when collecting coverage: {}", e));
                        TestState::Unrecoverable
                    }
                }
            },
            WaitStatus::Stopped(child, signal::SIGSTOP) => {
                if continue_exec(child, None).is_ok() {
                    TestState::Waiting{start_time:0}
                } else {
                    self.error_message = Some("Error processing SIGSTOP".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Stopped(_, signal::SIGSEGV) => TestState::Unrecoverable,
            WaitStatus::Stopped(c, s) => {
                let sig = if self.config.forward_signals {
                    Some(s)
                } else {
                    None
                };
                let _ = continue_exec(c, sig);
                TestState::Waiting{start_time:0}
            },
            WaitStatus::Signaled(_,_,_) => {
                if let Ok(s) = self.handle_signaled() {
                    s
                } else {
                    self.error_message = Some("Error attempting to handle tarpaulin being signaled".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Exited(child, sig) => {
                if child == self.parent {
                    TestState::End
                } else {
                    let _ = continue_exec(self.parent, None);
                    TestState::Waiting{start_time:0}
                }
            },
            _ => TestState::Unrecoverable,
        }
    }

    fn cleanup(&mut self)  {

    }
}

impl <'a>LinuxData<'a> {
    pub fn new(traces: &'a mut Vec<TracerData>, config: &'a Config) -> LinuxData<'a> {
        LinuxData {
            wait: WaitStatus::StillAlive,
            current: Pid::from_raw(0),
            parent: Pid::from_raw(0),
            breakpoints: HashMap::new(),
            traces: traces,
            config: config,
            error_message:None
        }
    }

    fn handle_ptrace_event(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_FORK) => {
                continue_exec(child, None)?;
                Ok(TestState::Waiting{start_time:0})
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_VFORK) => {
                continue_exec(child, None)?;
                Ok(TestState::Waiting{start_time:0})
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXEC) => {
                detach_child(child)?;
                Ok(TestState::Waiting{start_time:0})
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXIT) => {
                continue_exec(child, None)?;
                Ok(TestState::Waiting{start_time:0})
            },
            _ => Ok(TestState::Unrecoverable),
        }
    }

    fn collect_coverage_data(&mut self) -> Result<TestState> {
        let thread_count = 1;
        let mut unwarned = true;
        if let Ok(rip) = current_instruction_pointer(self.current) {
            let rip = (rip - 1) as u64;
            if  self.breakpoints.contains_key(&rip) {
                let bp = &mut self.breakpoints.get_mut(&rip).unwrap();
                let enable = (!self.config.no_count) && (thread_count < 2);
                if !enable && unwarned {
                    println!("Code is mulithreaded, disabling hit count");
                    unwarned = false;
                }
                // Don't reenable if multithreaded as can't yet sort out segfault issue
                let updated = if let Ok(x) = bp.process(self.current, enable) {
                     x
                } else {
                    false
                };
                if updated {
                    for t in self.traces.iter_mut()
                                        .filter(|x| x.address == Some(rip)) {
                        (*t).hits += 1;
                    }
                }
            } else {
                continue_exec(self.current, None)?;
            }
        } else {
            continue_exec(self.current, None)?;
        }
        Ok(TestState::Waiting{start_time:0})
    }

    fn handle_signaled(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::Signaled(child, signal::SIGTRAP, true) => {
                continue_exec(child, None)?;
                Ok(TestState::Waiting{start_time:0})
            },
            _ => {
                self.error_message = Some("Unexpected stop".to_string());
                Ok(TestState::Unrecoverable)
            },
        }
    }
}
<|endoftext|>


<commit_before>import option::none;
import option::some;
import util::orb;

type vbuf = rustrt::vbuf;

type operator2[T,U,V] = fn(&T, &U) -> V;

type array[T] = vec[mutable? T];

native "rust" mod rustrt {
    type vbuf;

    fn vec_buf[T](vec[T] v, uint offset) -> vbuf;

    fn vec_len[T](vec[T] v) -> uint;
    /**
     * Sometimes we modify the vec internal data via vec_buf and need to
     * update the vec's fill length accordingly.
     */
    fn vec_len_set[T](vec[T] v, uint n);

    /**
     * The T in vec_alloc[T, U] is the type of the vec to allocate.  The
     * U is the type of an element in the vec.  So to allocate a vec[U] we
     * want to invoke this as vec_alloc[vec[U], U].
     */
    fn vec_alloc[T, U](uint n_elts) -> vec[U];
    fn vec_alloc_mut[T, U](uint n_elts) -> vec[mutable U];

    fn refcount[T](vec[T] v) -> uint;

    fn vec_print_debug_info[T](vec[T] v);

    fn vec_from_vbuf[T](vbuf v, uint n_elts) -> vec[T];

    fn unsafe_vec_to_mut[T](vec[T] v) -> vec[mutable T];
}

fn alloc[T](uint n_elts) -> vec[T] {
    ret rustrt::vec_alloc[vec[T], T](n_elts);
}

fn alloc_mut[T](uint n_elts) -> vec[mutable T] {
    ret rustrt::vec_alloc_mut[vec[mutable T], T](n_elts);
}

fn refcount[T](array[T] v) -> uint {
    auto r = rustrt::refcount[T](v);
    if (r == dbg::const_refcount) {
        ret r;
    } else {
        // -2 because calling this function and the native function both
        // incremented the refcount.
        ret  r - 2u;
    }
}

fn vec_from_vbuf[T](vbuf v, uint n_elts) -> vec[T] {
    ret rustrt::vec_from_vbuf[T](v, n_elts);
}

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty[T]() -> vec[T] {
    ret alloc[T](0u);
}

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty_mut[T]() -> vec[mutable T] {
    ret alloc_mut[T](0u);
}

type init_op[T] = fn(uint i) -> T;

fn init_fn[T](&init_op[T] op, uint n_elts) -> vec[T] {
    let vec[T] v = alloc[T](n_elts);
    let uint i = 0u;
    while (i < n_elts) {
        v += [op(i)];
        i += 1u;
    }
    ret v;
}

fn init_fn_mut[T](&init_op[T] op, uint n_elts) -> vec[mutable T] {
    let vec[mutable T] v = alloc_mut[T](n_elts);
    let uint i = 0u;
    while (i < n_elts) {
        v += [mutable op(i)];
        i += 1u;
    }
    ret v;
}

fn init_elt[T](&T t, uint n_elts) -> vec[T] {
    /**
     * FIXME (issue #81): should be:
     *
     * fn elt_op[T](&T x, uint i) -> T { ret x; }
     * let init_op[T] inner = bind elt_op[T](t, _);
     * ret init_fn[T](inner, n_elts);
     */
    let vec[T] v = alloc[T](n_elts);
    let uint i = n_elts;
    while (i > 0u) {
        i -= 1u;
        v += [t];
    }
    ret v;
}

fn init_elt_mut[T](&T t, uint n_elts) -> vec[mutable T] {
    let vec[mutable T] v = alloc_mut[T](n_elts);
    let uint i = n_elts;
    while (i > 0u) {
        i -= 1u;
        v += [mutable t];
    }
    ret v;
}

fn buf[T](array[T] v) -> vbuf {
    ret rustrt::vec_buf[T](v, 0u);
}

fn len[T](array[T] v) -> uint {
    ret rustrt::vec_len[T](v);
}

fn len_set[T](array[T] v, uint n) {
    rustrt::vec_len_set[T](v, n);
}

fn buf_off[T](array[T] v, uint offset) -> vbuf {
     assert (offset < len[T](v));
    ret rustrt::vec_buf[T](v, offset);
}

fn print_debug_info[T](array[T] v) {
    rustrt::vec_print_debug_info[T](v);
}

// Returns the last element of v.
fn last[T](array[T] v) -> option::t[T] {
    auto l = len[T](v);
    if (l == 0u) {
        ret none[T];
    }
    ret some[T](v.(l - 1u));
}

// Returns elements from [start..end) from v.

fn slice[T](array[T] v, uint start, uint end) -> vec[T] {
    assert (start <= end);
    assert (end <= len[T](v));
    auto result = alloc[T](end - start);
    let uint i = start;
    while (i < end) {
        result += [v.(i)];
        i += 1u;
    }
    ret result;
}

// FIXME: Should go away eventually.
fn slice_mut[T](array[T] v, uint start, uint end) -> vec[mutable T] {
    assert (start <= end);
    assert (end <= len[T](v));
    auto result = alloc_mut[T](end - start);
    let uint i = start;
    while (i < end) {
        result += [mutable v.(i)];
        i += 1u;
    }
    ret result;
}

fn shift[T](&mutable array[T] v) -> T {
    auto ln = len[T](v);
    assert (ln > 0u);
    auto e = v.(0);
    v = slice[T](v, 1u, ln);
    ret e;
}

fn pop[T](&mutable array[T] v) -> T {
    auto ln = len[T](v);
    assert (ln > 0u);
    ln -= 1u;
    auto e = v.(ln);
    v = slice[T](v, 0u, ln);
    ret e;
}

fn top[T](&array[T] v) -> T {
    auto ln = len[T](v);
    assert (ln > 0u);
    ret v.(ln-1u);
}

fn push[T](&mutable array[T] v, &T t) {
    v += [t];
}

fn unshift[T](&mutable array[T] v, &T t) {
    auto res = alloc[T](len[T](v) + 1u);
    res += [t];
    res += v;
    v = res;
}

fn grow[T](&mutable array[T] v, uint n, &T initval) {
    let uint i = n;
    while (i > 0u) {
        i -= 1u;
        v += [initval];
    }
}

fn grow_set[T](&mutable vec[mutable T] v, uint index, &T initval, &T val) {
    auto length = vec::len(v);
    if (index >= length) {
        grow(v, index - length + 1u, initval);
    }
    v.(index) = val;
}

fn grow_init_fn[T](&array[T] v, uint n, fn()->T init_fn) {
    let uint i = n;
    while (i > 0u) {
        i -= 1u;
        v += [init_fn()];
    }
}

fn grow_init_fn_set[T](&array[T] v, uint index, fn()->T init_fn, &T val) {
    auto length = vec::len(v);
    if (index >= length) { grow_init_fn(v, index - length + 1u, init_fn); }
    v.(index) = val;
}

fn map[T, U](&fn(&T) -> U f, &vec[T] v) -> vec[U] {
    let vec[U] res = alloc[U](len[T](v));
    for (T ve in v) {
        res += [f(ve)];
    }
    ret res;
}

fn filter_map[T, U](&fn(&T) -> option::t[U] f, &vec[T] v) -> vec[U] {
    let vec[U] res = [];
    for(T ve in v) {
        alt(f(ve)) {
            case (some(?elt)) { res += [elt]; }
            case (none) {}
        }
    }
    ret res;
}

fn map2[T,U,V](&operator2[T,U,V] f, &vec[T] v0, &vec[U] v1) -> vec[V] {
    auto v0_len = len[T](v0);
    if (v0_len != len[U](v1)) {
        fail;
    }

    let vec[V] u = alloc[V](v0_len);
    auto i = 0u;
    while (i < v0_len) {
        u += [f({v0.(i)}, {v1.(i)})];
        i += 1u;
    }

    ret u;
}

fn find[T](fn (&T) -> bool f, &vec[T] v) -> option::t[T] {
    for (T elt in v) {
        if (f(elt)) {
            ret some[T](elt);
        }
    }

    ret none[T];
}

fn foldl[T, U](fn (&U, &T) -> U p, &U z, &vec[T] v) -> U {
    auto sz = len[T](v);

    if (sz == 0u) {
        ret z;
    }
    else {
        auto rest = slice[T](v, 1u, sz);

        ret (p(foldl[T,U](p, z, rest), v.(0)));
    }
}

fn unzip[T, U](&vec[tup(T, U)] v) -> tup(vec[T], vec[U]) {
    auto sz = len[tup(T, U)](v);

    if (sz == 0u) {
        ret tup(alloc[T](0u), alloc[U](0u));
    }
    else {
        auto rest = slice[tup(T, U)](v, 1u, sz);
        auto tl   = unzip[T, U](rest);
        auto a    = [v.(0)._0];
        auto b    = [v.(0)._1];
        ret tup(a + tl._0, b + tl._1);
    }
}

fn or(&vec[bool] v) -> bool {
    auto f = orb;
    ret vec::foldl[bool, bool](f, false, v);
}

fn clone[T](&vec[T] v) -> vec[T] {
    ret slice[T](v, 0u, len[T](v));
}

fn plus_option[T](&vec[T] v, &option::t[T] o) -> () {
    alt (o) {
        case (none) {}
        case (some(?x)) { v += [x]; }
    }
}

fn cat_options[T](&vec[option::t[T]] v) -> vec[T] {
    let vec[T] res = [];

    for (option::t[T] o in v) {
        alt (o) {
            case (none) { }
            case (some(?t)) {
                res += [t];
            }
        }
    }

    ret res;
}

// TODO: Remove in favor of built-in "freeze" operation when it's implemented.
fn freeze[T](vec[mutable T] v) -> vec[T] {
    let vec[T] result = [];
    for (T elem in v) {
        result += [elem];
    }
    ret result;
}

// Swaps two elements in a vector
fn swap[T](&vec[T] v, uint a, uint b) {
    let T t = v.(a);
    v.(a) = v.(b);
    v.(b) = t;
}

// In place vector reversal
fn reverse[T](&vec[T] v) -> () {
    let uint i = 0u;
    auto ln = len[T](v);

    while(i < ln / 2u) {
        swap(v, i, ln - i - 1u);
        i += 1u;
    }
}

// Functional vector reversal. Returns a reversed copy of v.
fn reversed[T](vec[T] v) -> vec[T] {
    let vec[T] res = [];

    auto i = len[T](v);
    if (i == 0u) {
        ret res;
    }
    else {
        i -= 1u;
    }

    while(i != 0u) {
        push[T](res, v.(i));
        i -= 1u;
    }
    push[T](res, v.(0));

    ret res;
}

/// Truncates the vector to length `new_len`.
/// FIXME: This relies on a typechecker bug (covariance vs. invariance).
fn truncate[T](&mutable vec[mutable? T] v, uint new_len) {
    v = slice[T](v, 0u, new_len);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Add vec::member<commit_after>import option::none;
import option::some;
import util::orb;

type vbuf = rustrt::vbuf;

type operator2[T,U,V] = fn(&T, &U) -> V;

type array[T] = vec[mutable? T];

native "rust" mod rustrt {
    type vbuf;

    fn vec_buf[T](vec[T] v, uint offset) -> vbuf;

    fn vec_len[T](vec[T] v) -> uint;
    /**
     * Sometimes we modify the vec internal data via vec_buf and need to
     * update the vec's fill length accordingly.
     */
    fn vec_len_set[T](vec[T] v, uint n);

    /**
     * The T in vec_alloc[T, U] is the type of the vec to allocate.  The
     * U is the type of an element in the vec.  So to allocate a vec[U] we
     * want to invoke this as vec_alloc[vec[U], U].
     */
    fn vec_alloc[T, U](uint n_elts) -> vec[U];
    fn vec_alloc_mut[T, U](uint n_elts) -> vec[mutable U];

    fn refcount[T](vec[T] v) -> uint;

    fn vec_print_debug_info[T](vec[T] v);

    fn vec_from_vbuf[T](vbuf v, uint n_elts) -> vec[T];

    fn unsafe_vec_to_mut[T](vec[T] v) -> vec[mutable T];
}

fn alloc[T](uint n_elts) -> vec[T] {
    ret rustrt::vec_alloc[vec[T], T](n_elts);
}

fn alloc_mut[T](uint n_elts) -> vec[mutable T] {
    ret rustrt::vec_alloc_mut[vec[mutable T], T](n_elts);
}

fn refcount[T](array[T] v) -> uint {
    auto r = rustrt::refcount[T](v);
    if (r == dbg::const_refcount) {
        ret r;
    } else {
        // -2 because calling this function and the native function both
        // incremented the refcount.
        ret  r - 2u;
    }
}

fn vec_from_vbuf[T](vbuf v, uint n_elts) -> vec[T] {
    ret rustrt::vec_from_vbuf[T](v, n_elts);
}

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty[T]() -> vec[T] {
    ret alloc[T](0u);
}

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty_mut[T]() -> vec[mutable T] {
    ret alloc_mut[T](0u);
}

type init_op[T] = fn(uint i) -> T;

fn init_fn[T](&init_op[T] op, uint n_elts) -> vec[T] {
    let vec[T] v = alloc[T](n_elts);
    let uint i = 0u;
    while (i < n_elts) {
        v += [op(i)];
        i += 1u;
    }
    ret v;
}

fn init_fn_mut[T](&init_op[T] op, uint n_elts) -> vec[mutable T] {
    let vec[mutable T] v = alloc_mut[T](n_elts);
    let uint i = 0u;
    while (i < n_elts) {
        v += [mutable op(i)];
        i += 1u;
    }
    ret v;
}

fn init_elt[T](&T t, uint n_elts) -> vec[T] {
    /**
     * FIXME (issue #81): should be:
     *
     * fn elt_op[T](&T x, uint i) -> T { ret x; }
     * let init_op[T] inner = bind elt_op[T](t, _);
     * ret init_fn[T](inner, n_elts);
     */
    let vec[T] v = alloc[T](n_elts);
    let uint i = n_elts;
    while (i > 0u) {
        i -= 1u;
        v += [t];
    }
    ret v;
}

fn init_elt_mut[T](&T t, uint n_elts) -> vec[mutable T] {
    let vec[mutable T] v = alloc_mut[T](n_elts);
    let uint i = n_elts;
    while (i > 0u) {
        i -= 1u;
        v += [mutable t];
    }
    ret v;
}

fn buf[T](array[T] v) -> vbuf {
    ret rustrt::vec_buf[T](v, 0u);
}

fn len[T](array[T] v) -> uint {
    ret rustrt::vec_len[T](v);
}

fn len_set[T](array[T] v, uint n) {
    rustrt::vec_len_set[T](v, n);
}

fn buf_off[T](array[T] v, uint offset) -> vbuf {
     assert (offset < len[T](v));
    ret rustrt::vec_buf[T](v, offset);
}

fn print_debug_info[T](array[T] v) {
    rustrt::vec_print_debug_info[T](v);
}

// Returns the last element of v.
fn last[T](array[T] v) -> option::t[T] {
    auto l = len[T](v);
    if (l == 0u) {
        ret none[T];
    }
    ret some[T](v.(l - 1u));
}

// Returns elements from [start..end) from v.

fn slice[T](array[T] v, uint start, uint end) -> vec[T] {
    assert (start <= end);
    assert (end <= len[T](v));
    auto result = alloc[T](end - start);
    let uint i = start;
    while (i < end) {
        result += [v.(i)];
        i += 1u;
    }
    ret result;
}

// FIXME: Should go away eventually.
fn slice_mut[T](array[T] v, uint start, uint end) -> vec[mutable T] {
    assert (start <= end);
    assert (end <= len[T](v));
    auto result = alloc_mut[T](end - start);
    let uint i = start;
    while (i < end) {
        result += [mutable v.(i)];
        i += 1u;
    }
    ret result;
}

fn shift[T](&mutable array[T] v) -> T {
    auto ln = len[T](v);
    assert (ln > 0u);
    auto e = v.(0);
    v = slice[T](v, 1u, ln);
    ret e;
}

fn pop[T](&mutable array[T] v) -> T {
    auto ln = len[T](v);
    assert (ln > 0u);
    ln -= 1u;
    auto e = v.(ln);
    v = slice[T](v, 0u, ln);
    ret e;
}

fn top[T](&array[T] v) -> T {
    auto ln = len[T](v);
    assert (ln > 0u);
    ret v.(ln-1u);
}

fn push[T](&mutable array[T] v, &T t) {
    v += [t];
}

fn unshift[T](&mutable array[T] v, &T t) {
    auto res = alloc[T](len[T](v) + 1u);
    res += [t];
    res += v;
    v = res;
}

fn grow[T](&mutable array[T] v, uint n, &T initval) {
    let uint i = n;
    while (i > 0u) {
        i -= 1u;
        v += [initval];
    }
}

fn grow_set[T](&mutable vec[mutable T] v, uint index, &T initval, &T val) {
    auto length = vec::len(v);
    if (index >= length) {
        grow(v, index - length + 1u, initval);
    }
    v.(index) = val;
}

fn grow_init_fn[T](&array[T] v, uint n, fn()->T init_fn) {
    let uint i = n;
    while (i > 0u) {
        i -= 1u;
        v += [init_fn()];
    }
}

fn grow_init_fn_set[T](&array[T] v, uint index, fn()->T init_fn, &T val) {
    auto length = vec::len(v);
    if (index >= length) { grow_init_fn(v, index - length + 1u, init_fn); }
    v.(index) = val;
}

fn map[T, U](&fn(&T) -> U f, &vec[T] v) -> vec[U] {
    let vec[U] res = alloc[U](len[T](v));
    for (T ve in v) {
        res += [f(ve)];
    }
    ret res;
}

fn filter_map[T, U](&fn(&T) -> option::t[U] f, &vec[T] v) -> vec[U] {
    let vec[U] res = [];
    for(T ve in v) {
        alt(f(ve)) {
            case (some(?elt)) { res += [elt]; }
            case (none) {}
        }
    }
    ret res;
}

fn map2[T,U,V](&operator2[T,U,V] f, &vec[T] v0, &vec[U] v1) -> vec[V] {
    auto v0_len = len[T](v0);
    if (v0_len != len[U](v1)) {
        fail;
    }

    let vec[V] u = alloc[V](v0_len);
    auto i = 0u;
    while (i < v0_len) {
        u += [f({v0.(i)}, {v1.(i)})];
        i += 1u;
    }

    ret u;
}

fn find[T](fn (&T) -> bool f, &vec[T] v) -> option::t[T] {
    for (T elt in v) {
        if (f(elt)) {
            ret some[T](elt);
        }
    }

    ret none[T];
}

fn member[T](&T x, &array[T] v) -> bool {
    for (T elt in v) {
        if (x == elt) { ret true; }
    }
    ret false;
}

fn foldl[T, U](fn (&U, &T) -> U p, &U z, &vec[T] v) -> U {
    auto sz = len[T](v);

    if (sz == 0u) {
        ret z;
    }
    else {
        auto rest = slice[T](v, 1u, sz);

        ret (p(foldl[T,U](p, z, rest), v.(0)));
    }
}

fn unzip[T, U](&vec[tup(T, U)] v) -> tup(vec[T], vec[U]) {
    auto sz = len[tup(T, U)](v);

    if (sz == 0u) {
        ret tup(alloc[T](0u), alloc[U](0u));
    }
    else {
        auto rest = slice[tup(T, U)](v, 1u, sz);
        auto tl   = unzip[T, U](rest);
        auto a    = [v.(0)._0];
        auto b    = [v.(0)._1];
        ret tup(a + tl._0, b + tl._1);
    }
}

fn or(&vec[bool] v) -> bool {
    auto f = orb;
    ret vec::foldl[bool, bool](f, false, v);
}

fn clone[T](&vec[T] v) -> vec[T] {
    ret slice[T](v, 0u, len[T](v));
}

fn plus_option[T](&vec[T] v, &option::t[T] o) -> () {
    alt (o) {
        case (none) {}
        case (some(?x)) { v += [x]; }
    }
}

fn cat_options[T](&vec[option::t[T]] v) -> vec[T] {
    let vec[T] res = [];

    for (option::t[T] o in v) {
        alt (o) {
            case (none) { }
            case (some(?t)) {
                res += [t];
            }
        }
    }

    ret res;
}

// TODO: Remove in favor of built-in "freeze" operation when it's implemented.
fn freeze[T](vec[mutable T] v) -> vec[T] {
    let vec[T] result = [];
    for (T elem in v) {
        result += [elem];
    }
    ret result;
}

// Swaps two elements in a vector
fn swap[T](&vec[T] v, uint a, uint b) {
    let T t = v.(a);
    v.(a) = v.(b);
    v.(b) = t;
}

// In place vector reversal
fn reverse[T](&vec[T] v) -> () {
    let uint i = 0u;
    auto ln = len[T](v);

    while(i < ln / 2u) {
        swap(v, i, ln - i - 1u);
        i += 1u;
    }
}

// Functional vector reversal. Returns a reversed copy of v.
fn reversed[T](vec[T] v) -> vec[T] {
    let vec[T] res = [];

    auto i = len[T](v);
    if (i == 0u) {
        ret res;
    }
    else {
        i -= 1u;
    }

    while(i != 0u) {
        push[T](res, v.(i));
        i -= 1u;
    }
    push[T](res, v.(0));

    ret res;
}

/// Truncates the vector to length `new_len`.
/// FIXME: This relies on a typechecker bug (covariance vs. invariance).
fn truncate[T](&mutable vec[mutable? T] v, uint new_len) {
    v = slice[T](v, 0u, new_len);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use objc_bringup::NSArray;
use std::borrow::Cow;
use std::convert::{AsRef, From};
use std::ffi::CStr;
use std::io::Error as _IoError;
use sys::MTLLibrary;
use {FromRaw, FromRawError, Function};

pub struct Library(id);

impl Library {
    #[allow(unused_variables)]
    pub fn new_function_with_name<S: AsRef<str>>(&mut self, function_name: S) -> Option<Function> {
        unsafe {
            let func_name_nsstr = NSString::alloc(nil).init_str(function_name.as_ref());
            let function = self.0.newFunctionWithName(func_name_nsstr);
            FromRaw::from_raw(function).ok()
        }
    }

    pub fn function_names(&self) -> Vec<Cow<str>> {
        let names_array = unsafe { self.0.functionNames() };
        let names_len = unsafe { names_array.count() };
        let mut names_vec = vec![];
        for i in 0..names_len {
            let name = unsafe {
                CStr::from_ptr(names_array.objectAtIndex(i).UTF8String()).to_string_lossy()
            };
            names_vec.push(name);
        }
        names_vec
    }

    pub fn set_label<S: AsRef<str>>(&mut self, label: S) {
        unsafe { self.0.setLabel(NSString::alloc(nil).init_str(label.as_ref())) }
    }

    pub fn get_label(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.label().UTF8String()).to_string_lossy() }
    }
}

impl_from_into_raw!(Library, "MTLLibrary");

pub enum LibraryError {
    SourceError,
    FromRaw(FromRawError),
    IoError(_IoError)
}

impl From<FromRawError> for LibraryError {
    fn from(error: FromRawError) -> Self {
        LibraryError::FromRaw(error)
    }
}
<commit_msg>Remove unused `allow(unused_variables)` annotation<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use objc_bringup::NSArray;
use std::borrow::Cow;
use std::convert::{AsRef, From};
use std::ffi::CStr;
use std::io::Error as _IoError;
use sys::MTLLibrary;
use {FromRaw, FromRawError, Function};

pub struct Library(id);

impl Library {
    pub fn new_function_with_name<S: AsRef<str>>(&mut self, function_name: S) -> Option<Function> {
        unsafe {
            let func_name_nsstr = NSString::alloc(nil).init_str(function_name.as_ref());
            let function = self.0.newFunctionWithName(func_name_nsstr);
            FromRaw::from_raw(function).ok()
        }
    }

    pub fn function_names(&self) -> Vec<Cow<str>> {
        let names_array = unsafe { self.0.functionNames() };
        let names_len = unsafe { names_array.count() };
        let mut names_vec = vec![];
        for i in 0..names_len {
            let name = unsafe {
                CStr::from_ptr(names_array.objectAtIndex(i).UTF8String()).to_string_lossy()
            };
            names_vec.push(name);
        }
        names_vec
    }

    pub fn set_label<S: AsRef<str>>(&mut self, label: S) {
        unsafe { self.0.setLabel(NSString::alloc(nil).init_str(label.as_ref())) }
    }

    pub fn get_label(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.label().UTF8String()).to_string_lossy() }
    }
}

impl_from_into_raw!(Library, "MTLLibrary");

pub enum LibraryError {
    SourceError,
    FromRaw(FromRawError),
    IoError(_IoError)
}

impl From<FromRawError> for LibraryError {
    fn from(error: FromRawError) -> Self {
        LibraryError::FromRaw(error)
    }
}
<|endoftext|>