<commit_before>extern crate rustc_serialize;

use std::collections::HashSet;
use std::env;
use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::exit;
use std::process::{Command, Stdio};

mod config;

fn main() {
    // Fetching the configuration for the build.
    let config = config::load();

    // Building the `android-artifacts` directory that will contain all the artifacts.
    let android_artifacts_dir = {
        let target_dir = find_project_target();
        target_dir.join("android-artifacts")
    };
    build_android_artifacts_dir(&android_artifacts_dir, &config);

    for build_target in config.build_targets.iter() {
        let build_target_dir = android_artifacts_dir.join(build_target);

        // Finding the tools in the NDK.
        let gcc_path = {
            let arch = if build_target.starts_with("arm") { "arm-linux-androideabi" }
                       else if build_target.starts_with("aarch64") { "aarch64-linux-android" }
                       else if build_target.starts_with("i") { "x86" }
                       else if build_target.starts_with("x86_64") { "x86_64" }
                       else if build_target.starts_with("mipsel") { "mipsel-linux-android" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };

            config.ndk_path.join(format!("toolchains/{}-4.9/prebuilt/linux-x86_64", arch))      // FIXME: correct host arch
                           .join(format!("bin/{}-gcc", arch))
        };

        let gcc_sysroot = {
            let arch = if build_target.starts_with("arm") { "arm" }
                       else if build_target.starts_with("aarch64") { "arm64" }
                       else if build_target.starts_with("i") { "x86" }
                       else if build_target.starts_with("x86_64") { "x86_64" }
                       else if build_target.starts_with("mips") { "mips" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };
            config.ndk_path.join(format!("platforms/android-{v}/arch-{a}",
                                         v = config.android_version, a = arch))
        };

        // Compiling android_native_app_glue.c
        if Command::new(&gcc_path)
            .arg(config.ndk_path.join("sources/android/native_app_glue/android_native_app_glue.c"))
            .arg("-c")
            .arg("-o").arg(build_target_dir.join("android_native_app_glue.o"))
            .arg("--sysroot").arg(&gcc_sysroot)
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .status().unwrap().code().unwrap() != 0
        {
            exit(1);
        }

        // Compiling injected-glue
        let injected_glue_lib = {
            if Command::new("rustc")
                .arg(android_artifacts_dir.join("injected-glue/lib.rs"))
                .arg("--crate-type").arg("rlib")
                .arg("--crate-name").arg("cargo_apk_injected_glue")
                .arg("--target").arg(build_target)
                .arg("--out-dir").arg(&build_target_dir)
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .status().unwrap().code().unwrap() != 0
            {
                exit(1);
            }

            let output = Command::new("rustc")
                .arg(android_artifacts_dir.join("injected-glue/lib.rs"))
                .arg("--crate-type").arg("rlib")
                .arg("--crate-name").arg("cargo_apk_injected_glue")
                .arg("--target").arg(build_target)
                .arg("--out-dir").arg(&build_target_dir)
                .arg("--print").arg("file-names")
                .stdout(Stdio::piped())
                .stderr(Stdio::inherit())
                .output().unwrap();
            let stdout = String::from_utf8(output.stdout).unwrap();
            build_target_dir.join(stdout.lines().next().unwrap())
        };

        // Compiling glue_obj.rs
        {
            let mut file = File::create(build_target_dir.join("glue_obj.rs")).unwrap();
            file.write_all(&include_bytes!("../glue_obj.rs")[..]).unwrap();
        }
        if Command::new("rustc")
            .arg(build_target_dir.join("glue_obj.rs"))
            .arg("--crate-type").arg("staticlib")
            .arg("--target").arg(build_target)
            .arg("--extern").arg(format!("cargo_apk_injected_glue={}", injected_glue_lib.to_string_lossy()))
            .arg("--emit").arg("obj")
            .arg("-o").arg(build_target_dir.join("glue_obj.o"))
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .status().unwrap().code().unwrap() != 0
        {
            exit(1);
        }

        // Directory where we will put the native libraries for ant to pick them up.
        let native_libraries_dir = {
            let abi = if build_target.starts_with("arm") { "armeabi" }
                      // TODO: armeabi-v7a
                      else if build_target.starts_with("aarch64") { "arm64-v8a" }
                      else if build_target.starts_with("i") { "x86" }
                      else if build_target.starts_with("x86_64") { "x86_64" }
                      else if build_target.starts_with("mips") { "mips" }
                      // TODO: mips64
                      else { panic!("Unknown or incompatible build target: {}", build_target) };

            android_artifacts_dir.join(format!("build/libs/{}", abi))
        };

        if fs::metadata(&native_libraries_dir).is_err() {
            fs::DirBuilder::new().recursive(true).create(&native_libraries_dir).unwrap();
        }

        // Compiling the crate thanks to `cargo rustc`. We set the linker to `linker_exe`, a hacky
        // linker that will tweak the options passed to `gcc`.
        if Command::new("cargo").arg("rustc")
            .arg("--verbose")
            .arg("--target").arg(build_target)
            .arg("--")
            .arg("-C").arg(format!("linker={}", android_artifacts_dir.join("linker_exe")
                                                                     .to_string_lossy()))
            .arg("--extern").arg(format!("cargo_apk_injected_glue={}", injected_glue_lib.to_string_lossy()))
            .env("CARGO_APK_GCC", gcc_path.as_os_str())
            .env("CARGO_APK_GCC_SYSROOT", gcc_sysroot.as_os_str())
            .env("CARGO_APK_NATIVE_APP_GLUE", build_target_dir.join("android_native_app_glue.o"))
            .env("CARGO_APK_GLUE_OBJ", build_target_dir.join("glue_obj.o"))
            .env("CARGO_APK_LINKER_OUTPUT", native_libraries_dir.join("libmain.so"))
            .env("CARGO_APK_LIB_PATHS_OUTPUT", build_target_dir.join("lib_paths"))
            .env("CARGO_APK_LIBS_OUTPUT", build_target_dir.join("libs"))
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .status().unwrap().code().unwrap() != 0
        {
            exit(1);
        }

        // Determine the list of library paths and libraries, and copy them to the right location.
        {
            let lib_paths: Vec<String> = {
                let l = BufReader::new(File::open(build_target_dir.join("lib_paths")).unwrap());
                l.lines().map(|l| l.unwrap()).collect()
            };

            let libs_list: HashSet<String> = {
                let l = BufReader::new(File::open(build_target_dir.join("libs")).unwrap());
                l.lines().map(|l| l.unwrap()).collect()
            };

            for dir in lib_paths.iter() {
                fs::read_dir(&dir).and_then(|paths| {
                    for path in paths {
                        let path = path.unwrap().path();
                        match (path.file_name(), path.extension()) {
                            (Some(filename), Some(ext)) => {
                                let filename = filename.to_str().unwrap();
                                if filename.starts_with("lib") && ext == "so" &&
                                   libs_list.contains(filename)
                                {
                                    fs::copy(&path, native_libraries_dir.join(filename)).unwrap();
                                }
                            }
                            _ => {}
                        }
                    }

                    Ok(())
                }).ok();
            }
        }
    }

    // Invoking `ant` from within `android-artifacts` in order to compile the project.
    if Command::new(Path::new("ant"))
        .arg("debug")
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .current_dir(android_artifacts_dir.join("build"))
        .status().unwrap().code().unwrap() != 0
    {
        exit(1);
    }
}

// Determines the location of the `target` directory of the project we're compiling.
fn find_project_target() -> PathBuf {
    let output = Command::new("cargo").arg("locate-project").output().unwrap();

    if !output.status.success() {
        if let Some(code) = output.status.code() {
            exit(code);
        } else {
            exit(-1);
        }
    }

    #[derive(RustcDecodable)]
    struct Data { root: String }
    let stdout = String::from_utf8(output.stdout).unwrap();
    let decoded: Data = rustc_serialize::json::decode(&stdout).unwrap();
    let path = Path::new(&decoded.root);
    path.parent().unwrap().join("target")
}

fn build_android_artifacts_dir(path: &Path, config: &config::Config) {
    if fs::metadata(path.join("build")).is_err() {
        fs::DirBuilder::new().recursive(true).create(path.join("build")).unwrap();
    }

    {
        fs::create_dir_all(path.join("injected-glue")).unwrap();

        let mut lib = File::create(path.join("injected-glue/lib.rs")).unwrap();
        lib.write_all(&include_bytes!("../injected-glue/lib.rs")[..]).unwrap();

        let mut ffi = File::create(path.join("injected-glue/ffi.rs")).unwrap();
        ffi.write_all(&include_bytes!("../injected-glue/ffi.rs")[..]).unwrap();
    }

    build_linker(path);
    build_manifest(path, "test", "test");
    build_java_src(path);
    build_build_xml(path);
    build_local_properties(path, config);
    build_project_properties(path, config);

    for target in config.build_targets.iter() {
        if fs::metadata(path.join(target)).is_err() {
            fs::DirBuilder::new().recursive(true).create(path.join(target)).unwrap();
        }
    }
}

fn build_linker(path: &Path) {
    let exe_file = path.join("linker_exe");
    let src_file = path.join("linker_src");

    if fs::metadata(&exe_file).is_ok() {
        return;
    }

    {
        let mut src_write = fs::File::create(&src_file).unwrap();
        src_write.write_all(&include_bytes!("../linker.rs")[..]).unwrap();
    }

    let status = Command::new("rustc").arg(src_file).arg("-o").arg(&exe_file).status().unwrap();
    assert!(status.success());

    assert!(fs::metadata(&exe_file).is_ok());
}

fn build_java_src(path: &Path) {
    let file = path.join("build/src/rust/glutin/MainActivity.java");
    if fs::metadata(&file).is_ok() { return; }
    fs::create_dir_all(file.parent().unwrap()).unwrap();
    let mut file = File::create(&file).unwrap();

    let libs_string = "".to_owned();

    // FIXME: this needs to insert each library that we want to load ; the problem is that they could
    //        vary between platforms
    /*for (name, _) in libs.iter() {
        // Strip off the 'lib' prefix and ".so" suffix.
        let line = format!("        System.loadLibrary(\"{}\");\n",
            name.trim_left_matches("lib").trim_right_matches(".so"));
        libs_string.push_str(&*line);
    }*/

    write!(file, r#"package rust.glutin;

public class MainActivity extends android.app.NativeActivity {{
    static {{
        {0}
    }}
}}"#, libs_string).unwrap();
}

fn build_manifest(path: &Path, crate_name: &str, activity_name: &str) {
    let file = path.join("build/AndroidManifest.xml");
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();

    write!(file, r#"<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">

    <uses-sdk android:minSdkVersion="9" />

    <uses-feature android:glEsVersion="0x00020000" android:required="true"></uses-feature>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application android:label="{0}">
        <activity android:name="{1}"
                android:label="{0}"
                android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<!-- END_INCLUDE(manifest) -->
"#, crate_name, activity_name).unwrap()
}

fn build_build_xml(path: &Path) {
    let file = path.join("build/build.xml");
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();

    write!(file, r#"<?xml version="1.0" encoding="UTF-8"?>
<project name="rust-android" default="help">
    <property file="local.properties" />
    <loadproperties srcFile="project.properties" />
    <import file="custom_rules.xml" optional="true" />
    <import file="${{sdk.dir}}/tools/ant/build.xml" />
</project>
"#).unwrap()
}

fn build_local_properties(path: &Path, config: &config::Config) {
    let file = path.join("build/local.properties");
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();

    let abs_dir = if config.sdk_path.is_absolute() {
        config.sdk_path.clone()
    } else {
        env::current_dir().unwrap().join(&config.sdk_path)
    };

    write!(file, r"sdk.dir={}", abs_dir.to_str().unwrap()).unwrap();
}

fn build_project_properties(path: &Path, config: &config::Config) {
    let file = path.join("build/project.properties");
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();
    write!(file, r"target=android-{}", config.android_version).unwrap();
}
<commit_msg>Be slightly more tolerant when building<commit_after>extern crate rustc_serialize;

use std::collections::HashSet;
use std::env;
use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::exit;
use std::process::{Command, Stdio};

mod config;

fn main() {
    // Fetching the configuration for the build.
    let config = config::load();

    // Building the `android-artifacts` directory that will contain all the artifacts.
    let android_artifacts_dir = {
        let target_dir = find_project_target();
        target_dir.join("android-artifacts")
    };
    build_android_artifacts_dir(&android_artifacts_dir, &config);

    for build_target in config.build_targets.iter() {
        let build_target_dir = android_artifacts_dir.join(build_target);

        // Finding the tools in the NDK.
        let gcc_path = {
            let arch = if build_target.starts_with("arm") { "arm-linux-androideabi" }
                       else if build_target.starts_with("aarch64") { "aarch64-linux-android" }
                       else if build_target.starts_with("i") { "x86" }
                       else if build_target.starts_with("x86_64") { "x86_64" }
                       else if build_target.starts_with("mipsel") { "mipsel-linux-android" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };

            config.ndk_path.join(format!("toolchains/{}-4.9/prebuilt/linux-x86_64", arch))      // FIXME: correct host arch
                           .join(format!("bin/{}-gcc", arch))
        };

        let gcc_sysroot = {
            let arch = if build_target.starts_with("arm") { "arm" }
                       else if build_target.starts_with("aarch64") { "arm64" }
                       else if build_target.starts_with("i") { "x86" }
                       else if build_target.starts_with("x86_64") { "x86_64" }
                       else if build_target.starts_with("mips") { "mips" }
                       // TODO: mips64
                       else { panic!("Unknown or incompatible build target: {}", build_target) };
            config.ndk_path.join(format!("platforms/android-{v}/arch-{a}",
                                         v = config.android_version, a = arch))
        };

        // Compiling android_native_app_glue.c
        if Command::new(&gcc_path)
            .arg(config.ndk_path.join("sources/android/native_app_glue/android_native_app_glue.c"))
            .arg("-c")
            .arg("-o").arg(build_target_dir.join("android_native_app_glue.o"))
            .arg("--sysroot").arg(&gcc_sysroot)
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .status().unwrap().code().unwrap() != 0
        {
            exit(1);
        }

        // Compiling injected-glue
        let injected_glue_lib = {
            if Command::new("rustc")
                .arg(android_artifacts_dir.join("injected-glue/lib.rs"))
                .arg("--crate-type").arg("rlib")
                .arg("--crate-name").arg("cargo_apk_injected_glue")
                .arg("--target").arg(build_target)
                .arg("--out-dir").arg(&build_target_dir)
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .status().unwrap().code().unwrap() != 0
            {
                exit(1);
            }

            let output = Command::new("rustc")
                .arg(android_artifacts_dir.join("injected-glue/lib.rs"))
                .arg("--crate-type").arg("rlib")
                .arg("--crate-name").arg("cargo_apk_injected_glue")
                .arg("--target").arg(build_target)
                .arg("--out-dir").arg(&build_target_dir)
                .arg("--print").arg("file-names")
                .stdout(Stdio::piped())
                .stderr(Stdio::inherit())
                .output().unwrap();
            let stdout = String::from_utf8(output.stdout).unwrap();
            build_target_dir.join(stdout.lines().next().unwrap())
        };

        // Compiling glue_obj.rs
        {
            let mut file = File::create(build_target_dir.join("glue_obj.rs")).unwrap();
            file.write_all(&include_bytes!("../glue_obj.rs")[..]).unwrap();
        }
        if Command::new("rustc")
            .arg(build_target_dir.join("glue_obj.rs"))
            .arg("--crate-type").arg("staticlib")
            .arg("--target").arg(build_target)
            .arg("--extern").arg(format!("cargo_apk_injected_glue={}", injected_glue_lib.to_string_lossy()))
            .arg("--emit").arg("obj")
            .arg("-o").arg(build_target_dir.join("glue_obj.o"))
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .status().unwrap().code().unwrap() != 0
        {
            exit(1);
        }

        // Directory where we will put the native libraries for ant to pick them up.
        let native_libraries_dir = {
            let abi = if build_target.starts_with("arm") { "armeabi" }
                      // TODO: armeabi-v7a
                      else if build_target.starts_with("aarch64") { "arm64-v8a" }
                      else if build_target.starts_with("i") { "x86" }
                      else if build_target.starts_with("x86_64") { "x86_64" }
                      else if build_target.starts_with("mips") { "mips" }
                      // TODO: mips64
                      else { panic!("Unknown or incompatible build target: {}", build_target) };

            android_artifacts_dir.join(format!("build/libs/{}", abi))
        };

        if fs::metadata(&native_libraries_dir).is_err() {
            fs::DirBuilder::new().recursive(true).create(&native_libraries_dir).unwrap();
        }

        // Compiling the crate thanks to `cargo rustc`. We set the linker to `linker_exe`, a hacky
        // linker that will tweak the options passed to `gcc`.
        if Command::new("cargo").arg("rustc")
            .arg("--verbose")
            .arg("--target").arg(build_target)
            .arg("--")
            .arg("-C").arg(format!("linker={}", android_artifacts_dir.join("linker_exe")
                                                                     .to_string_lossy()))
            .arg("--extern").arg(format!("cargo_apk_injected_glue={}", injected_glue_lib.to_string_lossy()))
            .env("CARGO_APK_GCC", gcc_path.as_os_str())
            .env("CARGO_APK_GCC_SYSROOT", gcc_sysroot.as_os_str())
            .env("CARGO_APK_NATIVE_APP_GLUE", build_target_dir.join("android_native_app_glue.o"))
            .env("CARGO_APK_GLUE_OBJ", build_target_dir.join("glue_obj.o"))
            .env("CARGO_APK_LINKER_OUTPUT", native_libraries_dir.join("libmain.so"))
            .env("CARGO_APK_LIB_PATHS_OUTPUT", build_target_dir.join("lib_paths"))
            .env("CARGO_APK_LIBS_OUTPUT", build_target_dir.join("libs"))
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .status().unwrap().code().unwrap() != 0
        {
            exit(1);
        }

        // Determine the list of library paths and libraries, and copy them to the right location.
        {
            let lib_paths: Vec<String> = {
                if let Ok(f) = File::open(build_target_dir.join("lib_paths")) {
                    let l = BufReader::new(f);
                    l.lines().map(|l| l.unwrap()).collect()
                } else {
                    vec![]
                }
            };

            let libs_list: HashSet<String> = {
                if let Ok(f) = File::open(build_target_dir.join("libs")) {
                    let l = BufReader::new(f);
                    l.lines().map(|l| l.unwrap()).collect()
                } else {
                    HashSet::new()
                }
            };

            for dir in lib_paths.iter() {
                fs::read_dir(&dir).and_then(|paths| {
                    for path in paths {
                        let path = path.unwrap().path();
                        match (path.file_name(), path.extension()) {
                            (Some(filename), Some(ext)) => {
                                let filename = filename.to_str().unwrap();
                                if filename.starts_with("lib") && ext == "so" &&
                                   libs_list.contains(filename)
                                {
                                    fs::copy(&path, native_libraries_dir.join(filename)).unwrap();
                                }
                            }
                            _ => {}
                        }
                    }

                    Ok(())
                }).ok();
            }
        }
    }

    // Invoking `ant` from within `android-artifacts` in order to compile the project.
    if Command::new(Path::new("ant"))
        .arg("debug")
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .current_dir(android_artifacts_dir.join("build"))
        .status().unwrap().code().unwrap() != 0
    {
        exit(1);
    }
}

// Determines the location of the `target` directory of the project we're compiling.
fn find_project_target() -> PathBuf {
    let output = Command::new("cargo").arg("locate-project").output().unwrap();

    if !output.status.success() {
        if let Some(code) = output.status.code() {
            exit(code);
        } else {
            exit(-1);
        }
    }

    #[derive(RustcDecodable)]
    struct Data { root: String }
    let stdout = String::from_utf8(output.stdout).unwrap();
    let decoded: Data = rustc_serialize::json::decode(&stdout).unwrap();
    let path = Path::new(&decoded.root);
    path.parent().unwrap().join("target")
}

fn build_android_artifacts_dir(path: &Path, config: &config::Config) {
    if fs::metadata(path.join("build")).is_err() {
        fs::DirBuilder::new().recursive(true).create(path.join("build")).unwrap();
    }

    {
        fs::create_dir_all(path.join("injected-glue")).unwrap();

        let mut lib = File::create(path.join("injected-glue/lib.rs")).unwrap();
        lib.write_all(&include_bytes!("../injected-glue/lib.rs")[..]).unwrap();

        let mut ffi = File::create(path.join("injected-glue/ffi.rs")).unwrap();
        ffi.write_all(&include_bytes!("../injected-glue/ffi.rs")[..]).unwrap();
    }

    build_linker(path);
    build_manifest(path, "test", "test");
    build_java_src(path);
    build_build_xml(path);
    build_local_properties(path, config);
    build_project_properties(path, config);

    for target in config.build_targets.iter() {
        if fs::metadata(path.join(target)).is_err() {
            fs::DirBuilder::new().recursive(true).create(path.join(target)).unwrap();
        }
    }
}

fn build_linker(path: &Path) {
    let exe_file = path.join("linker_exe");
    let src_file = path.join("linker_src");

    if fs::metadata(&exe_file).is_ok() {
        return;
    }

    {
        let mut src_write = fs::File::create(&src_file).unwrap();
        src_write.write_all(&include_bytes!("../linker.rs")[..]).unwrap();
    }

    let status = Command::new("rustc").arg(src_file).arg("-o").arg(&exe_file).status().unwrap();
    assert!(status.success());

    assert!(fs::metadata(&exe_file).is_ok());
}

fn build_java_src(path: &Path) {
    let file = path.join("build/src/rust/glutin/MainActivity.java");
    fs::create_dir_all(file.parent().unwrap()).unwrap();
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();

    let libs_string = "".to_owned();

    // FIXME: this needs to insert each library that we want to load ; the problem is that they could
    //        vary between platforms
    /*for (name, _) in libs.iter() {
        // Strip off the 'lib' prefix and ".so" suffix.
        let line = format!("        System.loadLibrary(\"{}\");\n",
            name.trim_left_matches("lib").trim_right_matches(".so"));
        libs_string.push_str(&*line);
    }*/

    write!(file, r#"package rust.glutin;

public class MainActivity extends android.app.NativeActivity {{
    static {{
        {0}
    }}
}}"#, libs_string).unwrap();
}

fn build_manifest(path: &Path, crate_name: &str, activity_name: &str) {
    let file = path.join("build/AndroidManifest.xml");
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();

    write!(file, r#"<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">

    <uses-sdk android:minSdkVersion="9" />

    <uses-feature android:glEsVersion="0x00020000" android:required="true"></uses-feature>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application android:label="{0}">
        <activity android:name="{1}"
                android:label="{0}"
                android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<!-- END_INCLUDE(manifest) -->
"#, crate_name, activity_name).unwrap()
}

fn build_build_xml(path: &Path) {
    let file = path.join("build/build.xml");
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();

    write!(file, r#"<?xml version="1.0" encoding="UTF-8"?>
<project name="rust-android" default="help">
    <property file="local.properties" />
    <loadproperties srcFile="project.properties" />
    <import file="custom_rules.xml" optional="true" />
    <import file="${{sdk.dir}}/tools/ant/build.xml" />
</project>
"#).unwrap()
}

fn build_local_properties(path: &Path, config: &config::Config) {
    let file = path.join("build/local.properties");
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();

    let abs_dir = if config.sdk_path.is_absolute() {
        config.sdk_path.clone()
    } else {
        env::current_dir().unwrap().join(&config.sdk_path)
    };

    write!(file, r"sdk.dir={}", abs_dir.to_str().unwrap()).unwrap();
}

fn build_project_properties(path: &Path, config: &config::Config) {
    let file = path.join("build/project.properties");
    if fs::metadata(&file).is_ok() { return; }
    let mut file = File::create(&file).unwrap();
    write!(file, r"target=android-{}", config.android_version).unwrap();
}
<|endoftext|>


<commit_before>use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;

/// Cutoff: fraction of sample rate (eg. frequencies below sample_rate / cutoff are preserved)
/// Transition band: fraction of sample rate (how harsh a cutoff this is)
pub fn lowpass_filter(cutoff: f64, band: f64) -> Vec<f64> {
    let mut n = (4.0 / band).ceil() as uint;
    if n % 2 == 1 { n += 1; }

    let sinc = |x: f64| -> f64 {
        (x * PI).sin() / (x * PI)
    };

    let sinc_wave = Vec::from_fn(n, |i| {
        sinc(2.0 * cutoff * (i as f64 - (n as f64 - 1.0) / 2.0))
    });

    let blackman_window = Vec::from_fn(n, |i| {
        0.42 - 0.5 * (2.0 * PI * i as f64 / (n as f64 - 1.0)).cos()
        + 0.08 * (4.0 * PI * i as f64 / (n as f64 - 1.0)).cos()
    });

    let filter: Vec<f64> =  sinc_wave.iter().zip(blackman_window.iter()).map(|tup| {
        *tup.val0() * *tup.val1()
    }).collect();

    // Normalize
    let sum = filter.iter().fold(0.0, |acc, el| {
        acc + *el
    });

    filter.iter().map(|el| {
        *el / sum
    }).collect()
}

pub fn highpass_filter(cutoff: f64, band: f64) -> Vec<f64> {
    spectral_invert(lowpass_filter(cutoff, band))
}

pub fn bandpass_filter(low_frequency: f64, high_frequency: f64, band: f64) -> Vec<f64> {
    assert!(low_frequency <= high_frequency);
    let lowpass = lowpass_filter(high_frequency, band);
    let highpass = highpass_filter(low_frequency, band);
    convolve(highpass, lowpass)
}

pub fn bandreject_filter(low_frequency: f64, high_frequency: f64, band: f64) -> Vec<f64> {
    assert!(low_frequency <= high_frequency);
    let lowpass = lowpass_filter(low_frequency, band);
    let highpass = highpass_filter(high_frequency, band);
    add(highpass, lowpass)
}

pub fn spectral_invert(filter: Vec<f64>) -> Vec<f64> {
    assert_eq!(filter.len() % 2, 0);
    let mut count = 0;

    filter.iter().map(|el| {
        let add = if count == filter.len() / 2 { 1.0 } else { 0.0 };
        count += 1;
        -*el + add
    }).collect()
}

// Output will be longer than input as we add to border
pub fn convolve(filter: Vec<f64>, input: Vec<f64>) -> Vec<f64> {
    let mut output: Vec<f64> = Vec::new();
    let h_len = (filter.len() / 2) as int;

    for i in range(-(filter.len() as int / 2), input.len() as int - 1) {
        output.push(0.0);
        for j in range(0i, filter.len() as int) {
            let input_idx = i + j;
            let output_idx = i + h_len;
            if input_idx < 0 || input_idx >= input.len() as int { continue; }
            output[output_idx as uint] += input[input_idx as uint] * filter[j as uint]
        }
    }

    output
}

pub fn add(left: Vec<f64>, right: Vec<f64>) -> Vec<f64> {
    left.iter().zip(right.iter()).map(|tup| {
        *tup.val0() + *tup.val1()
    }).collect()
}

pub fn cutoff_from_frequency(frequency: f64, sample_rate: uint) -> f64 {
    frequency / sample_rate as f64
}

#[test]
fn it_convolves() {
    let filter = vec!(1.0, 1.0, 1.0);
    let input = vec!(0.0, 0.0, 3.0, 0.0, 3.0, 0.0, 0.0);
    let output = vec!(0.0, 3.0, 3.0, 6.0, 3.0, 3.0, 0.0);
    assert_eq!(convolve(filter, input), output);
}
<commit_msg>Add test for sample addition<commit_after>use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;

/// Cutoff: fraction of sample rate (eg. frequencies below sample_rate / cutoff are preserved)
/// Transition band: fraction of sample rate (how harsh a cutoff this is)
pub fn lowpass_filter(cutoff: f64, band: f64) -> Vec<f64> {
    let mut n = (4.0 / band).ceil() as uint;
    if n % 2 == 1 { n += 1; }

    let sinc = |x: f64| -> f64 {
        (x * PI).sin() / (x * PI)
    };

    let sinc_wave = Vec::from_fn(n, |i| {
        sinc(2.0 * cutoff * (i as f64 - (n as f64 - 1.0) / 2.0))
    });

    let blackman_window = Vec::from_fn(n, |i| {
        0.42 - 0.5 * (2.0 * PI * i as f64 / (n as f64 - 1.0)).cos()
        + 0.08 * (4.0 * PI * i as f64 / (n as f64 - 1.0)).cos()
    });

    let filter: Vec<f64> =  sinc_wave.iter().zip(blackman_window.iter()).map(|tup| {
        *tup.val0() * *tup.val1()
    }).collect();

    // Normalize
    let sum = filter.iter().fold(0.0, |acc, el| {
        acc + *el
    });

    filter.iter().map(|el| {
        *el / sum
    }).collect()
}

pub fn highpass_filter(cutoff: f64, band: f64) -> Vec<f64> {
    spectral_invert(lowpass_filter(cutoff, band))
}

pub fn bandpass_filter(low_frequency: f64, high_frequency: f64, band: f64) -> Vec<f64> {
    assert!(low_frequency <= high_frequency);
    let lowpass = lowpass_filter(high_frequency, band);
    let highpass = highpass_filter(low_frequency, band);
    convolve(highpass, lowpass)
}

pub fn bandreject_filter(low_frequency: f64, high_frequency: f64, band: f64) -> Vec<f64> {
    assert!(low_frequency <= high_frequency);
    let lowpass = lowpass_filter(low_frequency, band);
    let highpass = highpass_filter(high_frequency, band);
    add(highpass, lowpass)
}

pub fn spectral_invert(filter: Vec<f64>) -> Vec<f64> {
    assert_eq!(filter.len() % 2, 0);
    let mut count = 0;

    filter.iter().map(|el| {
        let add = if count == filter.len() / 2 { 1.0 } else { 0.0 };
        count += 1;
        -*el + add
    }).collect()
}

// Output will be longer than input as we add to border
pub fn convolve(filter: Vec<f64>, input: Vec<f64>) -> Vec<f64> {
    let mut output: Vec<f64> = Vec::new();
    let h_len = (filter.len() / 2) as int;

    for i in range(-(filter.len() as int / 2), input.len() as int - 1) {
        output.push(0.0);
        for j in range(0i, filter.len() as int) {
            let input_idx = i + j;
            let output_idx = i + h_len;
            if input_idx < 0 || input_idx >= input.len() as int { continue; }
            output[output_idx as uint] += input[input_idx as uint] * filter[j as uint]
        }
    }

    output
}

pub fn add(left: Vec<f64>, right: Vec<f64>) -> Vec<f64> {
    left.iter().zip(right.iter()).map(|tup| {
        *tup.val0() + *tup.val1()
    }).collect()
}

pub fn cutoff_from_frequency(frequency: f64, sample_rate: uint) -> f64 {
    frequency / sample_rate as f64
}

#[test]
fn it_convolves() {
    let filter = vec!(1.0, 1.0, 1.0);
    let input = vec!(0.0, 0.0, 3.0, 0.0, 3.0, 0.0, 0.0);
    let output = vec!(0.0, 3.0, 3.0, 6.0, 3.0, 3.0, 0.0);
    assert_eq!(convolve(filter, input), output);
}

#[test]
fn it_does_elementwise_addition_of_two_samples() {
    let a = vec!(1.0, -1.0, -8.0);
    let b = vec!(-1.0, 5.0, 3.0);
    let expected = vec!(0.0, 4.0, -5.0);
    assert_eq!(add(a, b), expected);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate varlink;

use org_example_more::*;
use std::{thread, time};
use std::env;
use std::io;
use std::io::{Error, ErrorKind};
use std::process::exit;
use varlink::VarlinkService;

// Dynamically build the varlink rust code.
mod org_example_more;

struct MyOrgExampleMore;

impl VarlinkInterface for MyOrgExampleMore {
    fn ping(&self, call: &mut _CallPing, ping: Option<String>) -> io::Result<()> {
        return call.reply(ping);
    }

    fn stop_serving(&self, call: &mut _CallStopServing) -> io::Result<()> {
        call.reply()?;
        Err(Error::new(ErrorKind::ConnectionRefused, "Disconnect"))
    }

    fn test_more(&self, call: &mut _CallTestMore, n: Option<i64>) -> io::Result<()> {
        if n == None {
            return call.reply_invalid_parameter(Some("n".into()));
        }
        let n = n.unwrap();

        call.set_continues(true);

        call.reply(Some(State {
            start: Some(true),
            ..Default::default()
        }))?;

        for i in 0..n {
            thread::sleep(time::Duration::from_secs(1));
            call.reply(Some(State {
                progress: Some(i * 100 / n),
                ..Default::default()
            }))?;
        }

        call.reply(Some(State {
            progress: Some(100),
            ..Default::default()
        }))?;

        call.set_continues(false);

        call.reply(Some(State {
            end: Some(true),
            ..Default::default()
        }))
    }
}

fn run_app(address: String, timeout: u64) -> io::Result<()> {
    let myexamplemore = MyOrgExampleMore;
    let myinterface = new(Box::new(myexamplemore));
    let service = VarlinkService::new(
        "org.varlink",
        "test service",
        "0.1",
        "http://varlink.org",
        vec![Box::new(myinterface)],
    );
    varlink::listen(service, &address, 10, timeout)
}

fn main() {
    let mut args: Vec<_> = env::args().collect();
    match args.len() {
        2 => {}
        _ => {
            eprintln!("Usage: {} <varlink address>", args[0]);
            exit(1);
        }
    };

    if !args[1].starts_with("--varlink") {
        eprintln!("Usage: {} --varlink=<varlink address>", args[0]);
        exit(1);
    }

    exit(match run_app(args.swap_remove(1)[10..].into(), 0) {
        Ok(_) => 0,
        Err(err) => {
            eprintln!("error: {}", err);
            1
        }
    });
}

#[test]
fn test_unix() {
    if let Err(e) = run_app("unix:/tmp/org.example.more_unix".into(), 1) {
        panic!("error: {}", e);
    }
}

#[test]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn test_unix_abstract() {
    if let Err(e) = run_app("unix:@org.example.more_unix".into(), 1) {
        panic!("error: {}", e);
    }
}

#[test]
fn test_tcp() {
    if let Err(e) = run_app("tcp:0.0.0.0:12345".into(), 1) {
        panic!("error: {}", e);
    }
}

#[test]
fn test_client() {
    fn run_client_app(address: String) -> io::Result<()> {
        let con1 = varlink::Connection::new(&address)?;
        let new_addr;
        {
            let conn = con1.read().unwrap();
            new_addr = conn.address();
        }
        let call = org_example_more::VarlinkClient::new(con1);

        let con2 = varlink::Connection::new(&new_addr)?;
        let mut pingcall = org_example_more::VarlinkClient::new(con2);

        for reply in call.more().test_more(Some(4))? {
            let reply = reply?;
            assert!(reply.state.is_some());
            let state = reply.state.unwrap();
            match state {
                State {
                    start: Some(true),
                    end: None,
                    progress: None,
                    ..
                } => {
                    eprintln!("--- Start ---");
                }
                State {
                    start: None,
                    end: Some(true),
                    progress: None,
                    ..
                } => {
                    eprintln!("--- End ---");
                }
                State {
                    start: None,
                    end: None,
                    progress: Some(progress),
                    ..
                } => {
                    eprintln!("Progress: {}", progress);
                    if progress > 50 {
                        let reply = pingcall.ping(Some("Test".into()))?.recv()?;
                        println!("Pong: '{}'", reply.pong.unwrap());
                    }
                }
                _ => panic!("Got unknown state: {:?}", state),
            }
        }

        Ok(())
    }

    let child = thread::spawn(move || {
        if let Err(e) = run_app("unix:/tmp/org.example.more_client".into(), 1) {
            panic!("error: {}", e);
        }
    });

    // give server time to start
    thread::sleep(time::Duration::from_secs(1));

    assert!(run_client_app("unix:/tmp/org.example.more_client".into()).is_ok());
    assert!(child.join().is_ok());
}
<commit_msg>more tests<commit_after>#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate varlink;

use org_example_more::*;
use std::{thread, time};
use std::env;
use std::io;
use std::io::{Error, ErrorKind};
use std::process::exit;
use varlink::VarlinkService;

// Dynamically build the varlink rust code.
mod org_example_more;

struct MyOrgExampleMore;

impl VarlinkInterface for MyOrgExampleMore {
    fn ping(&self, call: &mut _CallPing, ping: Option<String>) -> io::Result<()> {
        return call.reply(ping);
    }

    fn stop_serving(&self, call: &mut _CallStopServing) -> io::Result<()> {
        call.reply()?;
        Err(Error::new(ErrorKind::ConnectionRefused, "Disconnect"))
    }

    fn test_more(&self, call: &mut _CallTestMore, n: Option<i64>) -> io::Result<()> {
        if n == None {
            return call.reply_invalid_parameter(Some("n".into()));
        }
        let n = n.unwrap();

        call.set_continues(true);

        call.reply(Some(State {
            start: Some(true),
            ..Default::default()
        }))?;

        for i in 0..n {
            thread::sleep(time::Duration::from_secs(1));
            call.reply(Some(State {
                progress: Some(i * 100 / n),
                ..Default::default()
            }))?;
        }

        call.reply(Some(State {
            progress: Some(100),
            ..Default::default()
        }))?;

        call.set_continues(false);

        call.reply(Some(State {
            end: Some(true),
            ..Default::default()
        }))
    }
}

fn run_app(address: String, timeout: u64) -> io::Result<()> {
    let myexamplemore = MyOrgExampleMore;
    let myinterface = new(Box::new(myexamplemore));
    let service = VarlinkService::new(
        "org.varlink",
        "test service",
        "0.1",
        "http://varlink.org",
        vec![Box::new(myinterface)],
    );
    varlink::listen(service, &address, 10, timeout)
}

fn main() {
    let mut args: Vec<_> = env::args().collect();
    match args.len() {
        2 => {}
        _ => {
            eprintln!("Usage: {} <varlink address>", args[0]);
            exit(1);
        }
    };

    if !args[1].starts_with("--varlink") {
        eprintln!("Usage: {} --varlink=<varlink address>", args[0]);
        exit(1);
    }

    exit(match run_app(args.swap_remove(1)[10..].into(), 0) {
        Ok(_) => 0,
        Err(err) => {
            eprintln!("error: {}", err);
            1
        }
    });
}

#[test]
fn test_unix() {
    if let Err(e) = run_app("unix:/tmp/org.example.more_unix".into(), 1) {
        panic!("error: {}", e);
    }
}

#[test]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn test_unix_abstract() {
    if let Err(e) = run_app("unix:@org.example.more_unix".into(), 1) {
        panic!("error: {}", e);
    }
}

#[test]
fn test_tcp() {
    if let Err(e) = run_app("tcp:0.0.0.0:12345".into(), 1) {
        panic!("error: {}", e);
    }
}

#[test]
fn test_client() {
    use varlink::OrgVarlinkServiceInterface;

    fn run_client_app(address: String) -> io::Result<()> {
        let con1 = varlink::Connection::new(&address)?;
        let new_addr;
        {
            let conn = con1.read().unwrap();
            new_addr = conn.address();
        }

        let mut call = varlink::OrgVarlinkServiceClient::new(con1.clone());
        let info = call.get_info()?.recv()?;
        assert_eq!(&info.vendor, "org.varlink");
        assert_eq!(&info.product, "test service");
        assert_eq!(&info.version, "0.1");
        assert_eq!(&info.url, "http://varlink.org");
        assert_eq!(
            info.interfaces.get(0).unwrap().as_ref(),
            "org.varlink.service"
        );

        let description = call.get_interface_description("org.example.more".into())?
            .recv()?;

        assert_eq!(
            &description.description.unwrap(),
            r#"# Example service
interface org.example.more

# Enum, returning either start, progress or end
# progress: [0-100]
type State (
  start: bool,
  progress: int,
  end: bool
)

# Returns the same string
method Ping(ping: string) -> (pong: string)

# Dummy progress method
# n: number of progress steps
method TestMore(n: int) -> (state: State)

# Stop serving
method StopServing() -> ()

# Something failed in TestMore
error TestMoreError (reason: string)
"#
        );

        let mut call = org_example_more::VarlinkClient::new(con1);

        let con2 = varlink::Connection::new(&new_addr)?;
        let mut pingcall = org_example_more::VarlinkClient::new(con2);

        for reply in call.more().test_more(Some(4))? {
            let reply = reply?;
            assert!(reply.state.is_some());
            let state = reply.state.unwrap();
            match state {
                State {
                    start: Some(true),
                    end: None,
                    progress: None,
                    ..
                } => {
                    eprintln!("--- Start ---");
                }
                State {
                    start: None,
                    end: Some(true),
                    progress: None,
                    ..
                } => {
                    eprintln!("--- End ---");
                }
                State {
                    start: None,
                    end: None,
                    progress: Some(progress),
                    ..
                } => {
                    eprintln!("Progress: {}", progress);
                    if progress > 50 {
                        let reply = pingcall.ping(Some("Test".into()))?.recv()?;
                        println!("Pong: '{}'", reply.pong.unwrap());
                    }
                }
                _ => panic!("Got unknown state: {:?}", state),
            }
        }

        let _r = call.stop_serving()?.recv()?;
        Ok(())
    }

    let child = thread::spawn(move || {
        if let Err(e) = run_app("unix:/tmp/org.example.more_client".into(), 1) {
            panic!("error: {}", e);
        }
    });

    // give server time to start
    thread::sleep(time::Duration::from_secs(1));

    assert!(run_client_app("unix:/tmp/org.example.more_client".into()).is_ok());
    assert!(child.join().is_ok());
}
<|endoftext|>


<commit_before>
use hyper;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
}

impl GitLab {

    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port:   port,
            private_token: private_token.to_string(),
            client: hyper::Client::new()
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", &domain, 80, &private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", &domain, 443, &private_token)
    }

    pub fn build_url(&self, command: &str) -> String {
        format!("{}://{}:{}/api/v{}/{}?private_token={}",
                                self.scheme,
                                self.domain,
                                self.port,
                                API_VERSION,
                                command,
                                self.private_token)
    }

    pub fn get(&self, command: &str) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url(&command);
        self.client.get(&url).send()
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        self.get("version")
    }

    // pub fn projects(&self) -> ProjectManager {
    //     ProjectManager()
    // }
}
<commit_msg>Attempt to read struct, but deadlocks<commit_after>
use std::io::Read;  // Trait providing read_to_string()

use hyper;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
}

impl GitLab {

    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port:   port,
            private_token: private_token.to_string(),
            client: hyper::Client::new()
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", &domain, 80, &private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", &domain, 443, &private_token)
    }

    pub fn build_url(&self, command: &str) -> String {
        format!("{}://{}:{}/api/v{}/{}?private_token={}",
                                self.scheme,
                                self.domain,
                                self.port,
                                API_VERSION,
                                command,
                                self.private_token)
    }

    pub fn get(&self, command: &str) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url(&command);
        self.client.get(&url).send()
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        self.get("version")
    }

    pub fn projects(&self) {
        let mut res: hyper::client::Response = self.get("projects").unwrap();
        println!("####################################################################");
        println!("res: {:?}", res);
        println!("####################################################################");
        println!("Response: {}", res.status);
        println!("####################################################################");
        println!("Headers:\n{}", res.headers);
        println!("####################################################################");

        println!("####################################################################");
        let mut body = String::new();
        println!("####################################################################");
        let result = res.read_to_string(&mut body);
        println!("####################################################################");
        println!("body:\n{}", body);
    }

    // pub fn projects(&self) -> ProjectManager {
    //     ProjectManager()
    // }
}

#[cfg(test)]
mod tests {
    use std::env;
    use gitlab::GitLab;

    #[test]
    fn list_projects() {
        let token = match env::var("GITLAB_TOKEN") {
            Ok(val) => val,
            Err(_)  => panic!("Please set environment variable 'GITLAB_TOKEN'"),
        };

        let gl = GitLab::new_https("gitlab.com", &token);
        println!("gl: {:?}", gl);

        gl.projects();
        // assert_eq!(gl.attempt_connection().unwrap().status, hyper::Ok);

    }
}
<|endoftext|>


<commit_before>use std::hash::Hash;
use std::hash::Hasher;
use std::hash::Writer;

use murmurhash64::murmur_hash64a;

pub struct MurmurState {
    bytes: Vec<u8>
}

impl MurmurState {
    #[inline]
    fn new() -> MurmurState {
        MurmurState { bytes: Vec::new() }
    }
}

impl Writer for MurmurState {
    #[inline]
    fn write(&mut self, bytes: &[u8]) {
        self.bytes.push_all(bytes);
    }
}

#[deriving(Copy)]
pub struct MurmurHasher {
    seed: u64
}

impl MurmurHasher {
    #[inline]
    pub fn new() -> MurmurHasher {
        MurmurHasher { seed: 0 }
    }

    #[inline]
    pub fn with_seed(seed: u64) -> MurmurHasher {
        MurmurHasher { seed: seed }
    }
}

impl Hasher<MurmurState> for MurmurHasher {
    #[inline]
    fn hash<Sized? T: Hash<MurmurState>>(&self, value: &T) -> u64 {
        let mut state = MurmurState::new();
        value.hash(&mut state);
        murmur_hash64a(state.bytes.as_slice(), self.seed)
    }
}

#[cfg(test)]
mod test {
    use super::MurmurHasher;

    #[test]
    fn hashmap_str() {
        use std::collections::HashMap;
        let mut hashmap: HashMap<&str, uint, MurmurHasher> = HashMap::with_hasher(MurmurHasher::new());
        hashmap.insert("abc", 123);
        hashmap.insert("def", 456);
        assert_eq!(Some(&123), hashmap.get("abc"));
        assert_eq!(Some(&456), hashmap.get("def"));
    }

    #[test]
    fn hashmap_uint() {
        use std::collections::HashMap;
        let mut hashmap: HashMap<uint, &str, MurmurHasher> = HashMap::with_hasher(MurmurHasher::new());
        hashmap.insert(123, "abc");
        hashmap.insert(456, "def");
        assert_eq!(Some(&"abc"), hashmap.get(&123));
        assert_eq!(Some(&"def"), hashmap.get(&456));
    }
}
<commit_msg>Document Hasher and don't use default type parameters<commit_after>use std::hash::Hash;
use std::hash::Hasher;
use std::hash::Writer;

use murmurhash64::murmur_hash64a;

pub struct MurmurState {
    bytes: Vec<u8>
}

impl MurmurState {
    #[inline]
    fn new() -> MurmurState {
        MurmurState { bytes: Vec::new() }
    }
}

impl Writer for MurmurState {
    #[inline]
    fn write(&mut self, bytes: &[u8]) {
        self.bytes.push_all(bytes);
    }
}

/// MurmurHash2 can also be used as the hash algorithm in a HashMap
/// (or similar). For this it implements the std::hash::Hasher trait.
///
/// # Basic Example
///
/// ```rust
/// # use std::collections::HashMap;
/// # use murmurhash64::MurmurHasher;
/// let mut hashmap = HashMap::with_hasher(MurmurHasher::new());
/// hashmap.insert("abc", 123i);
/// hashmap.insert("def", 456i);
/// assert_eq!(Some(&123), hashmap.get("abc"));
/// assert_eq!(Some(&456), hashmap.get("def"));
/// ```

#[deriving(Copy)]
pub struct MurmurHasher {
    seed: u64
}

impl MurmurHasher {
    #[inline]
    pub fn new() -> MurmurHasher {
        MurmurHasher { seed: 0 }
    }

    #[inline]
    pub fn with_seed(seed: u64) -> MurmurHasher {
        MurmurHasher { seed: seed }
    }
}

impl Hasher<MurmurState> for MurmurHasher {
    #[inline]
    fn hash<Sized? T: Hash<MurmurState>>(&self, value: &T) -> u64 {
        let mut state = MurmurState::new();
        value.hash(&mut state);
        murmur_hash64a(state.bytes.as_slice(), self.seed)
    }
}

#[cfg(test)]
mod test {
    use super::MurmurHasher;

    #[test]
    fn hashmap_str() {
        use std::collections::HashMap;
        let mut hashmap = HashMap::with_hasher(MurmurHasher::new());
        hashmap.insert("abc", 123i);
        hashmap.insert("def", 456i);
        assert_eq!(Some(&123), hashmap.get("abc"));
        assert_eq!(Some(&456), hashmap.get("def"));
    }

    #[test]
    fn hashmap_uint() {
        use std::collections::HashMap;
        let mut hashmap = HashMap::with_hasher(MurmurHasher::new());
        hashmap.insert(123u, "abc");
        hashmap.insert(456u, "def");
        assert_eq!(Some(&"abc"), hashmap.get(&123));
        assert_eq!(Some(&"def"), hashmap.get(&456));
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::fmt;
use std::ops::Deref;
use std::slice::Iter as SliceIter;
use std::rc::Rc;

use chrono::{
    DateTime,
    FixedOffset,
    UTC,
};

use super::rfc2047::decode_rfc2047;
use super::rfc822::Rfc822DateParser;
use super::results::{ParsingResult,ParsingError};

/// Trait for converting from RFC822 Header values into
/// Rust types.
pub trait FromHeader: Sized {
    /// Parse the `value` of the header.
    ///
    /// Returns None if the value failed to be parsed
    fn from_header(value: String) -> ParsingResult<Self>;
}

/// Trait for converting from a Rust type into a Header value.
pub trait ToHeader {
    /// Turn the `value` into a String suitable for being used in
    /// a message header.
    ///
    /// Returns None if the value cannot be stringified.
    fn to_header(value: Self) -> ParsingResult<String>;
}

/// Trait for converting from a Rust time into a Header value
/// that handles its own folding.
///
/// Be mindful that this trait does not mean that the value will
/// not be folded later, rather that the type returns a value that
/// should not be folded, given that the header value starts so far
/// in to a line.
/// [unstable]
pub trait ToFoldedHeader {
    fn to_folded_header(start_pos: usize, value: Self) -> ParsingResult<String>;
}

impl<T: ToHeader> ToFoldedHeader for T {
    fn to_folded_header(_: usize, value: T) -> ParsingResult<String> {
        // We ignore the start_position because the thing will fold anyway.
        ToHeader::to_header(value)
    }
}

impl FromHeader for String {
    fn from_header(value: String) -> ParsingResult<String> {
        #[derive(Debug,Clone,Copy)]
        enum ParseState {
            Normal(usize),
            SeenEquals(usize),
            SeenQuestion(usize, usize),
        }

        let mut state = ParseState::Normal(0);
        let mut decoded = String::new();

        let value_slice = &value[..];

        for (pos, c) in value.char_indices() {
            state = match (state, c) {
                (ParseState::SeenQuestion(start_pos, 4), '=') => {
                    let next_pos = pos + c.len_utf8();
                    // Go to decode if we've seen enough ?
                    let part_decoded = decode_rfc2047(&value_slice[start_pos..next_pos]);
                    let to_push = match part_decoded {
                        Some(ref s) => &s[..],
                        // Decoding failed, push the undecoded string in.
                        None => &value_slice[start_pos..pos],
                    };
                    decoded.push_str(to_push);
                    // Revert us to normal state, but starting at the next character.
                    ParseState::Normal(next_pos)
                },
                (ParseState::SeenQuestion(start_pos, count), '?') => {
                    ParseState::SeenQuestion(start_pos, count + 1)
                },
                (ParseState::SeenQuestion(start_pos, count), _) => {
                    if count > 4 {
                        // This isn't a RFC2047 sequence, so go back to a normal.
                        ParseState::Normal(start_pos)
                    } else {
                        state
                    }
                }
                (ParseState::SeenEquals(start_pos), '?') => {
                    ParseState::SeenQuestion(start_pos, 1)
                },
                (ParseState::SeenEquals(start_pos), _) => {
                    // This isn't a RFC2047 sequence, so go back to a normal.
                    ParseState::Normal(start_pos)
                }
                (ParseState::Normal(start_pos), '=') => {
                    if start_pos != pos {
                        // Push all up to the =, if there is stuff to push.
                        decoded.push_str(&value_slice[start_pos..pos]);
                    }
                    ParseState::SeenEquals(pos)
                },
                (ParseState::Normal(_), _) => state,
            };
        }

        // Don't forget to push on whatever we have left
        let last_start = match state {
            ParseState::Normal(start_pos) => start_pos,
            ParseState::SeenEquals(start_pos) => start_pos,
            ParseState::SeenQuestion(start_pos, _) => start_pos,
        };
        decoded.push_str(&value_slice[last_start..]);

        Ok(decoded)
    }
}

impl FromHeader for DateTime<FixedOffset> {
    fn from_header(value: String) -> ParsingResult<DateTime<FixedOffset>> {
        let mut parser = Rfc822DateParser::new(&value[..]);
        parser.consume_datetime()
    }
}

impl FromHeader for DateTime<UTC> {
    fn from_header(value: String) -> ParsingResult<DateTime<UTC>> {
        let dt: ParsingResult<DateTime<FixedOffset>> = FromHeader::from_header(value);
        dt.map(|i| i.with_timezone(&UTC))
    }
}

impl ToHeader for String {
    fn to_header(value: String) -> ParsingResult<String> {
        Ok(value)
    }
}

impl<'a> ToHeader for &'a str {
    fn to_header(value: &'a str) -> ParsingResult<String> {
        Ok(value.to_string())
    }
}

/// Represents an RFC 822 Header
/// [unstable]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Header {
    /// The name of this header
    pub name: String,
    value: String,
}

impl Header {
    /// Creates a new Header for the given `name` and `value`
    /// [unstable]
    pub fn new(name: String, value: String) -> Header {
        Header {
            name: name,
            value: value,
        }
    }

    /// Creates a new Header for the given `name` and `value`,
    /// as converted through the `ToHeader` or `ToFoldedHeader` trait.
    ///
    /// Returns None if the value failed to be converted.
    /// [unstable]
    pub fn new_with_value<T: ToFoldedHeader>(name: String, value: T) -> ParsingResult<Header> {
        let header_len = name.len() + 2;
        ToFoldedHeader::to_folded_header(header_len, value).map(|val| { Header::new(name.clone(), val) })
    }

    /// Get the value represented by this header, as parsed
    /// into whichever type `T`
    /// [unstable]
    pub fn get_value<T: FromHeader>(&self) -> ParsingResult<T> {
        FromHeader::from_header(self.value.clone())
    }
}

impl fmt::Display for Header {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}: {}", self.name, self.value)
    }
}

/// [unstable]
pub struct HeaderIter<'s> {
    iter: SliceIter<'s, Rc<Header>>
}

impl<'s> HeaderIter<'s> {
    /// [unstable]
    fn new(iter: SliceIter<'s, Rc<Header>>) -> HeaderIter<'s> {
        HeaderIter {
            iter: iter
        }
    }
}

impl<'s> Iterator for HeaderIter<'s> {
    type Item = &'s Header;

    fn next(&mut self) -> Option<&'s Header> {
        match self.iter.next() {
            Some(s) => Some(s.deref()),
            None => None,
        }
    }
}

/// A collection of Headers
/// [unstable]
#[derive(Eq,PartialEq,Debug,Clone)]
pub struct HeaderMap {
    // We store headers "twice" inside the HeaderMap.
    //
    // The first is as an ordered list of headers,
    // which is used to iterate over.
    ordered_headers: Vec<Rc<Header>>,
    // The second is as a mapping between header names
    // and all of the headers with that name.
    //
    // This allows quick retrival of a header by name.
    headers: HashMap<String, Vec<Rc<Header>>>,
}

impl HeaderMap {
    /// [unstable]
    pub fn new() -> HeaderMap {
        HeaderMap {
            ordered_headers: Vec::new(),
            headers: HashMap::new(),
        }
    }

    /// Adds a header to the collection
    /// [unstable]
    pub fn insert(&mut self, header: Header) {
        let header_name = header.name.clone();
        let rc = Rc::new(header);
        // Add to the ordered list of headers
        self.ordered_headers.push(rc.clone());

        // and to the mapping between header names and values.
        match self.headers.entry(header_name) {
            Entry::Occupied(mut entry) => {
                entry.get_mut().push(rc.clone());
            },
            Entry::Vacant(entry) => {
                // There haven't been any headers with this name
                // as of yet, so make a new list and push it in.
                let mut header_list = Vec::new();
                header_list.push(rc.clone());
                entry.insert(header_list);
            },
        };
    }

    /// Get an Iterator over the collection of headers.
    /// [unstable]
    pub fn iter(&self) -> HeaderIter {
        HeaderIter::new(self.ordered_headers.iter())
    }

    /// Get the last value of the header with `name`
    /// [unstable]
    pub fn get(&self, name: String) -> Option<&Header> {
        self.headers.get(&name).map(|headers| { headers.last().unwrap() })
                               .map(|rc| { rc.deref() })
    }

    /// Get the last value of the header with `name`, as a decoded type.
    /// [unstable]
    pub fn get_value<T: FromHeader>(&self, name: String) -> ParsingResult<T> {
        match self.get(name) {
            Some(ref header) => header.get_value(),
            None => Err(ParsingError::new("Couldn't find header value.".to_string())),
        }
    }

    /// [unstable]
    /// Get the number of headers within this map.
    pub fn len(&self) -> usize {
        self.ordered_headers.len()
    }

    /// [unstable]
    /// Find a list of headers of `name`, `None` if there
    /// are no headers with that name.
    pub fn find(&self, name: &String) -> Option<Vec<&Header>> {
        self.headers.get(name)
            .map(|rcs| rcs.iter().map(|rc| rc.deref()).collect())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashSet;

    use chrono::{
        DateTime,
        FixedOffset,
        UTC,
    };
    use chrono::offset::TimeZone;

    static SAMPLE_HEADERS: [(&'static str, &'static str); 4] = [
        ("Test", "Value"),
        ("Test", "Value 2"),
        ("Test-2", "Value 3"),
        ("Test-Multiline", "Foo\nBar"),
    ];

    fn make_sample_headers() -> Vec<Header> {
        SAMPLE_HEADERS.iter().map(|&(name, value)| {
            Header::new(name.to_string(), value.to_string())
        }).collect()
    }

    #[test]
    fn test_header_to_string() {
        let header = Header::new("Test".to_string(), "Value".to_string());
        assert_eq!(header.to_string(), "Test: Value".to_string());
    }

    #[test]
    fn test_string_get_value() {
        struct HeaderTest<'s> {
            input: &'s str,
            result: Option<&'s str>,
        }

        let tests = vec![
            HeaderTest {
                input: "Value",
                result: Some("Value"),
            },
            HeaderTest {
                input: "=?ISO-8859-1?Q?Test=20text?=",
                result: Some("Test text"),
            },
            HeaderTest {
                input: "=?ISO-8859-1?Q?Multiple?= =?utf-8?b?ZW5jb2Rpbmdz?=",
                result: Some("Multiple encodings"),
            },
            HeaderTest {
                input: "Some things with =?utf-8?b?ZW5jb2Rpbmdz?=, other things without.",
                result: Some("Some things with encodings, other things without."),
            },
            HeaderTest {
                input: "Encoding =?utf-8?q?fail",
                result: Some("Encoding =?utf-8?q?fail"),
            },
        ];

        for test in tests.into_iter() {
            let header = Header::new("Test".to_string(), test.input.to_string());
            let string_value = header.get_value::<String>().ok();
            assert_eq!(string_value, test.result.map(|s| { s.to_string() }));
        }
    }

    #[test]
    fn test_datetime_get_value() {
        let header = Header::new("Date".to_string(), "Wed, 17 Dec 2014 09:35:07 +0100".to_string());
        let dt_value = header.get_value::<DateTime<FixedOffset>>().unwrap();
        assert_eq!(dt_value, FixedOffset::east(3600).ymd(2014, 12, 17).and_hms(9, 35, 7));
    }

    #[test]
    fn test_datetime_utc_get_value() {
        let header = Header::new("Date".to_string(), "Wed, 17 Dec 2014 09:35:07 +0100".to_string());
        let dt_value = header.get_value::<DateTime<UTC>>().unwrap();
        assert_eq!(dt_value, UTC.ymd(2014, 12, 17).and_hms(8, 35, 7));
    }

    #[test]
    fn test_to_header_string() {
        let header = Header::new_with_value("Test".to_string(), "Value".to_string()).unwrap();
        let header_value = header.get_value::<String>().unwrap();
        assert_eq!(header_value, "Value".to_string());
    }

    #[test]
    fn test_to_header_str() {
        let header = Header::new_with_value("Test".to_string(), "Value").unwrap();
        let header_value = header.get_value::<String>().unwrap();
        assert_eq!(header_value, "Value".to_string());
    }

    #[test]
    fn test_header_map_len() {
        let mut headers = HeaderMap::new();
        for (i, header) in make_sample_headers().into_iter().enumerate() {
            headers.insert(header);
            assert_eq!(headers.len(), i + 1);
        }
    }
    #[test]
    fn test_header_map_iter() {
        let mut headers = HeaderMap::new();
        let mut expected_headers = HashSet::new();
        for header in make_sample_headers().into_iter() {
            headers.insert(header.clone());
            expected_headers.insert(header);
        }

        let mut count = 0;
        // Ensure all the headers returned are expected
        for header in headers.iter() {
            assert!(expected_headers.contains(header));
            count += 1;
        }
        // And that there is the right number of them
        assert_eq!(count, expected_headers.len());
    }
}
<commit_msg>Changed Rc to Arc to allow sending stuff between threads.<commit_after>use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::fmt;
use std::ops::Deref;
use std::slice::Iter as SliceIter;
use std::sync::Arc;

use chrono::{
    DateTime,
    FixedOffset,
    UTC,
};

use super::rfc2047::decode_rfc2047;
use super::rfc822::Rfc822DateParser;
use super::results::{ParsingResult,ParsingError};

/// Trait for converting from RFC822 Header values into
/// Rust types.
pub trait FromHeader: Sized {
    /// Parse the `value` of the header.
    ///
    /// Returns None if the value failed to be parsed
    fn from_header(value: String) -> ParsingResult<Self>;
}

/// Trait for converting from a Rust type into a Header value.
pub trait ToHeader {
    /// Turn the `value` into a String suitable for being used in
    /// a message header.
    ///
    /// Returns None if the value cannot be stringified.
    fn to_header(value: Self) -> ParsingResult<String>;
}

/// Trait for converting from a Rust time into a Header value
/// that handles its own folding.
///
/// Be mindful that this trait does not mean that the value will
/// not be folded later, rather that the type returns a value that
/// should not be folded, given that the header value starts so far
/// in to a line.
/// [unstable]
pub trait ToFoldedHeader {
    fn to_folded_header(start_pos: usize, value: Self) -> ParsingResult<String>;
}

impl<T: ToHeader> ToFoldedHeader for T {
    fn to_folded_header(_: usize, value: T) -> ParsingResult<String> {
        // We ignore the start_position because the thing will fold anyway.
        ToHeader::to_header(value)
    }
}

impl FromHeader for String {
    fn from_header(value: String) -> ParsingResult<String> {
        #[derive(Debug,Clone,Copy)]
        enum ParseState {
            Normal(usize),
            SeenEquals(usize),
            SeenQuestion(usize, usize),
        }

        let mut state = ParseState::Normal(0);
        let mut decoded = String::new();

        let value_slice = &value[..];

        for (pos, c) in value.char_indices() {
            state = match (state, c) {
                (ParseState::SeenQuestion(start_pos, 4), '=') => {
                    let next_pos = pos + c.len_utf8();
                    // Go to decode if we've seen enough ?
                    let part_decoded = decode_rfc2047(&value_slice[start_pos..next_pos]);
                    let to_push = match part_decoded {
                        Some(ref s) => &s[..],
                        // Decoding failed, push the undecoded string in.
                        None => &value_slice[start_pos..pos],
                    };
                    decoded.push_str(to_push);
                    // Revert us to normal state, but starting at the next character.
                    ParseState::Normal(next_pos)
                },
                (ParseState::SeenQuestion(start_pos, count), '?') => {
                    ParseState::SeenQuestion(start_pos, count + 1)
                },
                (ParseState::SeenQuestion(start_pos, count), _) => {
                    if count > 4 {
                        // This isn't a RFC2047 sequence, so go back to a normal.
                        ParseState::Normal(start_pos)
                    } else {
                        state
                    }
                }
                (ParseState::SeenEquals(start_pos), '?') => {
                    ParseState::SeenQuestion(start_pos, 1)
                },
                (ParseState::SeenEquals(start_pos), _) => {
                    // This isn't a RFC2047 sequence, so go back to a normal.
                    ParseState::Normal(start_pos)
                }
                (ParseState::Normal(start_pos), '=') => {
                    if start_pos != pos {
                        // Push all up to the =, if there is stuff to push.
                        decoded.push_str(&value_slice[start_pos..pos]);
                    }
                    ParseState::SeenEquals(pos)
                },
                (ParseState::Normal(_), _) => state,
            };
        }

        // Don't forget to push on whatever we have left
        let last_start = match state {
            ParseState::Normal(start_pos) => start_pos,
            ParseState::SeenEquals(start_pos) => start_pos,
            ParseState::SeenQuestion(start_pos, _) => start_pos,
        };
        decoded.push_str(&value_slice[last_start..]);

        Ok(decoded)
    }
}

impl FromHeader for DateTime<FixedOffset> {
    fn from_header(value: String) -> ParsingResult<DateTime<FixedOffset>> {
        let mut parser = Rfc822DateParser::new(&value[..]);
        parser.consume_datetime()
    }
}

impl FromHeader for DateTime<UTC> {
    fn from_header(value: String) -> ParsingResult<DateTime<UTC>> {
        let dt: ParsingResult<DateTime<FixedOffset>> = FromHeader::from_header(value);
        dt.map(|i| i.with_timezone(&UTC))
    }
}

impl ToHeader for String {
    fn to_header(value: String) -> ParsingResult<String> {
        Ok(value)
    }
}

impl<'a> ToHeader for &'a str {
    fn to_header(value: &'a str) -> ParsingResult<String> {
        Ok(value.to_string())
    }
}

/// Represents an RFC 822 Header
/// [unstable]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Header {
    /// The name of this header
    pub name: String,
    value: String,
}

impl Header {
    /// Creates a new Header for the given `name` and `value`
    /// [unstable]
    pub fn new(name: String, value: String) -> Header {
        Header {
            name: name,
            value: value,
        }
    }

    /// Creates a new Header for the given `name` and `value`,
    /// as converted through the `ToHeader` or `ToFoldedHeader` trait.
    ///
    /// Returns None if the value failed to be converted.
    /// [unstable]
    pub fn new_with_value<T: ToFoldedHeader>(name: String, value: T) -> ParsingResult<Header> {
        let header_len = name.len() + 2;
        ToFoldedHeader::to_folded_header(header_len, value).map(|val| { Header::new(name.clone(), val) })
    }

    /// Get the value represented by this header, as parsed
    /// into whichever type `T`
    /// [unstable]
    pub fn get_value<T: FromHeader>(&self) -> ParsingResult<T> {
        FromHeader::from_header(self.value.clone())
    }
}

impl fmt::Display for Header {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}: {}", self.name, self.value)
    }
}

/// [unstable]
pub struct HeaderIter<'s> {
    iter: SliceIter<'s, Arc<Header>>
}

impl<'s> HeaderIter<'s> {
    /// [unstable]
    fn new(iter: SliceIter<'s, Arc<Header>>) -> HeaderIter<'s> {
        HeaderIter {
            iter: iter
        }
    }
}

impl<'s> Iterator for HeaderIter<'s> {
    type Item = &'s Header;

    fn next(&mut self) -> Option<&'s Header> {
        match self.iter.next() {
            Some(s) => Some(s.deref()),
            None => None,
        }
    }
}

/// A collection of Headers
/// [unstable]
#[derive(Eq,PartialEq,Debug,Clone)]
pub struct HeaderMap {
    // We store headers "twice" inside the HeaderMap.
    //
    // The first is as an ordered list of headers,
    // which is used to iterate over.
    ordered_headers: Vec<Arc<Header>>,
    // The second is as a mapping between header names
    // and all of the headers with that name.
    //
    // This allows quick retrival of a header by name.
    headers: HashMap<String, Vec<Arc<Header>>>,
}

impl HeaderMap {
    /// [unstable]
    pub fn new() -> HeaderMap {
        HeaderMap {
            ordered_headers: Vec::new(),
            headers: HashMap::new(),
        }
    }

    /// Adds a header to the collection
    /// [unstable]
    pub fn insert(&mut self, header: Header) {
        let header_name = header.name.clone();
        let rc = Arc::new(header);
        // Add to the ordered list of headers
        self.ordered_headers.push(rc.clone());

        // and to the mapping between header names and values.
        match self.headers.entry(header_name) {
            Entry::Occupied(mut entry) => {
                entry.get_mut().push(rc.clone());
            },
            Entry::Vacant(entry) => {
                // There haven't been any headers with this name
                // as of yet, so make a new list and push it in.
                let mut header_list = Vec::new();
                header_list.push(rc.clone());
                entry.insert(header_list);
            },
        };
    }

    /// Get an Iterator over the collection of headers.
    /// [unstable]
    pub fn iter(&self) -> HeaderIter {
        HeaderIter::new(self.ordered_headers.iter())
    }

    /// Get the last value of the header with `name`
    /// [unstable]
    pub fn get(&self, name: String) -> Option<&Header> {
        self.headers.get(&name).map(|headers| { headers.last().unwrap() })
                               .map(|rc| { rc.deref() })
    }

    /// Get the last value of the header with `name`, as a decoded type.
    /// [unstable]
    pub fn get_value<T: FromHeader>(&self, name: String) -> ParsingResult<T> {
        match self.get(name) {
            Some(ref header) => header.get_value(),
            None => Err(ParsingError::new("Couldn't find header value.".to_string())),
        }
    }

    /// [unstable]
    /// Get the number of headers within this map.
    pub fn len(&self) -> usize {
        self.ordered_headers.len()
    }

    /// [unstable]
    /// Find a list of headers of `name`, `None` if there
    /// are no headers with that name.
    pub fn find(&self, name: &String) -> Option<Vec<&Header>> {
        self.headers.get(name)
            .map(|rcs| rcs.iter().map(|rc| rc.deref()).collect())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashSet;

    use chrono::{
        DateTime,
        FixedOffset,
        UTC,
    };
    use chrono::offset::TimeZone;

    static SAMPLE_HEADERS: [(&'static str, &'static str); 4] = [
        ("Test", "Value"),
        ("Test", "Value 2"),
        ("Test-2", "Value 3"),
        ("Test-Multiline", "Foo\nBar"),
    ];

    fn make_sample_headers() -> Vec<Header> {
        SAMPLE_HEADERS.iter().map(|&(name, value)| {
            Header::new(name.to_string(), value.to_string())
        }).collect()
    }

    #[test]
    fn test_header_to_string() {
        let header = Header::new("Test".to_string(), "Value".to_string());
        assert_eq!(header.to_string(), "Test: Value".to_string());
    }

    #[test]
    fn test_string_get_value() {
        struct HeaderTest<'s> {
            input: &'s str,
            result: Option<&'s str>,
        }

        let tests = vec![
            HeaderTest {
                input: "Value",
                result: Some("Value"),
            },
            HeaderTest {
                input: "=?ISO-8859-1?Q?Test=20text?=",
                result: Some("Test text"),
            },
            HeaderTest {
                input: "=?ISO-8859-1?Q?Multiple?= =?utf-8?b?ZW5jb2Rpbmdz?=",
                result: Some("Multiple encodings"),
            },
            HeaderTest {
                input: "Some things with =?utf-8?b?ZW5jb2Rpbmdz?=, other things without.",
                result: Some("Some things with encodings, other things without."),
            },
            HeaderTest {
                input: "Encoding =?utf-8?q?fail",
                result: Some("Encoding =?utf-8?q?fail"),
            },
        ];

        for test in tests.into_iter() {
            let header = Header::new("Test".to_string(), test.input.to_string());
            let string_value = header.get_value::<String>().ok();
            assert_eq!(string_value, test.result.map(|s| { s.to_string() }));
        }
    }

    #[test]
    fn test_datetime_get_value() {
        let header = Header::new("Date".to_string(), "Wed, 17 Dec 2014 09:35:07 +0100".to_string());
        let dt_value = header.get_value::<DateTime<FixedOffset>>().unwrap();
        assert_eq!(dt_value, FixedOffset::east(3600).ymd(2014, 12, 17).and_hms(9, 35, 7));
    }

    #[test]
    fn test_datetime_utc_get_value() {
        let header = Header::new("Date".to_string(), "Wed, 17 Dec 2014 09:35:07 +0100".to_string());
        let dt_value = header.get_value::<DateTime<UTC>>().unwrap();
        assert_eq!(dt_value, UTC.ymd(2014, 12, 17).and_hms(8, 35, 7));
    }

    #[test]
    fn test_to_header_string() {
        let header = Header::new_with_value("Test".to_string(), "Value".to_string()).unwrap();
        let header_value = header.get_value::<String>().unwrap();
        assert_eq!(header_value, "Value".to_string());
    }

    #[test]
    fn test_to_header_str() {
        let header = Header::new_with_value("Test".to_string(), "Value").unwrap();
        let header_value = header.get_value::<String>().unwrap();
        assert_eq!(header_value, "Value".to_string());
    }

    #[test]
    fn test_header_map_len() {
        let mut headers = HeaderMap::new();
        for (i, header) in make_sample_headers().into_iter().enumerate() {
            headers.insert(header);
            assert_eq!(headers.len(), i + 1);
        }
    }
    #[test]
    fn test_header_map_iter() {
        let mut headers = HeaderMap::new();
        let mut expected_headers = HashSet::new();
        for header in make_sample_headers().into_iter() {
            headers.insert(header.clone());
            expected_headers.insert(header);
        }

        let mut count = 0;
        // Ensure all the headers returned are expected
        for header in headers.iter() {
            assert!(expected_headers.contains(header));
            count += 1;
        }
        // And that there is the right number of them
        assert_eq!(count, expected_headers.len());
    }
}
<|endoftext|>


<commit_before>//! Visitation patterns for graphs.

use std::default::Default;

use bit_vec::BitVec;

use super::{Graph,Id};

// ================================================================
// Algorithm

/// Visitation-order selection helper.
pub trait Algorithm: Default {
    /// Select the next edge to follow given the current node index and
    fn next<G>(&mut self, g: &G, n: <G as Graph>::NodeId) -> Option<<G as Graph>::EdgeId>
        where G: Graph;
}

// We can use free-standing functions as visitors.
impl<G: Graph, F> Algorithm for F
    where F: FnMut(&G, <G as Graph>::NodeId) -> Option<<G as Graph>::EdgeId> {
    fn next<G>(&mut self, g: &G, n: <G as Graph>::NodeId) -> Option<<G as Graph>::EdgeId> {
        self(g, n)
    }
}

// ================================================================
// Visitor

/// Tracks required state when traversing a graph.
pub struct Visitor<G: Graph, A: Algorithm> {
    current_node: <G as Graph>::NodeId,
    visited: BitVec<u32>,
    algo: A
}
impl<G: Graph, A: Algorithm> Visitor<G, A> {
    /// Create a Visitor instance.
    pub fn new(g: &G, entry: <G as Graph>::NodeId) -> Self {
        Visitor{visited: BitVec::from_elem(g.node_count(), false), algo: Default::default()}
    }

    /// Check if a particular node has been visited.
    pub fn visited(&self, node: <G as Graph>::NodeId) -> bool {
        self.visited.get(node.index()).or_else(|| false)
    }
}

// ================================================================
// Algorithm implementations
<commit_msg>util/graph/visit.rs: correct comment above `Algorithm` impl for `FnMut`s<commit_after>//! Visitation patterns for graphs.

use std::default::Default;

use bit_vec::BitVec;

use super::{Graph,Id};

// ================================================================
// Algorithm

/// Visitation-order selection helper.
pub trait Algorithm: Default {
    /// Select the next edge to follow given the current node index and
    fn next<G>(&mut self, g: &G, n: <G as Graph>::NodeId) -> Option<<G as Graph>::EdgeId>
        where G: Graph;
}

// We can use free-standing functions as visit algorithms.
impl<G: Graph, F> Algorithm for F
    where F: FnMut(&G, <G as Graph>::NodeId) -> Option<<G as Graph>::EdgeId> {
    fn next<G>(&mut self, g: &G, n: <G as Graph>::NodeId) -> Option<<G as Graph>::EdgeId> {
        self(g, n)
    }
}

// ================================================================
// Visitor

/// Tracks required state when traversing a graph.
pub struct Visitor<G: Graph, A: Algorithm> {
    current_node: <G as Graph>::NodeId,
    visited: BitVec<u32>,
    algo: A
}
impl<G: Graph, A: Algorithm> Visitor<G, A> {
    /// Create a Visitor instance.
    pub fn new(g: &G, entry: <G as Graph>::NodeId) -> Self {
        Visitor{visited: BitVec::from_elem(g.node_count(), false), algo: Default::default()}
    }

    /// Check if a particular node has been visited.
    pub fn visited(&self, node: <G as Graph>::NodeId) -> bool {
        self.visited.get(node.index()).or_else(|| false)
    }
}

// ================================================================
// Algorithm implementations
<|endoftext|>


<commit_before>use query::ToSQL;

#[allow(dead_code)]
pub enum Values<'a> {
    Default,
    Specified(&'a [&'a str]),
    Query(&'a ToSQL)
}

#[allow(dead_code)]
impl<'a> Values<'a> {
    fn to_sql(&self) -> String {
        match *self {
            Values::Default => "DEFAULT VALUES".to_string(),
            Values::Specified(values) => {
                let mut rv = String::new();
                rv.push_str("VALUES");
                rv.push(' ');
                rv.push_str(&values.into_iter()
                            .map(|x| format!("({})", x))
                            .collect::<Vec<_>>()
                            .join(", "));
                rv
            },
            Values::Query(query) => query.to_sql()
        }
    }
}

#[allow(dead_code)]
pub enum Returning<'a> {
    All,
    Specified(&'a [&'a str])
}

#[allow(dead_code)]
impl<'a> Returning<'a> {
    pub fn to_sql(&self) -> String {
        match *self {
            Returning::All => "*".to_string(),
            Returning::Specified(values) => {
                format!("{}", values.join(", "))
            }
        }
    }
}

#[allow(dead_code)]
struct Insert<'a> {
    table: &'a str,
    columns: &'a [&'a str],
    values: Values<'a>,
    returning: Returning<'a>
}

#[allow(dead_code)]
impl<'a> Insert<'a> {
    fn to_sql(&self) -> String {
        let mut rv = String::new();
        rv.push_str("INSERT INTO");
        rv.push(' ');
        rv.push_str(&self.table);

        if !self.columns.is_empty() {
            rv.push(' ');
            rv.push('(');
            rv.push_str(&self.columns.join(", "));
            rv.push(')');
        }

        rv.push(' ');
        rv.push_str(&self.values.to_sql());
        rv.push(' ');
        rv.push_str("RETURNING");
        rv.push(' ');
        rv.push_str(&self.returning.to_sql());
        rv
    }
}

#[cfg(test)]
mod tests {
    use super::{Values, Insert, Returning};

    use query::{ToSQL, Query};
    use select::SelectType;
    use where_cl::WhereType;
    use distinct::DistinctType;
    use limit::LimitType;
    use offset::OffsetType;
    use for_cl::ForType;

    #[test]
    fn test_simple() {
        let insert = Insert {
            table: "test_table",
            columns: &[],
            values: Values::Default,
            returning: Returning::All
        };

        let expected = {
            "INSERT INTO test_table \
            DEFAULT VALUES \
            RETURNING *"
        };

        assert_eq!(insert.to_sql(), expected)
    }

    #[test]
    fn test_with_spec_return() {
        let returning_values = ["foo, bar"];
        let insert = Insert {
            table: "test_table",
            columns: &[],
            values: Values::Default,
            returning: Returning::Specified(&returning_values)
        };

        let expected = {
            "INSERT INTO test_table \
            DEFAULT VALUES \
            RETURNING foo, bar"
        };

        assert_eq!(insert.to_sql(), expected)
    }

    #[test]
    fn test_with_values() {
        let values = ["DEFAULT, fizz", "foo, bar"];
        let insert = Insert {
            table: "test_table",
            columns: &["foo", "bar"],
            values: Values::Specified(&values),
            returning: Returning::All
        };

        let expected = {
            "INSERT INTO test_table (foo, bar) \
            VALUES (DEFAULT, fizz), (foo, bar) \
            RETURNING *"
        };

        assert_eq!(insert.to_sql(), expected)
    }

    #[test]
    fn test_with_query() {
        let query = Query {
            select: SelectType::All,
            distinct: DistinctType::Empty,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
            limit: LimitType::Empty,
            offset: OffsetType::Empty,
            for_cl: ForType::Empty
        };

        let insert = Insert {
            table: "test_table",
            columns: &["foo", "bar"],
            values: Values::Query(&query),
            returning: Returning::All
        };

        let expected = {
            "INSERT INTO test_table (foo, bar) \
            SELECT * FROM test_table \
            RETURNING *"
        };

        assert_eq!(insert.to_sql(), expected)
    }
}
<commit_msg>Fixed ToSQL -> Query<commit_after>use query::{ToSQL, Query};

#[allow(dead_code)]
pub enum Values<'a> {
    Default,
    Specified(&'a [&'a str]),
    Query(&'a Query<'a>)
}

#[allow(dead_code)]
impl<'a> Values<'a> {
    fn to_sql(&self) -> String {
        match *self {
            Values::Default => "DEFAULT VALUES".to_string(),
            Values::Specified(values) => {
                let mut rv = String::new();
                rv.push_str("VALUES");
                rv.push(' ');
                rv.push_str(&values.into_iter()
                            .map(|x| format!("({})", x))
                            .collect::<Vec<_>>()
                            .join(", "));
                rv
            },
            Values::Query(query) => query.to_sql()
        }
    }
}

#[allow(dead_code)]
pub enum Returning<'a> {
    All,
    Specified(&'a [&'a str])
}

#[allow(dead_code)]
impl<'a> Returning<'a> {
    pub fn to_sql(&self) -> String {
        match *self {
            Returning::All => "*".to_string(),
            Returning::Specified(values) => {
                format!("{}", values.join(", "))
            }
        }
    }
}

#[allow(dead_code)]
struct Insert<'a> {
    table: &'a str,
    columns: &'a [&'a str],
    values: Values<'a>,
    returning: Returning<'a>
}

#[allow(dead_code)]
impl<'a> Insert<'a> {
    fn to_sql(&self) -> String {
        let mut rv = String::new();
        rv.push_str("INSERT INTO");
        rv.push(' ');
        rv.push_str(&self.table);

        if !self.columns.is_empty() {
            rv.push(' ');
            rv.push('(');
            rv.push_str(&self.columns.join(", "));
            rv.push(')');
        }

        rv.push(' ');
        rv.push_str(&self.values.to_sql());
        rv.push(' ');
        rv.push_str("RETURNING");
        rv.push(' ');
        rv.push_str(&self.returning.to_sql());
        rv
    }
}

#[cfg(test)]
mod tests {
    use super::{Values, Insert, Returning};

    use query::{ToSQL, Query};
    use select::SelectType;
    use where_cl::WhereType;
    use distinct::DistinctType;
    use limit::LimitType;
    use offset::OffsetType;
    use for_cl::ForType;

    #[test]
    fn test_simple() {
        let insert = Insert {
            table: "test_table",
            columns: &[],
            values: Values::Default,
            returning: Returning::All
        };

        let expected = {
            "INSERT INTO test_table \
            DEFAULT VALUES \
            RETURNING *"
        };

        assert_eq!(insert.to_sql(), expected)
    }

    #[test]
    fn test_with_spec_return() {
        let returning_values = ["foo, bar"];
        let insert = Insert {
            table: "test_table",
            columns: &[],
            values: Values::Default,
            returning: Returning::Specified(&returning_values)
        };

        let expected = {
            "INSERT INTO test_table \
            DEFAULT VALUES \
            RETURNING foo, bar"
        };

        assert_eq!(insert.to_sql(), expected)
    }

    #[test]
    fn test_with_values() {
        let values = ["DEFAULT, fizz", "foo, bar"];
        let insert = Insert {
            table: "test_table",
            columns: &["foo", "bar"],
            values: Values::Specified(&values),
            returning: Returning::All
        };

        let expected = {
            "INSERT INTO test_table (foo, bar) \
            VALUES (DEFAULT, fizz), (foo, bar) \
            RETURNING *"
        };

        assert_eq!(insert.to_sql(), expected)
    }

    #[test]
    fn test_with_query() {
        let query = Query {
            select: SelectType::All,
            distinct: DistinctType::Empty,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
            limit: LimitType::Empty,
            offset: OffsetType::Empty,
            for_cl: ForType::Empty
        };

        let insert = Insert {
            table: "test_table",
            columns: &["foo", "bar"],
            values: Values::Query(&query),
            returning: Returning::All
        };

        let expected = {
            "INSERT INTO test_table (foo, bar) \
            SELECT * FROM test_table \
            RETURNING *"
        };

        assert_eq!(insert.to_sql(), expected)
    }
}
<|endoftext|>