<commit_before>#![feature(rustc_private)]
#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
// warn on rustc internal lints
#![deny(rustc::internal)]

// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_interface;

use rustc_interface::interface;
use rustc_tools_util::VersionInfo;

use std::borrow::Cow;
use std::env;
use std::lazy::SyncLazy;
use std::ops::Deref;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{exit, Command};

/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
fn arg_value<'a, T: Deref<Target = str>>(
    args: &'a [T],
    find_arg: &str,
    pred: impl Fn(&str) -> bool,
) -> Option<&'a str> {
    let mut args = args.iter().map(Deref::deref);
    while let Some(arg) = args.next() {
        let mut arg = arg.splitn(2, '=');
        if arg.next() != Some(find_arg) {
            continue;
        }

        match arg.next().or_else(|| args.next()) {
            Some(v) if pred(v) => return Some(v),
            _ => {},
        }
    }
    None
}

#[test]
fn test_arg_value() {
    let args = &["--bar=bar", "--foobar", "123", "--foo"];

    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
    assert_eq!(arg_value(args, "--bar", |_| false), None);
    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
    assert_eq!(arg_value(args, "--foo", |_| true), None);
}

struct DefaultCallbacks;
impl rustc_driver::Callbacks for DefaultCallbacks {}

struct ClippyCallbacks;
impl rustc_driver::Callbacks for ClippyCallbacks {
    fn config(&mut self, config: &mut interface::Config) {
        let previous = config.register_lints.take();
        config.register_lints = Some(Box::new(move |sess, mut lint_store| {
            // technically we're ~guaranteed that this is none but might as well call anything that
            // is there already. Certainly it can't hurt.
            if let Some(previous) = &previous {
                (previous)(sess, lint_store);
            }

            let conf = clippy_lints::read_conf(&[], &sess);
            clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
            clippy_lints::register_pre_expansion_lints(&mut lint_store);
            clippy_lints::register_renamed(&mut lint_store);
        }));

        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
        // use for Clippy.
        config.opts.debugging_opts.mir_opt_level = 0;
    }
}

fn display_help() {
    println!(
        "\
Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
        --rustc              Pass all args to rustc
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"
    );
}

const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";

static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
    let hook = panic::take_hook();
    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
    hook
});

fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
    (*ICE_HOOK)(info);

    // Separate the output with an empty line
    eprintln!();

    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
        rustc_errors::ColorConfig::Auto,
        None,
        false,
        false,
        None,
        false,
    ));
    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);

    // a .span_bug or .bug call has already printed what
    // it wants to print.
    if !info.payload().is::<rustc_errors::ExplicitBug>() {
        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
        handler.emit_diagnostic(&d);
    }

    let version_info = rustc_tools_util::get_version_info!();

    let xs: Vec<Cow<'static, str>> = vec![
        "the compiler unexpectedly panicked. this is a bug.".into(),
        format!("we would appreciate a bug report: {}", bug_report_url).into(),
        format!("Clippy version: {}", version_info).into(),
    ];

    for note in &xs {
        handler.note_without_error(&note);
    }

    // If backtraces are enabled, also print the query stack
    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");

    let num_frames = if backtrace { None } else { Some(2) };

    interface::try_print_query_stack(&handler, num_frames);
}

fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
    home.and_then(|home| {
        toolchain.map(|toolchain| {
            let mut path = PathBuf::from(home);
            path.push("toolchains");
            path.push(toolchain);
            path
        })
    })
}

#[allow(clippy::too_many_lines)]
pub fn main() {
    rustc_driver::init_rustc_env_logger();
    SyncLazy::force(&ICE_HOOK);
    exit(rustc_driver::catch_with_exit_code(move || {
        let mut orig_args: Vec<String> = env::args().collect();

        // Get the sysroot, looking from most specific to this invocation to the least:
        // - command line
        // - runtime environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        // - sysroot from rustc in the path
        // - compile-time environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
        let have_sys_root_arg = sys_root_arg.is_some();
        let sys_root = sys_root_arg
            .map(PathBuf::from)
            .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
            .or_else(|| {
                let home = std::env::var("RUSTUP_HOME")
                    .or_else(|_| std::env::var("MULTIRUST_HOME"))
                    .ok();
                let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
                    .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
                    .ok();
                toolchain_path(home, toolchain)
            })
            .or_else(|| {
                Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| PathBuf::from(s.trim()))
            })
            .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
            .or_else(|| {
                let home = option_env!("RUSTUP_HOME")
                    .or(option_env!("MULTIRUST_HOME"))
                    .map(ToString::to_string);
                let toolchain = option_env!("RUSTUP_TOOLCHAIN")
                    .or(option_env!("MULTIRUST_TOOLCHAIN"))
                    .map(ToString::to_string);
                toolchain_path(home, toolchain)
            })
            .map(|pb| pb.to_string_lossy().to_string())
            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
        // uses
        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
            orig_args.remove(pos);
            orig_args[0] = "rustc".to_string();

            // if we call "rustc", we need to pass --sysroot here as well
            let mut args: Vec<String> = orig_args.clone();
            if !have_sys_root_arg {
                args.extend(vec!["--sysroot".into(), sys_root]);
            };

            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
        }

        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
            let version_info = rustc_tools_util::get_version_info!();
            println!("{}", version_info);
            exit(0);
        }

        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
        // We're invoking the compiler programmatically, so we ignore this/
        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());

        if wrapper_mode {
            // we still want to be able to invoke it normally though
            orig_args.remove(1);
        }

        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
            display_help();
            exit(0);
        }

        // this conditional check for the --sysroot flag is there so users can call
        // `clippy_driver` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = orig_args.clone();
        if !have_sys_root_arg {
            args.extend(vec!["--sysroot".into(), sys_root]);
        };

        let mut no_deps = false;
        let clippy_args = env::var("CLIPPY_ARGS")
            .unwrap_or_default()
            .split("__CLIPPY_HACKERY__")
            .filter_map(|s| match s {
                "" => None,
                "--no-deps" => {
                    no_deps = true;
                    None
                },
                _ => Some(s.to_string()),
            })
            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
            .collect::<Vec<String>>();

        // We enable Clippy if one of the following conditions is met
        // - IF Clippy is run on its test suite OR
        // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
        //    - IF `--no-deps` is not set (`!no_deps`) OR
        //    - IF `--no-deps` is set and Clippy is run on the specified primary package
        let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true");
        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
        let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();

        let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package));
        if clippy_enabled {
            args.extend(clippy_args);
        }

        let mut clippy = ClippyCallbacks;
        let mut default = DefaultCallbacks;
        let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
            if clippy_enabled { &mut clippy } else { &mut default };

        rustc_driver::RunCompiler::new(&args, callbacks).run()
    }))
}
<commit_msg>Make clippy set mir_opt_level using Option<commit_after>#![feature(rustc_private)]
#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
// warn on rustc internal lints
#![deny(rustc::internal)]

// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_interface;

use rustc_interface::interface;
use rustc_tools_util::VersionInfo;

use std::borrow::Cow;
use std::env;
use std::lazy::SyncLazy;
use std::ops::Deref;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{exit, Command};

/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
fn arg_value<'a, T: Deref<Target = str>>(
    args: &'a [T],
    find_arg: &str,
    pred: impl Fn(&str) -> bool,
) -> Option<&'a str> {
    let mut args = args.iter().map(Deref::deref);
    while let Some(arg) = args.next() {
        let mut arg = arg.splitn(2, '=');
        if arg.next() != Some(find_arg) {
            continue;
        }

        match arg.next().or_else(|| args.next()) {
            Some(v) if pred(v) => return Some(v),
            _ => {},
        }
    }
    None
}

#[test]
fn test_arg_value() {
    let args = &["--bar=bar", "--foobar", "123", "--foo"];

    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
    assert_eq!(arg_value(args, "--bar", |_| false), None);
    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
    assert_eq!(arg_value(args, "--foo", |_| true), None);
}

struct DefaultCallbacks;
impl rustc_driver::Callbacks for DefaultCallbacks {}

struct ClippyCallbacks;
impl rustc_driver::Callbacks for ClippyCallbacks {
    fn config(&mut self, config: &mut interface::Config) {
        let previous = config.register_lints.take();
        config.register_lints = Some(Box::new(move |sess, mut lint_store| {
            // technically we're ~guaranteed that this is none but might as well call anything that
            // is there already. Certainly it can't hurt.
            if let Some(previous) = &previous {
                (previous)(sess, lint_store);
            }

            let conf = clippy_lints::read_conf(&[], &sess);
            clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
            clippy_lints::register_pre_expansion_lints(&mut lint_store);
            clippy_lints::register_renamed(&mut lint_store);
        }));

        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
        // use for Clippy.
        config.opts.debugging_opts.mir_opt_level = Some(0);
    }
}

fn display_help() {
    println!(
        "\
Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
        --rustc              Pass all args to rustc
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"
    );
}

const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";

static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
    let hook = panic::take_hook();
    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
    hook
});

fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
    (*ICE_HOOK)(info);

    // Separate the output with an empty line
    eprintln!();

    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
        rustc_errors::ColorConfig::Auto,
        None,
        false,
        false,
        None,
        false,
    ));
    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);

    // a .span_bug or .bug call has already printed what
    // it wants to print.
    if !info.payload().is::<rustc_errors::ExplicitBug>() {
        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
        handler.emit_diagnostic(&d);
    }

    let version_info = rustc_tools_util::get_version_info!();

    let xs: Vec<Cow<'static, str>> = vec![
        "the compiler unexpectedly panicked. this is a bug.".into(),
        format!("we would appreciate a bug report: {}", bug_report_url).into(),
        format!("Clippy version: {}", version_info).into(),
    ];

    for note in &xs {
        handler.note_without_error(&note);
    }

    // If backtraces are enabled, also print the query stack
    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");

    let num_frames = if backtrace { None } else { Some(2) };

    interface::try_print_query_stack(&handler, num_frames);
}

fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
    home.and_then(|home| {
        toolchain.map(|toolchain| {
            let mut path = PathBuf::from(home);
            path.push("toolchains");
            path.push(toolchain);
            path
        })
    })
}

#[allow(clippy::too_many_lines)]
pub fn main() {
    rustc_driver::init_rustc_env_logger();
    SyncLazy::force(&ICE_HOOK);
    exit(rustc_driver::catch_with_exit_code(move || {
        let mut orig_args: Vec<String> = env::args().collect();

        // Get the sysroot, looking from most specific to this invocation to the least:
        // - command line
        // - runtime environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        // - sysroot from rustc in the path
        // - compile-time environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
        let have_sys_root_arg = sys_root_arg.is_some();
        let sys_root = sys_root_arg
            .map(PathBuf::from)
            .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
            .or_else(|| {
                let home = std::env::var("RUSTUP_HOME")
                    .or_else(|_| std::env::var("MULTIRUST_HOME"))
                    .ok();
                let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
                    .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
                    .ok();
                toolchain_path(home, toolchain)
            })
            .or_else(|| {
                Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| PathBuf::from(s.trim()))
            })
            .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
            .or_else(|| {
                let home = option_env!("RUSTUP_HOME")
                    .or(option_env!("MULTIRUST_HOME"))
                    .map(ToString::to_string);
                let toolchain = option_env!("RUSTUP_TOOLCHAIN")
                    .or(option_env!("MULTIRUST_TOOLCHAIN"))
                    .map(ToString::to_string);
                toolchain_path(home, toolchain)
            })
            .map(|pb| pb.to_string_lossy().to_string())
            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
        // uses
        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
            orig_args.remove(pos);
            orig_args[0] = "rustc".to_string();

            // if we call "rustc", we need to pass --sysroot here as well
            let mut args: Vec<String> = orig_args.clone();
            if !have_sys_root_arg {
                args.extend(vec!["--sysroot".into(), sys_root]);
            };

            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
        }

        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
            let version_info = rustc_tools_util::get_version_info!();
            println!("{}", version_info);
            exit(0);
        }

        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
        // We're invoking the compiler programmatically, so we ignore this/
        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());

        if wrapper_mode {
            // we still want to be able to invoke it normally though
            orig_args.remove(1);
        }

        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
            display_help();
            exit(0);
        }

        // this conditional check for the --sysroot flag is there so users can call
        // `clippy_driver` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = orig_args.clone();
        if !have_sys_root_arg {
            args.extend(vec!["--sysroot".into(), sys_root]);
        };

        let mut no_deps = false;
        let clippy_args = env::var("CLIPPY_ARGS")
            .unwrap_or_default()
            .split("__CLIPPY_HACKERY__")
            .filter_map(|s| match s {
                "" => None,
                "--no-deps" => {
                    no_deps = true;
                    None
                },
                _ => Some(s.to_string()),
            })
            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
            .collect::<Vec<String>>();

        // We enable Clippy if one of the following conditions is met
        // - IF Clippy is run on its test suite OR
        // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
        //    - IF `--no-deps` is not set (`!no_deps`) OR
        //    - IF `--no-deps` is set and Clippy is run on the specified primary package
        let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true");
        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
        let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();

        let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package));
        if clippy_enabled {
            args.extend(clippy_args);
        }

        let mut clippy = ClippyCallbacks;
        let mut default = DefaultCallbacks;
        let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
            if clippy_enabled { &mut clippy } else { &mut default };

        rustc_driver::RunCompiler::new(&args, callbacks).run()
    }))
}
<|endoftext|>


<commit_before>use std::io;
use std::fs;
use std::path::Path;

use csv;

use bio::stats::logprobs::LogProb;

use model::foldchange::LogFC;

pub struct Writer<W: io::Write> {
    inner: csv::Writer<W>
}

impl Writer<fs::File> {
    /// Write to a given file path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> Self {
        fs::File::create(path).map(|f| Writer::from_writer(f)).unwrap()
    }
}

impl<W: io::Write> Writer<W> {
    pub fn from_writer(w: W) -> Self {
        let mut writer = Writer {
            inner: csv::Writer::from_writer(w).delimiter(b'\t')
        };
        writer.inner.write(["feat", "diff_pep", "diff_fdr", "log2fc_ev", "log2fc_sd", "log2fc_ci_lower", "log2fc_ci_upper"].iter()).unwrap();

        writer
    }

    pub fn write(&mut self, feature: &str, differential_expression_pep: LogProb, fdr: LogProb, expected_value: LogFC, standard_deviation: LogFC, credible_interval: (LogFC, LogFC)) {
        self.inner.write([
            feature.to_owned(),
            format!("{:e}", differential_expression_pep.exp()),
            format!("{:e}", fdr.exp()),
            format!("{:.*}", 2, expected_value),
            format!("{:.*}", 4, standard_deviation),
            format!("{:.*}", 2, credible_interval.0),
            format!("{:.*}", 2, credible_interval.1)
        ].iter()).unwrap();
    }
}
<commit_msg>Limit digits of pep and fdr.<commit_after>use std::io;
use std::fs;
use std::path::Path;

use csv;

use bio::stats::logprobs::LogProb;

use model::foldchange::LogFC;

pub struct Writer<W: io::Write> {
    inner: csv::Writer<W>
}

impl Writer<fs::File> {
    /// Write to a given file path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> Self {
        fs::File::create(path).map(|f| Writer::from_writer(f)).unwrap()
    }
}

impl<W: io::Write> Writer<W> {
    pub fn from_writer(w: W) -> Self {
        let mut writer = Writer {
            inner: csv::Writer::from_writer(w).delimiter(b'\t')
        };
        writer.inner.write(["feat", "diff_pep", "diff_fdr", "log2fc_ev", "log2fc_sd", "log2fc_ci_lower", "log2fc_ci_upper"].iter()).unwrap();

        writer
    }

    pub fn write(&mut self, feature: &str, differential_expression_pep: LogProb, fdr: LogProb, expected_value: LogFC, standard_deviation: LogFC, credible_interval: (LogFC, LogFC)) {
        self.inner.write([
            feature.to_owned(),
            format!("{:.*e}", 2, differential_expression_pep.exp()),
            format!("{:.*e}", 2, fdr.exp()),
            format!("{:.*}", 2, expected_value),
            format!("{:.*}", 4, standard_deviation),
            format!("{:.*}", 2, credible_interval.0),
            format!("{:.*}", 2, credible_interval.1)
        ].iter()).unwrap();
    }
}
<|endoftext|>


<commit_before>use uci;
use search_algorithms::board::Board;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use uci::EngineComm;
use search_algorithms::alpha_beta::Score;
use std::error;
use uci::UciInfo;
use std::thread;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct UciOption {
    pub name: String,
    pub option_type: UciOptionType,
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum UciOptionType {
    Check(bool),
    Spin(i64, i64, i64), // Contains current value, minimum value, maximum value
    Combo(String, Vec<String>), // Contains current value, predefined values
    Button,
    String(String), // Contains the current value
}

pub trait UciEngine<B: Board>
    where Self: Sized {
    fn init() -> Self;

    fn uci_options(&mut self) -> Vec<UciOption>;

    fn set_uci_option(&mut self, uci_option: UciOption);

    fn search(self, board: B, time_limit: uci::TimeRestriction,
              engine_comm: Arc<Mutex<EngineComm>>, move_list: Option<Vec<B::Move>>)
    -> Box<Iterator<Item=UciInfo<B>>>;

    fn best_move(self, board: B, time_limit: uci::TimeRestriction,
                 move_list: Option<Vec<B::Move>>)
        -> Result<(Score, B::Move), Box<dyn error::Error>> {

        self.search(board, time_limit,
                    Arc::new(Mutex::new(EngineComm::new())), move_list)
            .last()
            .and_then(|uci_info|
                uci_info.pvs.get(0)
                    .and_then(|(score, pv)|
                        pv.get(0).map(|mv| (*score, mv.clone()))))
            .ok_or("Engine returned 0 moves".into())
    }

    fn best_moves_multipv(self, board: B, time_limit: uci::TimeRestriction,
                          move_list: Option<Vec<B::Move>>)
        -> Result<Vec<(Score, B::Move)>, Box<dyn error::Error>> {

        self.search(board, time_limit,
        Arc::new(Mutex::new(EngineComm::new())), move_list)
            .last()
            .map(|uci_info| uci_info.pvs.iter()
                .filter_map(|(score, moves)| moves.get(0).map(|mv| (*score, mv.clone())))
                .collect())
            .ok_or("Engine returned 0 moves".into())
    }

    fn search_async(self, board: B, time_limit: uci::TimeRestriction, engine_comm: Arc<Mutex<EngineComm>>,
                    move_list: Option<Vec<B::Move>>)
                    -> (thread::JoinHandle<()>, mpsc::Receiver<uci::UciInfo<B>>)
        where B: Board + Send + 'static + Eq, <B as Board>::Move: Sync + Send, Self: Sized + Send + 'static {
        let (sender, receiver) = mpsc::channel();
        let thread = thread::spawn(move || {
            for mv in self.search(board, time_limit, engine_comm, move_list) {
                sender.send(mv).unwrap();
            }
        });
        (thread, receiver)
    }
}<commit_msg>Optimize UciEngine default methods<commit_after>use uci;
use search_algorithms::board::Board;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use uci::EngineComm;
use search_algorithms::alpha_beta::Score;
use std::error;
use uci::UciInfo;
use std::thread;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct UciOption {
    pub name: String,
    pub option_type: UciOptionType,
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum UciOptionType {
    Check(bool),
    Spin(i64, i64, i64), // Contains current value, minimum value, maximum value
    Combo(String, Vec<String>), // Contains current value, predefined values
    Button,
    String(String), // Contains the current value
}

pub trait UciEngine<B: Board>
    where Self: Sized {
    fn init() -> Self;

    fn uci_options(&mut self) -> Vec<UciOption>;

    fn set_uci_option(&mut self, uci_option: UciOption);

    fn search(self, board: B, time_limit: uci::TimeRestriction,
              engine_comm: Arc<Mutex<EngineComm>>, move_list: Option<Vec<B::Move>>)
    -> Box<Iterator<Item=UciInfo<B>>>;

    fn best_move(self, board: B, time_limit: uci::TimeRestriction,
                 move_list: Option<Vec<B::Move>>)
        -> Result<(Score, B::Move), Box<dyn error::Error>> {

        self.search(board, time_limit,
                    Arc::new(Mutex::new(EngineComm::new())), move_list)
            .last()
            .and_then(|uci_info|
                uci_info.pvs.get(0)
                    .and_then(|(score, pv)|
                        pv.get(0).map(|mv| (*score, mv.clone()))))
            .ok_or_else(|| "Engine returned 0 moves".into())
    }

    fn best_moves_multipv(self, board: B, time_limit: uci::TimeRestriction,
                          move_list: Option<Vec<B::Move>>)
        -> Result<Vec<(Score, B::Move)>, Box<dyn error::Error>> {

        self.search(board, time_limit,
                    Arc::new(Mutex::new(EngineComm::new())), move_list)
            .last()
            .map(|uci_info| uci_info.pvs.iter()
                .filter_map(|(score, moves)| moves.get(0).map(|mv| (*score, mv.clone())))
                .collect())
            .ok_or_else(|| "Engine returned 0 moves".into())
    }

    fn search_async(self, board: B, time_limit: uci::TimeRestriction, engine_comm: Arc<Mutex<EngineComm>>,
                    move_list: Option<Vec<B::Move>>)
                    -> (thread::JoinHandle<()>, mpsc::Receiver<uci::UciInfo<B>>)
        where B: Board + Send + 'static + Eq, <B as Board>::Move: Sync + Send, Self: Sized + Send + 'static {
        let (sender, receiver) = mpsc::channel();
        let thread = thread::spawn(move || {
            for mv in self.search(board, time_limit, engine_comm, move_list) {
                sender.send(mv).unwrap();
            }
        });
        (thread, receiver)
    }
}<|endoftext|>


<commit_before>#[macro_use]
use game::gfx_macros::{TilemapSettings, Projection, VertexData, pipe, TileMapData};
use std::io::Cursor;
use gfx;
use image;
use cgmath;
use gfx_app::{Application, WindowTargets}; //
use gfx::{Resources, Factory, texture};
use gfx::handle::{ShaderResourceView};
use gfx::format::{Rgba8};
use gfx::traits::{FactoryExt};
use cgmath::{SquareMatrix, Matrix4, AffineMatrix3};
use cgmath::{Point3, Vector3};
use cgmath::{Transform};
use views::Point;
use game::constants::{MAP_FILE_PATH, TILES_PCS_W, TILES_PCS_H};
use genmesh::{Vertices, Triangulate};
use genmesh::generators::{Plane, SharedVertex, IndexedPolygon, SharedVertexIterator};
use views::{TileMap};
use data::{load_map_file};

const TILEMAP_BUF_LENGTH: usize = 4096;

pub fn load_texture<R, F>(factory: &mut F, data: &[u8]) -> Result<ShaderResourceView<R, [f32; 4]>, String> where R: Resources, F: Factory<R> {
  let img = image::load(Cursor::new(data), image::PNG).unwrap().to_rgba();
  let (width, height) = img.dimensions();
  let kind = texture::Kind::D2(width as texture::Size, height as texture::Size, texture::AaMode::Single);
  let (_, view) = factory.create_texture_immutable_u8::<Rgba8>(kind, &[&img]).unwrap();
  Ok(view)
}

fn cartesian_to_isometric(point_x: f32, point_y: f32) -> (f32, f32) {
  ((point_x - point_y), (point_x + point_y) / 2.0)
}

impl TileMapData {
  pub fn new_empty() -> TileMapData {
    TileMapData { data: [9.0, 9.0, 0.0, 0.0] }
  }

  pub fn new(data: [f32; 4]) -> TileMapData {
    TileMapData { data: data }
  }
}

pub struct TileMapPlane<R> where R: Resources {
  pub params: pipe::Data<R>,
  pub slice: gfx::Slice<R>,
  projection: Projection,
  is_projection_dirty: bool,
  tilemap_settings: TilemapSettings,
  is_tilemap_dirty: bool,
  pub data: Vec<TileMapData>,
}

impl<R> TileMapPlane<R> where R: Resources {
  pub fn new<F>(factory: &mut F, width: usize, height: usize, tile_size: usize,
                targets: WindowTargets<R>)
                -> TileMapPlane<R> where F: gfx::Factory<R> {
    let half_width = (tile_size * width) / 2;
    let half_height = (tile_size * height) / 2;
    let total_size = width * height;

    let tilesheet_bytes = &include_bytes!("../../assets/maps/terrain.png")[..];
    let tilesheet_width = 32;
    let tilesheet_height = 32;
    let tilesheet_tilesize = 32;

    let tilesheet_total_width = tilesheet_width * tilesheet_tilesize;
    let tilesheet_total_height = tilesheet_height * tilesheet_tilesize;
    let plane = Plane::subdivide(width, width);

    let vertex_data: Vec<VertexData> = plane.shared_vertex_iter()
      .map(|(x, y)| {
        let (raw_x, raw_y) = cartesian_to_isometric(x, y);
        let vertex_x = half_width as f32 * raw_x;
        let vertex_y = half_height as f32 * raw_y;

        let (u_pos, v_pos) = (((raw_x) / 4.0 - raw_y / 2.0), ((raw_x) / 4.0 + raw_y / 2.0));
        let tilemap_x = (u_pos * width as f32).floor();
        let tilemap_y = (v_pos * height as f32).floor();

        VertexData {
          pos: [vertex_x, vertex_y, 0.0],
          buf_pos: [tilemap_x as f32, tilemap_y as f32]
        }
      })
      .collect();

    let index_data: Vec<u32> = plane.indexed_polygon_iter()
      .triangulate()
      .vertices()
      .map(|i| i as u32)
      .collect();

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &index_data[..]);

    let tile_texture = load_texture(factory, tilesheet_bytes).unwrap();

    let params = pipe::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      tilemap: factory.create_constant_buffer(TILEMAP_BUF_LENGTH),
      tilemap_cb: factory.create_constant_buffer(1),
      tilesheet: (tile_texture, factory.create_sampler_linear()),
      out_color: targets.color,
      out_depth: targets.depth,
    };

    let mut charmap_data = Vec::with_capacity(total_size);

    for _ in 0..total_size {
      charmap_data.push(TileMapData::new_empty());
    }

    let view: AffineMatrix3<f32> = Transform::look_at(
      Point3::new(0.0, 0.0, 800.0),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );

    TileMapPlane {
      slice: slice,
      params: params,
      projection: Projection {
        model: Matrix4::identity().into(),
        view: view.mat.into(),
        proj: cgmath::perspective(cgmath::deg(60.0f32), targets.aspect_ratio, 0.1, 4000.0).into(),
      },
      is_projection_dirty: true,
      tilemap_settings: TilemapSettings {
        world_size: [width as f32, height as f32, tile_size as f32, 0.0],
        tilesheet_size: [tilesheet_width as f32, tilesheet_height as f32, tilesheet_total_width as f32, tilesheet_total_height as f32],
        offsets: [0.0, 0.0],
      },
      is_tilemap_dirty: true,
      data: charmap_data,
    }
  }

  pub fn resize(&mut self, targets: WindowTargets<R>) {
    self.params.out_color = targets.color;
    self.params.out_depth = targets.depth;
    self.projection.proj = cgmath::perspective(cgmath::deg(60.0f32), targets.aspect_ratio, 0.1, 4000.0).into();
    self.is_projection_dirty = true;
  }

  pub fn prepare_buffers<C>(&mut self, encoder: &mut gfx::Encoder<R, C>, update_data: bool) where C: gfx::CommandBuffer<R> {
    if update_data {
      encoder.update_buffer(&self.params.tilemap, self.data.as_slice(), 0).unwrap();
    }
    if self.is_projection_dirty {
      encoder.update_constant_buffer(&self.params.projection_cb, &self.projection);
      self.is_projection_dirty = false;
    }
    if self.is_tilemap_dirty {
      encoder.update_constant_buffer(&self.params.tilemap_cb, &self.tilemap_settings);
      self.is_tilemap_dirty = false;
    }
  }

  pub fn clear<C>(&self, encoder: &mut gfx::Encoder<R, C>) where C: gfx::CommandBuffer<R> {
    encoder.clear(&self.params.out_color,
                  [16.0 / 256.0, 14.0 / 256.0, 22.0 / 256.0, 1.0]);
    encoder.clear_depth(&self.params.out_depth, 1.0);
  }

  pub fn update_view(&mut self, view: &AffineMatrix3<f32>) {
    self.projection.view = view.mat.into();
    self.is_projection_dirty = true;
  }

  pub fn update_x_offset(&mut self, amount: f32) {
    self.tilemap_settings.offsets[0] = amount;
    self.is_tilemap_dirty = true;
  }

  pub fn update_y_offset(&mut self, amount: f32) {
    self.tilemap_settings.offsets[1] = amount;
    self.is_tilemap_dirty = true;
  }
}

pub struct Tilemap<R> where R: Resources {
  pub limit_coords: [usize; 2],
  pub focus_coords: [usize; 2],
  tilemap_size: [usize; 2],
  tilemap_plane: TileMapPlane<R>,
  focus_dirty: bool,
  tiles: Vec<usize>,
}

impl<R: gfx::Resources> TileMap<R> {
  pub fn set_focus(&mut self, focus: [usize; 2]) {
    if focus[0] <= self.limit_coords[0] && focus[1] <= self.limit_coords[1] {
      self.focus_coords = focus;
      let mut charmap_ypos = 0;
      for ypos in self.focus_coords[1]..self.focus_coords[1] + self.charmap_size[1] {
        let mut charmap_xpos = 0;
        for xpos in self.focus_coords[0]..self.focus_coords[0] + self.charmap_size[0] {
          let tile_idx = (ypos * self.tilemap_size[0]) + xpos;
          let charmap_idx = (charmap_ypos * self.charmap_size[0]) + charmap_xpos;
          self.tilemap_plane.data[charmap_idx] = self.tiles[tile_idx];
          charmap_xpos += 1;
        }
        charmap_ypos += 1;
      }
      self.focus_dirty = true;
    } else {
      panic!("tried to set focus to {:?} with tilemap_size of {:?}", focus, self.tilemap_size);
    }
  }

  fn calc_index(&self, xpos: usize, ypos: usize) -> usize {
    (ypos * self.tilemap_size[0]) + xpos
  }

  pub fn set_tile(&mut self, xpos: usize, ypos: usize, data: [f32; 4]) {
    let idx = self.calc_index(xpos, ypos);
    self.tiles[idx] = TileMapData::new(data);
  }

  pub fn populate_tilemap(&mut self, tilemap_size: [usize; 2]) {
    let map = load_map_file(MAP_FILE_PATH);
    for ypos in 0..self.tilemap_size[1] {
      for xpos in 0..self.tilemap_size[0] {
        self.set_tile(xpos, ypos, [7.0, 7.0, 0.0, 0.0]);
      }
    }
    self.set_tile(1, 1, [4.0, 4.0, 0.0, 0.0]);
    self.set_tile(3, 3, [4.0, 4.0, 0.0, 0.0]);
  }
}
<commit_msg>Initialize with empty tiles and set view distance to 1000<commit_after>#[macro_use]
use game::gfx_macros::{TilemapSettings, Projection, VertexData, pipe, TileMapData};
use std::io::Cursor;
use gfx;
use image;
use cgmath;
use gfx_app::{Application, WindowTargets}; //
use gfx::{Resources, Factory, texture};
use gfx::handle::{ShaderResourceView};
use gfx::format::{Rgba8};
use gfx::traits::{FactoryExt};
use cgmath::{SquareMatrix, Matrix4, AffineMatrix3};
use cgmath::{Point3, Vector3};
use cgmath::{Transform};
use views::Point;
use game::constants::{MAP_FILE_PATH, TILES_PCS_W, TILES_PCS_H};
use genmesh::{Vertices, Triangulate};
use genmesh::generators::{Plane, SharedVertex, IndexedPolygon, SharedVertexIterator};
use views::{TileMap};
use data::{load_map_file};

const TILEMAP_BUF_LENGTH: usize = 4096;

pub fn load_texture<R, F>(factory: &mut F, data: &[u8]) -> Result<ShaderResourceView<R, [f32; 4]>, String> where R: Resources, F: Factory<R> {
  let img = image::load(Cursor::new(data), image::PNG).unwrap().to_rgba();
  let (width, height) = img.dimensions();
  let kind = texture::Kind::D2(width as texture::Size, height as texture::Size, texture::AaMode::Single);
  let (_, view) = factory.create_texture_immutable_u8::<Rgba8>(kind, &[&img]).unwrap();
  Ok(view)
}

fn cartesian_to_isometric(point_x: f32, point_y: f32) -> (f32, f32) {
  ((point_x - point_y), (point_x + point_y) / 2.0)
}

impl TileMapData {
  pub fn new_empty() -> TileMapData {
    TileMapData { data: [32.0, 32.0, 0.0, 0.0] }
  }

  pub fn new(data: [f32; 4]) -> TileMapData {
    TileMapData { data: data }
  }
}

pub struct TileMapPlane<R> where R: Resources {
  pub params: pipe::Data<R>,
  pub slice: gfx::Slice<R>,
  projection: Projection,
  is_projection_dirty: bool,
  tilemap_settings: TilemapSettings,
  is_tilemap_dirty: bool,
  pub data: Vec<TileMapData>,
}

impl<R> TileMapPlane<R> where R: Resources {
  pub fn new<F>(factory: &mut F, width: usize, height: usize, tile_size: usize,
                targets: WindowTargets<R>)
                -> TileMapPlane<R> where F: gfx::Factory<R> {
    let half_width = (tile_size * width) / 2;
    let half_height = (tile_size * height) / 2;
    let total_size = width * height;

    let tilesheet_bytes = &include_bytes!("../../assets/maps/terrain.png")[..];
    let tilesheet_width = 32;
    let tilesheet_height = 32;
    let tilesheet_tilesize = 32;

    let tilesheet_total_width = tilesheet_width * tilesheet_tilesize;
    let tilesheet_total_height = tilesheet_height * tilesheet_tilesize;
    let plane = Plane::subdivide(width, width);

    let vertex_data: Vec<VertexData> = plane.shared_vertex_iter()
      .map(|(x, y)| {
        let (raw_x, raw_y) = cartesian_to_isometric(x, y);
        let vertex_x = half_width as f32 * raw_x;
        let vertex_y = half_height as f32 * raw_y;

        let (u_pos, v_pos) = (((raw_x) / 4.0 - raw_y / 2.0), ((raw_x) / 4.0 + raw_y / 2.0));
        let tilemap_x = (u_pos * width as f32).floor();
        let tilemap_y = (v_pos * height as f32).floor();

        VertexData {
          pos: [vertex_x, vertex_y, 0.0],
          buf_pos: [tilemap_x as f32, tilemap_y as f32]
        }
      })
      .collect();

    let index_data: Vec<u32> = plane.indexed_polygon_iter()
      .triangulate()
      .vertices()
      .map(|i| i as u32)
      .collect();

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &index_data[..]);

    let tile_texture = load_texture(factory, tilesheet_bytes).unwrap();

    let params = pipe::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      tilemap: factory.create_constant_buffer(TILEMAP_BUF_LENGTH),
      tilemap_cb: factory.create_constant_buffer(1),
      tilesheet: (tile_texture, factory.create_sampler_linear()),
      out_color: targets.color,
      out_depth: targets.depth,
    };

    let mut charmap_data = Vec::with_capacity(total_size);

    for _ in 0..total_size {
      charmap_data.push(TileMapData::new_empty());
    }

    let view: AffineMatrix3<f32> = Transform::look_at(
      Point3::new(0.0, 0.0, 1000.0),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );

    TileMapPlane {
      slice: slice,
      params: params,
      projection: Projection {
        model: Matrix4::identity().into(),
        view: view.mat.into(),
        proj: cgmath::perspective(cgmath::deg(60.0f32), targets.aspect_ratio, 0.1, 4000.0).into(),
      },
      is_projection_dirty: true,
      tilemap_settings: TilemapSettings {
        world_size: [width as f32, height as f32, tile_size as f32, 0.0],
        tilesheet_size: [tilesheet_width as f32, tilesheet_height as f32, tilesheet_total_width as f32, tilesheet_total_height as f32],
        offsets: [0.0, 0.0],
      },
      is_tilemap_dirty: true,
      data: charmap_data,
    }
  }

  pub fn resize(&mut self, targets: WindowTargets<R>) {
    self.params.out_color = targets.color;
    self.params.out_depth = targets.depth;
    self.projection.proj = cgmath::perspective(cgmath::deg(60.0f32), targets.aspect_ratio, 0.1, 4000.0).into();
    self.is_projection_dirty = true;
  }

  pub fn prepare_buffers<C>(&mut self, encoder: &mut gfx::Encoder<R, C>, update_data: bool) where C: gfx::CommandBuffer<R> {
    if update_data {
      encoder.update_buffer(&self.params.tilemap, self.data.as_slice(), 0).unwrap();
    }
    if self.is_projection_dirty {
      encoder.update_constant_buffer(&self.params.projection_cb, &self.projection);
      self.is_projection_dirty = false;
    }
    if self.is_tilemap_dirty {
      encoder.update_constant_buffer(&self.params.tilemap_cb, &self.tilemap_settings);
      self.is_tilemap_dirty = false;
    }
  }

  pub fn clear<C>(&self, encoder: &mut gfx::Encoder<R, C>) where C: gfx::CommandBuffer<R> {
    encoder.clear(&self.params.out_color,
                  [16.0 / 256.0, 14.0 / 256.0, 22.0 / 256.0, 1.0]);
    encoder.clear_depth(&self.params.out_depth, 1.0);
  }

  pub fn update_view(&mut self, view: &AffineMatrix3<f32>) {
    self.projection.view = view.mat.into();
    self.is_projection_dirty = true;
  }

  pub fn update_x_offset(&mut self, amount: f32) {
    self.tilemap_settings.offsets[0] = amount;
    self.is_tilemap_dirty = true;
  }

  pub fn update_y_offset(&mut self, amount: f32) {
    self.tilemap_settings.offsets[1] = amount;
    self.is_tilemap_dirty = true;
  }
}

pub struct Tilemap<R> where R: Resources {
  pub limit_coords: [usize; 2],
  pub focus_coords: [usize; 2],
  tilemap_size: [usize; 2],
  tilemap_plane: TileMapPlane<R>,
  focus_dirty: bool,
  tiles: Vec<usize>,
}

impl<R: gfx::Resources> TileMap<R> {
  pub fn set_focus(&mut self, focus: [usize; 2]) {
    if focus[0] <= self.limit_coords[0] && focus[1] <= self.limit_coords[1] {
      self.focus_coords = focus;
      let mut charmap_ypos = 0;
      for ypos in self.focus_coords[1]..self.focus_coords[1] + self.charmap_size[1] {
        let mut charmap_xpos = 0;
        for xpos in self.focus_coords[0]..self.focus_coords[0] + self.charmap_size[0] {
          let tile_idx = (ypos * self.tilemap_size[0]) + xpos;
          let charmap_idx = (charmap_ypos * self.charmap_size[0]) + charmap_xpos;
          self.tilemap_plane.data[charmap_idx] = self.tiles[tile_idx];
          charmap_xpos += 1;
        }
        charmap_ypos += 1;
      }
      self.focus_dirty = true;
    } else {
      panic!("tried to set focus to {:?} with tilemap_size of {:?}", focus, self.tilemap_size);
    }
  }

  fn calc_index(&self, xpos: usize, ypos: usize) -> usize {
    (ypos * self.tilemap_size[0]) + xpos
  }

  pub fn set_tile(&mut self, xpos: usize, ypos: usize, data: [f32; 4]) {
    let idx = self.calc_index(xpos, ypos);
    self.tiles[idx] = TileMapData::new(data);
  }

  pub fn populate_tilemap(&mut self, tilemap_size: [usize; 2]) {
    let map = load_map_file(MAP_FILE_PATH);
    for ypos in 0..self.tilemap_size[1] {
      for xpos in 0..self.tilemap_size[0] {
        self.set_tile(xpos, ypos, [7.0, 7.0, 0.0, 0.0]);
      }
    }
    self.set_tile(1, 1, [4.0, 4.0, 0.0, 0.0]);
    self.set_tile(3, 3, [4.0, 4.0, 0.0, 0.0]);
  }
}
<|endoftext|>


<commit_before>use time::now_utc;
use time::Tm;
use std::ascii::AsciiExt;
use openssl::crypto::hash::Hasher;
use openssl::crypto::hash::HashType::SHA256;
use serialize::hex::ToHex;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use request::Header;
use url::percent_encoding::{percent_encode_to, FORM_URLENCODED_ENCODE_SET};

struct QP<'a> {
    k: &'a str,
    v: &'a str,
}

#[derive(Clone)]
pub struct SigV4 {
    headers: BTreeMap<String, Vec<String>>,
    path: Option<String>,
    method: Option<String>,
    query: Option<String>,
    payload: Option<String>,
    date: Tm,
}

impl SigV4 {
    pub fn new() -> SigV4{
        let dt = now_utc();
        SigV4 {
            headers: BTreeMap::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
            date: dt,
        }
    }

    pub fn header(mut self, header: Header) -> SigV4 {
        append_header(&mut self.headers, header.key.as_slice(), header.value.as_slice());
        self
    }

    pub fn date(mut self) -> SigV4 {
        append_header(&mut self.headers, "X-Amz-Date",
                      self.date.strftime("%Y%m%dT%H%M%SZ").unwrap().to_string().as_slice());
        self
    }

    pub fn path(mut self, path: String) -> SigV4 {
        self.path = Some(path);
        self
    }

    pub fn method(mut self, method: String) -> SigV4 {
        self.method = Some(method);
        self
    }

    pub fn query(mut self, query: String) -> SigV4 {
        self.query = Some(query);
        self
    }

    pub fn payload(mut self, payload: String) -> SigV4 {
        self.payload = Some(payload);
        self
    }

    pub fn signature(self) -> String {
        self.hashed_canonical_request()
    }

    fn hashed_canonical_request(self) -> String {
        let mut h = Hasher::new(SHA256);
        h.update(self.canonical_request().as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn hashed_payload(&self) -> String {
        let val = match self.payload {
            Some(ref x) => x.to_string(),
            None => "".to_string(),
        };
        let mut h = Hasher::new(SHA256);
        h.update(val.as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn signed_headers(&self) -> String {
        let mut h = String::new();

        for (key,_) in self.headers.iter() {
            if key.as_slice() == "authorization" {
                continue;
            }
            h.push_str(key.as_slice());
            h.push(';')
        }
        h.trim_right_matches(';').to_string()
    }

    fn canonical_headers(&self) -> String {
        let mut h = String::new();

        for (key,value) in self.headers.iter() {
            if key.as_slice() == "authorization" {
                continue;
            }
            h.push_str(format!("{}:{}\n", key, canonical_value(value)).as_slice());
        }
        h
    }

    fn canonical_query_string(&self) -> String {
        match self.query {
            None => String::new(),
            Some(ref x) => {
                let mut h: Vec<QP> = Vec::new();
                for q in x.split('&') {
                    if q.contains_char('=') {
                        let n: Vec<&str> = q.splitn(1, '=').collect();
                        h.push(QP{k: n[0], v: n[1]})
                    } else {
                        h.push(QP{k: q, v: ""})
                    }
                };
                sort_query_string(h)
            }
        }
    }

    fn canonical_request(&self) -> String {
        format!("{}\n{}\n{}\n{}\n{}\n{}", expand_string(&self.method),
                expand_string(&self.path),
                self.canonical_query_string(),
                self.canonical_headers(),
                self.signed_headers(),
                self.hashed_payload()
        )
    }

}

fn sort_query_string(mut query: Vec<QP>) -> String {
    #[inline]
    fn byte_serialize(input: &str, output: &mut String) {
        for &byte in input.as_bytes().iter() {
            percent_encode_to(&[byte], FORM_URLENCODED_ENCODE_SET, output)
        }
    }

    let mut output = String::new();

    query.sort_by( |a, b| a.k.cmp(b.k));
    for item in query.iter() {
        if output.len() > 0 {
            output.push_str("&");
        }
        byte_serialize(item.k, &mut output);
        output.push_str("=");
        byte_serialize(item.v, &mut output);
    }

    output
    // it would be marvelous to use the below, but the AWS SigV4 spec says space must be %20, and
    // rust-url gives me back a +. So, roll our own for now.
    // let qs: Vec<(String, String)> = query.iter().map(|n| (n.k.to_string(), n.v.to_string())).collect();
    // form_urlencoded::serialize_owned(qs.as_slice())
}

fn append_header(map: &mut BTreeMap<String, Vec<String>>, key: &str, value: &str) {
    let k = key.to_ascii_lowercase().to_string();

    match map.entry(k) {
        Entry::Vacant(entry) => {
            let mut values = Vec::new();
            values.push(value.to_string());
            entry.set(values);
        },
        Entry::Occupied(entry) => {
            entry.into_mut().push(value.to_string());
        }
    };
}

fn expand_string(val: &Option<String>) -> String {
    match *val {
        None => "".to_string(),
        Some(ref x) => x.to_string(),
    }
}

fn canonical_value(val: &Vec<String>) -> String {
    let mut st = String::new();
    for v in val.iter() {
        if v.starts_with("\""){
            st.push_str(v.as_slice());
            st.push(',')
        } else {
            st.push_str(v.replace("  ", " ").trim());
            st.push(',')
        }
    }
    st.trim_right_matches(',').to_string()
}

#[cfg(test)]
mod tests {
    use super::SigV4;
    use request::Header;
    use time::strptime;
    use std::collections::BTreeMap;

    macro_rules! wrap_header (
        ($key:expr) => (
            Some(&vec!($key.to_string()))
        )
    );

    #[test]
    fn test_new_sigv4() {
        let sig = SigV4::new();
        assert_eq!(sig.headers.len(), 0)
    }

    #[test]
    fn test_date() {
        let sig = SigV4::new().date();
        assert!(sig.headers.contains_key("x-amz-date"))
    }

    #[test]
    fn test_add_header() {
        let h = Header{ key: "test".to_string(), value: "a string".to_string()};

        let sig = SigV4::new().header(h);
        assert_eq!(sig.headers.get("test"), wrap_header!("a string"))
    }

    #[test]
    fn test_add_second_value() {
        let h = Header{ key: "test".to_string(), value: "a string".to_string()};
        let h2 = Header{ key: "test".to_string(), value: "another string".to_string()};

        let sig = SigV4::new().header(h).header(h2);
        assert_eq!(sig.headers.get("test"), Some(&vec!("a string".to_string(), "another string".to_string())))
    }

    #[test]
    fn test_canonical_query_encoded() {
        let sig = SigV4::new().query("a space=woo woo&x-amz-header=foo".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "a%20space=woo%20woo&x-amz-header=foo")
    }

    #[test]
    fn test_canonical_query_valueless() {
        let sig = SigV4::new().query("other=&test&x-amz-header=foo".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "other=&test=&x-amz-header=foo")
    }

    #[test]
    fn test_canonical_query_sorted() {
        let sig = SigV4::new().query("foo=&bar=&baz=".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "bar=&baz=&foo=")
    }

    // Ensure that params with the same name stay in the same order after sorting
    #[test]
    fn test_canonical_query_complex() {
        let sig = SigV4::new().query("q.options=abc&q=xyz&q=mno".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "q=xyz&q=mno&q.options=abc")
    }

    #[test]
    fn test_hashed_request() {
        let h = Header{ key: "Content-Type".to_string(), value: "application/x-www-form-urlencoded; charset=utf-8".to_string() };
        let h2 = Header{ key: "Host".to_string(), value: "iam.amazonaws.com".to_string() };

        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: Some("/".to_string()),
            method: Some("POST".to_string()),
            query: None,
            payload: Some("Action=ListUsers&Version=2010-05-08".to_string()),
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date().header(h).header(h2);

        assert_eq!(sig.hashed_canonical_request().as_slice(), "3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2")
    }

    #[test]
    fn test_signed_headers() {
        let h = Header{ key: "test".to_string(),
            value: "a string".to_string()};
        let h2 = Header{ key: "Content-Type".to_string(),
            value: "application/x-www-form-urlencoded; charset=utf-8".to_string()};
        let h3 = Header{ key: "Authorization".to_string(),
            value: "none".to_string()};

        let sig = SigV4::new().date().header(h).header(h2).header(h3);
        assert_eq!(sig.signed_headers().as_slice(), "content-type;test;x-amz-date")
    }

    #[test]
    fn test_hashed_payload() {
        let sig = SigV4::new().
            payload("Action=ListUsers&Version=2010-05-08".to_string());
        assert_eq!(sig.hashed_payload(),
        "b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

    #[test]
    fn test_empty_payload() {
        let sig = SigV4::new();
        assert_eq!(sig.hashed_payload(),
        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_headers() {
        let h = Header{ key: "Xyz".to_string(), value: "1".to_string() };
        let h2 = Header{ key: "Abc".to_string(), value: "2".to_string() };
        let h3 = Header{ key: "Mno".to_string(), value: "3".to_string() };
        let h4 = Header{ key: "Authorization".to_string(), value: "4".to_string() };
        let sig = SigV4::new().header(h).header(h2).header(h3).header(h4);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:2\nmno:3\nxyz:1\n")
    }

    #[test]
    fn test_prune_whitespace() {
        let h = Header{ key: "Abc".to_string(), value: "a  b  c".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:a b c\n")
    }

    #[test]
    fn test_no_prune_quoted() {
        let h = Header{ key: "Abc".to_string(), value: "\"a  b  c\"".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:\"a  b  c\"\n")
    }

    #[test]
    fn test_specific_date() {
        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date();
        assert_eq!(sig.headers.get("x-amz-date"), wrap_header!("20110909T233600Z"))
    }

    #[test]
    fn test_empty_canonical_request() {
        let sig = SigV4::new();
        assert_eq!(sig.canonical_request().as_slice(), "\n\n\n\n\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_request() {
        let h = Header{ key: "Content-Type".to_string(), value: "application/x-www-form-urlencoded; charset=utf-8".to_string() };
        let h2 = Header{ key: "Host".to_string(), value: "iam.amazonaws.com".to_string() };

        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: Some("/".to_string()),
            method: Some("POST".to_string()),
            query: None,
            payload: Some("Action=ListUsers&Version=2010-05-08".to_string()),
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date().header(h).header(h2);

        assert_eq!(sig.canonical_request().as_slice(), r"POST
/

content-type:application/x-www-form-urlencoded; charset=utf-8
host:iam.amazonaws.com
x-amz-date:20110909T233600Z

content-type;host;x-amz-date
b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

}
<commit_msg>neaten up string joining<commit_after>use time::now_utc;
use time::Tm;
use std::ascii::AsciiExt;
use openssl::crypto::hash::Hasher;
use openssl::crypto::hash::HashType::SHA256;
use serialize::hex::ToHex;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use request::Header;
use url::percent_encoding::{percent_encode_to, FORM_URLENCODED_ENCODE_SET};

struct QP<'a> {
    k: &'a str,
    v: &'a str,
}

#[derive(Clone)]
pub struct SigV4 {
    headers: BTreeMap<String, Vec<String>>,
    path: Option<String>,
    method: Option<String>,
    query: Option<String>,
    payload: Option<String>,
    date: Tm,
}

impl SigV4 {
    pub fn new() -> SigV4{
        let dt = now_utc();
        SigV4 {
            headers: BTreeMap::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
            date: dt,
        }
    }

    pub fn header(mut self, header: Header) -> SigV4 {
        append_header(&mut self.headers, header.key.as_slice(), header.value.as_slice());
        self
    }

    pub fn date(mut self) -> SigV4 {
        append_header(&mut self.headers, "X-Amz-Date",
                      self.date.strftime("%Y%m%dT%H%M%SZ").unwrap().to_string().as_slice());
        self
    }

    pub fn path(mut self, path: String) -> SigV4 {
        self.path = Some(path);
        self
    }

    pub fn method(mut self, method: String) -> SigV4 {
        self.method = Some(method);
        self
    }

    pub fn query(mut self, query: String) -> SigV4 {
        self.query = Some(query);
        self
    }

    pub fn payload(mut self, payload: String) -> SigV4 {
        self.payload = Some(payload);
        self
    }

    pub fn signature(self) -> String {
        self.hashed_canonical_request()
    }

    fn hashed_canonical_request(self) -> String {
        let mut h = Hasher::new(SHA256);
        h.update(self.canonical_request().as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn hashed_payload(&self) -> String {
        let val = match self.payload {
            Some(ref x) => x.to_string(),
            None => "".to_string(),
        };
        let mut h = Hasher::new(SHA256);
        h.update(val.as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn signed_headers(&self) -> String {
        let mut h = String::new();

        for (key,_) in self.headers.iter() {
            if h.len() > 0 {
                h.push(';')
            }
            if key.as_slice() == "authorization" {
                continue;
            }
            h.push_str(key.as_slice());
        }
        h
    }

    fn canonical_headers(&self) -> String {
        let mut h = String::new();

        for (key,value) in self.headers.iter() {
            if key.as_slice() == "authorization" {
                continue;
            }
            h.push_str(format!("{}:{}\n", key, canonical_value(value)).as_slice());
        }
        h
    }

    fn canonical_query_string(&self) -> String {
        match self.query {
            None => String::new(),
            Some(ref x) => {
                let mut h: Vec<QP> = Vec::new();
                for q in x.split('&') {
                    if q.contains_char('=') {
                        let n: Vec<&str> = q.splitn(1, '=').collect();
                        h.push(QP{k: n[0], v: n[1]})
                    } else {
                        h.push(QP{k: q, v: ""})
                    }
                };
                sort_query_string(h)
            }
        }
    }

    fn canonical_request(&self) -> String {
        format!("{}\n{}\n{}\n{}\n{}\n{}", expand_string(&self.method),
                expand_string(&self.path),
                self.canonical_query_string(),
                self.canonical_headers(),
                self.signed_headers(),
                self.hashed_payload()
        )
    }

}

fn sort_query_string(mut query: Vec<QP>) -> String {
    #[inline]
    fn byte_serialize(input: &str, output: &mut String) {
        for &byte in input.as_bytes().iter() {
            percent_encode_to(&[byte], FORM_URLENCODED_ENCODE_SET, output)
        }
    }

    let mut output = String::new();

    query.sort_by( |a, b| a.k.cmp(b.k));
    for item in query.iter() {
        if output.len() > 0 {
            output.push_str("&");
        }
        byte_serialize(item.k, &mut output);
        output.push_str("=");
        byte_serialize(item.v, &mut output);
    }

    output
    // it would be marvelous to use the below, but the AWS SigV4 spec says space must be %20, and
    // rust-url gives me back a +. So, roll our own for now.
    // let qs: Vec<(String, String)> = query.iter().map(|n| (n.k.to_string(), n.v.to_string())).collect();
    // form_urlencoded::serialize_owned(qs.as_slice())
}

fn append_header(map: &mut BTreeMap<String, Vec<String>>, key: &str, value: &str) {
    let k = key.to_ascii_lowercase().to_string();

    match map.entry(k) {
        Entry::Vacant(entry) => {
            let mut values = Vec::new();
            values.push(value.to_string());
            entry.set(values);
        },
        Entry::Occupied(entry) => {
            entry.into_mut().push(value.to_string());
        }
    };
}

fn expand_string(val: &Option<String>) -> String {
    match *val {
        None => "".to_string(),
        Some(ref x) => x.to_string(),
    }
}

fn canonical_value(val: &Vec<String>) -> String {
    let mut st = String::new();
    for v in val.iter() {
        if st.len() > 0 {
            st.push(',')
        }
        if v.starts_with("\""){
            st.push_str(v.as_slice());
        } else {
            st.push_str(v.replace("  ", " ").trim());
        }
    }
    st
}

#[cfg(test)]
mod tests {
    use super::SigV4;
    use request::Header;
    use time::strptime;
    use std::collections::BTreeMap;

    macro_rules! wrap_header (
        ($key:expr) => (
            Some(&vec!($key.to_string()))
        )
    );

    #[test]
    fn test_new_sigv4() {
        let sig = SigV4::new();
        assert_eq!(sig.headers.len(), 0)
    }

    #[test]
    fn test_date() {
        let sig = SigV4::new().date();
        assert!(sig.headers.contains_key("x-amz-date"))
    }

    #[test]
    fn test_add_header() {
        let h = Header{ key: "test".to_string(), value: "a string".to_string()};

        let sig = SigV4::new().header(h);
        assert_eq!(sig.headers.get("test"), wrap_header!("a string"))
    }

    #[test]
    fn test_add_second_value() {
        let h = Header{ key: "test".to_string(), value: "a string".to_string()};
        let h2 = Header{ key: "test".to_string(), value: "another string".to_string()};

        let sig = SigV4::new().header(h).header(h2);
        assert_eq!(sig.headers.get("test"), Some(&vec!("a string".to_string(), "another string".to_string())))
    }

    #[test]
    fn test_canonical_query_encoded() {
        let sig = SigV4::new().query("a space=woo woo&x-amz-header=foo".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "a%20space=woo%20woo&x-amz-header=foo")
    }

    #[test]
    fn test_canonical_query_valueless() {
        let sig = SigV4::new().query("other=&test&x-amz-header=foo".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "other=&test=&x-amz-header=foo")
    }

    #[test]
    fn test_canonical_query_sorted() {
        let sig = SigV4::new().query("foo=&bar=&baz=".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "bar=&baz=&foo=")
    }

    // Ensure that params with the same name stay in the same order after sorting
    #[test]
    fn test_canonical_query_complex() {
        let sig = SigV4::new().query("q.options=abc&q=xyz&q=mno".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "q=xyz&q=mno&q.options=abc")
    }

    #[test]
    fn test_hashed_request() {
        let h = Header{ key: "Content-Type".to_string(), value: "application/x-www-form-urlencoded; charset=utf-8".to_string() };
        let h2 = Header{ key: "Host".to_string(), value: "iam.amazonaws.com".to_string() };

        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: Some("/".to_string()),
            method: Some("POST".to_string()),
            query: None,
            payload: Some("Action=ListUsers&Version=2010-05-08".to_string()),
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date().header(h).header(h2);

        assert_eq!(sig.hashed_canonical_request().as_slice(), "3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2")
    }

    #[test]
    fn test_signed_headers() {
        let h = Header{ key: "test".to_string(),
            value: "a string".to_string()};
        let h2 = Header{ key: "Content-Type".to_string(),
            value: "application/x-www-form-urlencoded; charset=utf-8".to_string()};
        let h3 = Header{ key: "Authorization".to_string(),
            value: "none".to_string()};

        let sig = SigV4::new().date().header(h).header(h2).header(h3);
        assert_eq!(sig.signed_headers().as_slice(), "content-type;test;x-amz-date")
    }

    #[test]
    fn test_hashed_payload() {
        let sig = SigV4::new().
            payload("Action=ListUsers&Version=2010-05-08".to_string());
        assert_eq!(sig.hashed_payload(),
        "b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

    #[test]
    fn test_empty_payload() {
        let sig = SigV4::new();
        assert_eq!(sig.hashed_payload(),
        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_headers() {
        let h = Header{ key: "Xyz".to_string(), value: "1".to_string() };
        let h2 = Header{ key: "Abc".to_string(), value: "2".to_string() };
        let h3 = Header{ key: "Mno".to_string(), value: "3".to_string() };
        let h4 = Header{ key: "Authorization".to_string(), value: "4".to_string() };
        let sig = SigV4::new().header(h).header(h2).header(h3).header(h4);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:2\nmno:3\nxyz:1\n")
    }

    #[test]
    fn test_prune_whitespace() {
        let h = Header{ key: "Abc".to_string(), value: "a  b  c".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:a b c\n")
    }

    #[test]
    fn test_no_prune_quoted() {
        let h = Header{ key: "Abc".to_string(), value: "\"a  b  c\"".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:\"a  b  c\"\n")
    }

    #[test]
    fn test_specific_date() {
        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date();
        assert_eq!(sig.headers.get("x-amz-date"), wrap_header!("20110909T233600Z"))
    }

    #[test]
    fn test_empty_canonical_request() {
        let sig = SigV4::new();
        assert_eq!(sig.canonical_request().as_slice(), "\n\n\n\n\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_request() {
        let h = Header{ key: "Content-Type".to_string(), value: "application/x-www-form-urlencoded; charset=utf-8".to_string() };
        let h2 = Header{ key: "Host".to_string(), value: "iam.amazonaws.com".to_string() };

        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: Some("/".to_string()),
            method: Some("POST".to_string()),
            query: None,
            payload: Some("Action=ListUsers&Version=2010-05-08".to_string()),
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date().header(h).header(h2);

        assert_eq!(sig.canonical_request().as_slice(), r"POST
/

content-type:application/x-www-form-urlencoded; charset=utf-8
host:iam.amazonaws.com
x-amz-date:20110909T233600Z

content-type;host;x-amz-date
b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

}
<|endoftext|>


<commit_before>#[no_mangle]
pub unsafe fn memcpy(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
    let mut i = 0;

    while i < n {
        *dst.offset(i as isize) = *src.offset(i as isize);
        i += 1;
    }

    dst
}
<commit_msg>libc: Add memset<commit_after>#[no_mangle]
pub unsafe fn memcpy(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
    let mut i = 0;

    while i < n {
        *dst.offset(i as isize) = *src.offset(i as isize);
        i += 1;
    }

    dst
}

#[no_mangle]
pub unsafe fn memset(ptr: *mut u8, value: i32, n: usize) -> *mut u8 {
    let mut i = 0;

    while i < n {
        *ptr.offset(i as isize) = value as u8;
        i += 1;
    }

    ptr
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
use futures::{
    future::{self, AbortHandle},
    prelude::*,
};
use log::info;
use publisher::Publisher as _;
use std::{
    collections::HashMap,
    io,
    net::SocketAddr,
    sync::{Arc, Mutex},
};
use subscriber::Subscriber as _;
use tarpc::{
    client, context,
    serde_transport::tcp,
    server::{self, Channel},
};
use tokio::net::ToSocketAddrs;
use tokio_serde::formats::Json;

pub mod subscriber {
    #[tarpc::service]
    pub trait Subscriber {
        async fn receive(message: String);
    }
}

pub mod publisher {
    #[tarpc::service]
    pub trait Publisher {
        async fn broadcast(message: String);
    }
}

#[derive(Clone, Debug)]
struct Subscriber {
    local_addr: SocketAddr,
}

#[tarpc::server]
impl subscriber::Subscriber for Subscriber {
    async fn receive(self, _: context::Context, message: String) {
        info!("[{}] received message: {}", self.local_addr, message);
    }
}

struct SubscriberHandle(AbortHandle);

impl Drop for SubscriberHandle {
    fn drop(&mut self) {
        self.0.abort();
    }
}

impl Subscriber {
    async fn connect(publisher_addr: impl ToSocketAddrs) -> io::Result<SubscriberHandle> {
        let publisher = tcp::connect(publisher_addr, Json::default()).await?;
        let local_addr = publisher.local_addr()?;
        let (handler, abort_handle) = future::abortable(
            server::BaseChannel::with_defaults(publisher)
                .respond_with(Subscriber { local_addr }.serve())
                .execute(),
        );
        tokio::spawn(async move {
            match handler.await {
                Ok(()) | Err(future::Aborted) => info!("[{}] subscriber shutdown.", local_addr),
            }
        });
        Ok(SubscriberHandle(abort_handle))
    }
}

#[derive(Clone, Debug)]
struct Publisher {
    clients: Arc<Mutex<HashMap<SocketAddr, subscriber::SubscriberClient>>>,
}

struct PublisherAddrs {
    publisher: SocketAddr,
    subscriptions: SocketAddr,
}

impl Publisher {
    async fn start(self) -> io::Result<PublisherAddrs> {
        let mut connecting_publishers = tcp::listen("localhost:0", Json::default).await?;

        let publisher_addrs = PublisherAddrs {
            publisher: connecting_publishers.local_addr(),
            subscriptions: Self::start_subscription_manager(self.clients.clone()).await?,
        };

        info!("[{}] listening for publishers.", publisher_addrs.publisher);
        tokio::spawn(async move {
            // Because this is just an example, we know there will only be one publisher. In more
            // realistic code, this would be a loop to continually accept new publisher
            // connections.
            let publisher = connecting_publishers.next().await.unwrap().unwrap();
            info!("[{}] publisher connected.", publisher.peer_addr().unwrap());

            server::BaseChannel::with_defaults(publisher)
                .respond_with(self.serve())
                .execute()
                .await
        });

        Ok(publisher_addrs)
    }

    async fn start_subscription_manager(
        clients: Arc<Mutex<HashMap<SocketAddr, subscriber::SubscriberClient>>>,
    ) -> io::Result<SocketAddr> {
        let mut connecting_subscribers = tcp::listen("localhost:0", Json::default)
            .await?
            .filter_map(|r| future::ready(r.ok()));
        let new_subscriber_addr = connecting_subscribers.get_ref().local_addr();
        info!("[{}] listening for subscribers.", new_subscriber_addr);

        tokio::spawn(async move {
            while let Some(conn) = connecting_subscribers.next().await {
                let subscriber_addr = conn.peer_addr().unwrap();
                info!("[{}] subscriber connected.", subscriber_addr);

                let tarpc::client::NewClient {
                    client: subscriber,
                    dispatch,
                } = subscriber::SubscriberClient::new(client::Config::default(), conn);
                clients.lock().unwrap().insert(subscriber_addr, subscriber);

                let dropped_clients = clients.clone();
                tokio::spawn(async move {
                    match dispatch.await {
                        Ok(()) => info!("[{:?}] subscriber connection closed", subscriber_addr),
                        Err(e) => info!(
                            "[{:?}] subscriber connection broken: {:?}",
                            subscriber_addr, e
                        ),
                    }
                    dropped_clients.lock().unwrap().remove(&subscriber_addr);
                });
            }
        });

        Ok(new_subscriber_addr)
    }
}

#[tarpc::server]
impl publisher::Publisher for Publisher {
    async fn broadcast(self, _: context::Context, message: String) {
        info!("received message to broadcast.");
        let mut clients = self.clients.lock().unwrap().clone();
        let mut publications = Vec::new();
        for client in clients.values_mut() {
            publications.push(client.receive(context::current(), message.clone()));
        }
        // Ignore failing subscribers. In a real pubsub, you'd want to continually retry until
        // subscribers ack. Of course, a lot would be different in a real pubsub :)
        future::join_all(publications).await;
    }
}

#[tokio::main]
async fn main() -> io::Result<()> {
    env_logger::init();

    let clients = Arc::new(Mutex::new(HashMap::new()));
    let addrs = Publisher { clients }.start().await?;

    let mut publisher = publisher::PublisherClient::new(
        client::Config::default(),
        tcp::connect(addrs.publisher, Json::default()).await?,
    )
    .spawn()?;

    let _subscriber0 = Subscriber::connect(addrs.subscriptions).await?;
    publisher
        .broadcast(context::current(), "hello to one".to_string())
        .await?;

    let _subscriber1 = Subscriber::connect(addrs.subscriptions).await?;
    publisher
        .broadcast(context::current(), "hello to all".to_string())
        .await?;

    drop(_subscriber0);

    publisher
        .broadcast(context::current(), "hello to who?".to_string())
        .await?;

    info!("done.");

    Ok(())
}
<commit_msg>Add documentation to PubSub example.<commit_after>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

/// - The PubSub server sets up TCP listeners on 2 ports, the "subscriber" port and the "publisher"
///   port. Because both publishers and subscribers initiate their connections to the PubSub
///   server, the server requires no prior knowledge of either publishers or subscribers.
///
/// - Subscribers connect to the server on the server's "subscriber" port. Once the connection is
///   established, the server acts as the client, sending messages to the subscribers via a
///   Subscriber service.
///
/// - Publishers connect to the server on the "publisher"port, and once connected, they send
///   messages to the server to be broadcast via a Publisher service.
///
///       Subscriber                        Publisher                       PubSub Server
/// T1        |                                 |                                 |
/// T2        |-----Connect------------------------------------------------------>|
/// T3        |                                 |                                 |
/// T4        |                                 |-----Connect-------------------->|
/// T5        |                                 |                                 |
/// T6        |                                 |-----Publish-------------------->|
/// T7        |                                 |                                 |
/// T8        |<------------------------------------------------------Receive-----|
/// T9        |-----(Receive OK)------------------------------------------------->|
/// T10       |                                 |                                 |
/// T11       |                                 |<--------------(Publish OK)------|

use futures::{
    future::{self, AbortHandle},
    prelude::*,
};
use log::info;
use publisher::Publisher as _;
use std::{
    collections::HashMap,
    io,
    net::SocketAddr,
    sync::{Arc, Mutex},
};
use subscriber::Subscriber as _;
use tarpc::{
    client, context,
    serde_transport::tcp,
    server::{self, Channel},
};
use tokio::net::ToSocketAddrs;
use tokio_serde::formats::Json;

pub mod subscriber {
    #[tarpc::service]
    pub trait Subscriber {
        async fn receive(message: String);
    }
}

pub mod publisher {
    #[tarpc::service]
    pub trait Publisher {
        async fn broadcast(message: String);
    }
}

#[derive(Clone, Debug)]
struct Subscriber {
    local_addr: SocketAddr,
}

#[tarpc::server]
impl subscriber::Subscriber for Subscriber {
    async fn receive(self, _: context::Context, message: String) {
        info!("[{}] received message: {}", self.local_addr, message);
    }
}

struct SubscriberHandle(AbortHandle);

impl Drop for SubscriberHandle {
    fn drop(&mut self) {
        self.0.abort();
    }
}

impl Subscriber {
    async fn connect(publisher_addr: impl ToSocketAddrs) -> io::Result<SubscriberHandle> {
        let publisher = tcp::connect(publisher_addr, Json::default()).await?;
        let local_addr = publisher.local_addr()?;
        let (handler, abort_handle) = future::abortable(
            server::BaseChannel::with_defaults(publisher)
                .respond_with(Subscriber { local_addr }.serve())
                .execute(),
        );
        tokio::spawn(async move {
            match handler.await {
                Ok(()) | Err(future::Aborted) => info!("[{}] subscriber shutdown.", local_addr),
            }
        });
        Ok(SubscriberHandle(abort_handle))
    }
}

#[derive(Clone, Debug)]
struct Publisher {
    clients: Arc<Mutex<HashMap<SocketAddr, subscriber::SubscriberClient>>>,
}

struct PublisherAddrs {
    publisher: SocketAddr,
    subscriptions: SocketAddr,
}

impl Publisher {
    async fn start(self) -> io::Result<PublisherAddrs> {
        let mut connecting_publishers = tcp::listen("localhost:0", Json::default).await?;

        let publisher_addrs = PublisherAddrs {
            publisher: connecting_publishers.local_addr(),
            subscriptions: Self::start_subscription_manager(self.clients.clone()).await?,
        };

        info!("[{}] listening for publishers.", publisher_addrs.publisher);
        tokio::spawn(async move {
            // Because this is just an example, we know there will only be one publisher. In more
            // realistic code, this would be a loop to continually accept new publisher
            // connections.
            let publisher = connecting_publishers.next().await.unwrap().unwrap();
            info!("[{}] publisher connected.", publisher.peer_addr().unwrap());

            server::BaseChannel::with_defaults(publisher)
                .respond_with(self.serve())
                .execute()
                .await
        });

        Ok(publisher_addrs)
    }

    async fn start_subscription_manager(
        clients: Arc<Mutex<HashMap<SocketAddr, subscriber::SubscriberClient>>>,
    ) -> io::Result<SocketAddr> {
        let mut connecting_subscribers = tcp::listen("localhost:0", Json::default)
            .await?
            .filter_map(|r| future::ready(r.ok()));
        let new_subscriber_addr = connecting_subscribers.get_ref().local_addr();
        info!("[{}] listening for subscribers.", new_subscriber_addr);

        tokio::spawn(async move {
            while let Some(conn) = connecting_subscribers.next().await {
                let subscriber_addr = conn.peer_addr().unwrap();
                info!("[{}] subscriber connected.", subscriber_addr);

                let tarpc::client::NewClient {
                    client: subscriber,
                    dispatch,
                } = subscriber::SubscriberClient::new(client::Config::default(), conn);
                clients.lock().unwrap().insert(subscriber_addr, subscriber);

                let dropped_clients = clients.clone();
                tokio::spawn(async move {
                    match dispatch.await {
                        Ok(()) => info!("[{:?}] subscriber connection closed", subscriber_addr),
                        Err(e) => info!(
                            "[{:?}] subscriber connection broken: {:?}",
                            subscriber_addr, e
                        ),
                    }
                    dropped_clients.lock().unwrap().remove(&subscriber_addr);
                });
            }
        });

        Ok(new_subscriber_addr)
    }
}

#[tarpc::server]
impl publisher::Publisher for Publisher {
    async fn broadcast(self, _: context::Context, message: String) {
        info!("received message to broadcast.");
        let mut clients = self.clients.lock().unwrap().clone();
        let mut publications = Vec::new();
        for client in clients.values_mut() {
            publications.push(client.receive(context::current(), message.clone()));
        }
        // Ignore failing subscribers. In a real pubsub, you'd want to continually retry until
        // subscribers ack. Of course, a lot would be different in a real pubsub :)
        future::join_all(publications).await;
    }
}

#[tokio::main]
async fn main() -> io::Result<()> {
    env_logger::init();

    let clients = Arc::new(Mutex::new(HashMap::new()));
    let addrs = Publisher { clients }.start().await?;

    let mut publisher = publisher::PublisherClient::new(
        client::Config::default(),
        tcp::connect(addrs.publisher, Json::default()).await?,
    )
    .spawn()?;

    let _subscriber0 = Subscriber::connect(addrs.subscriptions).await?;
    publisher
        .broadcast(context::current(), "hello to one".to_string())
        .await?;

    let _subscriber1 = Subscriber::connect(addrs.subscriptions).await?;
    publisher
        .broadcast(context::current(), "hello to all".to_string())
        .await?;

    drop(_subscriber0);

    publisher
        .broadcast(context::current(), "hello to who?".to_string())
        .await?;

    info!("done.");

    Ok(())
}
<|endoftext|>