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

mod cmd_finder;

use std::env;
use std::env::consts;
use std::fs;
use std::path::{Path, PathBuf};

static SHADERC_STATIC_LIB: &str = "shaderc_combined";
static SHADERC_SHARED_LIB: &str = "shaderc_shared";
static SHADERC_STATIC_LIB_FILE_UNIX: &str = "libshaderc_combined.a";
static SHADERC_STATIC_LIB_FILE_WIN: &str = "shaderc_combined.lib";

fn get_apple_sdk_path() -> Option<PathBuf> {
    let target = std::env::var("TARGET").unwrap();
    use std::process::Command;

    // tvOS (and the simulator) could be added here in the future.
    let sdk = if target == "x86_64-apple-ios" || target == "i386-apple-ios" {
        "iphonesimulator"
    } else if target == "aarch64-apple-ios"
        || target == "armv7-apple-ios"
        || target == "armv7s-apple-ios"
    {
        "iphoneos"
    } else {
        return None;
    };

    let output = if let Ok(out) = Command::new("xcrun")
        .args(&["--sdk", sdk, "--show-sdk-path"])
        .output()
    {
        out.stdout
    } else {
        return None;
    };
    let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcrun`");
    Some(PathBuf::from(prefix_str.trim_end().to_string()))
}

fn build_shaderc_unix(shaderc_dir: &PathBuf, use_ninja: bool, target_os: String) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        // CMake options
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        // Glslang options
        .define("ENABLE_SPVREMAPPER", "OFF")
        .define("ENABLE_GLSLANG_BINARIES", "OFF")
        // Shaderc options
        .define("SHADERC_SKIP_TESTS", "ON")
        // SPIRV-Tools options
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF");
    if use_ninja {
        config.generator("Ninja");
    }

    if target_os == "ios" {
        if let Some(path) = get_apple_sdk_path() {
            config.define("CMAKE_OSX_SYSROOT", path);
        }
    }

    config.build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    let linkage = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default();

    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        // CMake options
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        // Glslang options
        .define("ENABLE_SPVREMAPPER", "OFF")
        .define("ENABLE_GLSLANG_BINARIES", "OFF")
        // Shaderc options
        .define("SHADERC_SKIP_TESTS", "ON")
        // SPIRV-Tools options
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .generator("Ninja");

    // cmake-rs tries to be clever on Windows by injecting several
    // C/C++ flags, which causes problems. So I have to manually
    // define CMAKE_*_FLAGS_* here to suppress that.
    let config = if linkage.contains("crt-static") {
        // statically-linked CRT
        config
            .define("CMAKE_C_FLAGS", " /nologo /EHsc /MT")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MT")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MT")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MT")
    } else {
        // dynamically-linked CRT
        config
            .define("CMAKE_C_FLAGS", " /nologo /EHsc /MD")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MD")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MD")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MD")
            // prevent shaderc's cmake script messes with crt flags
            .define("SHADERC_ENABLE_SHARED_CRT", "ON")
    };

    config.build()
}

fn main() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let config_build_from_source = env::var("CARGO_FEATURE_BUILD_FROM_SOURCE").is_ok();
    let has_explicit_set_search_dir = env::var("SHADERC_LIB_DIR").is_ok();

    // Initialize explicit shaderc search directory first.
    let mut search_dir = if let Ok(lib_dir) = env::var("SHADERC_LIB_DIR") {
        println!(
            "cargo:warning=shaderc: searching native shaderc libraries in '{}'",
            lib_dir
        );
        Some(lib_dir)
    } else {
        None
    };

    // Try to find native shaderc library from Vulkan SDK if possible.
    if search_dir.is_none() {
        search_dir = if let Ok(sdk_dir) = env::var("VULKAN_SDK") {
            println!(
                "cargo:warning=shaderc: searching native shaderc libraries in Vulkan SDK '{}/lib'",
                sdk_dir
            );
            Some(format!("{}/lib/", sdk_dir))
        } else {
            None
        };
    }

    // If no explicit path is set and no explicit request is made to build from
    // source, check known system locations before falling back to build from source.
    // This set `search_dir` for later usage.
    if search_dir.is_none() && !config_build_from_source {
        println!(
            "cargo:warning=shaderc: searching for native shaderc libraries on system;  \
             use '--features build-from-source' to force building from source code"
        );

        if target_os == "macos" {
            // Vulkan SDK is installed in `/usr/local/` by default on macOS
            let macos_path = "/usr/local/lib/";
            if Path::new(macos_path).exists() {
                search_dir = Some(macos_path.to_owned());
            }
        } else if target_os == "linux" {
            // https://wiki.ubuntu.com/MultiarchSpec
            // https://wiki.debian.org/Multiarch/Implementation
            let debian_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap() {
                arch if arch == "x86" => "i386".to_owned(),
                arch => arch,
            };
            let debian_triple_path = format!("/usr/lib/{}-linux-gnu/", debian_arch);

            search_dir = if Path::new(&debian_triple_path).exists() {
                // Debian, Ubuntu and their derivatives.
                Some(debian_triple_path)
            } else if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64"
                && Path::new("/usr/lib64/").exists()
            {
                // Other distributions running on x86_64 usually use this path.
                Some("/usr/lib64/".to_owned())
            } else {
                // Other distributions, not x86_64.
                Some("/usr/lib/".to_owned())
            };
        }
    }

    // Canonicalize the sarch directory first.
    let search_dir = if let Some(search_dir) = search_dir {
        let path = Path::new(&search_dir);
        let cannonical = fs::canonicalize(&path);
        if path.is_relative() {
            println!(
                "cargo:warning=shaderc: the given search path '{:?}' is relative; \
                 path must be relative to shaderc-sys crate, \
                 likely not your current working directory",
                &path
            );
        } else if !path.is_dir() {
            println!(
                "cargo:warning=shaderc: the given search path '{:?}' is not a directory",
                &path
            );
        }
        if (cannonical.is_err()) && has_explicit_set_search_dir {
            println!("cargo:warning=shaderc: {:?}", cannonical.err().unwrap());
            println!(
                "cargo:warning=shaderc: failed to canonicalize the given search path '{:?}'",
                &path
            );
            None
        } else {
            cannonical.ok()
        }
    } else {
        None
    };

    // Try to build with the dynamic or static library if a path was explicit set
    // or implicitly chosen.
    if let Some(search_dir) = search_dir {
        let search_dir_str = search_dir.to_string_lossy();

        let static_lib_path = search_dir.join(if target_os == "windows" && target_env == "msvc" {
            SHADERC_STATIC_LIB_FILE_WIN
        } else {
            SHADERC_STATIC_LIB_FILE_UNIX
        });

        let dylib_name = format!(
            "{}{}{}",
            consts::DLL_PREFIX,
            SHADERC_SHARED_LIB,
            consts::DLL_SUFFIX
        );
        let dylib_path = search_dir.join(dylib_name);

        if let Some((lib_name, lib_kind)) = {
            if dylib_path.exists() {
                Some((SHADERC_SHARED_LIB, "dylib"))
            } else if static_lib_path.exists() {
                Some((SHADERC_STATIC_LIB, "static"))
            } else {
                None
            }
        } {
            match (target_os.as_str(), target_env.as_str()) {
                ("linux", _) => {
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("windows", "msvc") => {
                    println!("cargo:warning=shaderc: Windows MSVC static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    return;
                }
                ("windows", "gnu") => {
                    println!("cargo:warning=shaderc: Windows MinGW static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("macos", _) => {
                    println!("cargo:warning=shaderc: macOS static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                ("ios", _) => {
                    println!("cargo:warning=shaderc: macOS static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                (_, _) => {
                    println!(
                        "cargo:warning=shaderc: unsupported platform for linking against \
                         native shaderc libraries installed on system"
                    );
                }
            }
        }
    }

    if config_build_from_source {
        println!("cargo:warning=shaderc: requested to build from source");
    } else {
        println!(
            "cargo:warning=shaderc: cannot find native shaderc library on system; \
             falling back to build from source"
        );
    }

    let mut finder = cmd_finder::CommandFinder::new();

    finder.must_have("cmake");
    finder.must_have("git");
    finder.maybe_have("python").or(finder.maybe_have("python3")).unwrap_or_else(|| {
        panic!("Build requires one of `python` or `python3`");
    });

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        finder.must_have("ninja");
        build_shaderc_msvc(&shaderc_dir)
    } else {
        let has_ninja = finder.maybe_have("ninja").is_some();
        build_shaderc_unix(&shaderc_dir, has_ninja, target_os)
    };

    lib_path.push("lib");
    println!("cargo:rustc-link-search=native={}", lib_path.display());
    println!("cargo:rustc-link-lib=static={}", SHADERC_STATIC_LIB);

    emit_std_cpp_link();
}

fn emit_std_cpp_link() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    match (target_os.as_str(), target_env.as_str()) {
        ("linux", _) | ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"),
        ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"),
        _ => {}
    }
}
<commit_msg>Bring build.rs into proper formatting (#106)<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod cmd_finder;

use std::env;
use std::env::consts;
use std::fs;
use std::path::{Path, PathBuf};

static SHADERC_STATIC_LIB: &str = "shaderc_combined";
static SHADERC_SHARED_LIB: &str = "shaderc_shared";
static SHADERC_STATIC_LIB_FILE_UNIX: &str = "libshaderc_combined.a";
static SHADERC_STATIC_LIB_FILE_WIN: &str = "shaderc_combined.lib";

fn get_apple_sdk_path() -> Option<PathBuf> {
    let target = std::env::var("TARGET").unwrap();
    use std::process::Command;

    // tvOS (and the simulator) could be added here in the future.
    let sdk = if target == "x86_64-apple-ios" || target == "i386-apple-ios" {
        "iphonesimulator"
    } else if target == "aarch64-apple-ios"
        || target == "armv7-apple-ios"
        || target == "armv7s-apple-ios"
    {
        "iphoneos"
    } else {
        return None;
    };

    let output = if let Ok(out) = Command::new("xcrun")
        .args(&["--sdk", sdk, "--show-sdk-path"])
        .output()
    {
        out.stdout
    } else {
        return None;
    };
    let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcrun`");
    Some(PathBuf::from(prefix_str.trim_end().to_string()))
}

fn build_shaderc_unix(shaderc_dir: &PathBuf, use_ninja: bool, target_os: String) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        // CMake options
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        // Glslang options
        .define("ENABLE_SPVREMAPPER", "OFF")
        .define("ENABLE_GLSLANG_BINARIES", "OFF")
        // Shaderc options
        .define("SHADERC_SKIP_TESTS", "ON")
        // SPIRV-Tools options
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF");
    if use_ninja {
        config.generator("Ninja");
    }

    if target_os == "ios" {
        if let Some(path) = get_apple_sdk_path() {
            config.define("CMAKE_OSX_SYSROOT", path);
        }
    }

    config.build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    let linkage = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default();

    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        // CMake options
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        // Glslang options
        .define("ENABLE_SPVREMAPPER", "OFF")
        .define("ENABLE_GLSLANG_BINARIES", "OFF")
        // Shaderc options
        .define("SHADERC_SKIP_TESTS", "ON")
        // SPIRV-Tools options
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .generator("Ninja");

    // cmake-rs tries to be clever on Windows by injecting several
    // C/C++ flags, which causes problems. So I have to manually
    // define CMAKE_*_FLAGS_* here to suppress that.
    let config = if linkage.contains("crt-static") {
        // statically-linked CRT
        config
            .define("CMAKE_C_FLAGS", " /nologo /EHsc /MT")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MT")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MT")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MT")
    } else {
        // dynamically-linked CRT
        config
            .define("CMAKE_C_FLAGS", " /nologo /EHsc /MD")
            .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MD")
            .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MD")
            .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MD")
            // prevent shaderc's cmake script messes with crt flags
            .define("SHADERC_ENABLE_SHARED_CRT", "ON")
    };

    config.build()
}

fn main() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let config_build_from_source = env::var("CARGO_FEATURE_BUILD_FROM_SOURCE").is_ok();
    let has_explicit_set_search_dir = env::var("SHADERC_LIB_DIR").is_ok();

    // Initialize explicit shaderc search directory first.
    let mut search_dir = if let Ok(lib_dir) = env::var("SHADERC_LIB_DIR") {
        println!(
            "cargo:warning=shaderc: searching native shaderc libraries in '{}'",
            lib_dir
        );
        Some(lib_dir)
    } else {
        None
    };

    // Try to find native shaderc library from Vulkan SDK if possible.
    if search_dir.is_none() {
        search_dir = if let Ok(sdk_dir) = env::var("VULKAN_SDK") {
            println!(
                "cargo:warning=shaderc: searching native shaderc libraries in Vulkan SDK '{}/lib'",
                sdk_dir
            );
            Some(format!("{}/lib/", sdk_dir))
        } else {
            None
        };
    }

    // If no explicit path is set and no explicit request is made to build from
    // source, check known system locations before falling back to build from source.
    // This set `search_dir` for later usage.
    if search_dir.is_none() && !config_build_from_source {
        println!(
            "cargo:warning=shaderc: searching for native shaderc libraries on system;  \
             use '--features build-from-source' to force building from source code"
        );

        if target_os == "macos" {
            // Vulkan SDK is installed in `/usr/local/` by default on macOS
            let macos_path = "/usr/local/lib/";
            if Path::new(macos_path).exists() {
                search_dir = Some(macos_path.to_owned());
            }
        } else if target_os == "linux" {
            // https://wiki.ubuntu.com/MultiarchSpec
            // https://wiki.debian.org/Multiarch/Implementation
            let debian_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap() {
                arch if arch == "x86" => "i386".to_owned(),
                arch => arch,
            };
            let debian_triple_path = format!("/usr/lib/{}-linux-gnu/", debian_arch);

            search_dir = if Path::new(&debian_triple_path).exists() {
                // Debian, Ubuntu and their derivatives.
                Some(debian_triple_path)
            } else if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64"
                && Path::new("/usr/lib64/").exists()
            {
                // Other distributions running on x86_64 usually use this path.
                Some("/usr/lib64/".to_owned())
            } else {
                // Other distributions, not x86_64.
                Some("/usr/lib/".to_owned())
            };
        }
    }

    // Canonicalize the sarch directory first.
    let search_dir = if let Some(search_dir) = search_dir {
        let path = Path::new(&search_dir);
        let cannonical = fs::canonicalize(&path);
        if path.is_relative() {
            println!(
                "cargo:warning=shaderc: the given search path '{:?}' is relative; \
                 path must be relative to shaderc-sys crate, \
                 likely not your current working directory",
                &path
            );
        } else if !path.is_dir() {
            println!(
                "cargo:warning=shaderc: the given search path '{:?}' is not a directory",
                &path
            );
        }
        if (cannonical.is_err()) && has_explicit_set_search_dir {
            println!("cargo:warning=shaderc: {:?}", cannonical.err().unwrap());
            println!(
                "cargo:warning=shaderc: failed to canonicalize the given search path '{:?}'",
                &path
            );
            None
        } else {
            cannonical.ok()
        }
    } else {
        None
    };

    // Try to build with the dynamic or static library if a path was explicit set
    // or implicitly chosen.
    if let Some(search_dir) = search_dir {
        let search_dir_str = search_dir.to_string_lossy();

        let static_lib_path = search_dir.join(if target_os == "windows" && target_env == "msvc" {
            SHADERC_STATIC_LIB_FILE_WIN
        } else {
            SHADERC_STATIC_LIB_FILE_UNIX
        });

        let dylib_name = format!(
            "{}{}{}",
            consts::DLL_PREFIX,
            SHADERC_SHARED_LIB,
            consts::DLL_SUFFIX
        );
        let dylib_path = search_dir.join(dylib_name);

        if let Some((lib_name, lib_kind)) = {
            if dylib_path.exists() {
                Some((SHADERC_SHARED_LIB, "dylib"))
            } else if static_lib_path.exists() {
                Some((SHADERC_STATIC_LIB, "static"))
            } else {
                None
            }
        } {
            match (target_os.as_str(), target_env.as_str()) {
                ("linux", _) => {
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("windows", "msvc") => {
                    println!("cargo:warning=shaderc: Windows MSVC static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    return;
                }
                ("windows", "gnu") => {
                    println!("cargo:warning=shaderc: Windows MinGW static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("macos", _) => {
                    println!("cargo:warning=shaderc: macOS static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                ("ios", _) => {
                    println!("cargo:warning=shaderc: macOS static build is experimental");
                    println!("cargo:rustc-link-search=native={}", search_dir_str);
                    println!("cargo:rustc-link-lib={}={}", lib_kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                (_, _) => {
                    println!(
                        "cargo:warning=shaderc: unsupported platform for linking against \
                         native shaderc libraries installed on system"
                    );
                }
            }
        }
    }

    if config_build_from_source {
        println!("cargo:warning=shaderc: requested to build from source");
    } else {
        println!(
            "cargo:warning=shaderc: cannot find native shaderc library on system; \
             falling back to build from source"
        );
    }

    let mut finder = cmd_finder::CommandFinder::new();

    finder.must_have("cmake");
    finder.must_have("git");
    finder
        .maybe_have("python")
        .or(finder.maybe_have("python3"))
        .unwrap_or_else(|| {
            panic!("Build requires one of `python` or `python3`");
        });

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        finder.must_have("ninja");
        build_shaderc_msvc(&shaderc_dir)
    } else {
        let has_ninja = finder.maybe_have("ninja").is_some();
        build_shaderc_unix(&shaderc_dir, has_ninja, target_os)
    };

    lib_path.push("lib");
    println!("cargo:rustc-link-search=native={}", lib_path.display());
    println!("cargo:rustc-link-lib=static={}", SHADERC_STATIC_LIB);

    emit_std_cpp_link();
}

fn emit_std_cpp_link() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    match (target_os.as_str(), target_env.as_str()) {
        ("linux", _) | ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"),
        ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"),
        _ => {}
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add};
use calx::{V2, Dir6, HexGeom, LatticeNode, noise};
use content::TerrainType;

/// Unambiguous location in the game world.
#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Debug, RustcEncodable, RustcDecodable)]
pub struct Location {
    pub x: i8,
    pub y: i8,
    // TODO: Add third dimension for multiple persistent levels.
}

impl Location {
    pub fn new(x: i8, y: i8) -> Location { Location { x: x, y: y } }

    /*
    /// Return terrain at the location.
    pub fn terrain(&self) -> TerrainType {
        let mut ret = world::with(|w| w.area.terrain(*self));
        // Mobs standing on doors make the doors open.
        if ret == TerrainType::Door && self.has_mobs() {
            ret = TerrainType::OpenDoor;
        }
        // Grass is only occasionally fancy.
        // TODO: Make variant tiles into a generic method.
        if ret == TerrainType::Grass {
            let n = noise(self.x as i32 + self.y as i32 * 57);
            if n > 0.85 {
                ret = TerrainType::Grass2;
            }
        }
        ret
    }

    pub fn blocks_sight(&self) -> bool {
        self.terrain().blocks_sight()
    }

    pub fn blocks_walk(&self) -> bool {
        if self.terrain().blocks_walk() { return true; }
        if self.entities().iter().any(|e| e.blocks_walk()) {
            return true;
        }
        false
    }

    pub fn entities(&self) -> Vec<Entity> {
        world::with(|w| w.spatial.entities_at(*self))
    }

    /// Return the most significant entity at the location.
    pub fn main_entity(&self) -> Option<Entity> {
        let e = self.entities();
        // TODO: Actually make the result entity the most significant one (eg.
        // mob if there are both mobs and items).
        if e.len() > 0 { Some(e[0]) } else { None }
    }

    /// Return an item at the location that can be interacted with.
    pub fn top_item(&self) -> Option<Entity> {
        self.entities().iter().find(|e| e.can_be_picked_up()).map(|&x| x)
    }

    pub fn _has_entities(&self) -> bool { !self.entities().is_empty() }

    pub fn has_mobs(&self) -> bool {
        self.entities().iter().any(|e| e.is_mob())
    }

    /// Returns the mob at the given location, if any. The assumption is that
    /// there is always a single primary mob in locations that contain
    /// multiple mobs that can be selected as the return value.
    pub fn mob_at(&self) -> Option<Entity> {
        for &e in self.entities().iter() {
            if e.is_mob() {
                return Some(e);
            }
        }
        None
    }
    */

    /// Vector pointing from this location into the other one if the locations
    /// are on the same Euclidean plane.
    pub fn v2_at(&self, other: Location) -> Option<V2<i32>> {
        // Return None for pairs on different floors if multi-floor support is
        // added.
        Some(V2(other.x as i32, other.y as i32) - V2(self.x as i32, self.y as i32))
    }

    /// Hex distance from this location to the other one, if applicable.
    pub fn distance_from(&self, other: Location) -> Option<i32> {
        if let Some(v) = self.v2_at(other) { Some(v.hex_dist()) } else { None }
    }

    pub fn dir6_towards(&self, other: Location) -> Option<Dir6> {
        if let Some(v) = self.v2_at(other) { Some(Dir6::from_v2(v)) } else { None }
    }

    pub fn noise(&self) -> f32 {
        noise(self.x as i32 + self.y as i32 * 57)
    }

    /*
    /// Return the status of the location in the player's field of view.
    /// Returns None if the location is unexplored.
    pub fn fov_status(&self) -> Option<::FovStatus> {
        if let Some(p) = action::player() {
            match world::with(|w| {
                if let Some(ref mm) = w.map_memories().get(p) {
                    Ok (if mm.seen.contains(self) {
                        Some(::FovStatus::Seen)
                    } else if mm.remembered.contains(self) {
                        Some(::FovStatus::Remembered)
                    } else {
                        None
                    })
                } else {
                    Err(())
                }
            }) {
                Ok(x) => return x,
                _ => ()
            };
        }
        // Just show everything by default.
        Some(::FovStatus::Seen)
    }

    /// Area name for the location.
    pub fn name(&self) -> String {
        match action::current_depth() {
            0 => "Limbo".to_string(),
            1 => "Overworld".to_string(),
            n => format!("Dungeon {}", n - 1)
        }
    }

    /// Light level for the location.
    pub fn light(&self) -> Light {
        if action::current_depth() == 1 {
            // Topside, full light.
            return Light::new(1.0);
        }
        if self.terrain().is_luminous() {
            return Light::new(1.0);
        }

        if let Some(d) = self.distance_from(flags::camera()) {
            let lum = 0.8 - d as f32 / 10.0;
            return Light::new(if lum >= 0.0 { lum } else { 0.0 });
        }
        return Light::new(1.0);
    }
    */
}

impl Add<V2<i32>> for Location {
    type Output = Location;
    fn add(self, other: V2<i32>) -> Location {
        Location::new(
            (self.x as i32 + other.0) as i8,
            (self.y as i32 + other.1) as i8)
    }
}

/// An abstract type that maps a 2D plane into game world Locations. This can
/// be just a straightforward mapping, or it can involve something exotic like
/// a non-Euclidean space where the lines from the Chart origin are raycast
/// through portals.
pub trait Chart: Add<V2<i32>, Output=Location> {}

impl Chart for Location {}

/// The other half of a Chart, mapping Locations into 2D plane positions, if a
/// mapping exists. It depends on the weirdness of a space how trivial this is
/// to do.
pub trait Unchart {
    fn chart_pos(&self, loc: Location) -> Option<V2<i32>>;
}

impl Unchart for Location {
    fn chart_pos(&self, loc: Location) -> Option<V2<i32>> {
        Some(V2(loc.x as i32 - self.x as i32, loc.y as i32 - self.y as i32))
    }
}

impl LatticeNode for Location {
    fn neighbors(&self) -> Vec<Location> {
        Dir6::iter().map(|d| *self + d.to_v2()).collect()
    }
}
<commit_msg>Cleanup defunct code<commit_after>use std::ops::{Add};
use calx::{V2, Dir6, HexGeom, LatticeNode, noise};
use content::TerrainType;

/// Unambiguous location in the game world.
#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Debug, RustcEncodable, RustcDecodable)]
pub struct Location {
    pub x: i8,
    pub y: i8,
    // TODO: Add third dimension for multiple persistent levels.
}

impl Location {
    pub fn new(x: i8, y: i8) -> Location { Location { x: x, y: y } }

    /// Vector pointing from this location into the other one if the locations
    /// are on the same Euclidean plane.
    pub fn v2_at(&self, other: Location) -> Option<V2<i32>> {
        // Return None for pairs on different floors if multi-floor support is
        // added.
        Some(V2(other.x as i32, other.y as i32) - V2(self.x as i32, self.y as i32))
    }

    /// Hex distance from this location to the other one, if applicable.
    pub fn distance_from(&self, other: Location) -> Option<i32> {
        if let Some(v) = self.v2_at(other) { Some(v.hex_dist()) } else { None }
    }

    pub fn dir6_towards(&self, other: Location) -> Option<Dir6> {
        if let Some(v) = self.v2_at(other) { Some(Dir6::from_v2(v)) } else { None }
    }

    pub fn noise(&self) -> f32 {
        noise(self.x as i32 + self.y as i32 * 57)
    }
}

impl Add<V2<i32>> for Location {
    type Output = Location;
    fn add(self, other: V2<i32>) -> Location {
        Location::new(
            (self.x as i32 + other.0) as i8,
            (self.y as i32 + other.1) as i8)
    }
}

/// An abstract type that maps a 2D plane into game world Locations. This can
/// be just a straightforward mapping, or it can involve something exotic like
/// a non-Euclidean space where the lines from the Chart origin are raycast
/// through portals.
pub trait Chart: Add<V2<i32>, Output=Location> {}

impl Chart for Location {}

/// The other half of a Chart, mapping Locations into 2D plane positions, if a
/// mapping exists. It depends on the weirdness of a space how trivial this is
/// to do.
pub trait Unchart {
    fn chart_pos(&self, loc: Location) -> Option<V2<i32>>;
}

impl Unchart for Location {
    fn chart_pos(&self, loc: Location) -> Option<V2<i32>> {
        Some(V2(loc.x as i32 - self.x as i32, loc.y as i32 - self.y as i32))
    }
}

impl LatticeNode for Location {
    fn neighbors(&self) -> Vec<Location> {
        Dir6::iter().map(|d| *self + d.to_v2()).collect()
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 The Tink-Rust Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

//! Key manager for AES-CTR-HMAC keys.

use crate::subtle;
use prost::Message;
use tink_core::{utils::wrap_err, TinkError};
use tink_proto::HashType;

/// Maximal version of AES-CTR-HMAC keys.
pub const AES_CTR_HMAC_AEAD_KEY_VERSION: u32 = 0;
/// Type URL of AES-CTR-HMAC keys that Tink supports.
pub const AES_CTR_HMAC_AEAD_TYPE_URL: &str =
    "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey";
/// Minimum HMAC key size.
const MIN_HMAC_KEY_SIZE_IN_BYTES: usize = 16;
/// Minimum tag size.
const MIN_TAG_SIZE_IN_BYTES: usize = 10;

/// `AesCtrHmacAeadKeyManager` is an implementation of the [`tink_core::registry::KeyManager`]
/// trait. It generates new [`AesCtrHmacAeadKey`](tink_proto::AesCtrHmacAeadKey) keys and produces
/// new instances of [`subtle::EncryptThenAuthenticate`] that use [`subtle::AesCtr`].
#[derive(Default)]
pub(crate) struct AesCtrHmacAeadKeyManager {}

impl tink_core::registry::KeyManager for AesCtrHmacAeadKeyManager {
    /// Create an AEAD for the given serialized [`tink_proto::AesCtrHmacAeadKey`].
    fn primitive(&self, serialized_key: &[u8]) -> Result<tink_core::Primitive, TinkError> {
        if serialized_key.is_empty() {
            return Err("AesCtrHmacAeadKeyManager: empty key".into());
        }
        let key = tink_proto::AesCtrHmacAeadKey::decode(serialized_key)
            .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager: invalid key", e))?;

        let (aes_ctr_key, aes_params) = validate_aes_key(&key)?;
        let (hmac_key, hmac_params, hash) = validate_hmac_key(&key)?;

        let ctr = subtle::AesCtr::new(&aes_ctr_key.key_value, aes_params.iv_size as usize)
            .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager: cannot create new AES-CTR", e))?;
        let hmac =
            tink_mac::subtle::Hmac::new(hash, &hmac_key.key_value, hmac_params.tag_size as usize)
                .map_err(|e| {
                wrap_err(
                    "AesCtrHmacAeadKeyManager: cannot create mac primitive, error",
                    e,
                )
            })?;

        match subtle::EncryptThenAuthenticate::new(Box::new(ctr), Box::new(hmac), hmac_params.tag_size as usize) {
            Ok(p) => Ok(tink_core::Primitive::Aead(Box::new(p))),
            Err(e) => Err(wrap_err("AesCtrHmacAeadKeyManager: cannot create encrypt then authenticate primitive, error", e)),
        }
    }

    /// Create a new key according to the given serialized [`tink_proto::AesCtrHmacAeadKeyFormat`].
    fn new_key(&self, serialized_key_format: &[u8]) -> Result<Vec<u8>, TinkError> {
        if serialized_key_format.is_empty() {
            return Err("AesCtrHmacAeadKeyManager: empty key format".into());
        }
        let key_format = tink_proto::AesCtrHmacAeadKeyFormat::decode(serialized_key_format)
            .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager: invalid key format", e))?;

        let (aes_ctr_key_format, hmac_key_format) = validate_key_format(&key_format)?;
        let key = tink_proto::AesCtrHmacAeadKey {
            version: AES_CTR_HMAC_AEAD_KEY_VERSION,
            aes_ctr_key: Some(tink_proto::AesCtrKey {
                version: AES_CTR_HMAC_AEAD_KEY_VERSION,
                key_value: tink_core::subtle::random::get_random_bytes(
                    aes_ctr_key_format.key_size as usize,
                ),
                params: aes_ctr_key_format.params,
            }),
            hmac_key: Some(tink_proto::HmacKey {
                version: AES_CTR_HMAC_AEAD_KEY_VERSION,
                key_value: tink_core::subtle::random::get_random_bytes(
                    hmac_key_format.key_size as usize,
                ),
                params: hmac_key_format.params,
            }),
        };
        let mut sk = Vec::new();
        key.encode(&mut sk)
            .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager: failed to encode new key", e))?;
        Ok(sk)
    }

    fn type_url(&self) -> &'static str {
        AES_CTR_HMAC_AEAD_TYPE_URL
    }

    fn key_material_type(&self) -> tink_proto::key_data::KeyMaterialType {
        tink_proto::key_data::KeyMaterialType::Symmetric
    }
}

/// Validate and extract the AES parts of the given [`tink_proto::AesCtrHmacAeadKey`].
fn validate_aes_key(
    key: &tink_proto::AesCtrHmacAeadKey,
) -> Result<(&tink_proto::AesCtrKey, &tink_proto::AesCtrParams), TinkError> {
    tink_core::keyset::validate_key_version(key.version, AES_CTR_HMAC_AEAD_KEY_VERSION)
        .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;
    let aes_ctr_key = key
        .aes_ctr_key
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no AES key"))?;
    tink_core::keyset::validate_key_version(aes_ctr_key.version, AES_CTR_HMAC_AEAD_KEY_VERSION)
        .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;

    // Validate `AesCtrKey`.
    let key_size = aes_ctr_key.key_value.len();
    subtle::validate_aes_key_size(key_size).map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;
    let params = aes_ctr_key
        .params
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no AES key params"))?;
    if (params.iv_size as usize) < subtle::AES_CTR_MIN_IV_SIZE || params.iv_size > 16 {
        return Err(
            "AesCtrHmacAeadKeyManager: invalid AesCtrHmacAeadKey: IV size out of range".into(),
        );
    }
    Ok((aes_ctr_key, params))
}

/// Validate and extract the HMAC parts of the given [`tink_proto::AesCtrHmacAeadKey`].
fn validate_hmac_key(
    key: &tink_proto::AesCtrHmacAeadKey,
) -> Result<
    (
        &tink_proto::HmacKey,
        &tink_proto::HmacParams,
        tink_proto::HashType,
    ),
    TinkError,
> {
    let hmac_key = key
        .hmac_key
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no HMAC key"))?;
    tink_core::keyset::validate_key_version(hmac_key.version, AES_CTR_HMAC_AEAD_KEY_VERSION)
        .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;
    let hmac_params = hmac_key
        .params
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no HMAC params"))?;
    let hash = HashType::from_i32(hmac_params.hash)
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: unknown hash"))?;

    Ok((hmac_key, hmac_params, hash))
}

/// Validate the given [`tink_proto::AesCtrHmacAeadKeyFormat`].
fn validate_key_format(
    format: &tink_proto::AesCtrHmacAeadKeyFormat,
) -> Result<(tink_proto::AesCtrKeyFormat, tink_proto::HmacKeyFormat), TinkError> {
    // Validate AesCtrKeyFormat.
    let aes_ctr_format = format
        .aes_ctr_key_format
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no AES key format"))?;
    subtle::validate_aes_key_size(aes_ctr_format.key_size as usize)
        .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;
    let aes_params = aes_ctr_format
        .params
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no AES key params"))?;
    if (aes_params.iv_size as usize) < subtle::AES_CTR_MIN_IV_SIZE || aes_params.iv_size > 16 {
        return Err(
            "AesCtrHmacAeadKeyManager: invalid AesCtrHmacAeadKeyFormat: IV size out of range"
                .into(),
        );
    }

    // Validate HmacKeyFormat.
    let hmac_key_format = format
        .hmac_key_format
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no HMAC key format"))?;
    if (hmac_key_format.key_size as usize) < MIN_HMAC_KEY_SIZE_IN_BYTES {
        return Err("AesCtrHmacAeadKeyManager: HMAC key_size is too small".into());
    }
    let hmac_params = hmac_key_format
        .params
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no HMAC key params"))?;
    if (hmac_params.tag_size as usize) < MIN_TAG_SIZE_IN_BYTES {
        return Err(format!(
            "AesCtrHmacAeadKeyManager: invalid HmacParams: tag_size {} is too small",
            hmac_params.tag_size
        )
        .into());
    }

    let tag_size = match HashType::from_i32(hmac_params.hash) {
        Some(HashType::Sha1) => 20,
        Some(HashType::Sha256) => 32,
        Some(HashType::Sha512) => 64,
        _ => {
            return Err(format!(
                "AesCtrHmacAeadKeyManager: invalid HmacParams: hash_type {:?} not supported",
                hmac_params.hash
            )
            .into())
        }
    };

    if hmac_params.tag_size > tag_size {
        return Err(format!(
            "AesCtrHmacAeadKeyManager: invalid HmacParams: tag_size {} is too big for HashType {}",
            hmac_params.tag_size, hmac_params.hash
        )
        .into());
    }
    Ok((aes_ctr_format.clone(), hmac_key_format.clone()))
}
<commit_msg>tink-aead: add HMAC-SHA{224,384} support<commit_after>// Copyright 2020 The Tink-Rust Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

//! Key manager for AES-CTR-HMAC keys.

use crate::subtle;
use prost::Message;
use tink_core::{utils::wrap_err, TinkError};
use tink_proto::HashType;

/// Maximal version of AES-CTR-HMAC keys.
pub const AES_CTR_HMAC_AEAD_KEY_VERSION: u32 = 0;
/// Type URL of AES-CTR-HMAC keys that Tink supports.
pub const AES_CTR_HMAC_AEAD_TYPE_URL: &str =
    "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey";
/// Minimum HMAC key size.
const MIN_HMAC_KEY_SIZE_IN_BYTES: usize = 16;
/// Minimum tag size.
const MIN_TAG_SIZE_IN_BYTES: usize = 10;

/// `AesCtrHmacAeadKeyManager` is an implementation of the [`tink_core::registry::KeyManager`]
/// trait. It generates new [`AesCtrHmacAeadKey`](tink_proto::AesCtrHmacAeadKey) keys and produces
/// new instances of [`subtle::EncryptThenAuthenticate`] that use [`subtle::AesCtr`].
#[derive(Default)]
pub(crate) struct AesCtrHmacAeadKeyManager {}

impl tink_core::registry::KeyManager for AesCtrHmacAeadKeyManager {
    /// Create an AEAD for the given serialized [`tink_proto::AesCtrHmacAeadKey`].
    fn primitive(&self, serialized_key: &[u8]) -> Result<tink_core::Primitive, TinkError> {
        if serialized_key.is_empty() {
            return Err("AesCtrHmacAeadKeyManager: empty key".into());
        }
        let key = tink_proto::AesCtrHmacAeadKey::decode(serialized_key)
            .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager: invalid key", e))?;

        let (aes_ctr_key, aes_params) = validate_aes_key(&key)?;
        let (hmac_key, hmac_params, hash) = validate_hmac_key(&key)?;

        let ctr = subtle::AesCtr::new(&aes_ctr_key.key_value, aes_params.iv_size as usize)
            .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager: cannot create new AES-CTR", e))?;
        let hmac =
            tink_mac::subtle::Hmac::new(hash, &hmac_key.key_value, hmac_params.tag_size as usize)
                .map_err(|e| {
                wrap_err(
                    "AesCtrHmacAeadKeyManager: cannot create mac primitive, error",
                    e,
                )
            })?;

        match subtle::EncryptThenAuthenticate::new(Box::new(ctr), Box::new(hmac), hmac_params.tag_size as usize) {
            Ok(p) => Ok(tink_core::Primitive::Aead(Box::new(p))),
            Err(e) => Err(wrap_err("AesCtrHmacAeadKeyManager: cannot create encrypt then authenticate primitive, error", e)),
        }
    }

    /// Create a new key according to the given serialized [`tink_proto::AesCtrHmacAeadKeyFormat`].
    fn new_key(&self, serialized_key_format: &[u8]) -> Result<Vec<u8>, TinkError> {
        if serialized_key_format.is_empty() {
            return Err("AesCtrHmacAeadKeyManager: empty key format".into());
        }
        let key_format = tink_proto::AesCtrHmacAeadKeyFormat::decode(serialized_key_format)
            .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager: invalid key format", e))?;

        let (aes_ctr_key_format, hmac_key_format) = validate_key_format(&key_format)?;
        let key = tink_proto::AesCtrHmacAeadKey {
            version: AES_CTR_HMAC_AEAD_KEY_VERSION,
            aes_ctr_key: Some(tink_proto::AesCtrKey {
                version: AES_CTR_HMAC_AEAD_KEY_VERSION,
                key_value: tink_core::subtle::random::get_random_bytes(
                    aes_ctr_key_format.key_size as usize,
                ),
                params: aes_ctr_key_format.params,
            }),
            hmac_key: Some(tink_proto::HmacKey {
                version: AES_CTR_HMAC_AEAD_KEY_VERSION,
                key_value: tink_core::subtle::random::get_random_bytes(
                    hmac_key_format.key_size as usize,
                ),
                params: hmac_key_format.params,
            }),
        };
        let mut sk = Vec::new();
        key.encode(&mut sk)
            .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager: failed to encode new key", e))?;
        Ok(sk)
    }

    fn type_url(&self) -> &'static str {
        AES_CTR_HMAC_AEAD_TYPE_URL
    }

    fn key_material_type(&self) -> tink_proto::key_data::KeyMaterialType {
        tink_proto::key_data::KeyMaterialType::Symmetric
    }
}

/// Validate and extract the AES parts of the given [`tink_proto::AesCtrHmacAeadKey`].
fn validate_aes_key(
    key: &tink_proto::AesCtrHmacAeadKey,
) -> Result<(&tink_proto::AesCtrKey, &tink_proto::AesCtrParams), TinkError> {
    tink_core::keyset::validate_key_version(key.version, AES_CTR_HMAC_AEAD_KEY_VERSION)
        .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;
    let aes_ctr_key = key
        .aes_ctr_key
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no AES key"))?;
    tink_core::keyset::validate_key_version(aes_ctr_key.version, AES_CTR_HMAC_AEAD_KEY_VERSION)
        .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;

    // Validate `AesCtrKey`.
    let key_size = aes_ctr_key.key_value.len();
    subtle::validate_aes_key_size(key_size).map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;
    let params = aes_ctr_key
        .params
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no AES key params"))?;
    if (params.iv_size as usize) < subtle::AES_CTR_MIN_IV_SIZE || params.iv_size > 16 {
        return Err(
            "AesCtrHmacAeadKeyManager: invalid AesCtrHmacAeadKey: IV size out of range".into(),
        );
    }
    Ok((aes_ctr_key, params))
}

/// Validate and extract the HMAC parts of the given [`tink_proto::AesCtrHmacAeadKey`].
fn validate_hmac_key(
    key: &tink_proto::AesCtrHmacAeadKey,
) -> Result<
    (
        &tink_proto::HmacKey,
        &tink_proto::HmacParams,
        tink_proto::HashType,
    ),
    TinkError,
> {
    let hmac_key = key
        .hmac_key
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no HMAC key"))?;
    tink_core::keyset::validate_key_version(hmac_key.version, AES_CTR_HMAC_AEAD_KEY_VERSION)
        .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;
    let hmac_params = hmac_key
        .params
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no HMAC params"))?;
    let hash = HashType::from_i32(hmac_params.hash)
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: unknown hash"))?;

    Ok((hmac_key, hmac_params, hash))
}

/// Validate the given [`tink_proto::AesCtrHmacAeadKeyFormat`].
fn validate_key_format(
    format: &tink_proto::AesCtrHmacAeadKeyFormat,
) -> Result<(tink_proto::AesCtrKeyFormat, tink_proto::HmacKeyFormat), TinkError> {
    // Validate AesCtrKeyFormat.
    let aes_ctr_format = format
        .aes_ctr_key_format
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no AES key format"))?;
    subtle::validate_aes_key_size(aes_ctr_format.key_size as usize)
        .map_err(|e| wrap_err("AesCtrHmacAeadKeyManager", e))?;
    let aes_params = aes_ctr_format
        .params
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no AES key params"))?;
    if (aes_params.iv_size as usize) < subtle::AES_CTR_MIN_IV_SIZE || aes_params.iv_size > 16 {
        return Err(
            "AesCtrHmacAeadKeyManager: invalid AesCtrHmacAeadKeyFormat: IV size out of range"
                .into(),
        );
    }

    // Validate HmacKeyFormat.
    let hmac_key_format = format
        .hmac_key_format
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no HMAC key format"))?;
    if (hmac_key_format.key_size as usize) < MIN_HMAC_KEY_SIZE_IN_BYTES {
        return Err("AesCtrHmacAeadKeyManager: HMAC key_size is too small".into());
    }
    let hmac_params = hmac_key_format
        .params
        .as_ref()
        .ok_or_else(|| TinkError::new("AesCtrHmacAeadKeyManager: no HMAC key params"))?;
    if (hmac_params.tag_size as usize) < MIN_TAG_SIZE_IN_BYTES {
        return Err(format!(
            "AesCtrHmacAeadKeyManager: invalid HmacParams: tag_size {} is too small",
            hmac_params.tag_size
        )
        .into());
    }

    let tag_size = match HashType::from_i32(hmac_params.hash) {
        Some(HashType::Sha1) => 20,
        Some(HashType::Sha224) => 28,
        Some(HashType::Sha256) => 32,
        Some(HashType::Sha384) => 48,
        Some(HashType::Sha512) => 64,
        _ => {
            return Err(format!(
                "AesCtrHmacAeadKeyManager: invalid HmacParams: hash_type {:?} not supported",
                hmac_params.hash
            )
            .into())
        }
    };

    if hmac_params.tag_size > tag_size {
        return Err(format!(
            "AesCtrHmacAeadKeyManager: invalid HmacParams: tag_size {} is too big for HashType {}",
            hmac_params.tag_size, hmac_params.hash
        )
        .into());
    }
    Ok((aes_ctr_format.clone(), hmac_key_format.clone()))
}
<|endoftext|>


<commit_before>#![crate_type = "proc-macro"]
#![recursion_limit = "192"]
#![feature(trace_macros)]
#![feature(log_syntax)]

extern crate syn;
extern crate proc_macro;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(ModParse, attributes(module))]
pub fn parse(input: TokenStream) -> TokenStream {
    let source = input.to_string();
    let mut ast = syn::parse_derive_input(&source).expect("failed to parse rust syntax");
    let gen = impl_parse(&mut ast);
    gen.parse().expect("failed to serialize into rust syntax")
}

fn impl_parse(ast: &mut syn::DeriveInput) -> quote::Tokens {
    use syn::{Body, VariantData};

    let ref mut variants = match ast.body {
        Body::Struct(VariantData::Struct(ref vars)) => vars,
        _ => panic!("#[derive(Parse)] is only defined for braced structs"),
    };

    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    let atmatch = variants.iter()
        .filter_map(|field| {
        	let field_name = field.ident.as_ref().clone();

        	let ret = if field.attrs.iter().any(|attr| attr.name() == "param") {
        		Some( quote! { self.delegate._params.push(stringify!(#field_name) ) } )
        	} else if field.attrs.iter().any(|attr| attr.name() == "module") {
//        		Some ( quote! { self.delegate._modules.insert(stringify!(#field_name).to_owned(), self. #field_name .delegate()) } )
        		Some ( quote! { self.delegate._modules.push(stringify!(#field_name)) } )
        	} else { None };
        	//field.attrs.retain(|ref attr| !names.contains(&attr.name()) );
    		ret
        });
    let modlist = variants
        .iter()
        .filter_map(
        	|field| {
            	let field_name = field.ident.as_ref().clone();
                if field.attrs.iter().any(|attr| attr.name() == "module") {
                	Some(quote! { stringify!(#field_name) => Some(&mut self. #field_name ), })
            	} else {
                            None
                        }
                    });
    let foo = quote! {
        impl #impl_generics ModuleStruct<'a> for #name  #ty_generics  #where_clause {
            fn init_module(&mut self) {
            	self.delegate._name = stringify!(#name);
				#(#atmatch);*
				;
            }
            fn get_module(&mut self, name: &str) ->  Option<&mut ModIntf<'a>> {
            	match name {
                    #(#modlist),*
                    _ => None,
                }
            }
        }
    };
    println!("parse is {}", foo);
    foo
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}
<commit_msg>switch to add_module in macro<commit_after>#![crate_type = "proc-macro"]
#![recursion_limit = "192"]
#![feature(trace_macros)]
#![feature(log_syntax)]

extern crate syn;
extern crate proc_macro;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(ModParse, attributes(module))]
pub fn parse(input: TokenStream) -> TokenStream {
    let source = input.to_string();
    let mut ast = syn::parse_derive_input(&source).expect("failed to parse rust syntax");
    let gen = impl_parse(&mut ast);
    gen.parse().expect("failed to serialize into rust syntax")
}

fn impl_parse(ast: &mut syn::DeriveInput) -> quote::Tokens {
    use syn::{Body, VariantData};

    let ref mut variants = match ast.body {
        Body::Struct(VariantData::Struct(ref vars)) => vars,
        _ => panic!("#[derive(Parse)] is only defined for braced structs"),
    };

    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    let atmatch = variants.iter()
        .filter_map(|field| {
        	let field_name = field.ident.as_ref().clone();

        	let ret = if field.attrs.iter().any(|attr| attr.name() == "param") {
        		Some( quote! { self.delegate._params.push(stringify!(#field_name) ) } )
        	} else if field.attrs.iter().any(|attr| attr.name() == "module") {
//        		Some ( quote! { self.delegate._modules.insert(stringify!(#field_name).to_owned(), self. #field_name .delegate()) } )
        		Some ( quote! {
        			self.delegate.add_module(&mut self. #field_name );
        		 } )
        	} else { None };
        	//field.attrs.retain(|ref attr| !names.contains(&attr.name()) );
    		ret
        });
    let foo = quote! {
        impl #impl_generics ModuleStruct<'a> for #name  #ty_generics  #where_clause {
            fn init_module(&mut self) {
            	self.delegate._name = stringify!(#name);
				#(#atmatch);*
				;
            }
        }
    };
    println!("parse is {}", foo);
    foo
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}
<|endoftext|>


<commit_before><commit_msg>errors: add Into<::std::io::Error> conversion (#49)<commit_after><|endoftext|>


<commit_before>#[derive(Debug, serde_derive::Deserialize)]
pub struct Config {
    pub name: String,
    pub package_key: Option<String>,
    pub repo_key: Option<String>,
    pub srcdest: String,
    pub logdest: String,
    pub pkgbuild: String,
    pub builds: std::collections::HashMap<super::builder::Arch, BuildConfig>,
    pub s3: Option<S3Config>,
}

#[derive(Debug, serde_derive::Deserialize)]
pub struct BuildConfig {
    pub chroot: String,
}

#[derive(Debug, serde_derive::Deserialize)]
pub struct S3Config {
    pub bucket: String,
    pub region: Region,
}

#[derive(Debug)]
pub struct Region(rusoto_core::Region);

impl<'de> serde::Deserialize<'de> for Region {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct Visitor;
        impl<'de> serde::de::Visitor<'de> for Visitor {
            type Value = Region;

            fn expecting(
                &self,
                formatter: &mut std::fmt::Formatter,
            ) -> Result<(), std::fmt::Error> {
                write!(formatter, "a valid AWS region name")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                use std::str::FromStr;

                match rusoto_core::Region::from_str(v) {
                    Ok(r) => Ok(Region(r)),
                    Err(e) => Err(serde::de::Error::invalid_value(
                        serde::de::Unexpected::Str(v),
                        &format!("{}", e).as_str(),
                    )),
                }
            }
        }
        deserializer.deserialize_str(Visitor {})
    }
}

impl Config {
    pub fn from_reader<R>(reader: R) -> serde_yaml::Result<Self>
    where
        R: std::io::Read,
    {
        serde_yaml::from_reader(reader)
    }

    pub fn repo_dir(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        std::path::PathBuf::from(&self.name)
            .join("os")
            .join(format!("{}", arch))
    }

    pub fn db_path(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        let mut path = self.repo_dir(arch).join(&self.name).into_os_string();
        path.push(".db");
        std::path::PathBuf::from(path)
    }

    pub fn files_path(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        let mut path = self.repo_dir(arch).join(&self.name).into_os_string();
        path.push(".files");
        std::path::PathBuf::from(path)
    }

    pub fn package_dir(&self, package_name: &str) -> std::path::PathBuf {
        std::path::PathBuf::from(&self.pkgbuild).join(package_name)
    }
}

pub struct S3 {
    client: rusoto_s3::S3Client,
    bucket: String,
}

impl S3 {
    pub fn new(config: &S3Config) -> Self {
        let Region(ref region) = config.region;
        let client = rusoto_s3::S3Client::new(region.clone());
        S3 {
            client,
            bucket: config.bucket.to_owned(),
        }
    }

    pub async fn download_repository(
        &self,
        config: &Config,
        arch: &super::builder::Arch,
    ) -> Result<(), anyhow::Error> {
        self.get(config.db_path(arch)).await?;
        self.get(config.files_path(arch)).await
    }

    pub async fn upload_repository<P>(
        &self,
        config: &Config,
        arch: &super::builder::Arch,
        package_paths: &[P],
    ) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        const XZ_MIME_TYPE: &str = "application/x-xz";
        const ZST_MIME_TYPE: &str = "application/zstd";
        const OCTET_STREAM_MIME_TYPE: &str = "application/octet-stream";
        const SIG_MIME_TYPE: &str = "application/pgp-signature";
        const GZIP_MIME_TYPE: &str = "application/gzip";

        for package_path in package_paths {
            let mime_type = if let Some(ext) = package_path.as_ref().extension() {
                if ext == "zst" {
                    ZST_MIME_TYPE
                } else if ext == "xz" {
                    XZ_MIME_TYPE
                } else {
                    OCTET_STREAM_MIME_TYPE
                }
            } else {
                OCTET_STREAM_MIME_TYPE
            };
            self.put(package_path, mime_type).await?;
            if config.package_key.is_some() {
                let mut sig_path = package_path.as_ref().as_os_str().to_os_string();
                sig_path.push(".sig");
                self.put(&sig_path, SIG_MIME_TYPE).await?;
            }
        }
        self.put(config.files_path(arch), GZIP_MIME_TYPE).await?;
        let db_path = config.db_path(arch);
        self.put(&db_path, GZIP_MIME_TYPE).await?;
        if config.repo_key.is_some() {
            let mut sig_path = db_path.clone().into_os_string();
            sig_path.push(".sig");
            self.put(sig_path, SIG_MIME_TYPE).await?;
        }
        Ok(())
    }

    async fn get<P>(&self, path: P) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        use rusoto_s3::S3;

        let path = path.as_ref();
        let request = rusoto_s3::GetObjectRequest {
            bucket: self.bucket.to_owned(),
            key: path.to_string_lossy().into_owned(),
            ..rusoto_s3::GetObjectRequest::default()
        };
        println!("Download {}", path.display());
        match self.client.get_object(request).await {
            Ok(output) => {
                if let Some(mut body) = output.body {
                    use futures::StreamExt as _;
                    use tokio::io::AsyncWriteExt as _;

                    let file = tokio::fs::File::create(path).await?;
                    let mut writer = tokio::io::BufWriter::new(file);
                    while let Some(item) = body.next().await {
                        writer.write_all(&item?).await?;
                    }
                    writer.shutdown().await?;
                }
                Ok(())
            }
            Err(rusoto_core::RusotoError::Service(rusoto_s3::GetObjectError::NoSuchKey(_))) => {
                Ok(())
            }
            Err(e) => Err(anyhow::Error::from(e)),
        }
    }

    async fn put<P>(&self, path: P, content_type: &str) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        use futures::FutureExt as _;
        use futures::TryStreamExt as _;
        use rusoto_s3::S3;

        let path = path.as_ref();
        let metadata = tokio::fs::metadata(path).await?;
        let stream = rusoto_s3::StreamingBody::new(
            tokio::fs::read(path.to_owned())
                .into_stream()
                .map_ok(bytes::Bytes::from),
        );
        let request = rusoto_s3::PutObjectRequest {
            bucket: self.bucket.to_owned(),
            key: path.to_string_lossy().into_owned(),
            content_type: Some(content_type.to_owned()),
            content_length: Some(metadata.len() as i64),
            body: Some(stream),
            ..Default::default()
        };
        println!("Upload {}", path.display());
        self.client.put_object(request).await?;
        Ok(())
    }
}
<commit_msg>Parallelize S3 ops<commit_after>#[derive(Debug, serde_derive::Deserialize)]
pub struct Config {
    pub name: String,
    pub package_key: Option<String>,
    pub repo_key: Option<String>,
    pub srcdest: String,
    pub logdest: String,
    pub pkgbuild: String,
    pub builds: std::collections::HashMap<super::builder::Arch, BuildConfig>,
    pub s3: Option<S3Config>,
}

#[derive(Debug, serde_derive::Deserialize)]
pub struct BuildConfig {
    pub chroot: String,
}

#[derive(Debug, serde_derive::Deserialize)]
pub struct S3Config {
    pub bucket: String,
    pub region: Region,
}

#[derive(Debug)]
pub struct Region(rusoto_core::Region);

impl<'de> serde::Deserialize<'de> for Region {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct Visitor;
        impl<'de> serde::de::Visitor<'de> for Visitor {
            type Value = Region;

            fn expecting(
                &self,
                formatter: &mut std::fmt::Formatter,
            ) -> Result<(), std::fmt::Error> {
                write!(formatter, "a valid AWS region name")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                use std::str::FromStr;

                match rusoto_core::Region::from_str(v) {
                    Ok(r) => Ok(Region(r)),
                    Err(e) => Err(serde::de::Error::invalid_value(
                        serde::de::Unexpected::Str(v),
                        &format!("{}", e).as_str(),
                    )),
                }
            }
        }
        deserializer.deserialize_str(Visitor {})
    }
}

impl Config {
    pub fn from_reader<R>(reader: R) -> serde_yaml::Result<Self>
    where
        R: std::io::Read,
    {
        serde_yaml::from_reader(reader)
    }

    pub fn repo_dir(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        std::path::PathBuf::from(&self.name)
            .join("os")
            .join(format!("{}", arch))
    }

    pub fn db_path(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        let mut path = self.repo_dir(arch).join(&self.name).into_os_string();
        path.push(".db");
        std::path::PathBuf::from(path)
    }

    pub fn files_path(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        let mut path = self.repo_dir(arch).join(&self.name).into_os_string();
        path.push(".files");
        std::path::PathBuf::from(path)
    }

    pub fn package_dir(&self, package_name: &str) -> std::path::PathBuf {
        std::path::PathBuf::from(&self.pkgbuild).join(package_name)
    }
}

pub struct S3 {
    client: rusoto_s3::S3Client,
    bucket: String,
}

impl S3 {
    pub fn new(config: &S3Config) -> Self {
        let Region(ref region) = config.region;
        let client = rusoto_s3::S3Client::new(region.clone());
        S3 {
            client,
            bucket: config.bucket.to_owned(),
        }
    }

    pub async fn download_repository(
        &self,
        config: &Config,
        arch: &super::builder::Arch,
    ) -> Result<(), anyhow::Error> {
        let (r1, r2) = futures::join!(
            self.get(config.db_path(arch)),
            self.get(config.files_path(arch))
        );
        r1?;
        r2
    }

    pub async fn upload_repository<P>(
        &self,
        config: &Config,
        arch: &super::builder::Arch,
        package_paths: &[P],
    ) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        const XZ_MIME_TYPE: &str = "application/x-xz";
        const ZST_MIME_TYPE: &str = "application/zstd";
        const OCTET_STREAM_MIME_TYPE: &str = "application/octet-stream";
        const SIG_MIME_TYPE: &str = "application/pgp-signature";
        const GZIP_MIME_TYPE: &str = "application/gzip";

        let mut futures_unordered = futures::stream::FuturesUnordered::new();
        for package_path in package_paths {
            let mime_type = if let Some(ext) = package_path.as_ref().extension() {
                if ext == "zst" {
                    ZST_MIME_TYPE
                } else if ext == "xz" {
                    XZ_MIME_TYPE
                } else {
                    OCTET_STREAM_MIME_TYPE
                }
            } else {
                OCTET_STREAM_MIME_TYPE
            };
            futures_unordered.push(self.put(package_path.as_ref().to_owned(), mime_type));
            if config.package_key.is_some() {
                let mut sig_path = package_path.as_ref().as_os_str().to_os_string();
                sig_path.push(".sig");
                futures_unordered.push(self.put(std::path::PathBuf::from(sig_path), SIG_MIME_TYPE));
            }
        }
        futures_unordered.push(self.put(config.files_path(arch), GZIP_MIME_TYPE));
        let db_path = config.db_path(arch);
        futures_unordered.push(self.put(db_path.to_owned(), GZIP_MIME_TYPE));
        if config.repo_key.is_some() {
            let mut sig_path = db_path.into_os_string();
            sig_path.push(".sig");
            futures_unordered.push(self.put(std::path::PathBuf::from(sig_path), SIG_MIME_TYPE));
        }
        use futures::StreamExt as _;
        while let Some(result) = futures_unordered.next().await {
            result?;
        }
        Ok(())
    }

    async fn get<P>(&self, path: P) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        use rusoto_s3::S3;

        let path = path.as_ref();
        let request = rusoto_s3::GetObjectRequest {
            bucket: self.bucket.to_owned(),
            key: path.to_string_lossy().into_owned(),
            ..rusoto_s3::GetObjectRequest::default()
        };
        println!("Download {}", path.display());
        match self.client.get_object(request).await {
            Ok(output) => {
                if let Some(mut body) = output.body {
                    use futures::StreamExt as _;
                    use tokio::io::AsyncWriteExt as _;

                    let file = tokio::fs::File::create(path).await?;
                    let mut writer = tokio::io::BufWriter::new(file);
                    while let Some(item) = body.next().await {
                        writer.write_all(&item?).await?;
                    }
                    writer.shutdown().await?;
                }
                Ok(())
            }
            Err(rusoto_core::RusotoError::Service(rusoto_s3::GetObjectError::NoSuchKey(_))) => {
                Ok(())
            }
            Err(e) => Err(anyhow::Error::from(e)),
        }
    }

    async fn put<P>(&self, path: P, content_type: &str) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        use futures::FutureExt as _;
        use futures::TryStreamExt as _;
        use rusoto_s3::S3;

        let path = path.as_ref();
        let metadata = tokio::fs::metadata(path).await?;
        let stream = rusoto_s3::StreamingBody::new(
            tokio::fs::read(path.to_owned())
                .into_stream()
                .map_ok(bytes::Bytes::from),
        );
        let request = rusoto_s3::PutObjectRequest {
            bucket: self.bucket.to_owned(),
            key: path.to_string_lossy().into_owned(),
            content_type: Some(content_type.to_owned()),
            content_length: Some(metadata.len() as i64),
            body: Some(stream),
            ..Default::default()
        };
        println!("Upload {}", path.display());
        self.client.put_object(request).await?;
        Ok(())
    }
}
<|endoftext|>


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

use document_loader::DocumentLoader;
use dom::bindings::codegen::Bindings::DOMImplementationBinding;
use dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementationMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::error::Fallible;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bindings::xmlname::{namespace_from_domstring, validate_qualified_name};
use dom::document::{Document, IsHTMLDocument};
use dom::document::DocumentSource;
use dom::documenttype::DocumentType;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlheadelement::HTMLHeadElement;
use dom::htmlhtmlelement::HTMLHtmlElement;
use dom::htmltitleelement::HTMLTitleElement;
use dom::node::Node;
use dom::text::Text;
use dom::xmldocument::XMLDocument;

// https://dom.spec.whatwg.org/#domimplementation
#[dom_struct]
pub struct DOMImplementation {
    reflector_: Reflector,
    document: JS<Document>,
}

impl DOMImplementation {
    fn new_inherited(document: &Document) -> DOMImplementation {
        DOMImplementation {
            reflector_: Reflector::new(),
            document: JS::from_ref(document),
        }
    }

    pub fn new(document: &Document) -> Root<DOMImplementation> {
        let window = document.window();
        reflect_dom_object(box DOMImplementation::new_inherited(document),
                           window,
                           DOMImplementationBinding::Wrap)
    }
}

// https://dom.spec.whatwg.org/#domimplementation
impl DOMImplementationMethods for DOMImplementation {
    // https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype
    fn CreateDocumentType(&self,
                          qualified_name: DOMString,
                          pubid: DOMString,
                          sysid: DOMString)
                          -> Fallible<Root<DocumentType>> {
        try!(validate_qualified_name(&qualified_name));
        Ok(DocumentType::new(qualified_name, Some(pubid), Some(sysid), &self.document))
    }

    // https://dom.spec.whatwg.org/#dom-domimplementation-createdocument
    fn CreateDocument(&self,
                      maybe_namespace: Option<DOMString>,
                      qname: DOMString,
                      maybe_doctype: Option<&DocumentType>)
                      -> Fallible<Root<XMLDocument>> {
        let win = self.document.window();
        let loader = DocumentLoader::new(&self.document.loader());
        let namespace = namespace_from_domstring(maybe_namespace.to_owned());

        let content_type = match namespace {
            ns!(html) => "application/xhtml+xml",
            ns!(svg) => "image/svg+xml",
            _ => "application/xml"
        };

        // Step 1.
        let doc = XMLDocument::new(win,
                                   None,
                                   None,
                                   IsHTMLDocument::NonHTMLDocument,
                                   Some(DOMString::from(content_type)),
                                   None,
                                   DocumentSource::NotFromParser,
                                   loader);
        // Step 2-3.
        let maybe_elem = if qname.is_empty() {
            None
        } else {
            match doc.upcast::<Document>().CreateElementNS(maybe_namespace, qname) {
                Err(error) => return Err(error),
                Ok(elem) => Some(elem),
            }
        };

        {
            let doc_node = doc.upcast::<Node>();

            // Step 4.
            if let Some(doc_type) = maybe_doctype {
                doc_node.AppendChild(doc_type.upcast()).unwrap();
            }

            // Step 5.
            if let Some(ref elem) = maybe_elem {
                doc_node.AppendChild(elem.upcast()).unwrap();
            }
        }

        // Step 6.
        // FIXME: https://github.com/mozilla/servo/issues/1522

        // Step 7.
        Ok(doc)
    }

    // https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
    fn CreateHTMLDocument(&self, title: Option<DOMString>) -> Root<Document> {
        let win = self.document.window();
        let loader = DocumentLoader::new(&self.document.loader());

        // Step 1-2.
        let doc = Document::new(win,
                                None,
                                None,
                                IsHTMLDocument::HTMLDocument,
                                None,
                                None,
                                DocumentSource::NotFromParser,
                                loader,
                                None,
                                None);

        {
            // Step 3.
            let doc_node = doc.upcast::<Node>();
            let doc_type = DocumentType::new(DOMString::from("html"), None, None, &doc);
            doc_node.AppendChild(doc_type.upcast()).unwrap();
        }

        {
            // Step 4.
            let doc_node = doc.upcast::<Node>();
            let doc_html = Root::upcast::<Node>(HTMLHtmlElement::new(local_name!("html"),
                                                                     None,
                                                                     &doc));
            doc_node.AppendChild(&doc_html).expect("Appending failed");

            {
                // Step 5.
                let doc_head = Root::upcast::<Node>(HTMLHeadElement::new(local_name!("head"),
                                                                         None,
                                                                         &doc));
                doc_html.AppendChild(&doc_head).unwrap();

                // Step 6.
                match title {
                    None => (),
                    Some(title_str) => {
                        // Step 6.1.
                        let doc_title =
                            Root::upcast::<Node>(HTMLTitleElement::new(local_name!("title"),
                                                                       None,
                                                                       &doc));
                        doc_head.AppendChild(&doc_title).unwrap();

                        // Step 6.2.
                        let title_text = Text::new(title_str, &doc);
                        doc_title.AppendChild(title_text.upcast()).unwrap();
                    }
                }
            }

            // Step 7.
            let doc_body = HTMLBodyElement::new(local_name!("body"), None, &doc);
            doc_html.AppendChild(doc_body.upcast()).unwrap();
        }

        // Step 8.
        // FIXME: https://github.com/mozilla/servo/issues/1522

        // Step 9.
        doc
    }

    // https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
    fn HasFeature(&self) -> bool {
        true
    }
}
<commit_msg>script: Simplify DOMImplementation::CreateHTMLDocument.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use document_loader::DocumentLoader;
use dom::bindings::codegen::Bindings::DOMImplementationBinding;
use dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementationMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::error::Fallible;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bindings::xmlname::{namespace_from_domstring, validate_qualified_name};
use dom::document::{Document, IsHTMLDocument};
use dom::document::DocumentSource;
use dom::documenttype::DocumentType;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlheadelement::HTMLHeadElement;
use dom::htmlhtmlelement::HTMLHtmlElement;
use dom::htmltitleelement::HTMLTitleElement;
use dom::node::Node;
use dom::text::Text;
use dom::xmldocument::XMLDocument;

// https://dom.spec.whatwg.org/#domimplementation
#[dom_struct]
pub struct DOMImplementation {
    reflector_: Reflector,
    document: JS<Document>,
}

impl DOMImplementation {
    fn new_inherited(document: &Document) -> DOMImplementation {
        DOMImplementation {
            reflector_: Reflector::new(),
            document: JS::from_ref(document),
        }
    }

    pub fn new(document: &Document) -> Root<DOMImplementation> {
        let window = document.window();
        reflect_dom_object(box DOMImplementation::new_inherited(document),
                           window,
                           DOMImplementationBinding::Wrap)
    }
}

// https://dom.spec.whatwg.org/#domimplementation
impl DOMImplementationMethods for DOMImplementation {
    // https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype
    fn CreateDocumentType(&self,
                          qualified_name: DOMString,
                          pubid: DOMString,
                          sysid: DOMString)
                          -> Fallible<Root<DocumentType>> {
        try!(validate_qualified_name(&qualified_name));
        Ok(DocumentType::new(qualified_name, Some(pubid), Some(sysid), &self.document))
    }

    // https://dom.spec.whatwg.org/#dom-domimplementation-createdocument
    fn CreateDocument(&self,
                      maybe_namespace: Option<DOMString>,
                      qname: DOMString,
                      maybe_doctype: Option<&DocumentType>)
                      -> Fallible<Root<XMLDocument>> {
        let win = self.document.window();
        let loader = DocumentLoader::new(&self.document.loader());
        let namespace = namespace_from_domstring(maybe_namespace.to_owned());

        let content_type = match namespace {
            ns!(html) => "application/xhtml+xml",
            ns!(svg) => "image/svg+xml",
            _ => "application/xml"
        };

        // Step 1.
        let doc = XMLDocument::new(win,
                                   None,
                                   None,
                                   IsHTMLDocument::NonHTMLDocument,
                                   Some(DOMString::from(content_type)),
                                   None,
                                   DocumentSource::NotFromParser,
                                   loader);
        // Step 2-3.
        let maybe_elem = if qname.is_empty() {
            None
        } else {
            match doc.upcast::<Document>().CreateElementNS(maybe_namespace, qname) {
                Err(error) => return Err(error),
                Ok(elem) => Some(elem),
            }
        };

        {
            let doc_node = doc.upcast::<Node>();

            // Step 4.
            if let Some(doc_type) = maybe_doctype {
                doc_node.AppendChild(doc_type.upcast()).unwrap();
            }

            // Step 5.
            if let Some(ref elem) = maybe_elem {
                doc_node.AppendChild(elem.upcast()).unwrap();
            }
        }

        // Step 6.
        // FIXME: https://github.com/mozilla/servo/issues/1522

        // Step 7.
        Ok(doc)
    }

    // https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
    fn CreateHTMLDocument(&self, title: Option<DOMString>) -> Root<Document> {
        let win = self.document.window();
        let loader = DocumentLoader::new(&self.document.loader());

        // Step 1-2.
        let doc = Document::new(win,
                                None,
                                None,
                                IsHTMLDocument::HTMLDocument,
                                None,
                                None,
                                DocumentSource::NotFromParser,
                                loader,
                                None,
                                None);

        {
            // Step 3.
            let doc_node = doc.upcast::<Node>();
            let doc_type = DocumentType::new(DOMString::from("html"), None, None, &doc);
            doc_node.AppendChild(doc_type.upcast()).unwrap();
        }

        {
            // Step 4.
            let doc_node = doc.upcast::<Node>();
            let doc_html = Root::upcast::<Node>(HTMLHtmlElement::new(local_name!("html"),
                                                                     None,
                                                                     &doc));
            doc_node.AppendChild(&doc_html).expect("Appending failed");

            {
                // Step 5.
                let doc_head = Root::upcast::<Node>(HTMLHeadElement::new(local_name!("head"),
                                                                         None,
                                                                         &doc));
                doc_html.AppendChild(&doc_head).unwrap();

                // Step 6.
                if let Some(title_str) = title {
                    // Step 6.1.
                    let doc_title =
                        Root::upcast::<Node>(HTMLTitleElement::new(local_name!("title"),
                                                                   None,
                                                                   &doc));
                    doc_head.AppendChild(&doc_title).unwrap();

                    // Step 6.2.
                    let title_text = Text::new(title_str, &doc);
                    doc_title.AppendChild(title_text.upcast()).unwrap();
                }
            }

            // Step 7.
            let doc_body = HTMLBodyElement::new(local_name!("body"), None, &doc);
            doc_html.AppendChild(doc_body.upcast()).unwrap();
        }

        // Step 8.
        // FIXME: https://github.com/mozilla/servo/issues/1522

        // Step 9.
        doc
    }

    // https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
    fn HasFeature(&self) -> bool {
        true
    }
}
<|endoftext|>