<commit_before>extern crate curl;
extern crate flate2;
extern crate pkg_config;
extern crate semver;
extern crate tar;

use std::error::Error;
use std::fs::File;
use std::io::BufWriter;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process;
use std::process::Command;
use std::{env, fs};

use curl::easy::Easy;
use flate2::read::GzDecoder;
use semver::Version;
use tar::Archive;

const FRAMEWORK_LIBRARY: &'static str = "tensorflow_framework";
const LIBRARY: &'static str = "tensorflow";
const REPOSITORY: &'static str = "https://github.com/tensorflow/tensorflow.git";
const FRAMEWORK_TARGET: &'static str = "tensorflow:libtensorflow_framework.so";
const TARGET: &'static str = "tensorflow:libtensorflow.so";
// `VERSION` and `TAG` are separate because the tag is not always `'v' + VERSION`.
const VERSION: &'static str = "1.4.0";
const TAG: &'static str = "v1.4.0";
const MIN_BAZEL: &'static str = "0.5.4";

macro_rules! get(($name:expr) => (ok!(env::var($name))));
macro_rules! ok(($expression:expr) => ($expression.unwrap()));
macro_rules! log {
    ($fmt:expr) => (println!(concat!("libtensorflow-sys/build.rs:{}: ", $fmt), line!()));
    ($fmt:expr, $($arg:tt)*) => (println!(concat!("libtensorflow-sys/build.rs:{}: ", $fmt),
    line!(), $($arg)*));
}
macro_rules! log_var(($var:ident) => (log!(concat!(stringify!($var), " = {:?}"), $var)));

fn main() {
    if check_windows_lib() {
        log!("Returning early because {} was already found", LIBRARY);
        return;
    }

    if pkg_config::find_library(LIBRARY).is_ok() {
        log!("Returning early because {} was already found", LIBRARY);
        return;
    }

    let force_src = match env::var("TF_RUST_BUILD_FROM_SRC") {
        Ok(s) => s == "true",
        Err(_) => false,
    };

    if !force_src && env::consts::ARCH == "x86_64" && (env::consts::OS == "linux" || env::consts::OS == "macos") {
        install_prebuilt();
    } else {
        build_from_src();
    }
}

#[cfg(not(target_env = "msvc"))]
fn check_windows_lib() -> bool {
    false
}

#[cfg(target_env = "msvc")]
fn check_windows_lib() -> bool {
    let windows_lib: &str = &format!("{}.lib", LIBRARY);
    if let Ok(path) = env::var("PATH") {
        for p in path.split(";") {
            let path = Path::new(p).join(windows_lib);
            if path.exists() {
                println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
                println!("cargo:rustc-link-search=native={}", p);
                return true
            }
        }
    }
    false
}

fn remove_suffix(value: &mut String, suffix: &str) {
    if value.ends_with(suffix) {
        let n = value.len();
        value.truncate(n - suffix.len());
    }
}

fn extract<P: AsRef<Path>, P2: AsRef<Path>>(archive_path: P, extract_to: P2) {
    let file = File::open(archive_path).unwrap();
    let unzipped = GzDecoder::new(file).unwrap();
    let mut a = Archive::new(unzipped);
    a.unpack(extract_to).unwrap();
}

// Downloads and unpacks a prebuilt binary. Only works for certain platforms.
fn install_prebuilt() {
    // Figure out the file names.
    let os = match env::consts::OS {
        "macos" => "darwin",
        x => x,
    };
    let proc_type = if cfg!(feature = "tensorflow_gpu") {"gpu"} else {"cpu"};
    let binary_url = format!(
        "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-{}-{}-{}-{}.tar.gz",
        proc_type, os, env::consts::ARCH, VERSION);
    log_var!(binary_url);
    let short_file_name = binary_url.split("/").last().unwrap();
    let mut base_name = short_file_name.to_string();
    remove_suffix(&mut base_name, ".tar.gz");
    log_var!(base_name);
    let download_dir = match env::var("TF_RUST_DOWNLOAD_DIR") {
        Ok(s) => PathBuf::from(s),
        Err(_) => PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join("target"),
    };
    if !download_dir.exists() {
        fs::create_dir(&download_dir).unwrap();
    }
    let file_name = download_dir.join(short_file_name);
    log_var!(file_name);

    // Download the tarball.
    if !file_name.exists() {
        let f = File::create(&file_name).unwrap();
        let mut writer = BufWriter::new(f);
        let mut easy = Easy::new();
        easy.url(&binary_url).unwrap();
        easy.write_function(move |data| {
            Ok(writer.write(data).unwrap())
        }).unwrap();
        easy.perform().unwrap();

        let response_code = easy.response_code().unwrap();
        if response_code != 200 {
            panic!("Unexpected response code {} for {}", response_code, binary_url);
        }
    }

    // Extract the tarball.
    let unpacked_dir = download_dir.join(base_name);
    let lib_dir = unpacked_dir.join("lib");
    let framework_library_file = format!("lib{}.so", FRAMEWORK_LIBRARY);
    let library_file = format!("lib{}.so", LIBRARY);
    let framework_library_full_path = lib_dir.join(&framework_library_file);
    let library_full_path = lib_dir.join(&library_file);
    if !framework_library_full_path.exists() || !library_full_path.exists() {
        extract(file_name, &unpacked_dir);
    }

    println!("cargo:rustc-link-lib=dylib={}", FRAMEWORK_LIBRARY);
    println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
    let output = PathBuf::from(&get!("OUT_DIR"));
    let new_framework_library_full_path = output.join(&framework_library_file);
    if new_framework_library_full_path.exists() {
        log!("File {} already exists, deleting.", new_framework_library_full_path.display());
        std::fs::remove_file(&new_framework_library_full_path).unwrap();
    }
    let new_library_full_path = output.join(&library_file);
    if new_library_full_path.exists() {
        log!("File {} already exists, deleting.", new_library_full_path.display());
        std::fs::remove_file(&new_library_full_path).unwrap();
    }

    log!("Copying {} to {}...", library_full_path.display(), new_library_full_path.display());
    std::fs::copy(&library_full_path, &new_library_full_path).unwrap();
    log!("Copying {} to {}...", framework_library_full_path.display(), new_framework_library_full_path.display());
    std::fs::copy(&framework_library_full_path, &new_framework_library_full_path).unwrap();
    println!("cargo:rustc-link-search={}", output.display());
}

fn build_from_src() {
    let output = PathBuf::from(&get!("OUT_DIR"));
    log_var!(output);
    let source = PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join(format!("target/source-{}", TAG));
    log_var!(source);
    let lib_dir = output.join(format!("lib-{}", TAG));
    log_var!(lib_dir);
    if lib_dir.exists() {
        log!("Directory {:?} already exists", lib_dir);
    } else {
        log!("Creating directory {:?}", lib_dir);
        fs::create_dir(lib_dir.clone()).unwrap();
    }
    let framework_library_path = lib_dir.join(format!("lib{}.so", FRAMEWORK_LIBRARY));
    log_var!(framework_library_path);
    let library_path = lib_dir.join(format!("lib{}.so", LIBRARY));
    log_var!(library_path);
    if library_path.exists() && framework_library_path.exists() {
        log!("{:?} and {:?} already exist, not building", library_path, framework_library_path);
    } else {
        if let Err(e) = check_bazel() {
            println!("cargo:error=Bazel must be installed at version {} or greater. (Error: {})",
                     MIN_BAZEL,
                     e);
            process::exit(1);
        }
        let framework_target_path = &FRAMEWORK_TARGET.replace(":", "/");
        log_var!(framework_target_path);
        let target_path = &TARGET.replace(":", "/");
        log_var!(target_path);
        if !Path::new(&source.join(".git")).exists() {
            run("git", |command| {
                command.arg("clone")
                    .arg(format!("--branch={}", TAG))
                    .arg("--recursive")
                    .arg(REPOSITORY)
                    .arg(&source)
            });
        }
        // Only configure if not previously configured.  Configuring runs a
        // `bazel clean`, which we don't want, because we want to be able to
        // continue from a cancelled build.
        let configure_hint_file_pb = source.join(".rust-configured");
        let configure_hint_file = Path::new(&configure_hint_file_pb);
        if !configure_hint_file.exists() {
            run("bash",
                |command| command.current_dir(&source)
                .env("TF_NEED_CUDA", if cfg!(feature = "tensorflow_gpu") {"1"} else {"0"})
                .arg("-c")
                .arg("yes ''|./configure"));
            File::create(configure_hint_file).unwrap();
        }
        run("bazel", |command| {
            command.current_dir(&source)
                .arg("build")
                .arg(format!("--jobs={}", get!("NUM_JOBS")))
                .arg("--compilation_mode=opt")
                .arg("--copt=-march=native")
                .arg(TARGET)
        });
        let framework_target_bazel_bin = source.join("bazel-bin").join(framework_target_path);
        log!("Copying {:?} to {:?}", framework_target_bazel_bin, framework_library_path);
        fs::copy(framework_target_bazel_bin, framework_library_path).unwrap();
        let target_bazel_bin = source.join("bazel-bin").join(target_path);
        log!("Copying {:?} to {:?}", target_bazel_bin, library_path);
        fs::copy(target_bazel_bin, library_path).unwrap();
    }

    println!("cargo:rustc-link-lib=dylib={}", FRAMEWORK_LIBRARY);
    println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
    println!("cargo:rustc-link-search={}", lib_dir.display());
}

fn run<F>(name: &str, mut configure: F)
    where F: FnMut(&mut Command) -> &mut Command
{
    let mut command = Command::new(name);
    let configured = configure(&mut command);
    log!("Executing {:?}", configured);
    if !ok!(configured.status()).success() {
        panic!("failed to execute {:?}", configured);
    }
    log!("Command {:?} finished successfully", configured);
}

// Building TF 0.11.0rc1 with Bazel 0.3.0 gives this error when running `configure`:
//   expected ConfigurationTransition or NoneType for 'cfg' while calling label_list but got
// string instead:     data.
//       ERROR: com.google.devtools.build.lib.packages.BuildFileContainsErrorsException: error
// loading package '': Extension file 'tensorflow/tensorflow.bzl' has errors.
// And the simple solution is to require Bazel 0.3.1 or higher.
fn check_bazel() -> Result<(), Box<Error>> {
    let mut command = Command::new("bazel");
    command.arg("version");
    log!("Executing {:?}", command);
    let out = command.output()?;
    log!("Command {:?} finished successfully", command);
    let stdout = String::from_utf8(out.stdout)?;
    let mut found_version = false;
    for line in stdout.lines() {
        if line.starts_with("Build label:") {
            found_version = true;
            let mut version_str = line.split(":")
                .nth(1)
                .unwrap()
                .split(" ")
                .nth(1)
                .unwrap()
                .trim();
            if version_str.ends_with('-') {
                // hyphen is 1 byte long, so it's safe
                version_str = &version_str[..version_str.len() - 1];
            }
            let version = Version::parse(version_str)?;
            let want = Version::parse(MIN_BAZEL)?;
            if version < want {
                return Err(format!("Installed version {} is less than required version {}",
                                   version_str,
                                   MIN_BAZEL)
                    .into());
            }
        }
    }
    if !found_version {
        return Err("Did not find version number in `bazel version` output.".into());
    }
    Ok(())
}
<commit_msg>Add TF_RUST_BAZEL_OPTS environment variable<commit_after>extern crate curl;
extern crate flate2;
extern crate pkg_config;
extern crate semver;
extern crate tar;

use std::error::Error;
use std::fs::File;
use std::io::BufWriter;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process;
use std::process::Command;
use std::{env, fs};

use curl::easy::Easy;
use flate2::read::GzDecoder;
use semver::Version;
use tar::Archive;

const FRAMEWORK_LIBRARY: &'static str = "tensorflow_framework";
const LIBRARY: &'static str = "tensorflow";
const REPOSITORY: &'static str = "https://github.com/tensorflow/tensorflow.git";
const FRAMEWORK_TARGET: &'static str = "tensorflow:libtensorflow_framework.so";
const TARGET: &'static str = "tensorflow:libtensorflow.so";
// `VERSION` and `TAG` are separate because the tag is not always `'v' + VERSION`.
const VERSION: &'static str = "1.4.0";
const TAG: &'static str = "v1.4.0";
const MIN_BAZEL: &'static str = "0.5.4";

macro_rules! get(($name:expr) => (ok!(env::var($name))));
macro_rules! ok(($expression:expr) => ($expression.unwrap()));
macro_rules! log {
    ($fmt:expr) => (println!(concat!("libtensorflow-sys/build.rs:{}: ", $fmt), line!()));
    ($fmt:expr, $($arg:tt)*) => (println!(concat!("libtensorflow-sys/build.rs:{}: ", $fmt),
    line!(), $($arg)*));
}
macro_rules! log_var(($var:ident) => (log!(concat!(stringify!($var), " = {:?}"), $var)));

fn main() {
    if check_windows_lib() {
        log!("Returning early because {} was already found", LIBRARY);
        return;
    }

    if pkg_config::find_library(LIBRARY).is_ok() {
        log!("Returning early because {} was already found", LIBRARY);
        return;
    }

    let force_src = match env::var("TF_RUST_BUILD_FROM_SRC") {
        Ok(s) => s == "true",
        Err(_) => false,
    };

    if !force_src && env::consts::ARCH == "x86_64" && (env::consts::OS == "linux" || env::consts::OS == "macos") {
        install_prebuilt();
    } else {
        build_from_src();
    }
}

#[cfg(not(target_env = "msvc"))]
fn check_windows_lib() -> bool {
    false
}

#[cfg(target_env = "msvc")]
fn check_windows_lib() -> bool {
    let windows_lib: &str = &format!("{}.lib", LIBRARY);
    if let Ok(path) = env::var("PATH") {
        for p in path.split(";") {
            let path = Path::new(p).join(windows_lib);
            if path.exists() {
                println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
                println!("cargo:rustc-link-search=native={}", p);
                return true
            }
        }
    }
    false
}

fn remove_suffix(value: &mut String, suffix: &str) {
    if value.ends_with(suffix) {
        let n = value.len();
        value.truncate(n - suffix.len());
    }
}

fn extract<P: AsRef<Path>, P2: AsRef<Path>>(archive_path: P, extract_to: P2) {
    let file = File::open(archive_path).unwrap();
    let unzipped = GzDecoder::new(file).unwrap();
    let mut a = Archive::new(unzipped);
    a.unpack(extract_to).unwrap();
}

// Downloads and unpacks a prebuilt binary. Only works for certain platforms.
fn install_prebuilt() {
    // Figure out the file names.
    let os = match env::consts::OS {
        "macos" => "darwin",
        x => x,
    };
    let proc_type = if cfg!(feature = "tensorflow_gpu") {"gpu"} else {"cpu"};
    let binary_url = format!(
        "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-{}-{}-{}-{}.tar.gz",
        proc_type, os, env::consts::ARCH, VERSION);
    log_var!(binary_url);
    let short_file_name = binary_url.split("/").last().unwrap();
    let mut base_name = short_file_name.to_string();
    remove_suffix(&mut base_name, ".tar.gz");
    log_var!(base_name);
    let download_dir = match env::var("TF_RUST_DOWNLOAD_DIR") {
        Ok(s) => PathBuf::from(s),
        Err(_) => PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join("target"),
    };
    if !download_dir.exists() {
        fs::create_dir(&download_dir).unwrap();
    }
    let file_name = download_dir.join(short_file_name);
    log_var!(file_name);

    // Download the tarball.
    if !file_name.exists() {
        let f = File::create(&file_name).unwrap();
        let mut writer = BufWriter::new(f);
        let mut easy = Easy::new();
        easy.url(&binary_url).unwrap();
        easy.write_function(move |data| {
            Ok(writer.write(data).unwrap())
        }).unwrap();
        easy.perform().unwrap();

        let response_code = easy.response_code().unwrap();
        if response_code != 200 {
            panic!("Unexpected response code {} for {}", response_code, binary_url);
        }
    }

    // Extract the tarball.
    let unpacked_dir = download_dir.join(base_name);
    let lib_dir = unpacked_dir.join("lib");
    let framework_library_file = format!("lib{}.so", FRAMEWORK_LIBRARY);
    let library_file = format!("lib{}.so", LIBRARY);
    let framework_library_full_path = lib_dir.join(&framework_library_file);
    let library_full_path = lib_dir.join(&library_file);
    if !framework_library_full_path.exists() || !library_full_path.exists() {
        extract(file_name, &unpacked_dir);
    }

    println!("cargo:rustc-link-lib=dylib={}", FRAMEWORK_LIBRARY);
    println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
    let output = PathBuf::from(&get!("OUT_DIR"));
    let new_framework_library_full_path = output.join(&framework_library_file);
    if new_framework_library_full_path.exists() {
        log!("File {} already exists, deleting.", new_framework_library_full_path.display());
        std::fs::remove_file(&new_framework_library_full_path).unwrap();
    }
    let new_library_full_path = output.join(&library_file);
    if new_library_full_path.exists() {
        log!("File {} already exists, deleting.", new_library_full_path.display());
        std::fs::remove_file(&new_library_full_path).unwrap();
    }

    log!("Copying {} to {}...", library_full_path.display(), new_library_full_path.display());
    std::fs::copy(&library_full_path, &new_library_full_path).unwrap();
    log!("Copying {} to {}...", framework_library_full_path.display(), new_framework_library_full_path.display());
    std::fs::copy(&framework_library_full_path, &new_framework_library_full_path).unwrap();
    println!("cargo:rustc-link-search={}", output.display());
}

fn build_from_src() {
    let output = PathBuf::from(&get!("OUT_DIR"));
    log_var!(output);
    let source = PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join(format!("target/source-{}", TAG));
    log_var!(source);
    let lib_dir = output.join(format!("lib-{}", TAG));
    log_var!(lib_dir);
    if lib_dir.exists() {
        log!("Directory {:?} already exists", lib_dir);
    } else {
        log!("Creating directory {:?}", lib_dir);
        fs::create_dir(lib_dir.clone()).unwrap();
    }
    let framework_library_path = lib_dir.join(format!("lib{}.so", FRAMEWORK_LIBRARY));
    log_var!(framework_library_path);
    let library_path = lib_dir.join(format!("lib{}.so", LIBRARY));
    log_var!(library_path);
    if library_path.exists() && framework_library_path.exists() {
        log!("{:?} and {:?} already exist, not building", library_path, framework_library_path);
    } else {
        if let Err(e) = check_bazel() {
            println!("cargo:error=Bazel must be installed at version {} or greater. (Error: {})",
                     MIN_BAZEL,
                     e);
            process::exit(1);
        }
        let framework_target_path = &FRAMEWORK_TARGET.replace(":", "/");
        log_var!(framework_target_path);
        let target_path = &TARGET.replace(":", "/");
        log_var!(target_path);
        if !Path::new(&source.join(".git")).exists() {
            run("git", |command| {
                command.arg("clone")
                    .arg(format!("--branch={}", TAG))
                    .arg("--recursive")
                    .arg(REPOSITORY)
                    .arg(&source)
            });
        }
        // Only configure if not previously configured.  Configuring runs a
        // `bazel clean`, which we don't want, because we want to be able to
        // continue from a cancelled build.
        let configure_hint_file_pb = source.join(".rust-configured");
        let configure_hint_file = Path::new(&configure_hint_file_pb);
        if !configure_hint_file.exists() {
            run("bash",
                |command| command.current_dir(&source)
                .env("TF_NEED_CUDA", if cfg!(feature = "tensorflow_gpu") {"1"} else {"0"})
                .arg("-c")
                .arg("yes ''|./configure"));
            File::create(configure_hint_file).unwrap();
        }
        // Allows us to pass in --incompatible_load_argument_is_label=false
        // to work around https://github.com/tensorflow/tensorflow/issues/15492
        let bazel_args_string = if let Ok(args) = env::var("TF_RUST_BAZEL_OPTS") {
            args
        } else {
            "".to_string()
        };
        run("bazel", |command| {
            command.current_dir(&source)
                .arg("build")
                .arg(format!("--jobs={}", get!("NUM_JOBS")))
                .arg("--compilation_mode=opt")
                .arg("--copt=-march=native")
                .args(bazel_args_string.split_whitespace())
                .arg(TARGET)
        });
        let framework_target_bazel_bin = source.join("bazel-bin").join(framework_target_path);
        log!("Copying {:?} to {:?}", framework_target_bazel_bin, framework_library_path);
        fs::copy(framework_target_bazel_bin, framework_library_path).unwrap();
        let target_bazel_bin = source.join("bazel-bin").join(target_path);
        log!("Copying {:?} to {:?}", target_bazel_bin, library_path);
        fs::copy(target_bazel_bin, library_path).unwrap();
    }

    println!("cargo:rustc-link-lib=dylib={}", FRAMEWORK_LIBRARY);
    println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
    println!("cargo:rustc-link-search={}", lib_dir.display());
}

fn run<F>(name: &str, mut configure: F)
    where F: FnMut(&mut Command) -> &mut Command
{
    let mut command = Command::new(name);
    let configured = configure(&mut command);
    log!("Executing {:?}", configured);
    if !ok!(configured.status()).success() {
        panic!("failed to execute {:?}", configured);
    }
    log!("Command {:?} finished successfully", configured);
}

// Building TF 0.11.0rc1 with Bazel 0.3.0 gives this error when running `configure`:
//   expected ConfigurationTransition or NoneType for 'cfg' while calling label_list but got
// string instead:     data.
//       ERROR: com.google.devtools.build.lib.packages.BuildFileContainsErrorsException: error
// loading package '': Extension file 'tensorflow/tensorflow.bzl' has errors.
// And the simple solution is to require Bazel 0.3.1 or higher.
fn check_bazel() -> Result<(), Box<Error>> {
    let mut command = Command::new("bazel");
    command.arg("version");
    log!("Executing {:?}", command);
    let out = command.output()?;
    log!("Command {:?} finished successfully", command);
    let stdout = String::from_utf8(out.stdout)?;
    let mut found_version = false;
    for line in stdout.lines() {
        if line.starts_with("Build label:") {
            found_version = true;
            let mut version_str = line.split(":")
                .nth(1)
                .unwrap()
                .split(" ")
                .nth(1)
                .unwrap()
                .trim();
            if version_str.ends_with('-') {
                // hyphen is 1 byte long, so it's safe
                version_str = &version_str[..version_str.len() - 1];
            }
            let version = Version::parse(version_str)?;
            let want = Version::parse(MIN_BAZEL)?;
            if version < want {
                return Err(format!("Installed version {} is less than required version {}",
                                   version_str,
                                   MIN_BAZEL)
                    .into());
            }
        }
    }
    if !found_version {
        return Err("Did not find version number in `bazel version` output.".into());
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::sync::{Arc, Mutex};

use log::{error, info};
use serde_derive::{Deserialize, Serialize};

use crate::util;
use crate::worker;
use octobot_lib::errors::*;
use octobot_lib::http_client::HTTPClient;
use octobot_lib::metrics::Metrics;

#[derive(Serialize, Clone, PartialEq, Eq, Debug)]
pub struct SlackAttachment {
    pub text: String,
    pub title: Option<String>,
    pub title_link: Option<String>,
    pub color: Option<String>,
    pub mrkdwn_in: Option<Vec<String>>,
}

#[derive(Deserialize)]
struct SlackResponse {
    ok: bool,
    error: Option<String>,
}

impl SlackAttachment {
    pub fn new(text: &str) -> SlackAttachment {
        SlackAttachment {
            text: text.to_string(),
            title: None,
            title_link: None,
            color: None,
            mrkdwn_in: None,
        }
    }
}

pub struct SlackAttachmentBuilder {
    attachment: SlackAttachment,
}

impl SlackAttachmentBuilder {
    pub fn new(text: &str) -> SlackAttachmentBuilder {
        SlackAttachmentBuilder {
            attachment: SlackAttachment::new(text),
        }
    }

    pub fn text<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.text = value.into();
        self
    }

    pub fn markdown<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.text = value.into();
        self.attachment.mrkdwn_in = Some(vec!["text".into()]);
        self
    }

    pub fn title<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.title = Some(value.into());
        self
    }

    pub fn title_link<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.title_link = Some(value.into());
        self
    }

    pub fn color<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.color = Some(value.into());
        self
    }

    pub fn build(&self) -> SlackAttachment {
        self.attachment.clone()
    }
}

#[derive(Serialize, Clone, PartialEq)]
struct SlackMessage {
    text: String,
    attachments: Vec<SlackAttachment>,
    channel: String,
}

// the main object for sending messages to slack
struct Slack {
    client: Arc<HTTPClient>,
    recent_messages: Mutex<Vec<SlackMessage>>,
}

const TRIM_MESSAGES_AT: usize = 200;
const TRIM_MESSAGES_TO: usize = 20;

impl Slack {
    pub fn new(bot_token: String, metrics: Arc<Metrics>) -> Slack {
        let mut headers = reqwest::header::HeaderMap::new();
        headers.append(
            reqwest::header::AUTHORIZATION,
            format!("Bearer {}", bot_token).parse().unwrap(),
        );

        let client = Arc::new(
            HTTPClient::new_with_headers("https://slack.com/api", headers)
                .unwrap()
                .with_metrics(
                    metrics.slack_api_responses.clone(),
                    metrics.slack_api_duration.clone(),
                ),
        );

        Slack {
            client,
            recent_messages: Mutex::new(Vec::new()),
        }
    }

    async fn send(&self, channel: &str, msg: &str, attachments: Vec<SlackAttachment>) {
        let slack_msg = SlackMessage {
            text: msg.to_string(),
            attachments,
            channel: channel.to_string(),
        };

        if !self.is_unique(&slack_msg) {
            info!("Skipping duplicate message to {}", channel);
            return;
        }

        info!("Sending message to #{}", channel);

        let res: Result<SlackResponse> = self.client.post("/chat.postMessage", &slack_msg).await;
        match res {
            Ok(r) => {
                if r.ok {
                    info!("Successfully sent slack message")
                } else {
                    error!(
                        "Error sending slack message: {}",
                        r.error.unwrap_or_default(),
                    )
                }
            }
            Err(e) => error!("Error sending slack message: {}", e),
        }
    }

    fn is_unique(&self, req: &SlackMessage) -> bool {
        let mut recent_messages = self.recent_messages.lock().unwrap();
        util::check_unique_event(
            req.clone(),
            &mut *recent_messages,
            TRIM_MESSAGES_AT,
            TRIM_MESSAGES_TO,
        )
    }
}

#[derive(Debug, PartialEq, Clone)]
pub struct SlackRequest {
    pub channel: String,
    pub msg: String,
    pub attachments: Vec<SlackAttachment>,
}

struct Runner {
    slack: Arc<Slack>,
}

pub fn req(channel: &str, msg: &str, attachments: &[SlackAttachment]) -> SlackRequest {
    SlackRequest {
        channel: channel.into(),
        msg: msg.into(),
        attachments: attachments.into(),
    }
}

pub fn new_runner(
    bot_token: String,
    metrics: Arc<Metrics>,
) -> Arc<dyn worker::Runner<SlackRequest>> {
    Arc::new(Runner {
        slack: Arc::new(Slack::new(bot_token, metrics)),
    })
}

#[async_trait::async_trait]
impl worker::Runner<SlackRequest> for Runner {
    async fn handle(&self, req: SlackRequest) {
        self.slack
            .send(&req.channel, &req.msg, req.attachments)
            .await;
    }
}
<commit_msg>Include slack recipient in error message (#314)<commit_after>use std::sync::{Arc, Mutex};

use log::{debug, error, info};
use serde_derive::{Deserialize, Serialize};

use crate::util;
use crate::worker;
use octobot_lib::errors::*;
use octobot_lib::http_client::HTTPClient;
use octobot_lib::metrics::Metrics;

#[derive(Serialize, Clone, PartialEq, Eq, Debug)]
pub struct SlackAttachment {
    pub text: String,
    pub title: Option<String>,
    pub title_link: Option<String>,
    pub color: Option<String>,
    pub mrkdwn_in: Option<Vec<String>>,
}

#[derive(Deserialize)]
struct SlackResponse {
    ok: bool,
    error: Option<String>,
}

impl SlackAttachment {
    pub fn new(text: &str) -> SlackAttachment {
        SlackAttachment {
            text: text.to_string(),
            title: None,
            title_link: None,
            color: None,
            mrkdwn_in: None,
        }
    }
}

pub struct SlackAttachmentBuilder {
    attachment: SlackAttachment,
}

impl SlackAttachmentBuilder {
    pub fn new(text: &str) -> SlackAttachmentBuilder {
        SlackAttachmentBuilder {
            attachment: SlackAttachment::new(text),
        }
    }

    pub fn text<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.text = value.into();
        self
    }

    pub fn markdown<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.text = value.into();
        self.attachment.mrkdwn_in = Some(vec!["text".into()]);
        self
    }

    pub fn title<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.title = Some(value.into());
        self
    }

    pub fn title_link<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.title_link = Some(value.into());
        self
    }

    pub fn color<S: Into<String>>(&mut self, value: S) -> &mut SlackAttachmentBuilder {
        self.attachment.color = Some(value.into());
        self
    }

    pub fn build(&self) -> SlackAttachment {
        self.attachment.clone()
    }
}

#[derive(Serialize, Clone, PartialEq)]
struct SlackMessage {
    text: String,
    attachments: Vec<SlackAttachment>,
    channel: String,
}

// the main object for sending messages to slack
struct Slack {
    client: Arc<HTTPClient>,
    recent_messages: Mutex<Vec<SlackMessage>>,
}

const TRIM_MESSAGES_AT: usize = 200;
const TRIM_MESSAGES_TO: usize = 20;

impl Slack {
    pub fn new(bot_token: String, metrics: Arc<Metrics>) -> Slack {
        let mut headers = reqwest::header::HeaderMap::new();
        headers.append(
            reqwest::header::AUTHORIZATION,
            format!("Bearer {}", bot_token).parse().unwrap(),
        );

        let client = Arc::new(
            HTTPClient::new_with_headers("https://slack.com/api", headers)
                .unwrap()
                .with_metrics(
                    metrics.slack_api_responses.clone(),
                    metrics.slack_api_duration.clone(),
                ),
        );

        Slack {
            client,
            recent_messages: Mutex::new(Vec::new()),
        }
    }

    async fn send(&self, channel: &str, msg: &str, attachments: Vec<SlackAttachment>) {
        let slack_msg = SlackMessage {
            text: msg.to_string(),
            attachments,
            channel: channel.to_string(),
        };

        if !self.is_unique(&slack_msg) {
            info!("Skipping duplicate message to {}", channel);
            return;
        }

        debug!("Sending message to #{}", channel);

        let res: Result<SlackResponse> = self.client.post("/chat.postMessage", &slack_msg).await;
        match res {
            Ok(r) => {
                if r.ok {
                    info!("Successfully sent slack message to {}", channel)
                } else {
                    error!(
                        "Error sending slack message to {}: {}",
                        channel,
                        r.error.unwrap_or_default(),
                    )
                }
            }
            Err(e) => error!("Error sending slack message to {}: {}", channel, e),
        }
    }

    fn is_unique(&self, req: &SlackMessage) -> bool {
        let mut recent_messages = self.recent_messages.lock().unwrap();
        util::check_unique_event(
            req.clone(),
            &mut *recent_messages,
            TRIM_MESSAGES_AT,
            TRIM_MESSAGES_TO,
        )
    }
}

#[derive(Debug, PartialEq, Clone)]
pub struct SlackRequest {
    pub channel: String,
    pub msg: String,
    pub attachments: Vec<SlackAttachment>,
}

struct Runner {
    slack: Arc<Slack>,
}

pub fn req(channel: &str, msg: &str, attachments: &[SlackAttachment]) -> SlackRequest {
    SlackRequest {
        channel: channel.into(),
        msg: msg.into(),
        attachments: attachments.into(),
    }
}

pub fn new_runner(
    bot_token: String,
    metrics: Arc<Metrics>,
) -> Arc<dyn worker::Runner<SlackRequest>> {
    Arc::new(Runner {
        slack: Arc::new(Slack::new(bot_token, metrics)),
    })
}

#[async_trait::async_trait]
impl worker::Runner<SlackRequest> for Runner {
    async fn handle(&self, req: SlackRequest) {
        self.slack
            .send(&req.channel, &req.msg, req.attachments)
            .await;
    }
}
<|endoftext|>


<commit_before>//! Compiler plugin for Rust-PHF
//!
//! See the documentation for the `phf` crate for more details.
#![crate_id="github.com/sfackler/rust-phf/phf_mac"]
#![crate_type="dylib"]
#![doc(html_root_url="http://www.rust-ci.org/sfackler/rust-phf/doc")]
#![feature(managed_boxes, macro_registrar, quote)]

extern crate collections;
extern crate rand;
extern crate syntax;
extern crate time;
extern crate phf;

use collections::HashMap;
use std::os;
use syntax::ast;
use syntax::ast::{Name, TokenTree, LitStr, MutImmutable, Expr, ExprVec, ExprLit};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MRExpr,
                        NormalTT,
                        BasicMacroExpander};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF, FAT_ARROW};
use rand::{Rng, SeedableRng, XorShiftRng};

static DEFAULT_LAMBDA: uint = 5;

static FIXED_SEED: [u32, ..4] = [3141592653, 589793238, 462643383, 2795028841];

#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("phf_map"),
             NormalTT(~BasicMacroExpander {
                expander: expand_mphf_map,
                span: None
             },
             None));
}

struct Entry {
    key_str: InternedString,
    key: @Expr,
    value: @Expr
}

fn expand_mphf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
    let entries = match parse_entries(cx, tts) {
        Some(entries) => entries,
        None => return MacResult::dummy_expr(sp)
    };

    if has_duplicates(cx, sp, entries.as_slice()) {
        return MacResult::dummy_expr(sp);
    }

    let mut rng: XorShiftRng = SeedableRng::from_seed(FIXED_SEED);
    let start = time::precise_time_s();
    let state;
    loop {
        match generate_hash(entries.as_slice(), &mut rng) {
            Some(s) => {
                state = s;
                break;
            }
            None => {}
        }
    }
    let time = time::precise_time_s() - start;
    if os::getenv("PHF_STATS").is_some() {
        cx.span_note(sp, format!("PHF generation took {} seconds", time));
    }

    create_map(cx, sp, entries, state)
}

fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                Vec::from_slice(tts));
    let mut entries = Vec::new();

    let mut bad = false;
    while parser.token != EOF {
        let key = cx.expand_expr(parser.parse_expr());

        let key_str = match key.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(key.span, "expected string literal");
                        bad = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(key.span, "expected string literal");
                bad = true;
                InternedString::new("")
            }
        };

        if !parser.eat(&FAT_ARROW) {
            cx.span_err(parser.span, "expected `=>`");
            return None;
        }

        let value = parser.parse_expr();

        entries.push(Entry {
            key_str: key_str,
            key: key,
            value: value
        });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if entries.len() > phf::MAX_SIZE {
        cx.span_err(parser.span,
                    format!("maps with more than {} entries are not supported",
                            phf::MAX_SIZE));
        return None;
    }

    if bad {
        return None;
    }

    Some(entries)
}

fn has_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
    let mut dups = false;
    let mut strings = HashMap::new();
    for entry in entries.iter() {
        strings.insert_or_update_with(entry.key_str.clone(), (entry, true),
                                      |_, &(orig, ref mut first)| {
                if *first {
                    cx.span_err(sp, format!("duplicate key \"{}\"",
                                            entry.key_str));
                    cx.span_note(orig.key.span, "one occurrence here");
                    *first = false;
                }
                cx.span_note(entry.key.span, "one occurrence here");
                dups = true;
            });
    }

    dups
}

struct HashState {
    k1: u64,
    k2: u64,
    disps: Vec<(uint, uint)>,
    map: Vec<uint>,
}

fn generate_hash(entries: &[Entry], rng: &mut XorShiftRng)
                 -> Option<HashState> {
    struct Bucket {
        idx: uint,
        keys: Vec<uint>,
    }

    struct Hashes {
        g: uint,
        f1: uint,
        f2: uint,
    }

    let k1 = rng.gen();
    let k2 = rng.gen();

    let hashes: Vec<Hashes> = entries.iter().map(|entry| {
            let (g, f1, f2) = phf::hash(entry.key_str.get(), k1, k2);
            Hashes {
                g: g,
                f1: f1,
                f2: f2
            }
        }).collect();

    let buckets_len = (entries.len() + DEFAULT_LAMBDA - 1) / DEFAULT_LAMBDA;
    let mut buckets = Vec::from_fn(buckets_len,
                                   |i| Bucket { idx: i, keys: Vec::new() });

    for (i, hash) in hashes.iter().enumerate() {
        buckets.get_mut(hash.g % buckets_len).keys.push(i);
    }

    // Sort descending
    buckets.sort_by(|a, b| b.keys.len().cmp(&a.keys.len()));

    let table_len = entries.len();
    let mut map = Vec::from_elem(table_len, None);
    let mut disps = Vec::from_elem(buckets_len, (0u, 0u));
    let mut try_map = HashMap::new();
    'buckets: for bucket in buckets.iter() {
        for d1 in range(0, table_len) {
            'disps_l: for d2 in range(0, table_len) {
                try_map.clear();
                for &key in bucket.keys.iter() {
                    let idx = phf::displace(hashes.get(key).f1,
                                            hashes.get(key).f2,
                                            d1,
                                            d2) % table_len;
                    if map.get(idx).is_some() || try_map.find(&idx).is_some() {
                        continue 'disps_l;
                    }
                    try_map.insert(idx, key);
                }

                // We've picked a good set of disps
                *disps.get_mut(bucket.idx) = (d1, d2);
                for (&idx, &key) in try_map.iter() {
                    *map.get_mut(idx) = Some(key);
                }
                continue 'buckets;
            }
        }

        // Unable to find displacements for a bucket
        return None;
    }

    let map = map.move_iter().map(|i| i.expect("should have a value"))
            .collect();

    Some(HashState {
        k1: k1,
        k2: k2,
        disps: disps,
        map: map,
    })
}

fn create_map(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
              -> MacResult {
    let disps = state.disps.iter().map(|&(d1, d2)| {
            quote_expr!(&*cx, ($d1, $d2))
        }).collect();
    let disps = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(disps, MutImmutable),
        span: sp,
    };
    let entries = state.map.iter().map(|&idx| {
            let &Entry { key, value, .. } = entries.get(idx);
            quote_expr!(&*cx, ($key, $value))
        }).collect();
    let entries = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(entries, MutImmutable),
        span: sp,
    };

    let k1 = state.k1;
    let k2 = state.k2;
    MRExpr(quote_expr!(cx, PhfMap {
        k1: $k1,
        k2: $k2,
        disps: &'static $disps,
        entries: &'static $entries,
    }))
}
<commit_msg>Style cleanup<commit_after>//! Compiler plugin for Rust-PHF
//!
//! See the documentation for the `phf` crate for more details.
#![crate_id="github.com/sfackler/rust-phf/phf_mac"]
#![crate_type="dylib"]
#![doc(html_root_url="http://www.rust-ci.org/sfackler/rust-phf/doc")]
#![feature(managed_boxes, macro_registrar, quote)]

extern crate collections;
extern crate rand;
extern crate syntax;
extern crate time;
extern crate phf;

use collections::HashMap;
use std::os;
use syntax::ast;
use syntax::ast::{Name, TokenTree, LitStr, MutImmutable, Expr, ExprVec, ExprLit};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MRExpr,
                        NormalTT,
                        BasicMacroExpander};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF, FAT_ARROW};
use rand::{Rng, SeedableRng, XorShiftRng};

static DEFAULT_LAMBDA: uint = 5;

static FIXED_SEED: [u32, ..4] = [3141592653, 589793238, 462643383, 2795028841];

#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("phf_map"),
             NormalTT(~BasicMacroExpander {
                expander: expand_phf_map,
                span: None
             },
             None));
}

struct Entry {
    key_str: InternedString,
    key: @Expr,
    value: @Expr
}

fn expand_phf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
    let entries = match parse_entries(cx, tts) {
        Some(entries) => entries,
        None => return MacResult::dummy_expr(sp)
    };

    if has_duplicates(cx, sp, entries.as_slice()) {
        return MacResult::dummy_expr(sp);
    }

    let mut rng: XorShiftRng = SeedableRng::from_seed(FIXED_SEED);
    let start = time::precise_time_s();
    let state;
    loop {
        match generate_hash(entries.as_slice(), &mut rng) {
            Some(s) => {
                state = s;
                break;
            }
            None => {}
        }
    }
    let time = time::precise_time_s() - start;
    if os::getenv("PHF_STATS").is_some() {
        cx.span_note(sp, format!("PHF generation took {} seconds", time));
    }

    create_map(cx, sp, entries, state)
}

fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                Vec::from_slice(tts));
    let mut entries = Vec::new();

    let mut bad = false;
    while parser.token != EOF {
        let key = cx.expand_expr(parser.parse_expr());

        let key_str = match key.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(key.span, "expected string literal");
                        bad = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(key.span, "expected string literal");
                bad = true;
                InternedString::new("")
            }
        };

        if !parser.eat(&FAT_ARROW) {
            cx.span_err(parser.span, "expected `=>`");
            return None;
        }

        let value = parser.parse_expr();

        entries.push(Entry {
            key_str: key_str,
            key: key,
            value: value
        });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if entries.len() > phf::MAX_SIZE {
        cx.span_err(parser.span,
                    format!("maps with more than {} entries are not supported",
                            phf::MAX_SIZE));
        return None;
    }

    if bad {
        return None;
    }

    Some(entries)
}

fn has_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
    let mut dups = false;
    let mut strings = HashMap::new();
    for entry in entries.iter() {
        strings.insert_or_update_with(entry.key_str.clone(), (entry, true),
                                      |_, &(orig, ref mut first)| {
            if *first {
                cx.span_err(sp, format!("duplicate key \"{}\"",
                                        entry.key_str));
                cx.span_note(orig.key.span, "one occurrence here");
                *first = false;
            }
            cx.span_note(entry.key.span, "one occurrence here");
            dups = true;
        });
    }

    dups
}

struct HashState {
    k1: u64,
    k2: u64,
    disps: Vec<(uint, uint)>,
    map: Vec<uint>,
}

fn generate_hash(entries: &[Entry], rng: &mut XorShiftRng)
                 -> Option<HashState> {
    struct Bucket {
        idx: uint,
        keys: Vec<uint>,
    }

    struct Hashes {
        g: uint,
        f1: uint,
        f2: uint,
    }

    let k1 = rng.gen();
    let k2 = rng.gen();

    let hashes: Vec<Hashes> = entries.iter().map(|entry| {
        let (g, f1, f2) = phf::hash(entry.key_str.get(), k1, k2);
        Hashes {
            g: g,
            f1: f1,
            f2: f2
        }
    }).collect();

    let buckets_len = (entries.len() + DEFAULT_LAMBDA - 1) / DEFAULT_LAMBDA;
    let mut buckets = Vec::from_fn(buckets_len,
                                   |i| Bucket { idx: i, keys: Vec::new() });

    for (i, hash) in hashes.iter().enumerate() {
        buckets.get_mut(hash.g % buckets_len).keys.push(i);
    }

    // Sort descending
    buckets.sort_by(|a, b| b.keys.len().cmp(&a.keys.len()));

    let table_len = entries.len();
    let mut map = Vec::from_elem(table_len, None);
    let mut disps = Vec::from_elem(buckets_len, (0u, 0u));
    let mut try_map = HashMap::new();
    'buckets: for bucket in buckets.iter() {
        for d1 in range(0, table_len) {
            'disps_l: for d2 in range(0, table_len) {
                try_map.clear();
                for &key in bucket.keys.iter() {
                    let idx = phf::displace(hashes.get(key).f1,
                                            hashes.get(key).f2,
                                            d1,
                                            d2) % table_len;
                    if map.get(idx).is_some() || try_map.find(&idx).is_some() {
                        continue 'disps_l;
                    }
                    try_map.insert(idx, key);
                }

                // We've picked a good set of disps
                *disps.get_mut(bucket.idx) = (d1, d2);
                for (&idx, &key) in try_map.iter() {
                    *map.get_mut(idx) = Some(key);
                }
                continue 'buckets;
            }
        }

        // Unable to find displacements for a bucket
        return None;
    }

    let map = map.move_iter().map(|i| i.expect("should have a value"))
            .collect();

    Some(HashState {
        k1: k1,
        k2: k2,
        disps: disps,
        map: map,
    })
}

fn create_map(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
              -> MacResult {
    let disps = state.disps.iter().map(|&(d1, d2)| {
            quote_expr!(&*cx, ($d1, $d2))
        }).collect();
    let disps = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(disps, MutImmutable),
        span: sp,
    };
    let entries = state.map.iter().map(|&idx| {
            let &Entry { key, value, .. } = entries.get(idx);
            quote_expr!(&*cx, ($key, $value))
        }).collect();
    let entries = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(entries, MutImmutable),
        span: sp,
    };

    let k1 = state.k1;
    let k2 = state.k2;
    MRExpr(quote_expr!(cx, PhfMap {
        k1: $k1,
        k2: $k2,
        disps: &'static $disps,
        entries: &'static $entries,
    }))
}
<|endoftext|>


<commit_before>///
/// This example parses, sorts and groups the iris dataset
/// and does some simple manipulations.
///
/// Iterators and itertools functionality are used throughout.
///
///

extern crate itertools;

use itertools::Itertools;
use std::str::FromStr;
use std::collections::HashMap;
use std::num::ParseFloatError;

static DATA: &'static str = include_str!("iris.data");

#[derive(Clone, Debug)]
struct Iris {
    name: String,
    data: [f32; 4],
}

#[derive(Clone, Debug)]
enum ParseError {
    Numeric(ParseFloatError),
    Other(&'static str),
}

impl From<ParseFloatError> for ParseError {
    fn from(err: ParseFloatError) -> Self {
        ParseError::Numeric(err)
    }
}

/// Parse an Iris from a comma-separated line
impl FromStr for Iris {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut iris = Iris { name: "".into(), data: [0.; 4] };
        let mut parts = s.split(",").map(str::trim);

        // using Iterator::by_ref()
        for (index, part) in parts.by_ref().take(4).enumerate() {
            iris.data[index] = try!(part.parse::<f32>());
        }
        if let Some(name) = parts.next() {
            iris.name = name.into();
        } else {
            return Err(ParseError::Other("Missing name"))
        }
        Ok(iris)
    }
}

fn main() {
    // using Itertools::fold_results to create the result of parsing
    let irises = DATA.lines()
                     .map(str::parse)
                     .fold_results(Vec::new(), |mut v, iris: Iris| {
                         v.push(iris);
                         v
                     });
    let mut irises = match irises {
        Err(e) => {
            println!("Error parsing: {:?}", e);
            std::process::exit(1);
        }
        Ok(data) => data,
    };

    // Sort them and group them
    irises.sort_by(|a, b| Ord::cmp(&a.name, &b.name));

    // using Iterator::cycle()
    let mut plot_symbols = "+ox".chars().cycle();
    let mut symbolmap = HashMap::new();

    // using Itertools::group_by_lazy
    for (species, species_group) in &irises.iter().group_by_lazy(|iris| &iris.name) {
        // assign a plot symbol
        symbolmap.entry(species).or_insert_with(|| {
            plot_symbols.next().unwrap()
        });
        println!("{} (symbol={})", species, symbolmap[species]);

        for iris in species_group {
            // using Itertools::format for lazy formatting
            println!("{}",
                     iris.data.iter()
                     .format(", ", |elt, f| f(&format_args!("{:>3.1}", elt))));
        }

    }

    // Look at all combinations of the four columns
    //
    // See https://en.wikipedia.org/wiki/Iris_flower_data_set
    //
    // using Itertools::combinations
    for (a, b) in (0..4).combinations() {
        println!("Column {} vs {}:", a, b);
        let n = 30;
        let mut plot = vec![' '; n * n];

        // using Itertools::fold1
        let min_max = |vec: &[Iris], col| {
            vec.iter()
               .map(move |iris| iris.data[col])
               .map(|x| (x, x))
               .fold1(|(min, max), (elt, _)|
                   (f32::min(min, elt), f32::max(max, elt))
               ).expect("Can't find min/max of empty iterator")
        };
        let (min_x, max_x) = min_max(&irises, a);
        let (min_y, max_y) = min_max(&irises, b);

        for (symbol, x, y) in irises.iter()
            .map(|ir| (symbolmap[&ir.name], ir.data[a], ir.data[b]))
        {
            // round to the grid
            let ix = ((x - min_x) / (max_x - min_x) * ((n - 1) as f32)) as usize;
            let iy = ((y - min_y) / (max_y - min_y) * ((n - 1) as f32)) as usize;
            let iy = n - 1 - iy; // reverse y axis' direction

            plot[n * iy + ix] = symbol;
        }

        // render plot
        //
        // using Itertools::join
        for line in plot.chunks(n) {
            println!("{}", line.iter().join(" "))
        }
    }
}
<commit_msg>examples: Small improvements to iris<commit_after>///
/// This example parses, sorts and groups the iris dataset
/// and does some simple manipulations.
///
/// Iterators and itertools functionality are used throughout.
///
///

extern crate itertools;

use itertools::Itertools;
use std::collections::HashMap;
use std::iter::repeat;
use std::num::ParseFloatError;
use std::str::FromStr;

static DATA: &'static str = include_str!("iris.data");

#[derive(Clone, Debug)]
struct Iris {
    name: String,
    data: [f32; 4],
}

#[derive(Clone, Debug)]
enum ParseError {
    Numeric(ParseFloatError),
    Other(&'static str),
}

impl From<ParseFloatError> for ParseError {
    fn from(err: ParseFloatError) -> Self {
        ParseError::Numeric(err)
    }
}

/// Parse an Iris from a comma-separated line
impl FromStr for Iris {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut iris = Iris { name: "".into(), data: [0.; 4] };
        let mut parts = s.split(",").map(str::trim);

        // using Iterator::by_ref()
        for (index, part) in parts.by_ref().take(4).enumerate() {
            iris.data[index] = try!(part.parse::<f32>());
        }
        if let Some(name) = parts.next() {
            iris.name = name.into();
        } else {
            return Err(ParseError::Other("Missing name"))
        }
        Ok(iris)
    }
}

fn main() {
    // using Itertools::fold_results to create the result of parsing
    let irises = DATA.lines()
                     .map(str::parse)
                     .fold_results(Vec::new(), |mut v, iris: Iris| {
                         v.push(iris);
                         v
                     });
    let mut irises = match irises {
        Err(e) => {
            println!("Error parsing: {:?}", e);
            std::process::exit(1);
        }
        Ok(data) => data,
    };

    // Sort them and group them
    irises.sort_by(|a, b| Ord::cmp(&a.name, &b.name));

    // using Iterator::cycle()
    let mut plot_symbols = "+ox".chars().cycle();
    let mut symbolmap = HashMap::new();

    // using Itertools::group_by_lazy
    for (species, species_group) in &irises.iter().group_by_lazy(|iris| &iris.name) {
        // assign a plot symbol
        symbolmap.entry(species).or_insert_with(|| {
            plot_symbols.next().unwrap()
        });
        println!("{} (symbol={})", species, symbolmap[species]);

        for iris in species_group {
            // using Itertools::format for lazy formatting
            println!("{}",
                     iris.data.iter()
                     .format(", ", |elt, f| f(&format_args!("{:>3.1}", elt))));
        }

    }

    // Look at all combinations of the four columns
    //
    // See https://en.wikipedia.org/wiki/Iris_flower_data_set
    //
    let n = 30; // plot size
    let mut plot = vec![' '; n * n];

    // using Itertools::combinations
    for (a, b) in (0..4).combinations() {
        println!("Column {} vs {}:", a, b);

        // Clear plot
        //
        // using std::iter::repeat;
        // using Itertools::set_from
        plot.iter_mut().set_from(repeat(' '));

        // using Itertools::fold1
        let min_max = |data: &[Iris], col| {
            data.iter()
                .map(|iris| iris.data[col])
                .map(|x| (x, x))
                .fold1(|(min, max), (elt, _)|
                    (f32::min(min, elt), f32::max(max, elt))
                ).expect("Can't find min/max of empty iterator")
        };
        let (min_x, max_x) = min_max(&irises, a);
        let (min_y, max_y) = min_max(&irises, b);

        // Plot the data points
        let round_to_grid = |x, min, max| ((x - min) / (max - min) * ((n - 1) as f32)) as usize;
        let flip = |ix| n - 1 - ix; // reverse axis direction

        for iris in &irises {
            let ix = round_to_grid(iris.data[a], min_x, max_x);
            let iy = flip(round_to_grid(iris.data[b], min_y, max_y));
            plot[n * iy + ix] = symbolmap[&iris.name];
        }

        // render plot
        //
        // using Itertools::join
        for line in plot.chunks(n) {
            println!("{}", line.iter().join(" "))
        }
    }
}
<|endoftext|>


<commit_before>extern crate polydraw;

use polydraw::{Application, Renderer, Frame};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

struct Poly {
   points: Vec<Point>,
   color: RGB,
}

impl Poly {
   fn min_max_y_index(&self) -> (usize, usize) {
      let (first, rest) = self.points.split_first().unwrap();

      let mut min_y = first.y;
      let mut max_y = min_y;
      let mut min_index: usize = 0;
      let mut max_index: usize = 0;

      for (index, point) in rest.iter().enumerate() {
         if point.y < min_y {
            min_index = index + 1;
            min_y = point.y;
         } else if point.y > max_y {
            max_index = index + 1;
            max_y = point.y;
         }
      }

      (min_index, max_index)
   }
}

struct Scene {
   polys: Vec<Poly>,
}

impl Scene {
   #[inline]
   fn new() -> Self {
      Scene {
         polys: Vec::new(),
      }
   }

   #[inline]
   pub fn push(&mut self, poly: Poly) {
      self.polys.push(poly);
   }
}

struct DevRenderer {
   scene: Scene,
}

impl DevRenderer {
   fn new(scene: Scene) -> Self {
      DevRenderer {
         scene: scene,
      }
   }

   fn _render_poly(&self, frame: &mut Frame, poly: &Poly) {
      let (min_y_index, max_y_index) = poly.min_max_y_index();

      println!("MIN [{:?}] = {:?}", min_y_index, poly.points[min_y_index]);

      println!("MAX [{:?}] = {:?}", max_y_index, poly.points[max_y_index]);

      let points_len = poly.points.len();
      let count = if max_y_index > min_y_index {
         max_y_index - min_y_index
      } else {
         points_len - max_y_index + min_y_index - 1
      };

      println!("COUNT = {}", count);

      let mut index = min_y_index;
      for _ in 0..count {
         if index >= points_len {
            index -= points_len;
         }

         println!("[{}] = {:?}", index, poly.points[index]);

         index += 1;
      }

      for point in poly.points.iter() {
         frame.put_pixel(point.x as i32, point.y as i32, &poly.color)
      }
   }
}

impl Renderer for DevRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      for poly in self.scene.polys.iter() {
         self._render_poly(frame, poly);
      }

      panic!("Bye!");
   }
}

fn main() {
   let mut scene = Scene::new();

   let poly_a = Poly {
      points: vec![
         Point::new(1100, 400),
         Point::new(500, 700),
         Point::new(200, 200),
      ],
      color: RGB::new(34, 78, 29),
   };

   let poly_b = Poly {
      points: vec![
         Point::new(100, 500),
         Point::new(700, 100),
         Point::new(900, 200),
         Point::new(900, 1000),
      ],
      color: RGB::new(128, 59, 89),
   };

   scene.push(poly_a);
   scene.push(poly_b);

   let mut renderer = DevRenderer::new(scene);

   Application::new()
      .renderer(&mut renderer)
      .title("Dev Rasterizer")
      .size(800, 450)
      .run();
}

<commit_msg>Tracing the polygons from both sides<commit_after>extern crate polydraw;

use polydraw::{Application, Renderer, Frame};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

struct Poly {
   points: Vec<Point>,
   color: RGB,
}

impl Poly {
   fn min_max_y_index(&self) -> (usize, usize) {
      let (first, rest) = self.points.split_first().unwrap();

      let mut min_y = first.y;
      let mut max_y = min_y;
      let mut min_index: usize = 0;
      let mut max_index: usize = 0;

      for (index, point) in rest.iter().enumerate() {
         if point.y < min_y {
            min_index = index + 1;
            min_y = point.y;
         } else if point.y > max_y {
            max_index = index + 1;
            max_y = point.y;
         }
      }

      (min_index, max_index)
   }
}

struct Scene {
   polys: Vec<Poly>,
}

impl Scene {
   #[inline]
   fn new() -> Self {
      Scene {
         polys: Vec::new(),
      }
   }

   #[inline]
   pub fn push(&mut self, poly: Poly) {
      self.polys.push(poly);
   }
}

struct DevRenderer {
   scene: Scene,
}

impl DevRenderer {
   fn new(scene: Scene) -> Self {
      DevRenderer {
         scene: scene,
      }
   }

   fn _render_poly(&self, frame: &mut Frame, poly: &Poly) {
      let (min_y_index, max_y_index) = poly.min_max_y_index();

      println!("MIN [{:?}] = {:?}", min_y_index, poly.points[min_y_index]);

      println!("MAX [{:?}] = {:?}", max_y_index, poly.points[max_y_index]);

      let points_len = poly.points.len();
      let clockwise_count = if max_y_index > min_y_index {
         max_y_index - min_y_index + 1
      } else {
         points_len - min_y_index + max_y_index + 1
      };

      let counter_count = points_len - clockwise_count + 2;

      println!("CLOCKWISE COUNT = {}", clockwise_count);

      let mut index = min_y_index;
      for _ in 0..clockwise_count {
         if index == points_len {
            index = 0;
         }

         println!("[{}] = {:?}", index, poly.points[index]);

         index += 1;
      }

      println!("COUNTER COUNT = {}", counter_count);

      let mut index = min_y_index;
      for _ in 0..counter_count {
         println!("[{}] = {:?}", index, poly.points[index]);

         if index == 0 {
            index = points_len - 1;
         } else {
            index -= 1;
         }
      }

      for point in poly.points.iter() {
         frame.put_pixel(point.x as i32, point.y as i32, &poly.color)
      }
   }
}

impl Renderer for DevRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      for poly in self.scene.polys.iter() {
         self._render_poly(frame, poly);
      }

      panic!("Bye!");
   }
}

fn main() {
   let mut scene = Scene::new();

   let poly_a = Poly {
      points: vec![
         Point::new(1100, 400),
         Point::new(500, 700),
         Point::new(200, 200),
      ],
      color: RGB::new(34, 78, 29),
   };

   let poly_b = Poly {
      points: vec![
         Point::new(100, 500),
         Point::new(700, 100),
         Point::new(900, 200),
         Point::new(900, 1000),
      ],
      color: RGB::new(128, 59, 89),
   };

   scene.push(poly_a);
   scene.push(poly_b);

   let mut renderer = DevRenderer::new(scene);

   Application::new()
      .renderer(&mut renderer)
      .title("Dev Rasterizer")
      .size(800, 450)
      .run();
}

<|endoftext|>


<commit_before>use std::mem::MaybeUninit;
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};

pub unsafe fn new_date(env: Env, value: f64) -> Local {
    let mut local = MaybeUninit::zeroed();
    let status = napi::create_date(env, value, local.as_mut_ptr());
    assert_eq!(status, napi::Status::Ok);
    local.assume_init()
}

pub unsafe fn value(env: Env, p: Local) -> f64 {
    let mut value = 0.0;
    let status = napi::get_date_value(env, p, &mut value as *mut _);
    assert_eq!(status, napi::Status::Ok);
    return value;
}
<commit_msg>feat: add safety doc comments to neon runtime date api<commit_after>use std::mem::MaybeUninit;
use crate::napi::bindings as napi;
use crate::raw::{Env, Local};

/// Create a new date object
///
/// # Safety
///
/// `env` is a raw pointer. Please ensure it points to a napi_env that is valid for the current context.
pub unsafe fn new_date(env: Env, value: f64) -> Local {
    let mut local = MaybeUninit::zeroed();
    let status = napi::create_date(env, value, local.as_mut_ptr());
    assert_eq!(status, napi::Status::Ok);
    local.assume_init()
}

/// Get the value of a date object
///
/// # Safety
///
/// `env` is a raw pointer. Please ensure it points to a napi_env that is valid for the current context.
pub unsafe fn value(env: Env, p: Local) -> f64 {
    let mut value = 0.0;
    let status = napi::get_date_value(env, p, &mut value as *mut _);
    assert_eq!(status, napi::Status::Ok);
    return value;
}
<|endoftext|>


<commit_before>extern crate gcc;

use std::env;
use std::path::Path;
use std::process::Command;

struct Sources {
    files: Vec<&'static str>,
}

impl Sources {
    fn new() -> Sources {
        Sources { files: Vec::new() }
    }

    fn extend(&mut self, sources: &[&'static str]) {
        self.files.extend(sources);
    }
}

fn main() {
    if !Path::new("compiler-rt/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    let target = env::var("TARGET").expect("TARGET was not set");
    let cfg = &mut gcc::Config::new();

    if target.contains("msvc") {
        cfg.define("__func__", Some("__FUNCTION__"));
    } else {
        cfg.flag("-fno-builtin");
        cfg.flag("-fomit-frame-pointer");
        cfg.flag("-ffreestanding");
    }

    let mut sources = Sources::new();

    sources.extend(&[
        "muldi3.c",
        "mulosi4.c",
        "mulodi4.c",
        "divsi3.c",
        "divdi3.c",
        "modsi3.c",
        "moddi3.c",
        "divmodsi4.c",
        "divmoddi4.c",
        "ashldi3.c",
        "ashrdi3.c",
        "lshrdi3.c",
        "udivdi3.c",
        "umoddi3.c",
        "udivmoddi4.c",
        "udivsi3.c",
        "umodsi3.c",
        "udivmodsi4.c",
        "adddf3.c",
        "addsf3.c",
        "powidf2.c",
        "powisf2.c",
    ]);

    for src in sources.files.iter() {
        cfg.file(Path::new("compiler-rt/lib/builtins").join(src));
    }

    cfg.compile("libcompiler-rt.a");
}
<commit_msg>Auto merge of #138 - rust-lang-nursery:cdylib-rebuild, r=japaric<commit_after>extern crate gcc;

use std::env;
use std::path::Path;
use std::process::Command;

struct Sources {
    files: Vec<&'static str>,
}

impl Sources {
    fn new() -> Sources {
        Sources { files: Vec::new() }
    }

    fn extend(&mut self, sources: &[&'static str]) {
        self.files.extend(sources);
    }
}

fn main() {
    if !Path::new("compiler-rt/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    let target = env::var("TARGET").expect("TARGET was not set");
    let cfg = &mut gcc::Config::new();

    if target.contains("msvc") {
        cfg.define("__func__", Some("__FUNCTION__"));
    } else {
        cfg.flag("-fno-builtin");
        cfg.flag("-fomit-frame-pointer");
        cfg.flag("-ffreestanding");
    }

    let mut sources = Sources::new();

    sources.extend(&[
        "muldi3.c",
        "mulosi4.c",
        "mulodi4.c",
        "divsi3.c",
        "divdi3.c",
        "modsi3.c",
        "moddi3.c",
        "divmodsi4.c",
        "divmoddi4.c",
        "ashldi3.c",
        "ashrdi3.c",
        "lshrdi3.c",
        "udivdi3.c",
        "umoddi3.c",
        "udivmoddi4.c",
        "udivsi3.c",
        "umodsi3.c",
        "udivmodsi4.c",
        "adddf3.c",
        "addsf3.c",
        "powidf2.c",
        "powisf2.c",
    ]);

    let builtins_dir = Path::new("compiler-rt/lib/builtins");
    for src in sources.files.iter() {
        cfg.file(builtins_dir.join(src));
    }

    cfg.compile("libcompiler-rt.a");

    println!("cargo:rerun-if-changed=build.rs");

    for source in sources.files.iter() {
        println!("cargo:rerun-if-changed={}", builtins_dir.join(source).display());
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use net::hsts::{HstsEntry, HstsList};
use net_traits::IncludeSubdomains;
use std::collections::HashMap;
use time;

#[test]
fn test_hsts_entry_is_not_expired_when_it_has_no_timestamp() {
    let entry = HstsEntry {
        host: "mozilla.org".to_owned(),
        include_subdomains: false,
        max_age: Some(20),
        timestamp: None
    };

    assert!(!entry.is_expired());
}

#[test]
fn test_hsts_entry_is_not_expired_when_it_has_no_max_age() {
    let entry = HstsEntry {
        host: "mozilla.org".to_owned(),
        include_subdomains: false,
        max_age: None,
        timestamp: Some(time::get_time().sec as u64)
    };

    assert!(!entry.is_expired());
}

#[test]
fn test_hsts_entry_is_expired_when_it_has_reached_its_max_age() {
    let entry = HstsEntry {
        host: "mozilla.org".to_owned(),
        include_subdomains: false,
        max_age: Some(10),
        timestamp: Some(time::get_time().sec as u64 - 20u64)
    };

    assert!(entry.is_expired());
}

#[test]
fn test_hsts_entry_cant_be_created_with_ipv6_address_as_host() {
    let entry = HstsEntry::new(
        "2001:0db8:0000:0000:0000:ff00:0042:8329".to_owned(), IncludeSubdomains::NotIncluded, None
    );

    assert!(entry.is_none(), "able to create HstsEntry with IPv6 host");
}

#[test]
fn test_hsts_entry_cant_be_created_with_ipv4_address_as_host() {
    let entry = HstsEntry::new(
        "4.4.4.4".to_owned(), IncludeSubdomains::NotIncluded, None
    );

    assert!(entry.is_none(), "able to create HstsEntry with IPv4 host");
}

#[test]
fn test_base_domain_in_entries_map() {
    let entries_map = HashMap::new();

    let mut list = HstsList {
        entries_map: entries_map
    };

    list.push(HstsEntry::new("servo.mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());
    list.push(HstsEntry::new("firefox.mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());
    list.push(HstsEntry::new("bugzilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());

    assert_eq!(list.entries_map.len(), 2);
    assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 2);
}

#[test]
fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::NotIncluded, Some(500000u64)).unwrap()));
    let mut list = HstsList {
        entries_map: entries_map
    };

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, Some(0)).unwrap());

    assert!(list.is_host_secure("mozilla.org") == false)
}

#[test]
fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_already_matched() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()));
    let mut list = HstsList {
        entries_map: entries_map
    };

    list.push(HstsEntry::new("servo.mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());

    assert!(list.entries_map.get("mozilla.org").unwrap().len() == 1)
}

#[test]
fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_subdomains() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()));
    let mut list = HstsList {
        entries_map: entries_map
    };

    assert!(list.is_host_secure("servo.mozilla.org"));

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());

    assert!(!list.is_host_secure("servo.mozilla.org"))
}

#[test]
fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::NotIncluded, None).unwrap()));
    let mut list = HstsList {
        entries_map: entries_map
    };

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());

    assert!(list.entries_map.get("mozilla.org").unwrap().len() == 1)
}

#[test]
fn test_push_multiple_entrie_to_hsts_list_should_add_them_all() {
    let mut list = HstsList {
        entries_map: HashMap::new()
    };

    assert!(!list.is_host_secure("mozilla.org"));
    assert!(!list.is_host_secure("bugzilla.org"));

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::Included, None).unwrap());
    list.push(HstsEntry::new("bugzilla.org".to_owned(),
        IncludeSubdomains::Included, None).unwrap());

    assert!(list.is_host_secure("mozilla.org"));
    assert!(list.is_host_secure("bugzilla.org"));
}

#[test]
fn test_push_entry_to_hsts_list_should_add_an_entry() {
    let mut list = HstsList {
        entries_map: HashMap::new()
    };

    assert!(!list.is_host_secure("mozilla.org"));

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::Included, None).unwrap());

    assert!(list.is_host_secure("mozilla.org"));
}

#[test]
fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
    let mock_preload_content = b"derp";
    assert!(HstsList::from_preload(mock_preload_content).is_none(), "invalid preload list should not have parsed")
}

#[test]
fn test_parse_hsts_preload_should_return_none_when_json_contains_no_entries_map_key() {
    let mock_preload_content = b"{\"nothing\": \"to see here\"}";
    assert!(HstsList::from_preload(mock_preload_content).is_none(), "invalid preload list should not have parsed")
}

#[test]
fn test_parse_hsts_preload_should_decode_host_and_includes_subdomains() {
    let mock_preload_content = b"{\
                                     \"entries\": [\
                                        {\"host\": \"mozilla.org\",\
                                         \"include_subdomains\": false}\
                                     ]\
                                 }";
    let hsts_list = HstsList::from_preload(mock_preload_content);
    let entries_map = hsts_list.unwrap().entries_map;

    assert_eq!(entries_map.get("mozilla.org").unwrap()[0].host, "mozilla.org");
    assert!(!entries_map.get("mozilla.org").unwrap()[0].include_subdomains);
}

#[test]
fn test_hsts_list_with_no_entries_map_does_not_is_host_secure() {
    let hsts_list = HstsList {
        entries_map: HashMap::new()
    };

    assert!(!hsts_list.is_host_secure("mozilla.org"));
}

#[test]
fn test_hsts_list_with_exact_domain_entry_is_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(),  vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::NotIncluded, None).unwrap()]);

    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(hsts_list.is_host_secure("mozilla.org"));
}

#[test]
fn test_hsts_list_with_subdomain_when_include_subdomains_is_true_is_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(hsts_list.is_host_secure("servo.mozilla.org"));
}

#[test]
fn test_hsts_list_with_subdomain_when_include_subdomains_is_false_is_not_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::NotIncluded, None).unwrap()]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(!hsts_list.is_host_secure("servo.mozilla.org"));
}

#[test]
fn test_hsts_list_with_subdomain_when_host_is_not_a_subdomain_is_not_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(!hsts_list.is_host_secure("servo-mozilla.org"));
}

#[test]
fn test_hsts_list_with_subdomain_when_host_is_exact_match_is_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(hsts_list.is_host_secure("mozilla.org"));
}

#[test]
fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry {
            host: "mozilla.org".to_owned(),
            include_subdomains: false,
            max_age: Some(20),
            timestamp: Some(time::get_time().sec as u64 - 100u64)
        }]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(!hsts_list.is_host_secure("mozilla.org"));
}

#[test]
fn test_preload_hsts_domains_well_formed() {
    let hsts_list = HstsList::from_servo_preload();
    assert!(!hsts_list.entries_map.is_empty());
}
<commit_msg>Use specific assertion for net hsts tests<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use net::hsts::{HstsEntry, HstsList};
use net_traits::IncludeSubdomains;
use std::collections::HashMap;
use time;

#[test]
fn test_hsts_entry_is_not_expired_when_it_has_no_timestamp() {
    let entry = HstsEntry {
        host: "mozilla.org".to_owned(),
        include_subdomains: false,
        max_age: Some(20),
        timestamp: None
    };

    assert!(!entry.is_expired());
}

#[test]
fn test_hsts_entry_is_not_expired_when_it_has_no_max_age() {
    let entry = HstsEntry {
        host: "mozilla.org".to_owned(),
        include_subdomains: false,
        max_age: None,
        timestamp: Some(time::get_time().sec as u64)
    };

    assert!(!entry.is_expired());
}

#[test]
fn test_hsts_entry_is_expired_when_it_has_reached_its_max_age() {
    let entry = HstsEntry {
        host: "mozilla.org".to_owned(),
        include_subdomains: false,
        max_age: Some(10),
        timestamp: Some(time::get_time().sec as u64 - 20u64)
    };

    assert!(entry.is_expired());
}

#[test]
fn test_hsts_entry_cant_be_created_with_ipv6_address_as_host() {
    let entry = HstsEntry::new(
        "2001:0db8:0000:0000:0000:ff00:0042:8329".to_owned(), IncludeSubdomains::NotIncluded, None
    );

    assert!(entry.is_none(), "able to create HstsEntry with IPv6 host");
}

#[test]
fn test_hsts_entry_cant_be_created_with_ipv4_address_as_host() {
    let entry = HstsEntry::new(
        "4.4.4.4".to_owned(), IncludeSubdomains::NotIncluded, None
    );

    assert!(entry.is_none(), "able to create HstsEntry with IPv4 host");
}

#[test]
fn test_base_domain_in_entries_map() {
    let entries_map = HashMap::new();

    let mut list = HstsList {
        entries_map: entries_map
    };

    list.push(HstsEntry::new("servo.mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());
    list.push(HstsEntry::new("firefox.mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());
    list.push(HstsEntry::new("bugzilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());

    assert_eq!(list.entries_map.len(), 2);
    assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 2);
}

#[test]
fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::NotIncluded, Some(500000u64)).unwrap()));
    let mut list = HstsList {
        entries_map: entries_map
    };

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, Some(0)).unwrap());

    assert_eq!(list.is_host_secure("mozilla.org"), false)
}

#[test]
fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_already_matched() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()));
    let mut list = HstsList {
        entries_map: entries_map
    };

    list.push(HstsEntry::new("servo.mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());

    assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1)
}

#[test]
fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_subdomains() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()));
    let mut list = HstsList {
        entries_map: entries_map
    };

    assert!(list.is_host_secure("servo.mozilla.org"));

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());

    assert!(!list.is_host_secure("servo.mozilla.org"))
}

#[test]
fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::NotIncluded, None).unwrap()));
    let mut list = HstsList {
        entries_map: entries_map
    };

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::NotIncluded, None).unwrap());

    assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1)
}

#[test]
fn test_push_multiple_entrie_to_hsts_list_should_add_them_all() {
    let mut list = HstsList {
        entries_map: HashMap::new()
    };

    assert!(!list.is_host_secure("mozilla.org"));
    assert!(!list.is_host_secure("bugzilla.org"));

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::Included, None).unwrap());
    list.push(HstsEntry::new("bugzilla.org".to_owned(),
        IncludeSubdomains::Included, None).unwrap());

    assert!(list.is_host_secure("mozilla.org"));
    assert!(list.is_host_secure("bugzilla.org"));
}

#[test]
fn test_push_entry_to_hsts_list_should_add_an_entry() {
    let mut list = HstsList {
        entries_map: HashMap::new()
    };

    assert!(!list.is_host_secure("mozilla.org"));

    list.push(HstsEntry::new("mozilla.org".to_owned(),
        IncludeSubdomains::Included, None).unwrap());

    assert!(list.is_host_secure("mozilla.org"));
}

#[test]
fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
    let mock_preload_content = b"derp";
    assert!(HstsList::from_preload(mock_preload_content).is_none(), "invalid preload list should not have parsed")
}

#[test]
fn test_parse_hsts_preload_should_return_none_when_json_contains_no_entries_map_key() {
    let mock_preload_content = b"{\"nothing\": \"to see here\"}";
    assert!(HstsList::from_preload(mock_preload_content).is_none(), "invalid preload list should not have parsed")
}

#[test]
fn test_parse_hsts_preload_should_decode_host_and_includes_subdomains() {
    let mock_preload_content = b"{\
                                     \"entries\": [\
                                        {\"host\": \"mozilla.org\",\
                                         \"include_subdomains\": false}\
                                     ]\
                                 }";
    let hsts_list = HstsList::from_preload(mock_preload_content);
    let entries_map = hsts_list.unwrap().entries_map;

    assert_eq!(entries_map.get("mozilla.org").unwrap()[0].host, "mozilla.org");
    assert!(!entries_map.get("mozilla.org").unwrap()[0].include_subdomains);
}

#[test]
fn test_hsts_list_with_no_entries_map_does_not_is_host_secure() {
    let hsts_list = HstsList {
        entries_map: HashMap::new()
    };

    assert!(!hsts_list.is_host_secure("mozilla.org"));
}

#[test]
fn test_hsts_list_with_exact_domain_entry_is_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(),  vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::NotIncluded, None).unwrap()]);

    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(hsts_list.is_host_secure("mozilla.org"));
}

#[test]
fn test_hsts_list_with_subdomain_when_include_subdomains_is_true_is_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(hsts_list.is_host_secure("servo.mozilla.org"));
}

#[test]
fn test_hsts_list_with_subdomain_when_include_subdomains_is_false_is_not_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::NotIncluded, None).unwrap()]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(!hsts_list.is_host_secure("servo.mozilla.org"));
}

#[test]
fn test_hsts_list_with_subdomain_when_host_is_not_a_subdomain_is_not_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(!hsts_list.is_host_secure("servo-mozilla.org"));
}

#[test]
fn test_hsts_list_with_subdomain_when_host_is_exact_match_is_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
            IncludeSubdomains::Included, None).unwrap()]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(hsts_list.is_host_secure("mozilla.org"));
}

#[test]
fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
    let mut entries_map = HashMap::new();
    entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry {
            host: "mozilla.org".to_owned(),
            include_subdomains: false,
            max_age: Some(20),
            timestamp: Some(time::get_time().sec as u64 - 100u64)
        }]);
    let hsts_list = HstsList {
        entries_map: entries_map
    };

    assert!(!hsts_list.is_host_secure("mozilla.org"));
}

#[test]
fn test_preload_hsts_domains_well_formed() {
    let hsts_list = HstsList::from_servo_preload();
    assert!(!hsts_list.entries_map.is_empty());
}
<|endoftext|>