<commit_before>#![feature(inner_deref)]

extern crate cargo_metadata;

use std::path::{PathBuf, Path};
use std::io::{self, Write};
use std::process::Command;
use std::fs::{self, File};

const CARGO_MIRI_HELP: &str = r#"Interprets bin crates

Usage:
    cargo miri [subcommand] [options] [--] [<opts>...]

Subcommands:
    run                      Run binaries (default)
    test                     Run tests
    setup                    Only perform automatic setup, but without asking questions (for getting a proper libstd)

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

The feature `cargo-miri` is automatically defined for convenience. You can use
it to configure the resource limits

    #![cfg_attr(feature = "cargo-miri", memory_size = 42)]

available resource limits are `memory_size`, `step_limit`, `stack_limit`
"#;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum MiriCommand {
    Run,
    Test,
    Setup,
}

fn show_help() {
    println!("{}", CARGO_MIRI_HELP);
}

fn show_version() {
    println!("miri {} ({} {})",
        env!("CARGO_PKG_VERSION"), env!("VERGEN_SHA_SHORT"), env!("VERGEN_COMMIT_DATE"));
}

fn show_error(msg: String) -> ! {
    eprintln!("fatal error: {}", msg);
    std::process::exit(1)
}

fn list_targets(mut args: impl Iterator<Item=String>) -> impl Iterator<Item=cargo_metadata::Target> {
    // We need to get the manifest, and then the metadata, to enumerate targets.
    let manifest_path_arg = args.find(|val| {
        val.starts_with("--manifest-path=")
    });

    let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(
        manifest_path_arg.as_ref().map(AsRef::as_ref),
    )
    {
        metadata
    } else {
        show_error(format!("error: Could not obtain cargo metadata."));
    };

    let manifest_path = manifest_path_arg.map(|arg| {
        PathBuf::from(Path::new(&arg["--manifest-path=".len()..]))
    });

    let current_dir = std::env::current_dir();

    let package_index = metadata
        .packages
        .iter()
        .position(|package| {
            let package_manifest_path = Path::new(&package.manifest_path);
            if let Some(ref manifest_path) = manifest_path {
                package_manifest_path == manifest_path
            } else {
                let current_dir = current_dir.as_ref().expect(
                    "could not read current directory",
                );
                let package_manifest_directory = package_manifest_path.parent().expect(
                    "could not find parent directory of package manifest",
                );
                package_manifest_directory == current_dir
            }
        })
        .expect("could not find matching package");
    let package = metadata.packages.remove(package_index);

    // Finally we got the list of targets to build
    package.targets.into_iter()
}

fn ask(question: &str) {
    let mut buf = String::new();
    print!("{} [Y/n] ", question);
    io::stdout().flush().unwrap();
    io::stdin().read_line(&mut buf).unwrap();
    match buf.trim().to_lowercase().as_ref() {
        "" | "y" | "yes" => {}, // proceed
        "n" | "no" => show_error(format!("Aborting as per your request")),
        a => show_error(format!("I do not understand `{}`", a))
    };
}

/// Perform the setup requires to make `cargo miri` work: Getting a custom-built libstd. Then sets MIRI_SYSROOT.
/// Skipped if MIRI_SYSROOT is already set, in that case we expect the user has done all this already.
fn setup(ask_user: bool) {
    if std::env::var("MIRI_SYSROOT").is_ok() {
        return;
    }

    // First, we need xargo
    if Command::new("xargo").arg("--version").output().is_err()
    {
        if ask_user {
            ask("It seems you do not have xargo installed. I will run `cargo install xargo`. Proceed?");
        } else {
            println!("Installing xargo: `cargo install xargo`");
        }
        if !Command::new("cargo").args(&["install", "xargo"]).status().unwrap().success() {
            show_error(format!("Failed to install xargo"));
        }
    }

    // Then, we also need rust-src.  Let's see if it is already installed.
    let sysroot = Command::new("rustc").args(&["--print", "sysroot"]).output().unwrap().stdout;
    let sysroot = std::str::from_utf8(&sysroot[..]).unwrap();
    let src = Path::new(sysroot.trim_end_matches('\n')).join("lib").join("rustlib").join("src");
    if !src.exists() {
        if ask_user {
            ask("It seems you do not have the rust-src component installed. I will run `rustup component add rust-src`. Proceed?");
        } else {
            println!("Installing rust-src component: `rustup component add rust-src`");
        }
        if !Command::new("rustup").args(&["component", "add", "rust-src"]).status().unwrap().success() {
            show_error(format!("Failed to install rust-src component"));
        }
    }

    // Next, we need our own libstd. We will do this work in whatever is a good cache dir for this platform.
    let dirs = directories::ProjectDirs::from("miri", "miri", "miri").unwrap();
    let dir = dirs.cache_dir();
    if !dir.exists() {
        fs::create_dir_all(&dir).unwrap();
    }
    // The interesting bit: Xargo.toml
    File::create(dir.join("Xargo.toml")).unwrap()
        .write_all(br#"
[dependencies.std]
features = ["panic_unwind"]

[dependencies.test]
stage = 1
        "#).unwrap();
    // The boring bits: A dummy project for xargo
    File::create(dir.join("Cargo.toml")).unwrap()
        .write_all(br#"
[package]
name = "miri-xargo"
description = "A dummy project for building libstd with xargo."
version = "0.0.0"

[lib]
path = "lib.rs"
        "#).unwrap();
    File::create(dir.join("lib.rs")).unwrap();
    // Run xargo
    if !Command::new("xargo").arg("build").arg("-q")
        .current_dir(&dir)
        .env("RUSTFLAGS", miri::miri_default_args().join(" "))
        .env("XARGO_HOME", dir.to_str().unwrap())
        .status().unwrap().success()
    {
        show_error(format!("Failed to run xargo"));
    }

    // That should be it!
    let sysroot = dir.join("HOST");
    std::env::set_var("MIRI_SYSROOT", &sysroot);
    if !ask_user {
        println!("A libstd for miri is now available in `{}`", sysroot.display());
    }
}

fn main() {
    // Check for version and help flags even when invoked as 'cargo-miri'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is when `cargo miri` is called.  We call `cargo rustc` for
        // each applicable target, but with the RUSTC env var set to the `cargo-miri`
        // binary so that we come back in the other branch, and dispatch
        // the invocations to rustc and miri, respectively.

        let (subcommand, skip) = match std::env::args().nth(2).deref() {
            Some("test") => (MiriCommand::Test, 3),
            Some("run") => (MiriCommand::Run, 3),
            Some("setup") => (MiriCommand::Setup, 3),
            // Default command, if there is an option or nothing
            Some(s) if s.starts_with("-") => (MiriCommand::Run, 2),
            None => (MiriCommand::Run, 2),
            // Unvalid command
            Some(s) => {
                show_error(format!("Unknown command `{}`", s))
            }
        };

        // We always setup
        let ask = subcommand != MiriCommand::Setup;
        setup(ask);

        // Now run the command.
        for target in list_targets(std::env::args().skip(skip)) {
            let args = std::env::args().skip(skip);
            let kind = target.kind.get(0).expect(
                "badly formatted cargo metadata: target::kind is an empty array",
            );
            match (subcommand, &kind[..]) {
                (MiriCommand::Test, "test") => {
                    // For test binaries we call `cargo rustc --test target -- <rustc args>`
                    if let Err(code) = process(
                        vec!["--test".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (MiriCommand::Test, "lib") => {
                    // For libraries we call `cargo rustc -- --test <rustc args>`
                    // Notice now that `--test` is a rustc arg rather than a cargo arg. This tells
                    // rustc to build a test harness which calls all #[test] functions. We don't
                    // use the harness since we execute each #[test] function's MIR ourselves before
                    // compilation even completes, but this option is necessary to build the library.
                    if let Err(code) = process(
                        vec!["--".to_string(), "--test".to_string()].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (MiriCommand::Run, "bin") => {
                    // For ordinary binaries we call `cargo rustc --bin target -- <rustc args>`
                    if let Err(code) = process(
                        vec!["--bin".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                _ => {}
            }
        }
    } else {
        // This arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC` env var set to itself:
        // Dependencies get dispatched to rustc, the final test/binary to miri.

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let Ok(sysroot) = ::std::env::var("MIRI_SYSROOT") {
            sysroot
        } else if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("RUST_SYSROOT")
                .map(|s| s.to_owned())
                .or_else(|| {
                    Command::new("rustc")
                        .arg("--print")
                        .arg("sysroot")
                        .output()
                        .ok()
                        .and_then(|out| String::from_utf8(out.stdout).ok())
                        .map(|s| s.trim().to_owned())
                })
                .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if std::env::args().any(|s| s == "--sysroot") {
            std::env::args().skip(1).collect()
        } else {
            std::env::args()
                .skip(1)
                .chain(Some("--sysroot".to_owned()))
                .chain(Some(sys_root))
                .collect()
        };
        args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));

        // this check ensures that dependencies are built but not interpreted and the final crate is
        // interpreted but not built
        let miri_enabled = std::env::args().any(|s| s == "--emit=dep-info,metadata");

        let mut command = if miri_enabled {
            let mut path = std::env::current_exe().expect("current executable path invalid");
            path.set_file_name("miri");
            Command::new(path)
        } else {
            Command::new("rustc")
        };

        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);

        match command.args(&args).status() {
            Ok(exit) => {
                if !exit.success() {
                    std::process::exit(exit.code().unwrap_or(42));
                }
            }
            Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e),
            Err(ref e) => panic!("error during rustc call: {:?}", e),
        }
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("--emit=dep-info,metadata".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-miri""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<commit_msg>do not even look for cargo metadata in 'cargo miri setup'<commit_after>#![feature(inner_deref)]

extern crate cargo_metadata;

use std::path::{PathBuf, Path};
use std::io::{self, Write};
use std::process::Command;
use std::fs::{self, File};

const CARGO_MIRI_HELP: &str = r#"Interprets bin crates

Usage:
    cargo miri [subcommand] [options] [--] [<opts>...]

Subcommands:
    run                      Run binaries (default)
    test                     Run tests
    setup                    Only perform automatic setup, but without asking questions (for getting a proper libstd)

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

The feature `cargo-miri` is automatically defined for convenience. You can use
it to configure the resource limits

    #![cfg_attr(feature = "cargo-miri", memory_size = 42)]

available resource limits are `memory_size`, `step_limit`, `stack_limit`
"#;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum MiriCommand {
    Run,
    Test,
    Setup,
}

fn show_help() {
    println!("{}", CARGO_MIRI_HELP);
}

fn show_version() {
    println!("miri {} ({} {})",
        env!("CARGO_PKG_VERSION"), env!("VERGEN_SHA_SHORT"), env!("VERGEN_COMMIT_DATE"));
}

fn show_error(msg: String) -> ! {
    eprintln!("fatal error: {}", msg);
    std::process::exit(1)
}

fn list_targets(mut args: impl Iterator<Item=String>) -> impl Iterator<Item=cargo_metadata::Target> {
    // We need to get the manifest, and then the metadata, to enumerate targets.
    let manifest_path_arg = args.find(|val| {
        val.starts_with("--manifest-path=")
    });

    let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(
        manifest_path_arg.as_ref().map(AsRef::as_ref),
    )
    {
        metadata
    } else {
        show_error(format!("error: Could not obtain cargo metadata."));
    };

    let manifest_path = manifest_path_arg.map(|arg| {
        PathBuf::from(Path::new(&arg["--manifest-path=".len()..]))
    });

    let current_dir = std::env::current_dir();

    let package_index = metadata
        .packages
        .iter()
        .position(|package| {
            let package_manifest_path = Path::new(&package.manifest_path);
            if let Some(ref manifest_path) = manifest_path {
                package_manifest_path == manifest_path
            } else {
                let current_dir = current_dir.as_ref().expect(
                    "could not read current directory",
                );
                let package_manifest_directory = package_manifest_path.parent().expect(
                    "could not find parent directory of package manifest",
                );
                package_manifest_directory == current_dir
            }
        })
        .expect("could not find matching package");
    let package = metadata.packages.remove(package_index);

    // Finally we got the list of targets to build
    package.targets.into_iter()
}

fn ask(question: &str) {
    let mut buf = String::new();
    print!("{} [Y/n] ", question);
    io::stdout().flush().unwrap();
    io::stdin().read_line(&mut buf).unwrap();
    match buf.trim().to_lowercase().as_ref() {
        "" | "y" | "yes" => {}, // proceed
        "n" | "no" => show_error(format!("Aborting as per your request")),
        a => show_error(format!("I do not understand `{}`", a))
    };
}

/// Perform the setup requires to make `cargo miri` work: Getting a custom-built libstd. Then sets MIRI_SYSROOT.
/// Skipped if MIRI_SYSROOT is already set, in that case we expect the user has done all this already.
fn setup(ask_user: bool) {
    if std::env::var("MIRI_SYSROOT").is_ok() {
        return;
    }

    // First, we need xargo
    if Command::new("xargo").arg("--version").output().is_err()
    {
        if ask_user {
            ask("It seems you do not have xargo installed. I will run `cargo install xargo`. Proceed?");
        } else {
            println!("Installing xargo: `cargo install xargo`");
        }
        if !Command::new("cargo").args(&["install", "xargo"]).status().unwrap().success() {
            show_error(format!("Failed to install xargo"));
        }
    }

    // Then, we also need rust-src.  Let's see if it is already installed.
    let sysroot = Command::new("rustc").args(&["--print", "sysroot"]).output().unwrap().stdout;
    let sysroot = std::str::from_utf8(&sysroot[..]).unwrap();
    let src = Path::new(sysroot.trim_end_matches('\n')).join("lib").join("rustlib").join("src");
    if !src.exists() {
        if ask_user {
            ask("It seems you do not have the rust-src component installed. I will run `rustup component add rust-src`. Proceed?");
        } else {
            println!("Installing rust-src component: `rustup component add rust-src`");
        }
        if !Command::new("rustup").args(&["component", "add", "rust-src"]).status().unwrap().success() {
            show_error(format!("Failed to install rust-src component"));
        }
    }

    // Next, we need our own libstd. We will do this work in whatever is a good cache dir for this platform.
    let dirs = directories::ProjectDirs::from("miri", "miri", "miri").unwrap();
    let dir = dirs.cache_dir();
    if !dir.exists() {
        fs::create_dir_all(&dir).unwrap();
    }
    // The interesting bit: Xargo.toml
    File::create(dir.join("Xargo.toml")).unwrap()
        .write_all(br#"
[dependencies.std]
features = ["panic_unwind"]

[dependencies.test]
stage = 1
        "#).unwrap();
    // The boring bits: A dummy project for xargo
    File::create(dir.join("Cargo.toml")).unwrap()
        .write_all(br#"
[package]
name = "miri-xargo"
description = "A dummy project for building libstd with xargo."
version = "0.0.0"

[lib]
path = "lib.rs"
        "#).unwrap();
    File::create(dir.join("lib.rs")).unwrap();
    // Run xargo
    if !Command::new("xargo").arg("build").arg("-q")
        .current_dir(&dir)
        .env("RUSTFLAGS", miri::miri_default_args().join(" "))
        .env("XARGO_HOME", dir.to_str().unwrap())
        .status().unwrap().success()
    {
        show_error(format!("Failed to run xargo"));
    }

    // That should be it!
    let sysroot = dir.join("HOST");
    std::env::set_var("MIRI_SYSROOT", &sysroot);
    if !ask_user {
        println!("A libstd for miri is now available in `{}`", sysroot.display());
    }
}

fn main() {
    // Check for version and help flags even when invoked as 'cargo-miri'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is when `cargo miri` is called.  We call `cargo rustc` for
        // each applicable target, but with the RUSTC env var set to the `cargo-miri`
        // binary so that we come back in the other branch, and dispatch
        // the invocations to rustc and miri, respectively.

        let (subcommand, skip) = match std::env::args().nth(2).deref() {
            Some("test") => (MiriCommand::Test, 3),
            Some("run") => (MiriCommand::Run, 3),
            Some("setup") => (MiriCommand::Setup, 3),
            // Default command, if there is an option or nothing
            Some(s) if s.starts_with("-") => (MiriCommand::Run, 2),
            None => (MiriCommand::Run, 2),
            // Unvalid command
            Some(s) => {
                show_error(format!("Unknown command `{}`", s))
            }
        };

        // We always setup
        let ask = subcommand != MiriCommand::Setup;
        setup(ask);
        if subcommand == MiriCommand::Setup {
            // Stop here.
            return;
        }

        // Now run the command.
        for target in list_targets(std::env::args().skip(skip)) {
            let args = std::env::args().skip(skip);
            let kind = target.kind.get(0).expect(
                "badly formatted cargo metadata: target::kind is an empty array",
            );
            match (subcommand, &kind[..]) {
                (MiriCommand::Test, "test") => {
                    // For test binaries we call `cargo rustc --test target -- <rustc args>`
                    if let Err(code) = process(
                        vec!["--test".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (MiriCommand::Test, "lib") => {
                    // For libraries we call `cargo rustc -- --test <rustc args>`
                    // Notice now that `--test` is a rustc arg rather than a cargo arg. This tells
                    // rustc to build a test harness which calls all #[test] functions. We don't
                    // use the harness since we execute each #[test] function's MIR ourselves before
                    // compilation even completes, but this option is necessary to build the library.
                    if let Err(code) = process(
                        vec!["--".to_string(), "--test".to_string()].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (MiriCommand::Run, "bin") => {
                    // For ordinary binaries we call `cargo rustc --bin target -- <rustc args>`
                    if let Err(code) = process(
                        vec!["--bin".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                _ => {}
            }
        }
    } else {
        // This arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC` env var set to itself:
        // Dependencies get dispatched to rustc, the final test/binary to miri.

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let Ok(sysroot) = ::std::env::var("MIRI_SYSROOT") {
            sysroot
        } else if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("RUST_SYSROOT")
                .map(|s| s.to_owned())
                .or_else(|| {
                    Command::new("rustc")
                        .arg("--print")
                        .arg("sysroot")
                        .output()
                        .ok()
                        .and_then(|out| String::from_utf8(out.stdout).ok())
                        .map(|s| s.trim().to_owned())
                })
                .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if std::env::args().any(|s| s == "--sysroot") {
            std::env::args().skip(1).collect()
        } else {
            std::env::args()
                .skip(1)
                .chain(Some("--sysroot".to_owned()))
                .chain(Some(sys_root))
                .collect()
        };
        args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));

        // this check ensures that dependencies are built but not interpreted and the final crate is
        // interpreted but not built
        let miri_enabled = std::env::args().any(|s| s == "--emit=dep-info,metadata");

        let mut command = if miri_enabled {
            let mut path = std::env::current_exe().expect("current executable path invalid");
            path.set_file_name("miri");
            Command::new(path)
        } else {
            Command::new("rustc")
        };

        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);

        match command.args(&args).status() {
            Ok(exit) => {
                if !exit.success() {
                    std::process::exit(exit.code().unwrap_or(42));
                }
            }
            Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e),
            Err(ref e) => panic!("error during rustc call: {:?}", e),
        }
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("--emit=dep-info,metadata".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-miri""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<|endoftext|>


<commit_before>//! Check Disk usage

extern crate nix;
extern crate regex;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate structopt;

extern crate tabin_plugins;

use std::collections::HashSet;
use std::cmp::max;

use structopt::StructOpt;
use regex::Regex;
use nix::sys::statvfs::vfs;
use tabin_plugins::Status;
use tabin_plugins::procfs::Mount;
use tabin_plugins::linux::bytes_to_human_size;

/// Check all mounted file systems for disk usage.
///
/// For some reason this check generally generates values that are between 1% and
/// 3% higher than `df`, even though AFAICT we're both just calling statvfs a bunch
/// of times.
#[derive(StructOpt, Deserialize, Debug)]
#[structopt(name = "check-disk (part of tabin-plugins)",
            raw(setting = "structopt::clap::AppSettings::ColoredHelp"))]
struct Args {
    #[structopt(short = "w", long = "warn", help = "Percent to warn at", default_value = "80")]
    warn: f64,
    #[structopt(short = "c", long = "crit", help = "Percent to go critical at",
                default_value = "90")]
    crit: f64,
    #[structopt(short = "W", long = "warn-inodes", help = "Percent of inode usage to warn at",
                default_value = "80")]
    warn_inodes: f64,
    #[structopt(short = "C", long = "crit-inodes",
                help = "Percent of inode usage to go critical at", default_value = "90")]
    crit_inodes: f64,

    #[structopt(long = "pattern", name = "regex",
                help = "Only check filesystems that match this regex")]
    pattern: Option<String>,
    #[structopt(long = "exclude-pattern", name = "exclude-regex",
                help = "Only check filesystems that match this regex")]
    exclude_pattern: Option<String>,
    #[structopt(long = "type", name = "fs-type",
                help = "Only check filesystems that are of this type, e.g. \
                        ext4 or tmpfs. See 'man 8 mount' for more examples.")]
    fs_type: Option<String>,
    #[structopt(long = "exclude-type", name = "exclude-fs-type",
                help = "Do not check filesystems that are of this type.")]
    exclude_type: Option<String>,
    #[structopt(long = "info",
                help = "Print information of all known filesystems. \
                        Similar to df.")]
    info: bool,
}

fn main() {
    let args = Args::from_args();

    let mut mounts = Mount::load_all().unwrap();
    mounts.sort_by(|l, r| l.file.len().cmp(&r.file.len()));

    let status = match filter(mounts, &args) {
        Ok(ms) => do_check(&ms, &args),
        Err(e) => {
            println!("{}", e.msg);
            Status::Critical
        }
    };

    status.exit();
}

#[derive(Debug, PartialEq, Eq)]
struct ErrorMsg {
    msg: String,
}

type DiskResult<T> = Result<T, ErrorMsg>;

#[derive(Debug)]
struct MountStat {
    mount: Mount,
    stat: vfs::Statvfs,
}

fn percent(part: u64, whole: u64) -> f64 {
    100.0 - (part as f64 / whole as f64) * 100.0
}

fn maybe_regex(pattern: &Option<String>) -> DiskResult<Option<Regex>> {
    if let Some(ref pattern) = *pattern {
        let re = match Regex::new(&pattern) {
            Ok(re) => re,
            Err(e) => {
                return Err(ErrorMsg {
                    msg: format!("Unable to filter disks like {:?}: {}", pattern, e),
                })
            }
        };
        Ok(Some(re))
    } else {
        Ok(None)
    }
}

/// Convert Mounts into MountStats, applying filters from args
///
/// This:
///
/// * calls `statvfs` on every filesystem in `/proc/mounts`
/// * filters out dummy (0-block) and /proc filesystems
/// * Only shows one of any given `/dev/` filesystem's mount points (the
///   shortest, same as df)
/// * Applies the `pattern` and `type` filters
fn filter(mounts: Vec<Mount>, args: &Args) -> DiskResult<Vec<MountStat>> {
    let mut devices = HashSet::new();
    let include_regex = try!(maybe_regex(&args.pattern));
    let exclude_regex = try!(maybe_regex(&args.exclude_pattern));
    let ms = mounts.into_iter()
        .filter_map(|mount| {
            let stat = vfs::Statvfs::for_path(mount.file.as_bytes()).unwrap();
            if stat.f_blocks > 0 && !mount.file.starts_with("/proc") {
                Some(MountStat {
                    mount: mount,
                    stat: stat,
                })
            } else {
                None
            }
        })
        // If the same device is mounted multiple times, we only want one. In
        // To match df we expect these to come in in sorted order and just keep
        // the first one.
        .filter_map(|ms| {
            if ms.mount.spec.starts_with("/dev") {
                if devices.contains(&ms.mount.spec) {
                    None
                } else {
                    devices.insert(ms.mount.spec.clone());
                    Some(ms)
                }
            } else {
                Some(ms)
            }
        })
        .filter(|ms|
                if let Some(ref re) = include_regex {
                    re.is_match(&ms.mount.file)
                } else {
                    true
                })
        .filter(|ms|
                if let Some(ref re) = exclude_regex {
                    !re.is_match(&ms.mount.file)
                } else {
                    true
                })
        .filter(|ms|
                if let Some(ref vfstype) = args.fs_type {
                    ms.mount.vfstype == *vfstype
                } else {
                    true
                })
        .filter(|ms|
                if let Some(ref vfstype) = args.exclude_type {
                    ms.mount.vfstype != *vfstype
                } else {
                    true
                })
        .collect::<Vec<_>>();
    Ok(ms)
}

fn do_check(mountstats: &[MountStat], args: &Args) -> Status {
    let mut status = Status::Ok;
    for ms in mountstats {
        let pcnt = percent(ms.stat.f_bavail, ms.stat.f_blocks);
        if pcnt > args.crit {
            status = Status::Critical;
            println!(
                "CRITICAL: {} has {:.1}% of its {}B used (> {:.1}%)",
                ms.mount.file,
                pcnt,
                bytes_to_human_size(ms.stat.f_blocks * ms.stat.f_frsize),
                args.crit
            )
        } else if pcnt > args.warn {
            status = max(status, Status::Warning);
            println!(
                "WARNING: {} has {:.1}% of its {}B used (> {:.1}%)",
                ms.mount.file,
                pcnt,
                bytes_to_human_size(ms.stat.f_blocks * ms.stat.f_frsize),
                args.warn
            )
        }

        let ipcnt = percent(ms.stat.f_favail, ms.stat.f_files);
        if ipcnt > args.crit_inodes {
            status = Status::Critical;
            println!(
                "CRITICAL: {} has {:.1}% of its {} inodes used (> {:.1}%)",
                ms.mount.file,
                ipcnt,
                bytes_to_human_size(ms.stat.f_files),
                args.crit_inodes
            );
        } else if ipcnt > args.warn_inodes {
            status = max(status, Status::Warning);
            println!(
                "WARNING: {} has {:.1}% of its {} inodes used (> {:.1}%)",
                ms.mount.file,
                ipcnt,
                bytes_to_human_size(ms.stat.f_files),
                args.warn_inodes
            );
        }
    }
    if status == Status::Ok {
        println!(
            "OKAY: {} filesystems checked, none are above {}% disk or {}% inode usage",
            mountstats.len(),
            args.warn,
            args.warn_inodes
        );
    }

    if args.info {
        println!(
            "{:<15} {:>7} {:>5}% {:>7} {:>5}% {:<20}",
            "Filesystem", "Size", "Use", "INodes", "IUse", "Mounted on"
        );
        for ms in mountstats {
            println!(
                "{:<15} {:>7} {:>5.1}% {:>7} {:>5.1}% {:<20}",
                ms.mount.spec,
                bytes_to_human_size(ms.stat.f_blocks * ms.stat.f_frsize),
                percent(ms.stat.f_bavail, ms.stat.f_blocks),
                bytes_to_human_size(ms.stat.f_files),
                percent(ms.stat.f_favail, ms.stat.f_files),
                ms.mount.file
            );
        }
    }

    status
}

#[cfg(test)]
mod unit {
    use super::{maybe_regex, Args};
    use structopt::StructOpt;

    #[test]
    fn validate_docstring() {
        let args: Args = Args::from_iter(["arg0", "--crit", "5"].into_iter());
        assert_eq!(args.crit, 5.0);
        let args: Args = Args::from_iter(["arg0", "--pattern", "hello"].into_iter());
        assert_eq!(args.pattern.unwrap(), "hello");
    }

    #[test]
    fn check_maybe_regex() {
        if let Err(emsg) = maybe_regex(&Some("[hello".to_owned())) {
            let expected = r#"Unable to filter disks like "[hello":"#;
            assert!(
                emsg.msg.contains(expected),
                "\nExpected something containing: {}\n\
                 But instead received         : {}",
                expected,
                emsg.msg
            );
        } else {
            panic!("Should have gotten an error");
        }
    }
}
<commit_msg>check-disk: move filters that can be before statvfs, above statvfs<commit_after>//! Check Disk usage

extern crate nix;
extern crate regex;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate structopt;

extern crate tabin_plugins;

use std::collections::HashSet;
use std::cmp::max;

use structopt::StructOpt;
use regex::Regex;
use nix::sys::statvfs::vfs;
use tabin_plugins::Status;
use tabin_plugins::procfs::Mount;
use tabin_plugins::linux::bytes_to_human_size;

/// Check all mounted file systems for disk usage.
///
/// For some reason this check generally generates values that are between 1% and
/// 3% higher than `df`, even though AFAICT we're both just calling statvfs a bunch
/// of times.
#[derive(StructOpt, Deserialize, Debug)]
#[structopt(name = "check-disk (part of tabin-plugins)",
            raw(setting = "structopt::clap::AppSettings::ColoredHelp"))]
struct Args {
    #[structopt(short = "w", long = "warn", help = "Percent to warn at", default_value = "80")]
    warn: f64,
    #[structopt(short = "c", long = "crit", help = "Percent to go critical at",
                default_value = "90")]
    crit: f64,
    #[structopt(short = "W", long = "warn-inodes", help = "Percent of inode usage to warn at",
                default_value = "80")]
    warn_inodes: f64,
    #[structopt(short = "C", long = "crit-inodes",
                help = "Percent of inode usage to go critical at", default_value = "90")]
    crit_inodes: f64,

    #[structopt(long = "pattern", name = "regex",
                help = "Only check filesystems that match this regex")]
    pattern: Option<String>,
    #[structopt(long = "exclude-pattern", name = "exclude-regex",
                help = "Only check filesystems that match this regex")]
    exclude_pattern: Option<String>,
    #[structopt(long = "type", name = "fs-type",
                help = "Only check filesystems that are of this type, e.g. \
                        ext4 or tmpfs. See 'man 8 mount' for more examples.")]
    fs_type: Option<String>,
    #[structopt(long = "exclude-type", name = "exclude-fs-type",
                help = "Do not check filesystems that are of this type.")]
    exclude_type: Option<String>,
    #[structopt(long = "info",
                help = "Print information of all known filesystems. \
                        Similar to df.")]
    info: bool,
}

fn main() {
    let args = Args::from_args();

    let mut mounts = Mount::load_all().unwrap();
    mounts.sort_by(|l, r| l.file.len().cmp(&r.file.len()));

    let status = match filter(mounts, &args) {
        Ok(ms) => do_check(&ms, &args),
        Err(e) => {
            println!("{}", e.msg);
            Status::Critical
        }
    };

    status.exit();
}

#[derive(Debug, PartialEq, Eq)]
struct ErrorMsg {
    msg: String,
}

type DiskResult<T> = Result<T, ErrorMsg>;

#[derive(Debug)]
struct MountStat {
    mount: Mount,
    stat: vfs::Statvfs,
}

fn percent(part: u64, whole: u64) -> f64 {
    100.0 - (part as f64 / whole as f64) * 100.0
}

fn maybe_regex(pattern: &Option<String>) -> DiskResult<Option<Regex>> {
    if let Some(ref pattern) = *pattern {
        let re = match Regex::new(&pattern) {
            Ok(re) => re,
            Err(e) => {
                return Err(ErrorMsg {
                    msg: format!("Unable to filter disks like {:?}: {}", pattern, e),
                })
            }
        };
        Ok(Some(re))
    } else {
        Ok(None)
    }
}

/// Convert Mounts into MountStats, applying filters from args
///
/// This:
///
/// * calls `statvfs` on every filesystem in `/proc/mounts`
/// * filters out dummy (0-block) and /proc filesystems
/// * Only shows one of any given `/dev/` filesystem's mount points (the
///   shortest, same as df)
/// * Applies the `pattern` and `type` filters
fn filter(mounts: Vec<Mount>, args: &Args) -> DiskResult<Vec<MountStat>> {
    let mut devices = HashSet::new();
    let include_regex = try!(maybe_regex(&args.pattern));
    let exclude_regex = try!(maybe_regex(&args.exclude_pattern));
    let ms = mounts.into_iter()
        .filter(|mount|
                if let Some(ref re) = include_regex {
                    re.is_match(&mount.file)
                } else {
                    true
                })
        .filter(|mount|
                if let Some(ref re) = exclude_regex {
                    !re.is_match(&mount.file)
                } else {
                    true
                })
        .filter_map(|mount| {
            let stat = vfs::Statvfs::for_path(mount.file.as_bytes()).unwrap();
            if stat.f_blocks > 0 && !mount.file.starts_with("/proc") {
                Some(MountStat {
                    mount: mount,
                    stat: stat,
                })
            } else {
                None
            }
        })
        // If the same device is mounted multiple times, we only want one. In
        // To match df we expect these to come in in sorted order and just keep
        // the first one.
        .filter_map(|ms| {
            if ms.mount.spec.starts_with("/dev") {
                if devices.contains(&ms.mount.spec) {
                    None
                } else {
                    devices.insert(ms.mount.spec.clone());
                    Some(ms)
                }
            } else {
                Some(ms)
            }
        })
        .filter(|ms|
                if let Some(ref vfstype) = args.fs_type {
                    ms.mount.vfstype == *vfstype
                } else {
                    true
                })
        .filter(|ms|
                if let Some(ref vfstype) = args.exclude_type {
                    ms.mount.vfstype != *vfstype
                } else {
                    true
                })
        .collect::<Vec<_>>();
    Ok(ms)
}

fn do_check(mountstats: &[MountStat], args: &Args) -> Status {
    let mut status = Status::Ok;
    for ms in mountstats {
        let pcnt = percent(ms.stat.f_bavail, ms.stat.f_blocks);
        if pcnt > args.crit {
            status = Status::Critical;
            println!(
                "CRITICAL: {} has {:.1}% of its {}B used (> {:.1}%)",
                ms.mount.file,
                pcnt,
                bytes_to_human_size(ms.stat.f_blocks * ms.stat.f_frsize),
                args.crit
            )
        } else if pcnt > args.warn {
            status = max(status, Status::Warning);
            println!(
                "WARNING: {} has {:.1}% of its {}B used (> {:.1}%)",
                ms.mount.file,
                pcnt,
                bytes_to_human_size(ms.stat.f_blocks * ms.stat.f_frsize),
                args.warn
            )
        }

        let ipcnt = percent(ms.stat.f_favail, ms.stat.f_files);
        if ipcnt > args.crit_inodes {
            status = Status::Critical;
            println!(
                "CRITICAL: {} has {:.1}% of its {} inodes used (> {:.1}%)",
                ms.mount.file,
                ipcnt,
                bytes_to_human_size(ms.stat.f_files),
                args.crit_inodes
            );
        } else if ipcnt > args.warn_inodes {
            status = max(status, Status::Warning);
            println!(
                "WARNING: {} has {:.1}% of its {} inodes used (> {:.1}%)",
                ms.mount.file,
                ipcnt,
                bytes_to_human_size(ms.stat.f_files),
                args.warn_inodes
            );
        }
    }
    if status == Status::Ok {
        println!(
            "OKAY: {} filesystems checked, none are above {}% disk or {}% inode usage",
            mountstats.len(),
            args.warn,
            args.warn_inodes
        );
    }

    if args.info {
        println!(
            "{:<15} {:>7} {:>5}% {:>7} {:>5}% {:<20}",
            "Filesystem", "Size", "Use", "INodes", "IUse", "Mounted on"
        );
        for ms in mountstats {
            println!(
                "{:<15} {:>7} {:>5.1}% {:>7} {:>5.1}% {:<20}",
                ms.mount.spec,
                bytes_to_human_size(ms.stat.f_blocks * ms.stat.f_frsize),
                percent(ms.stat.f_bavail, ms.stat.f_blocks),
                bytes_to_human_size(ms.stat.f_files),
                percent(ms.stat.f_favail, ms.stat.f_files),
                ms.mount.file
            );
        }
    }

    status
}

#[cfg(test)]
mod unit {
    use super::{maybe_regex, Args};
    use structopt::StructOpt;

    #[test]
    fn validate_docstring() {
        let args: Args = Args::from_iter(["arg0", "--crit", "5"].into_iter());
        assert_eq!(args.crit, 5.0);
        let args: Args = Args::from_iter(["arg0", "--pattern", "hello"].into_iter());
        assert_eq!(args.pattern.unwrap(), "hello");
    }

    #[test]
    fn check_maybe_regex() {
        if let Err(emsg) = maybe_regex(&Some("[hello".to_owned())) {
            let expected = r#"Unable to filter disks like "[hello":"#;
            assert!(
                emsg.msg.contains(expected),
                "\nExpected something containing: {}\n\
                 But instead received         : {}",
                expected,
                emsg.msg
            );
        } else {
            panic!("Should have gotten an error");
        }
    }
}
<|endoftext|>


<commit_before>use index::btree::cursor::RTCursor;
use index::btree::node::read_node;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::DeletionSet;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Ordering;
use index::Slice;
use std::fmt::Debug;
use std::marker::PhantomData;

pub fn search_node<KS, PS>(
    node_ref: &NodeCellRef,
    key: &EntryKey,
    ordering: Ordering,
    deleted: &DeletionSet,
) -> RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("searching for {:?}", key);
    read_node(node_ref, |node_handler: &NodeReadHandler<KS, PS>| {
        let node = &**node_handler;
        let gen_empty_cursor = || RTCursor {
            index: 0,
            ordering,
            page: None,
            marker: PhantomData,
            deleted: deleted.clone(),
            current: None,
        };
        if let Some(right_node) = node.key_at_right_node(key) {
            debug!("Search found a node at the right side");
            return search_node(right_node, key, ordering, deleted);
        }
        debug!("search node have keys {:?}", node.keys());
        let mut pos = node.search(key);
        match node {
            &NodeData::External(ref n) => {
                debug!(
                    "search in external for {:?}, len {}, ordering {:?}, content: {:?}",
                    key,
                    n.len,
                    ordering,
                    &n.keys.as_slice_immute()[..n.len]
                );
                if ordering == Ordering::Backward {
                    debug!("found cursor pos {} for backwards, will be corrected", pos);
                    if pos > 0 && (pos >= n.len || &n.keys.as_slice_immute()[pos] != key) {
                        pos -= 1;
                    }
                    debug!("cursor pos have been corrected to {}", pos);
                }
                RTCursor::new(pos, node_ref, ordering, deleted)
            }
            &NodeData::Internal(ref n) => {
                debug!(
                    "search in internal node for {:?}, len {}, pos {}",
                    key, n.len, pos
                );
                let next_node_ref = &n.ptrs.as_slice_immute()[pos];
                debug_assert!(pos <= n.len);
                debug_assert!(
                    !next_node_ref.is_default(),
                    "default node at pos {}, len {}, keys {:?}",
                    pos,
                    n.len,
                    &n.keys.as_slice_immute()[..pos]
                );
                search_node(next_node_ref, key, ordering, deleted)
            }
            &NodeData::Empty(ref n) => search_node(&n.right, key, ordering, deleted),
            &NodeData::None => gen_empty_cursor(),
        }
    })
}

pub enum MutSearchResult {
    External,
    Internal(NodeCellRef),
}

pub fn mut_search<KS, PS>(node_ref: &NodeCellRef, key: &EntryKey) -> MutSearchResult
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    read_node(node_ref, |node: &NodeReadHandler<KS, PS>| match &**node {
        &NodeData::Internal(ref n) => {
            let pos = n.search(key);
            let sub_node = n.ptrs.as_slice_immute()[pos].clone();
            MutSearchResult::Internal(sub_node)
        }
        &NodeData::External(_) => MutSearchResult::External,
        &NodeData::Empty(ref n) => mut_search::<KS, PS>(&n.right, key),
        &NodeData::None => unreachable!(),
    })
}
<commit_msg>non-cascading mut search<commit_after>use index::btree::cursor::RTCursor;
use index::btree::node::read_node;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::DeletionSet;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Ordering;
use index::Slice;
use std::fmt::Debug;
use std::marker::PhantomData;
use index::btree::NodeData::Empty;

pub fn search_node<KS, PS>(
    node_ref: &NodeCellRef,
    key: &EntryKey,
    ordering: Ordering,
    deleted: &DeletionSet,
) -> RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("searching for {:?}", key);
    read_node(node_ref, |node_handler: &NodeReadHandler<KS, PS>| {
        let node = &**node_handler;
        let gen_empty_cursor = || RTCursor {
            index: 0,
            ordering,
            page: None,
            marker: PhantomData,
            deleted: deleted.clone(),
            current: None,
        };
        if let Some(right_node) = node.key_at_right_node(key) {
            debug!("Search found a node at the right side");
            return search_node(right_node, key, ordering, deleted);
        }
        debug!("search node have keys {:?}", node.keys());
        let mut pos = node.search(key);
        match node {
            &NodeData::External(ref n) => {
                debug!(
                    "search in external for {:?}, len {}, ordering {:?}, content: {:?}",
                    key,
                    n.len,
                    ordering,
                    &n.keys.as_slice_immute()[..n.len]
                );
                if ordering == Ordering::Backward {
                    debug!("found cursor pos {} for backwards, will be corrected", pos);
                    if pos > 0 && (pos >= n.len || &n.keys.as_slice_immute()[pos] != key) {
                        pos -= 1;
                    }
                    debug!("cursor pos have been corrected to {}", pos);
                }
                RTCursor::new(pos, node_ref, ordering, deleted)
            }
            &NodeData::Internal(ref n) => {
                debug!(
                    "search in internal node for {:?}, len {}, pos {}",
                    key, n.len, pos
                );
                let next_node_ref = &n.ptrs.as_slice_immute()[pos];
                debug_assert!(pos <= n.len);
                debug_assert!(
                    !next_node_ref.is_default(),
                    "default node at pos {}, len {}, keys {:?}",
                    pos,
                    n.len,
                    &n.keys.as_slice_immute()[..pos]
                );
                search_node(next_node_ref, key, ordering, deleted)
            }
            &NodeData::Empty(ref n) => search_node(&n.right, key, ordering, deleted),
            &NodeData::None => gen_empty_cursor(),
        }
    })
}

pub enum MutSearchResult {
    External,
    Internal(NodeCellRef),
}

pub fn mut_search<KS, PS>(node_ref: &NodeCellRef, key: &EntryKey) -> MutSearchResult
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    enum R {
        Result(MutSearchResult),
        Empty(NodeCellRef)
    }
    let res = read_node(node_ref, |node: &NodeReadHandler<KS, PS>| match &**node {
        &NodeData::Internal(ref n) => {
            let pos = n.search(key);
            let sub_node = n.ptrs.as_slice_immute()[pos].clone();
            R::Result(MutSearchResult::Internal(sub_node))
        }
        &NodeData::External(_) => R::Result(MutSearchResult::External),
        &NodeData::Empty(ref n) => R::Empty(n.right.clone()),
        &NodeData::None => unreachable!(),
    });
    match res {
        R::Result(r) => r,
        R::Empty(r) => mut_search::<KS, PS>(&r, key)
    }
}
<|endoftext|>


<commit_before>/// Module for 16 bit arithmetic (ALU instructions)
use jeebie::core::cpu::CPU;
use jeebie::registers::{ Flags, Register16 };

// 'JP nn' C3 12
pub fn JP_nn(cpu: &mut CPU) {
    let addr = cpu.get_immediate16();
    cpu.jump(addr);
}

// 'JP NZ,nn' C2 12
pub fn JP_NZ_nn(cpu: &mut CPU) {
    cpu.jump_not_flag(Flags::Zero, Register16::Immediate16);
}

// 'JP Z,nn' CA 12
pub fn JP_Z_nn(cpu: &mut CPU) {
    cpu.jump_flag(Flags::Zero, Register16::Immediate16);
}

// 'JP NC,nn' D2 12
pub fn JP_NC_nn(cpu: &mut CPU) {
    cpu.jump_not_flag(Flags::Carry, Register16::Immediate16);
}

// 'JP C,nn' DA 12
pub fn JP_C_nn(cpu: &mut CPU) {
    cpu.jump_flag(Flags::Zero, Register16::Immediate16);
}

// 'JP (HL)' E9 4
pub fn JP_HL(cpu: &mut CPU) {
    let addr = cpu.get16(Register16::HL);
    cpu.jump(addr);
}

// 'JR n' 18 8
pub fn JR_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1; // it isn't updated yet
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16); // n is loaded as a signed bit
    cpu.jump(addr);
}

// 'JR NZ,*' 20 8
pub fn JR_NZ_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1;
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16);
    cpu.jump_not_flag(Flags::Zero, Register16::Value16(addr));
}

// 'JR Z,*' 28 8
pub fn JR_Z_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1;
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16);
    cpu.jump_flag(Flags::Zero, Register16::Value16(addr));
}

// 'JR NC,*' 30 8
pub fn JR_NC_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1;
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16);
    cpu.jump_not_flag(Flags::Carry, Register16::Value16(addr));
}

// 'JR C,*' 38 8
pub fn JR_C_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1;
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16);
    cpu.jump_flag(Flags::Carry, Register16::Value16(addr));
}

// 'CALL nn' CD 12
pub fn CALL_nn(cpu: &mut CPU) {
    let call_addr = cpu.get16(Register16::Immediate16);
    let next_instr = cpu.reg.pc.wrapping_add(1);
    cpu.push_stack(Register16::Value16(next_instr));
    cpu.jump(call_addr);
}

// 'CALL NZ,nn' C4 12
pub fn CALL_NZ_nn(cpu: &mut CPU) {
    if !cpu.reg.is_set(Flags::Zero) {
        CALL_nn(cpu);
    }
}

// 'CALL Z,nn' CC 12
pub fn CALL_Z_nn(cpu: &mut CPU) {
    if cpu.reg.is_set(Flags::Zero) {
        CALL_nn(cpu);
    }
}

// 'CALL NC,nn' D4 12
pub fn CALL_NC_nn(cpu: &mut CPU) {
    if !cpu.reg.is_set(Flags::Carry) {
        CALL_nn(cpu);
    }
}

// 'CALL C,nn' DC 12
pub fn CALL_C_nn(cpu: &mut CPU) {
    if cpu.reg.is_set(Flags::Carry) {
        CALL_nn(cpu);
    }
}

// 'RST 00H' C7 32
pub fn RST_00h(cpu: &mut CPU) {
    cpu.restart(0x00);
}

// 'RST 08H' CF 32
pub fn RST_08h(cpu: &mut CPU) {
    cpu.restart(0x08);
}

// 'RST 10H' D7 32
pub fn RST_10h(cpu: &mut CPU) {
    cpu.restart(0x10);
}

// 'RST 18H' DF 32
pub fn RST_18h(cpu: &mut CPU) {
    cpu.restart(0x18);
}

// 'RST 20H' E7 32
pub fn RST_20h(cpu: &mut CPU) {
    cpu.restart(0x20);
}

// 'RST 28H' EF 32
pub fn RST_28h(cpu: &mut CPU) {
    cpu.restart(0x28);
}

// 'RST 30H' F7 32
pub fn RST_30h(cpu: &mut CPU) {
    cpu.restart(0x30);
}

// 'RST 38H' FF 32
pub fn RST_38h(cpu: &mut CPU) {
    cpu.restart(0x38);
}

// 'RET' C9 8
pub fn RET(cpu: &mut CPU) {
    cpu.pop_stack(Register16::PC);
}

// 'RET NZ' C0 8
pub fn RET_NZ(cpu: &mut CPU) {
    cpu.return_flag(Flags::Zero);
}

// 'RET Z' C8 8
pub fn RET_Z(cpu: &mut CPU) {
    cpu.return_not_flag(Flags::Zero);
}

// 'RET NC' D0 8
pub fn RET_NC(cpu: &mut CPU) {
    cpu.return_flag(Flags::Carry);
}

// 'RET C' D8 8
pub fn RET_C(cpu: &mut CPU) {
    cpu.return_not_flag(Flags::Carry);
}

<commit_msg>Fixed module comment<commit_after>/// Module for jump and related instructions (call, return, reset)
use jeebie::core::cpu::CPU;
use jeebie::registers::{ Flags, Register16 };

// 'JP nn' C3 12
pub fn JP_nn(cpu: &mut CPU) {
    let addr = cpu.get_immediate16();
    cpu.jump(addr);
}

// 'JP NZ,nn' C2 12
pub fn JP_NZ_nn(cpu: &mut CPU) {
    cpu.jump_not_flag(Flags::Zero, Register16::Immediate16);
}

// 'JP Z,nn' CA 12
pub fn JP_Z_nn(cpu: &mut CPU) {
    cpu.jump_flag(Flags::Zero, Register16::Immediate16);
}

// 'JP NC,nn' D2 12
pub fn JP_NC_nn(cpu: &mut CPU) {
    cpu.jump_not_flag(Flags::Carry, Register16::Immediate16);
}

// 'JP C,nn' DA 12
pub fn JP_C_nn(cpu: &mut CPU) {
    cpu.jump_flag(Flags::Zero, Register16::Immediate16);
}

// 'JP (HL)' E9 4
pub fn JP_HL(cpu: &mut CPU) {
    let addr = cpu.get16(Register16::HL);
    cpu.jump(addr);
}

// 'JR n' 18 8
pub fn JR_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1; // it isn't updated yet
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16); // n is loaded as a signed bit
    cpu.jump(addr);
}

// 'JR NZ,*' 20 8
pub fn JR_NZ_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1;
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16);
    cpu.jump_not_flag(Flags::Zero, Register16::Value16(addr));
}

// 'JR Z,*' 28 8
pub fn JR_Z_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1;
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16);
    cpu.jump_flag(Flags::Zero, Register16::Value16(addr));
}

// 'JR NC,*' 30 8
pub fn JR_NC_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1;
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16);
    cpu.jump_not_flag(Flags::Carry, Register16::Value16(addr));
}

// 'JR C,*' 38 8
pub fn JR_C_n(cpu: &mut CPU) {
    let pc = cpu.reg.pc + 1;
    let n = cpu.mem.read_b(cpu.reg.pc);
    let addr = pc.wrapping_add((n as i8) as u16);
    cpu.jump_flag(Flags::Carry, Register16::Value16(addr));
}

// 'CALL nn' CD 12
pub fn CALL_nn(cpu: &mut CPU) {
    let call_addr = cpu.get16(Register16::Immediate16);
    let next_instr = cpu.reg.pc.wrapping_add(1);
    cpu.push_stack(Register16::Value16(next_instr));
    cpu.jump(call_addr);
}

// 'CALL NZ,nn' C4 12
pub fn CALL_NZ_nn(cpu: &mut CPU) {
    if !cpu.reg.is_set(Flags::Zero) {
        CALL_nn(cpu);
    }
}

// 'CALL Z,nn' CC 12
pub fn CALL_Z_nn(cpu: &mut CPU) {
    if cpu.reg.is_set(Flags::Zero) {
        CALL_nn(cpu);
    }
}

// 'CALL NC,nn' D4 12
pub fn CALL_NC_nn(cpu: &mut CPU) {
    if !cpu.reg.is_set(Flags::Carry) {
        CALL_nn(cpu);
    }
}

// 'CALL C,nn' DC 12
pub fn CALL_C_nn(cpu: &mut CPU) {
    if cpu.reg.is_set(Flags::Carry) {
        CALL_nn(cpu);
    }
}

// 'RST 00H' C7 32
pub fn RST_00h(cpu: &mut CPU) {
    cpu.restart(0x00);
}

// 'RST 08H' CF 32
pub fn RST_08h(cpu: &mut CPU) {
    cpu.restart(0x08);
}

// 'RST 10H' D7 32
pub fn RST_10h(cpu: &mut CPU) {
    cpu.restart(0x10);
}

// 'RST 18H' DF 32
pub fn RST_18h(cpu: &mut CPU) {
    cpu.restart(0x18);
}

// 'RST 20H' E7 32
pub fn RST_20h(cpu: &mut CPU) {
    cpu.restart(0x20);
}

// 'RST 28H' EF 32
pub fn RST_28h(cpu: &mut CPU) {
    cpu.restart(0x28);
}

// 'RST 30H' F7 32
pub fn RST_30h(cpu: &mut CPU) {
    cpu.restart(0x30);
}

// 'RST 38H' FF 32
pub fn RST_38h(cpu: &mut CPU) {
    cpu.restart(0x38);
}

// 'RET' C9 8
pub fn RET(cpu: &mut CPU) {
    cpu.pop_stack(Register16::PC);
}

// 'RET NZ' C0 8
pub fn RET_NZ(cpu: &mut CPU) {
    cpu.return_flag(Flags::Zero);
}

// 'RET Z' C8 8
pub fn RET_Z(cpu: &mut CPU) {
    cpu.return_not_flag(Flags::Zero);
}

// 'RET NC' D0 8
pub fn RET_NC(cpu: &mut CPU) {
    cpu.return_flag(Flags::Carry);
}

// 'RET C' D8 8
pub fn RET_C(cpu: &mut CPU) {
    cpu.return_not_flag(Flags::Carry);
}

<|endoftext|>


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

use rand::{self, Rng, ThreadRng};
use slog::{self, Drain, OwnedKVList, Record};
use slog_async;
use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::path::PathBuf;
use std::sync::Mutex;
use time;

use tikv::util;
use tikv::util::logger;
use tikv::util::security::SecurityConfig;

/// A random generator of kv.
/// Every iter should be taken in µs. See also `benches::bench_kv_iter`.
pub struct KvGenerator {
    key_len: usize,
    value_len: usize,
    rng: ThreadRng,
}

impl KvGenerator {
    pub fn new(key_len: usize, value_len: usize) -> KvGenerator {
        KvGenerator {
            key_len,
            value_len,
            rng: rand::thread_rng(),
        }
    }
}

impl Iterator for KvGenerator {
    type Item = (Vec<u8>, Vec<u8>);

    fn next(&mut self) -> Option<(Vec<u8>, Vec<u8>)> {
        let mut k = vec![0; self.key_len];
        self.rng.fill_bytes(&mut k);
        let mut v = vec![0; self.value_len];
        self.rng.fill_bytes(&mut v);

        Some((k, v))
    }
}

/// Generate n pair of kvs.
#[allow(dead_code)]
pub fn generate_random_kvs(n: usize, key_len: usize, value_len: usize) -> Vec<(Vec<u8>, Vec<u8>)> {
    let kv_generator = KvGenerator::new(key_len, value_len);
    kv_generator.take(n).collect()
}

/// A logger that add a test case tag before each line of log.
struct CaseTraceLogger {
    f: Option<Mutex<File>>,
}

impl Drain for CaseTraceLogger {
    type Ok = ();
    type Err = slog::Never;
    fn log(&self, record: &Record, _: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
        let tag = util::get_tag_from_thread_name().map_or_else(|| "".into(), |s| s + " ");

        let t = time::now();
        let time_str = time::strftime("%Y/%m/%d %H:%M:%S.%f", &t).unwrap();
        // todo allow formatter to be configurable.
        let message = format!(
            "{}{} {}:{}: [{}] {}\n",
            tag,
            &time_str[..time_str.len() - 6],
            record.file().rsplit('/').nth(0).unwrap(),
            record.line(),
            record.level(),
            record.msg(),
        );

        if let Some(ref out) = self.f {
            let mut w = out.lock().unwrap();
            let _ = w.write(message.as_bytes());
        } else {
            let mut w = io::stderr();
            let _ = w.write(message.as_bytes());
        }
        Ok(())
    }
}

impl Drop for CaseTraceLogger {
    fn drop(&mut self) {
        if let Some(ref w) = self.f {
            w.lock().unwrap().flush().unwrap();
        }
    }
}

// A help function to initial logger.
fn init_log() {
    let output = env::var("LOG_FILE").ok();
    let level = logger::get_level_by_string(&env::var("LOG_LEVEL").unwrap_or_else(|_| "debug".to_owned()))
        .unwrap();
    let writer = output.map(|f| Mutex::new(File::create(f).unwrap()));
    // we don't mind set it multiple times.
    let drain = CaseTraceLogger { f: writer };
    let drain = slog_async::Async::new(drain).build().fuse();
    let logger = slog::Logger::root_typed(drain, slog_o!());
    let _ = logger::init_log_for_tikv_only(logger, level);
}

/// Set up ci test fail case log.
pub fn ci_setup() {
    if env::var("CI").is_ok() && env::var("LOG_FILE").is_ok() {
        init_log();
    }
    if env::var("PANIC_ABORT").is_ok() {
        // Panics as aborts, it's helpful for debugging,
        // but also stops tests immediately.
        util::panic_hook::set_exit_hook(true);
    }
}

pub fn new_security_cfg() -> SecurityConfig {
    let p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    SecurityConfig {
        ca_path: format!("{}", p.join("tests/data/ca.crt").display()),
        cert_path: format!("{}", p.join("tests/data/server.crt").display()),
        key_path: format!("{}", p.join("tests/data/server.pem").display()),
        override_ssl_target: "example.com".to_owned(),
    }
}
<commit_msg>tests: fix tracing test (#3204)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::path::PathBuf;
use std::sync::Mutex;

use rand::{self, Rng, ThreadRng};
use slog::{self, Drain, OwnedKVList, Record};
use time;

use tikv::util;
use tikv::util::logger;
use tikv::util::security::SecurityConfig;

/// A random generator of kv.
/// Every iter should be taken in µs. See also `benches::bench_kv_iter`.
pub struct KvGenerator {
    key_len: usize,
    value_len: usize,
    rng: ThreadRng,
}

impl KvGenerator {
    pub fn new(key_len: usize, value_len: usize) -> KvGenerator {
        KvGenerator {
            key_len,
            value_len,
            rng: rand::thread_rng(),
        }
    }
}

impl Iterator for KvGenerator {
    type Item = (Vec<u8>, Vec<u8>);

    fn next(&mut self) -> Option<(Vec<u8>, Vec<u8>)> {
        let mut k = vec![0; self.key_len];
        self.rng.fill_bytes(&mut k);
        let mut v = vec![0; self.value_len];
        self.rng.fill_bytes(&mut v);

        Some((k, v))
    }
}

/// Generate n pair of kvs.
#[allow(dead_code)]
pub fn generate_random_kvs(n: usize, key_len: usize, value_len: usize) -> Vec<(Vec<u8>, Vec<u8>)> {
    let kv_generator = KvGenerator::new(key_len, value_len);
    kv_generator.take(n).collect()
}

/// A logger that add a test case tag before each line of log.
struct CaseTraceLogger {
    f: Option<Mutex<File>>,
}

impl Drain for CaseTraceLogger {
    type Ok = ();
    type Err = slog::Never;
    fn log(&self, record: &Record, _: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
        let tag = util::get_tag_from_thread_name().map_or_else(|| "".into(), |s| s + " ");

        let t = time::now();
        let time_str = time::strftime("%Y/%m/%d %H:%M:%S.%f", &t).unwrap();
        // todo allow formatter to be configurable.
        let message = format!(
            "{}{} {}:{}: [{}] {}\n",
            tag,
            &time_str[..time_str.len() - 6],
            record.file().rsplit('/').nth(0).unwrap(),
            record.line(),
            record.level(),
            record.msg(),
        );

        if let Some(ref out) = self.f {
            let mut w = out.lock().unwrap();
            let _ = w.write(message.as_bytes());
        } else {
            let mut w = io::stderr();
            let _ = w.write(message.as_bytes());
        }
        Ok(())
    }
}

impl Drop for CaseTraceLogger {
    fn drop(&mut self) {
        if let Some(ref w) = self.f {
            w.lock().unwrap().flush().unwrap();
        }
    }
}

// A help function to initial logger.
fn init_log() {
    let output = env::var("LOG_FILE").ok();
    let level = logger::get_level_by_string(&env::var("LOG_LEVEL").unwrap_or_else(|_| "debug".to_owned()))
        .unwrap();
    let writer = output.map(|f| Mutex::new(File::create(f).unwrap()));
    // we don't mind set it multiple times.
    let drain = CaseTraceLogger { f: writer };
    // CaseTraceLogger relies on test's thread name, however slog_async has
    // its own thread, and the name is "".
    // TODO: Enable the slog_async when the [Custom test frameworks][1] is mature,
    //       and hook the slog_async logger to every test cases.
    //
    // [1]: https://github.com/rust-lang/rfcs/blob/master/text/2318-custom-test-frameworks.md
    //
    // let drain = slog_async::Async::new(drain).build().fuse();
    let logger = slog::Logger::root_typed(drain, slog_o!());
    let _ = logger::init_log_for_tikv_only(logger, level);
}

/// Set up ci test fail case log.
pub fn ci_setup() {
    if env::var("CI").is_ok() && env::var("LOG_FILE").is_ok() {
        init_log();
    }
    if env::var("PANIC_ABORT").is_ok() {
        // Panics as aborts, it's helpful for debugging,
        // but also stops tests immediately.
        util::panic_hook::set_exit_hook(true);
    }
}

pub fn new_security_cfg() -> SecurityConfig {
    let p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    SecurityConfig {
        ca_path: format!("{}", p.join("tests/data/ca.crt").display()),
        cert_path: format!("{}", p.join("tests/data/server.crt").display()),
        key_path: format!("{}", p.join("tests/data/server.pem").display()),
        override_ssl_target: "example.com".to_owned(),
    }
}
<|endoftext|>


<commit_before>#[allow(dead_code)];

extern crate extra;
extern crate arena;
extern crate collections;

use std::os;
use std::str;
use std::io::fs::File;

mod session;
mod context;
mod expr;
mod eval;
mod grammar;
mod ast;
mod resolve;
mod entity;
mod virtual_clock;
mod exec;

fn main() {
	let sess = session::Session::new();

	let args = os::args();
	let source_utf8 = File::open(&Path::new(args[1])).read_to_end().unwrap();
	let source = str::from_utf8(source_utf8);
	let module = grammar::module(source.unwrap());

	let module = module.unwrap();

	let mut prelude = resolve::Scope::new();
	prelude.names.insert(~"time", expr::EntityItem(&resolve::time_call_fn as &entity::Entity));

	let mut ctx = context::Context::new(&sess);
	ctx.domain = sess.arena.alloc(|| virtual_clock::VirtualClockDomain::new()) as &context::Domain;

	let modscope = resolve::resolve_module(&mut ctx, &prelude, &module);

	let main = match modscope.get("main").unwrap() {
		expr::EntityItem(s) => s,
		_ => fail!("Main is not an event"),
	};

	let w = virtual_clock::Wire::new();
	let event = main.resolve_call(&mut ctx, &resolve::Params{ positional: ~[resolve::EntityItem(&w)], body: None});

	exec::print_step_tree(&event, 0);
	exec::exec(&event);
}
<commit_msg>rust: libextra is gone<commit_after>#[allow(dead_code)];

extern crate arena;
extern crate collections;

use std::os;
use std::str;
use std::io::fs::File;

mod session;
mod context;
mod expr;
mod eval;
mod grammar;
mod ast;
mod resolve;
mod entity;
mod virtual_clock;
mod exec;

fn main() {
	let sess = session::Session::new();

	let args = os::args();
	let source_utf8 = File::open(&Path::new(args[1])).read_to_end().unwrap();
	let source = str::from_utf8(source_utf8);
	let module = grammar::module(source.unwrap());

	let module = module.unwrap();

	let mut prelude = resolve::Scope::new();
	prelude.names.insert(~"time", expr::EntityItem(&resolve::time_call_fn as &entity::Entity));

	let mut ctx = context::Context::new(&sess);
	ctx.domain = sess.arena.alloc(|| virtual_clock::VirtualClockDomain::new()) as &context::Domain;

	let modscope = resolve::resolve_module(&mut ctx, &prelude, &module);

	let main = match modscope.get("main").unwrap() {
		expr::EntityItem(s) => s,
		_ => fail!("Main is not an event"),
	};

	let w = virtual_clock::Wire::new();
	let event = main.resolve_call(&mut ctx, &resolve::Params{ positional: ~[resolve::EntityItem(&w)], body: None});

	exec::print_step_tree(&event, 0);
	exec::exec(&event);
}
<|endoftext|>


<commit_before>extern crate toml;
extern crate handlebars;
extern crate serde_json;

use serde_json::value::Map;
use std::env;
use std::fmt::Write as FmtWrite;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::result;

#[derive(Debug)]
pub enum Error {
    Message(&'static str),
    Io(::std::io::Error),
    Fmt(::std::fmt::Error),
    Render(handlebars::RenderError),
    Template(handlebars::TemplateError),
    TomlDe(toml::de::Error),
    Env(env::VarError),
}

impl From<&'static str> for Error {
    fn from(value: &'static str) -> Error {
        Error::Message(value)
    }
}

impl From<::std::io::Error> for Error {
    fn from(value: ::std::io::Error) -> Error {
        Error::Io(value)
    }
}

impl From<::std::fmt::Error> for Error {
    fn from(value: ::std::fmt::Error) -> Error {
        Error::Fmt(value)
    }
}

impl From<env::VarError> for Error {
    fn from(value: env::VarError) -> Error {
        Error::Env(value)
    }
}

impl From<handlebars::RenderError> for Error {
    fn from(value: handlebars::RenderError) -> Error {
        Error::Render(value)
    }
}

impl From<handlebars::TemplateError> for Error {
    fn from(value: handlebars::TemplateError) -> Error {
        Error::Template(value)
    }
}

impl From<toml::de::Error> for Error {
    fn from(value: toml::de::Error) -> Error {
        Error::TomlDe(value)
    }
}

type Result<T> = result::Result<T, Error>;

fn read_file(path: &str) -> String {
    let mut f = File::open(path)
        .map_err(|e| format!("cannot open: {}: {}", path, e))
        .unwrap();
    let mut content = String::new();
    f.read_to_string(&mut content).unwrap();
    content
}

/// Generate and build themes.rs
fn process_colors() -> Result<()> {
    let out_dir = env::var("OUT_DIR")?;
    let out_path = Path::new(&out_dir);

    let colors_content = read_file("themes.toml");
    let template_content = read_file("src/static/doc._.css.hbs");

    let value: toml::Value = colors_content.parse()?;
    let schemes = value.as_table().ok_or_else(
        || Error::Message("not a table"),
    )?;

    let mut handlebar = handlebars::Handlebars::new();

    handlebar.register_template_string("doc", template_content)?;

    let mut entries = Vec::new();
    let mut themes = String::new();

    let default = schemes.get("default").unwrap().as_table().ok_or_else(|| {
        Error::Message("not a table")
    })?;

    for (key, value) in schemes {
        if key == "default" {
            continue;
        }

        let mut colors_in = default.clone();
        colors_in.extend(
            value
                .as_table()
                .ok_or_else(|| Error::Message("not a table"))?
                .clone(),
        );

        let mut colors = Map::new();

        for (k, color) in colors_in {
            let value = color.as_str().ok_or_else(
                || Error::Message("expected string"),
            )?;
            colors.insert(k.to_owned(), handlebars::to_json(&value));
        }

        let result = handlebar.render("doc", &colors)?;

        let key_upper = key.to_uppercase();

        let name = format!("doc.{}.css", key);

        writeln!(
            themes,
            "const DOC_CSS_{}: &[u8] = include_bytes!(\"{}/{}\");",
            key_upper,
            out_path.display(),
            name
        )?;

        entries.push(format!("(\"{}\", DOC_CSS_{})", key, key_upper));

        let mut f = File::create(out_path.join(name))?;
        f.write_all(&result.into_bytes())?;
    }

    writeln!(themes, "")?;
    writeln!(
        themes,
        "pub fn build_themes_vec() -> Vec<(&'static str, &'static [u8])> {{"
    )?;
    writeln!(themes, "  vec![{}]", entries.join(", "))?;
    writeln!(themes, "}}")?;

    let mut themes_file = File::create(out_path.join("themes.rs"))?;
    themes_file.write_all(&themes.into_bytes())?;

    Ok(())
}

fn main() {
    println!("cargo:rerun-if-changed=themes.toml");
    println!("cargo:rerun-if-changed=src/static/doc._.css.hbs");
    println!("cargo:rerun-if-changed=src/static/normalize.css");
    process_colors().unwrap();
}
<commit_msg>Fix path in generated file<commit_after>extern crate toml;
extern crate handlebars;
extern crate serde_json;

use serde_json::value::Map;
use std::env;
use std::fmt::Write as FmtWrite;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::result;

#[derive(Debug)]
pub enum Error {
    Message(&'static str),
    Io(::std::io::Error),
    Fmt(::std::fmt::Error),
    Render(handlebars::RenderError),
    Template(handlebars::TemplateError),
    TomlDe(toml::de::Error),
    Env(env::VarError),
}

impl From<&'static str> for Error {
    fn from(value: &'static str) -> Error {
        Error::Message(value)
    }
}

impl From<::std::io::Error> for Error {
    fn from(value: ::std::io::Error) -> Error {
        Error::Io(value)
    }
}

impl From<::std::fmt::Error> for Error {
    fn from(value: ::std::fmt::Error) -> Error {
        Error::Fmt(value)
    }
}

impl From<env::VarError> for Error {
    fn from(value: env::VarError) -> Error {
        Error::Env(value)
    }
}

impl From<handlebars::RenderError> for Error {
    fn from(value: handlebars::RenderError) -> Error {
        Error::Render(value)
    }
}

impl From<handlebars::TemplateError> for Error {
    fn from(value: handlebars::TemplateError) -> Error {
        Error::Template(value)
    }
}

impl From<toml::de::Error> for Error {
    fn from(value: toml::de::Error) -> Error {
        Error::TomlDe(value)
    }
}

type Result<T> = result::Result<T, Error>;

fn read_file(path: &str) -> String {
    let mut f = File::open(path)
        .map_err(|e| format!("cannot open: {}: {}", path, e))
        .unwrap();
    let mut content = String::new();
    f.read_to_string(&mut content).unwrap();
    content
}

/// Generate and build themes.rs
fn process_colors() -> Result<()> {
    let out_dir = env::var("OUT_DIR")?;
    let out_path = Path::new(&out_dir);

    let colors_content = read_file("themes.toml");
    let template_content = read_file("src/static/doc._.css.hbs");

    let value: toml::Value = colors_content.parse()?;
    let schemes = value.as_table().ok_or_else(
        || Error::Message("not a table"),
    )?;

    let mut handlebar = handlebars::Handlebars::new();

    handlebar.register_template_string("doc", template_content)?;

    let mut entries = Vec::new();
    let mut themes = String::new();

    let default = schemes.get("default").unwrap().as_table().ok_or_else(|| {
        Error::Message("not a table")
    })?;

    for (key, value) in schemes {
        if key == "default" {
            continue;
        }

        let mut colors_in = default.clone();
        colors_in.extend(
            value
                .as_table()
                .ok_or_else(|| Error::Message("not a table"))?
                .clone(),
        );

        let mut colors = Map::new();

        for (k, color) in colors_in {
            let value = color.as_str().ok_or_else(
                || Error::Message("expected string"),
            )?;
            colors.insert(k.to_owned(), handlebars::to_json(&value));
        }

        let result = handlebar.render("doc", &colors)?;

        let key_upper = key.to_uppercase();

        let name = format!("doc.{}.css", key);

        writeln!(
            themes,
            "const DOC_CSS_{}: &[u8] = include_bytes!(concat!(env!(\"OUT_DIR\"), \"/{}\"));",
            key_upper,
            name
        )?;

        entries.push(format!("(\"{}\", DOC_CSS_{})", key, key_upper));

        let mut f = File::create(out_path.join(name))?;
        f.write_all(&result.into_bytes())?;
    }

    writeln!(themes, "")?;
    writeln!(
        themes,
        "pub fn build_themes_vec() -> Vec<(&'static str, &'static [u8])> {{"
    )?;
    writeln!(themes, "  vec![{}]", entries.join(", "))?;
    writeln!(themes, "}}")?;

    let mut themes_file = File::create(out_path.join("themes.rs"))?;
    themes_file.write_all(&themes.into_bytes())?;

    Ok(())
}

fn main() {
    println!("cargo:rerun-if-changed=themes.toml");
    println!("cargo:rerun-if-changed=src/static/doc._.css.hbs");
    println!("cargo:rerun-if-changed=src/static/normalize.css");
    process_colors().unwrap();
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis;
use library;
use env::Env;
use super::child_properties;
use super::function;
use super::general;
use super::properties;
use super::signal;
use super::trait_impls;
use super::trampoline;

pub fn generate(
    w: &mut Write,
    env: &Env,
    analysis: &analysis::object::Info,
    generate_display_trait: bool,
) -> Result<()> {
    try!(general::start_comments(w, &env.config));
    try!(general::uses(w, env, &analysis.imports));

    try!(general::define_object_type(
        w,
        env,
        &analysis.name,
        &analysis.c_type,
        &analysis.c_class_type.as_ref().map(|s| &s[..]),
        &analysis.rust_class_type.as_ref().map(|s| &s[..]),
        &analysis.get_type,
        &analysis.supertypes,
    ));

    if need_generate_inherent(analysis) {
        try!(writeln!(w));
        try!(write!(w, "impl {} {{", analysis.name));
        for func_analysis in &analysis.constructors() {
            try!(function::generate(w, env, func_analysis, false, false, 1));
        }

        if !need_generate_trait(analysis) {
            for func_analysis in &analysis.methods() {
                try!(function::generate(w, env, func_analysis, false, false, 1));
            }

            for property in &analysis.properties {
                try!(properties::generate(w, env, property, false, false, 1));
            }

            for child_property in &analysis.child_properties {
                try!(child_properties::generate(
                    w,
                    env,
                    child_property,
                    false,
                    false,
                    1,
                ));
            }
        }

        for func_analysis in &analysis.functions() {
            try!(function::generate(w, env, func_analysis, false, false, 1));
        }

        if !need_generate_trait(analysis) {
            for signal_analysis in analysis
                .signals
                .iter()
                .chain(analysis.notify_signals.iter())
            {
                try!(signal::generate(
                    w,
                    env,
                    signal_analysis,
                    &analysis.trampolines,
                    false,
                    false,
                    1,
                ));
            }
        }

        try!(writeln!(w, "}}"));

        try!(general::declare_default_from_new(
            w,
            env,
            &analysis.name,
            &analysis.functions
        ));
    }

    try!(trait_impls::generate(
        w,
        &analysis.name,
        &analysis.functions,
        &analysis.specials,
        if need_generate_trait(analysis) {
            Some(&analysis.trait_name)
        } else {
            None
        },
    ));

    if analysis.concurrency != library::Concurrency::None {
        try!(writeln!(w));
    }

    match analysis.concurrency {
        library::Concurrency::Send | library::Concurrency::SendSync => {
            try!(writeln!(w, "unsafe impl Send for {} {{}}", analysis.name));
        }
        library::Concurrency::SendUnique => {
            if env.namespaces.is_glib_crate {
                try!(writeln!(w, "unsafe impl ::SendUnique for {} {{", analysis.name));
            } else {
                try!(writeln!(w, "unsafe impl glib::SendUnique for {} {{", analysis.name));
            }

            try!(writeln!(w, "    fn is_unique(&self) -> bool {{"));
            try!(writeln!(w, "        self.ref_count() == 1"));
            try!(writeln!(w, "    }}"));

            try!(writeln!(w, "}}"));
        },
        _ => (),
    }

    if let library::Concurrency::SendSync = analysis.concurrency {
        try!(writeln!(w, "unsafe impl Sync for {} {{}}", analysis.name));
    }

    if need_generate_trait(analysis) {
        try!(writeln!(w));
        try!(generate_trait(w, env, analysis));
    }

    if !analysis.trampolines.is_empty() {
        for trampoline in &analysis.trampolines {
            try!(trampoline::generate(
                w,
                env,
                trampoline,
                need_generate_trait(analysis),
                &analysis.name,
            ));
        }
    }

    if generate_display_trait {
        try!(writeln!(
            w,
            "\nimpl fmt::Display for {} {{",
            analysis.name,
        ));
        // Generate Display trait implementation.
        try!(writeln!(w, "\tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\
                            \t\twrite!(f, \"{}\")\n\
                          \t}}\n\
                        }}", analysis.name));
    }

    Ok(())
}

fn generate_trait(
    w: &mut Write,
    env: &Env,
    analysis: &analysis::object::Info,
) -> Result<()> {
    try!(write!(w, "pub trait {}: 'static {{", analysis.trait_name));

    for func_analysis in &analysis.methods() {
        try!(function::generate(w, env, func_analysis, true, true, 1));
    }
    for property in &analysis.properties {
        try!(properties::generate(w, env, property, true, true, 1));
    }
    for child_property in &analysis.child_properties {
        try!(child_properties::generate(
            w,
            env,
            child_property,
            true,
            true,
            1,
        ));
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        try!(signal::generate(
            w,
            env,
            signal_analysis,
            &analysis.trampolines,
            true,
            true,
            1,
        ));
    }
    try!(writeln!(w, "}}"));

    try!(writeln!(w));
    // FIXME remove 'static
    try!(write!(
        w,
        "impl<O: 'static + IsA<{}>> {} for O {{",
        analysis.name,
        analysis.trait_name,
    ));

    for func_analysis in &analysis.methods() {
        try!(function::generate(w, env, func_analysis, true, false, 1));
    }
    for property in &analysis.properties {
        try!(properties::generate(w, env, property, true, false, 1));
    }
    for child_property in &analysis.child_properties {
        try!(child_properties::generate(
            w,
            env,
            child_property,
            true,
            false,
            1,
        ));
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        try!(signal::generate(
            w,
            env,
            signal_analysis,
            &analysis.trampolines,
            true,
            false,
            1,
        ));
    }
    try!(writeln!(w, "}}"));

    Ok(())
}

fn need_generate_inherent(analysis: &analysis::object::Info) -> bool {
    analysis.has_constructors || analysis.has_functions || !need_generate_trait(analysis)
}

fn need_generate_trait(analysis: &analysis::object::Info) -> bool {
    analysis.generate_trait
}

pub fn generate_reexports(
    env: &Env,
    analysis: &analysis::object::Info,
    module_name: &str,
    contents: &mut Vec<String>,
    traits: &mut Vec<String>,
) {
    let mut cfgs: Vec<String> = Vec::new();
    if let Some(cfg) = general::cfg_condition_string(&analysis.cfg_condition, false, 0) {
        cfgs.push(cfg);
    }
    if let Some(cfg) = general::version_condition_string(env, analysis.version, false, 0) {
        cfgs.push(cfg);
    }
    contents.push("".to_owned());
    contents.extend_from_slice(&cfgs);
    contents.push(format!("mod {};", module_name));
    contents.extend_from_slice(&cfgs);
    contents.push(format!("pub use self::{}::{};", module_name, analysis.name));
    if need_generate_trait(analysis) {
        contents.extend_from_slice(&cfgs);
        contents.push(format!(
            "pub use self::{}::{};",
            module_name,
            analysis.trait_name
        ));
        for cfg in &cfgs {
            traits.push(format!("\t{}", cfg));
        }
        traits.push(format!("\tpub use super::{};", analysis.trait_name));
    }
}
<commit_msg>Remove unneeded 'static trait bound for Ext trait impls<commit_after>use std::io::{Result, Write};

use analysis;
use library;
use env::Env;
use super::child_properties;
use super::function;
use super::general;
use super::properties;
use super::signal;
use super::trait_impls;
use super::trampoline;

pub fn generate(
    w: &mut Write,
    env: &Env,
    analysis: &analysis::object::Info,
    generate_display_trait: bool,
) -> Result<()> {
    try!(general::start_comments(w, &env.config));
    try!(general::uses(w, env, &analysis.imports));

    try!(general::define_object_type(
        w,
        env,
        &analysis.name,
        &analysis.c_type,
        &analysis.c_class_type.as_ref().map(|s| &s[..]),
        &analysis.rust_class_type.as_ref().map(|s| &s[..]),
        &analysis.get_type,
        &analysis.supertypes,
    ));

    if need_generate_inherent(analysis) {
        try!(writeln!(w));
        try!(write!(w, "impl {} {{", analysis.name));
        for func_analysis in &analysis.constructors() {
            try!(function::generate(w, env, func_analysis, false, false, 1));
        }

        if !need_generate_trait(analysis) {
            for func_analysis in &analysis.methods() {
                try!(function::generate(w, env, func_analysis, false, false, 1));
            }

            for property in &analysis.properties {
                try!(properties::generate(w, env, property, false, false, 1));
            }

            for child_property in &analysis.child_properties {
                try!(child_properties::generate(
                    w,
                    env,
                    child_property,
                    false,
                    false,
                    1,
                ));
            }
        }

        for func_analysis in &analysis.functions() {
            try!(function::generate(w, env, func_analysis, false, false, 1));
        }

        if !need_generate_trait(analysis) {
            for signal_analysis in analysis
                .signals
                .iter()
                .chain(analysis.notify_signals.iter())
            {
                try!(signal::generate(
                    w,
                    env,
                    signal_analysis,
                    &analysis.trampolines,
                    false,
                    false,
                    1,
                ));
            }
        }

        try!(writeln!(w, "}}"));

        try!(general::declare_default_from_new(
            w,
            env,
            &analysis.name,
            &analysis.functions
        ));
    }

    try!(trait_impls::generate(
        w,
        &analysis.name,
        &analysis.functions,
        &analysis.specials,
        if need_generate_trait(analysis) {
            Some(&analysis.trait_name)
        } else {
            None
        },
    ));

    if analysis.concurrency != library::Concurrency::None {
        try!(writeln!(w));
    }

    match analysis.concurrency {
        library::Concurrency::Send | library::Concurrency::SendSync => {
            try!(writeln!(w, "unsafe impl Send for {} {{}}", analysis.name));
        }
        library::Concurrency::SendUnique => {
            if env.namespaces.is_glib_crate {
                try!(writeln!(w, "unsafe impl ::SendUnique for {} {{", analysis.name));
            } else {
                try!(writeln!(w, "unsafe impl glib::SendUnique for {} {{", analysis.name));
            }

            try!(writeln!(w, "    fn is_unique(&self) -> bool {{"));
            try!(writeln!(w, "        self.ref_count() == 1"));
            try!(writeln!(w, "    }}"));

            try!(writeln!(w, "}}"));
        },
        _ => (),
    }

    if let library::Concurrency::SendSync = analysis.concurrency {
        try!(writeln!(w, "unsafe impl Sync for {} {{}}", analysis.name));
    }

    if need_generate_trait(analysis) {
        try!(writeln!(w));
        try!(generate_trait(w, env, analysis));
    }

    if !analysis.trampolines.is_empty() {
        for trampoline in &analysis.trampolines {
            try!(trampoline::generate(
                w,
                env,
                trampoline,
                need_generate_trait(analysis),
                &analysis.name,
            ));
        }
    }

    if generate_display_trait {
        try!(writeln!(
            w,
            "\nimpl fmt::Display for {} {{",
            analysis.name,
        ));
        // Generate Display trait implementation.
        try!(writeln!(w, "\tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\
                            \t\twrite!(f, \"{}\")\n\
                          \t}}\n\
                        }}", analysis.name));
    }

    Ok(())
}

fn generate_trait(
    w: &mut Write,
    env: &Env,
    analysis: &analysis::object::Info,
) -> Result<()> {
    try!(write!(w, "pub trait {}: 'static {{", analysis.trait_name));

    for func_analysis in &analysis.methods() {
        try!(function::generate(w, env, func_analysis, true, true, 1));
    }
    for property in &analysis.properties {
        try!(properties::generate(w, env, property, true, true, 1));
    }
    for child_property in &analysis.child_properties {
        try!(child_properties::generate(
            w,
            env,
            child_property,
            true,
            true,
            1,
        ));
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        try!(signal::generate(
            w,
            env,
            signal_analysis,
            &analysis.trampolines,
            true,
            true,
            1,
        ));
    }
    try!(writeln!(w, "}}"));

    try!(writeln!(w));
    try!(write!(
        w,
        "impl<O: IsA<{}>> {} for O {{",
        analysis.name,
        analysis.trait_name,
    ));

    for func_analysis in &analysis.methods() {
        try!(function::generate(w, env, func_analysis, true, false, 1));
    }
    for property in &analysis.properties {
        try!(properties::generate(w, env, property, true, false, 1));
    }
    for child_property in &analysis.child_properties {
        try!(child_properties::generate(
            w,
            env,
            child_property,
            true,
            false,
            1,
        ));
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        try!(signal::generate(
            w,
            env,
            signal_analysis,
            &analysis.trampolines,
            true,
            false,
            1,
        ));
    }
    try!(writeln!(w, "}}"));

    Ok(())
}

fn need_generate_inherent(analysis: &analysis::object::Info) -> bool {
    analysis.has_constructors || analysis.has_functions || !need_generate_trait(analysis)
}

fn need_generate_trait(analysis: &analysis::object::Info) -> bool {
    analysis.generate_trait
}

pub fn generate_reexports(
    env: &Env,
    analysis: &analysis::object::Info,
    module_name: &str,
    contents: &mut Vec<String>,
    traits: &mut Vec<String>,
) {
    let mut cfgs: Vec<String> = Vec::new();
    if let Some(cfg) = general::cfg_condition_string(&analysis.cfg_condition, false, 0) {
        cfgs.push(cfg);
    }
    if let Some(cfg) = general::version_condition_string(env, analysis.version, false, 0) {
        cfgs.push(cfg);
    }
    contents.push("".to_owned());
    contents.extend_from_slice(&cfgs);
    contents.push(format!("mod {};", module_name));
    contents.extend_from_slice(&cfgs);
    contents.push(format!("pub use self::{}::{};", module_name, analysis.name));
    if need_generate_trait(analysis) {
        contents.extend_from_slice(&cfgs);
        contents.push(format!(
            "pub use self::{}::{};",
            module_name,
            analysis.trait_name
        ));
        for cfg in &cfgs {
            traits.push(format!("\t{}", cfg));
        }
        traits.push(format!("\tpub use super::{};", analysis.trait_name));
    }
}
<|endoftext|>