<commit_before>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::{PackageIdSpec, Workspace};
use ops;
use util::CargoResult;

pub struct DocOptions<'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a>,
}

pub fn doc(ws: &Workspace,
           options: &DocOptions) -> CargoResult<()> {
    let package = try!(ws.current());

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if options.compile_opts.spec.is_empty() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.crate_name()));
            } else {
                assert!(bin_names.insert(target.crate_name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                bail!("cannot document a package where a library and a binary \
                       have the same name. Consider renaming one or marking \
                       the target as `doc = false`")
            }
        }
    }

    try!(ops::compile(ws, &options.compile_opts));

    if options.open_result {
        let name = if options.compile_opts.spec.len() > 1 {
            bail!("Passing multiple packages and `open` is not supported")
        } else if options.compile_opts.spec.len() == 1 {
            try!(PackageIdSpec::parse(&options.compile_opts.spec[0]))
                                             .name().replace("-", "_")
        } else {
            match lib_names.iter().chain(bin_names.iter()).nth(0) {
                Some(s) => s.to_string(),
                None => return Ok(())
            }
        };

        // Don't bother locking here as if this is getting deleted there's
        // nothing we can do about it and otherwise if it's getting overwritten
        // then that's also ok!
        let mut target_dir = ws.target_dir();
        if let Some(triple) = options.compile_opts.target {
            target_dir.push(Path::new(triple).file_stem().unwrap());
        }
        let path = target_dir.join("doc").join(&name).join("index.html");
        let path = path.into_path_unlocked();
        if fs::metadata(&path).is_ok() {
            let mut shell = options.compile_opts.config.shell();
            match open_docs(&path) {
                Ok(m) => try!(shell.status("Launching", m)),
                Err(e) => {
                    try!(shell.warn(
                            "warning: could not determine a browser to open docs with, tried:"));
                    for method in e {
                        try!(shell.warn(format!("\t{}", method)));
                    }
                }
            }
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    use std::env;
    let mut methods = Vec::new();
    // trying $BROWSER
    match env::var("BROWSER"){
        Ok(name) => match Command::new(name).arg(path).status() {
            Ok(_) => return Ok("$BROWSER"),
            Err(_) => methods.push("$BROWSER")
        },
        Err(_) => () // Do nothing here if $BROWSER is not found
    }

    for m in ["xdg-open", "gnome-open", "kde-open"].iter() {
        match Command::new(m).arg(path).status() {
            Ok(_) => return Ok(m),
            Err(_) => methods.push(m)
        }
    }

    Err(methods)
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("cmd").arg("/C").arg(path).status() {
        Ok(_) => return Ok("cmd /C"),
        Err(_) => return Err(vec!["cmd /C"])
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("open").arg(path).status() {
        Ok(_) => return Ok("open"),
        Err(_) => return Err(vec!["open"])
    };
}
<commit_msg>match block not needed<commit_after>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::{PackageIdSpec, Workspace};
use ops;
use util::CargoResult;

pub struct DocOptions<'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a>,
}

pub fn doc(ws: &Workspace,
           options: &DocOptions) -> CargoResult<()> {
    let package = try!(ws.current());

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if options.compile_opts.spec.is_empty() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.crate_name()));
            } else {
                assert!(bin_names.insert(target.crate_name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                bail!("cannot document a package where a library and a binary \
                       have the same name. Consider renaming one or marking \
                       the target as `doc = false`")
            }
        }
    }

    try!(ops::compile(ws, &options.compile_opts));

    if options.open_result {
        let name = if options.compile_opts.spec.len() > 1 {
            bail!("Passing multiple packages and `open` is not supported")
        } else if options.compile_opts.spec.len() == 1 {
            try!(PackageIdSpec::parse(&options.compile_opts.spec[0]))
                                             .name().replace("-", "_")
        } else {
            match lib_names.iter().chain(bin_names.iter()).nth(0) {
                Some(s) => s.to_string(),
                None => return Ok(())
            }
        };

        // Don't bother locking here as if this is getting deleted there's
        // nothing we can do about it and otherwise if it's getting overwritten
        // then that's also ok!
        let mut target_dir = ws.target_dir();
        if let Some(triple) = options.compile_opts.target {
            target_dir.push(Path::new(triple).file_stem().unwrap());
        }
        let path = target_dir.join("doc").join(&name).join("index.html");
        let path = path.into_path_unlocked();
        if fs::metadata(&path).is_ok() {
            let mut shell = options.compile_opts.config.shell();
            match open_docs(&path) {
                Ok(m) => try!(shell.status("Launching", m)),
                Err(e) => {
                    try!(shell.warn(
                            "warning: could not determine a browser to open docs with, tried:"));
                    for method in e {
                        try!(shell.warn(format!("\t{}", method)));
                    }
                }
            }
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    use std::env;
    let mut methods = Vec::new();
    // trying $BROWSER
    if let Ok(name) = env::var("BROWSER") {
        match Command::new(name).arg(path).status() {
            Ok(_) => return Ok("$BROWSER"),
            Err(_) => methods.push("$BROWSER")
        }
    }

    for m in ["xdg-open", "gnome-open", "kde-open"].iter() {
        match Command::new(m).arg(path).status() {
            Ok(_) => return Ok(m),
            Err(_) => methods.push(m)
        }
    }

    Err(methods)
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("cmd").arg("/C").arg(path).status() {
        Ok(_) => return Ok("cmd /C"),
        Err(_) => return Err(vec!["cmd /C"])
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("open").arg(path).status() {
        Ok(_) => return Ok("open"),
        Err(_) => return Err(vec!["open"])
    };
}
<|endoftext|>


<commit_before>use crate::core::compiler::RustcTargetData;
use crate::core::resolver::{HasDevUnits, ResolveOpts};
use crate::core::{Shell, Workspace};
use crate::ops;
use crate::util::CargoResult;
use std::collections::HashMap;
use std::path::Path;
use std::process::Command;

/// Strongly typed options for the `cargo doc` command.
#[derive(Debug)]
pub struct DocOptions {
    /// Whether to attempt to open the browser after compiling the docs
    pub open_result: bool,
    /// Options to pass through to the compiler
    pub compile_opts: ops::CompileOptions,
}

/// Main method for `cargo doc`.
pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
    let specs = options.compile_opts.spec.to_package_id_specs(ws)?;
    let opts = ResolveOpts::new(
        /*dev_deps*/ true,
        &options.compile_opts.features,
        options.compile_opts.all_features,
        !options.compile_opts.no_default_features,
    );
    let target_data = RustcTargetData::new(ws, &options.compile_opts.build_config.requested_kinds)?;
    let ws_resolve = ops::resolve_ws_with_opts(
        ws,
        &target_data,
        &options.compile_opts.build_config.requested_kinds,
        &opts,
        &specs,
        HasDevUnits::No,
        crate::core::resolver::features::ForceAllTargets::No,
    )?;

    let ids = ws_resolve.targeted_resolve.specs_to_ids(&specs)?;
    let pkgs = ws_resolve.pkg_set.get_many(ids)?;

    let mut lib_names = HashMap::new();
    let mut bin_names = HashMap::new();
    let mut names = Vec::new();
    for package in &pkgs {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                if let Some(prev) = lib_names.insert(target.crate_name(), package) {
                    anyhow::bail!(
                        "The library `{}` is specified by packages `{}` and \
                         `{}` but can only be documented once. Consider renaming \
                         or marking one of the targets as `doc = false`.",
                        target.crate_name(),
                        prev,
                        package
                    );
                }
            } else if let Some(prev) = bin_names.insert(target.crate_name(), package) {
                anyhow::bail!(
                    "The binary `{}` is specified by packages `{}` and \
                     `{}` but can be documented only once. Consider renaming \
                     or marking one of the targets as `doc = false`.",
                    target.crate_name(),
                    prev,
                    package
                );
            }
            names.push(target.crate_name());
        }
    }

    let open_kind = if options.open_result {
        Some(options.compile_opts.build_config.single_requested_kind()?)
    } else {
        None
    };

    let compilation = ops::compile(ws, &options.compile_opts)?;

    if let Some(kind) = open_kind {
        let name = match names.first() {
            Some(s) => s.to_string(),
            None => return Ok(()),
        };
        let path = compilation.root_output[&kind]
            .with_file_name("doc")
            .join(&name)
            .join("index.html");
        if path.exists() {
            let mut shell = ws.config().shell();
            shell.status("Opening", path.display())?;
            open_docs(&path, &mut shell)?;
        }
    }

    Ok(())
}

fn open_docs(path: &Path, shell: &mut Shell) -> CargoResult<()> {
    match std::env::var_os("BROWSER") {
        Some(browser) => {
            if let Err(e) = Command::new(&browser).arg(path).status() {
                shell.warn(format!(
                    "Couldn't open docs with {}: {}",
                    browser.to_string_lossy(),
                    e
                ))?;
            }
        }
        None => {
            if let Err(e) = opener::open(&path) {
                let e = e.into();
                crate::display_warning_with_error("couldn't open docs", &e, shell);
            }
        }
    };

    Ok(())
}
<commit_msg>Implement `RustDocTargetData`<commit_after>use crate::core::compiler::RustcTargetData;
use crate::core::resolver::{HasDevUnits, ResolveOpts};
use crate::core::{Shell, Workspace};
use crate::ops;
use crate::util::CargoResult;
use serde::{Deserialize, Serialize};
use serde_json::{from_str, to_writer};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process::Command;

/// Strongly typed options for the `cargo doc` command.
#[derive(Debug)]
pub struct DocOptions {
    /// Whether to attempt to open the browser after compiling the docs
    pub open_result: bool,
    /// Options to pass through to the compiler
    pub compile_opts: ops::CompileOptions,
}

/// Main method for `cargo doc`.
pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
    let specs = options.compile_opts.spec.to_package_id_specs(ws)?;
    let opts = ResolveOpts::new(
        /*dev_deps*/ true,
        &options.compile_opts.features,
        options.compile_opts.all_features,
        !options.compile_opts.no_default_features,
    );
    let target_data = RustcTargetData::new(ws, &options.compile_opts.build_config.requested_kinds)?;
    let ws_resolve = ops::resolve_ws_with_opts(
        ws,
        &target_data,
        &options.compile_opts.build_config.requested_kinds,
        &opts,
        &specs,
        HasDevUnits::No,
        crate::core::resolver::features::ForceAllTargets::No,
    )?;

    let ids = ws_resolve.targeted_resolve.specs_to_ids(&specs)?;
    let pkgs = ws_resolve.pkg_set.get_many(ids)?;

    let mut lib_names = HashMap::new();
    let mut bin_names = HashMap::new();
    let mut names = Vec::new();
    for package in &pkgs {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                if let Some(prev) = lib_names.insert(target.crate_name(), package) {
                    anyhow::bail!(
                        "The library `{}` is specified by packages `{}` and \
                         `{}` but can only be documented once. Consider renaming \
                         or marking one of the targets as `doc = false`.",
                        target.crate_name(),
                        prev,
                        package
                    );
                }
            } else if let Some(prev) = bin_names.insert(target.crate_name(), package) {
                anyhow::bail!(
                    "The binary `{}` is specified by packages `{}` and \
                     `{}` but can be documented only once. Consider renaming \
                     or marking one of the targets as `doc = false`.",
                    target.crate_name(),
                    prev,
                    package
                );
            }
            names.push(target.crate_name());
        }
    }

    let open_kind = if options.open_result {
        Some(options.compile_opts.build_config.single_requested_kind()?)
    } else {
        None
    };

    // Before compiling, we need to make sure that if there were any previous docs
    // already compiled, they were compiled with the same Rustc version that we're currently
    // using. Otherways we must remove the `doc/` folder and compile again.
    //
    // This is important because the .js/.html&.css files that are generated by Rustc don't have
    // any versioning. Therefore, we can fall in weird bugs and behaviours if we mix different
    // compiler versions of these js/.html&.css files.
    check_rustdoc_appropiate_versioning(ws, &target_data)?;

    let compilation = ops::compile(ws, &options.compile_opts)?;

    if let Some(kind) = open_kind {
        let name = match names.first() {
            Some(s) => s.to_string(),
            None => return Ok(()),
        };
        let path = compilation.root_output[&kind]
            .with_file_name("doc")
            .join(&name)
            .join("index.html");
        if path.exists() {
            let mut shell = ws.config().shell();
            shell.status("Opening", path.display())?;
            open_docs(&path, &mut shell)?;
        }
    }

    Ok(())
}

fn open_docs(path: &Path, shell: &mut Shell) -> CargoResult<()> {
    match std::env::var_os("BROWSER") {
        Some(browser) => {
            if let Err(e) = Command::new(&browser).arg(path).status() {
                shell.warn(format!(
                    "Couldn't open docs with {}: {}",
                    browser.to_string_lossy(),
                    e
                ))?;
            }
        }
        None => {
            if let Err(e) = opener::open(&path) {
                let e = e.into();
                crate::display_warning_with_error("couldn't open docs", &e, shell);
            }
        }
    };

    Ok(())
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RustDocTargetData {
    rustc_verbose_version: String,
}

impl RustDocTargetData {
    pub fn with_actual_rustc_version(rustc_verbose_version: &str) -> Self {
        Self {
            rustc_verbose_version: rustc_verbose_version.to_string(),
        }
    }

    pub fn path_to_fingerprint(ws: &Workspace<'_>) -> PathBuf {
        ws.root().join("target").join(".rustdoc_fingerprint.json")
    }

    pub fn from_file(file: &mut File) -> anyhow::Result<Self> {
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let rustdoc_target_data: Self = from_str(&contents)?;
        Ok(rustdoc_target_data)
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::{env, fs, path::PathBuf, process::Command};

fn link(name: &str, bundled: bool) {
    use std::env::var;
    let target = var("TARGET").unwrap();
    let target: Vec<_> = target.split('-').collect();
    if target.get(2) == Some(&"windows") {
        println!("cargo:rustc-link-lib=dylib={}", name);
        if bundled && target.get(3) == Some(&"gnu") {
            let dir = var("CARGO_MANIFEST_DIR").unwrap();
            println!("cargo:rustc-link-search=native={}/{}", dir, target[0]);
        }
    }
}

fn fail_on_empty_directory(name: &str) {
    if fs::read_dir(name).unwrap().count() == 0 {
        println!(
            "The `{}` directory is empty, did you forget to pull the submodules?",
            name
        );
        println!("Try `git submodule update --init --recursive`");
        panic!();
    }
}

fn rocksdb_include_dir() -> String {
    match env::var("ROCKSDB_INCLUDE_DIR") {
        Ok(val) => val,
        Err(_) => "rocksdb/include".to_string(),
    }
}

fn bindgen_rocksdb() {
    let bindings = bindgen::Builder::default()
        .header(rocksdb_include_dir() + "/rocksdb/c.h")
        .derive_debug(false)
        .blocklist_type("max_align_t") // https://github.com/rust-lang-nursery/rust-bindgen/issues/550
        .ctypes_prefix("libc")
        .size_t_is_usize(true)
        .generate()
        .expect("unable to generate rocksdb bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("unable to write rocksdb bindings");
}

fn build_rocksdb() {
    let target = env::var("TARGET").unwrap();

    let mut config = cc::Build::new();
    config.include("rocksdb/include/");
    config.include("rocksdb/");
    config.include("rocksdb/third-party/gtest-1.8.1/fused-src/");

    if cfg!(feature = "snappy") {
        config.define("SNAPPY", Some("1"));
        config.include("snappy/");
    }

    if cfg!(feature = "lz4") {
        config.define("LZ4", Some("1"));
        config.include("lz4/lib/");
    }

    if cfg!(feature = "zstd") {
        config.define("ZSTD", Some("1"));
        if let Some(path) = env::var_os("DEP_ZSTD_INCLUDE") {
            config.include(path);
        }
    }

    if cfg!(feature = "zlib") {
        config.define("ZLIB", Some("1"));
        if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
            config.include(path);
        }
    }

    if cfg!(feature = "bzip2") {
        config.define("BZIP2", Some("1"));
        if let Some(path) = env::var_os("DEP_BZIP2_INCLUDE") {
            config.include(path);
        }
    }

    if cfg!(feature = "rtti") {
        config.define("USE_RTTI", Some("1"));
    }

    config.include(".");
    config.define("NDEBUG", Some("1"));

    let mut lib_sources = include_str!("rocksdb_lib_sources.txt")
        .trim()
        .split('\n')
        .map(str::trim)
        .collect::<Vec<&'static str>>();

    // We have a pregenerated a version of build_version.cc in the local directory
    lib_sources = lib_sources
        .iter()
        .cloned()
        .filter(|&file| file != "util/build_version.cc")
        .collect::<Vec<&'static str>>();

    if target.contains("x86_64") {
        // This is needed to enable hardware CRC32C. Technically, SSE 4.2 is
        // only available since Intel Nehalem (about 2010) and AMD Bulldozer
        // (about 2011).
        let target_feature = env::var("CARGO_CFG_TARGET_FEATURE").unwrap();
        let target_features: Vec<_> = target_feature.split(',').collect();
        if target_features.contains(&"sse2") {
            config.flag_if_supported("-msse2");
        }
        if target_features.contains(&"sse4.1") {
            config.flag_if_supported("-msse4.1");
        }
        if target_features.contains(&"sse4.2") {
            config.flag_if_supported("-msse4.2");
            config.define("HAVE_SSE42", Some("1"));
        }

        if !target.contains("android") && target_features.contains(&"pclmulqdq") {
            config.define("HAVE_PCLMUL", Some("1"));
            config.flag_if_supported("-mpclmul");
        }
    }

    if target.contains("aarch64") {
        lib_sources.push("util/crc32c_arm64.cc")
    }

    if target.contains("darwin") {
        config.define("OS_MACOSX", None);
        config.define("ROCKSDB_PLATFORM_POSIX", None);
        config.define("ROCKSDB_LIB_IO_POSIX", None);
    } else if target.contains("android") {
        config.define("OS_ANDROID", None);
        config.define("ROCKSDB_PLATFORM_POSIX", None);
        config.define("ROCKSDB_LIB_IO_POSIX", None);
    } else if target.contains("linux") {
        config.define("OS_LINUX", None);
        config.define("ROCKSDB_PLATFORM_POSIX", None);
        config.define("ROCKSDB_LIB_IO_POSIX", None);
    } else if target.contains("freebsd") {
        config.define("OS_FREEBSD", None);
        config.define("ROCKSDB_PLATFORM_POSIX", None);
        config.define("ROCKSDB_LIB_IO_POSIX", None);
    } else if target.contains("windows") {
        link("rpcrt4", false);
        link("shlwapi", false);
        config.define("DWIN32", None);
        config.define("OS_WIN", None);
        config.define("_MBCS", None);
        config.define("WIN64", None);
        config.define("NOMINMAX", None);
        config.define("WITH_WINDOWS_UTF8_FILENAMES", "ON");

        if &target == "x86_64-pc-windows-gnu" {
            // Tell MinGW to create localtime_r wrapper of localtime_s function.
            config.define("_POSIX_C_SOURCE", Some("1"));
            // Tell MinGW to use at least Windows Vista headers instead of the ones of Windows XP.
            // (This is minimum supported version of rocksdb)
            config.define("_WIN32_WINNT", Some("_WIN32_WINNT_VISTA"));
        }

        // Remove POSIX-specific sources
        lib_sources = lib_sources
            .iter()
            .cloned()
            .filter(|file| {
                !matches!(
                    *file,
                    "port/port_posix.cc"
                        | "env/env_posix.cc"
                        | "env/fs_posix.cc"
                        | "env/io_posix.cc"
                )
            })
            .collect::<Vec<&'static str>>();

        // Add Windows-specific sources
        lib_sources.extend([
            "port/win/env_default.cc",
            "port/win/port_win.cc",
            "port/win/xpress_win.cc",
            "port/win/io_win.cc",
            "port/win/win_thread.cc",
            "port/win/env_win.cc",
            "port/win/win_logger.cc",
        ]);

        if cfg!(feature = "jemalloc") {
            lib_sources.push("port/win/win_jemalloc.cc");
        }
    }

    config.define("ROCKSDB_SUPPORT_THREAD_LOCAL", None);

    if cfg!(feature = "jemalloc") {
        config.define("WITH_JEMALLOC", "ON");
    }

    if target.contains("msvc") {
        config.flag("-EHsc");
        config.flag("-std:c++17");
    } else {
        config.flag(&cxx_standard());
        // matches the flags in CMakeLists.txt from rocksdb
        config.flag("-Wsign-compare");
        config.flag("-Wshadow");
        config.flag("-Wno-unused-parameter");
        config.flag("-Wno-unused-variable");
        config.flag("-Woverloaded-virtual");
        config.flag("-Wnon-virtual-dtor");
        config.flag("-Wno-missing-field-initializers");
        config.flag("-Wno-strict-aliasing");
        config.flag("-Wno-invalid-offsetof");
    }

    for file in lib_sources {
        let file = "rocksdb/".to_string() + file;
        config.file(&file);
    }

    config.file("build_version.cc");

    config.cpp(true);
    config.flag_if_supported("-std=c++17");
    config.compile("librocksdb.a");
}

fn build_snappy() {
    let target = env::var("TARGET").unwrap();
    let endianness = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap();
    let mut config = cc::Build::new();

    config.include("snappy/");
    config.include(".");
    config.define("NDEBUG", Some("1"));
    config.extra_warnings(false);

    if target.contains("msvc") {
        config.flag("-EHsc");
    } else {
        // Snappy requires C++11.
        // See: https://github.com/google/snappy/blob/master/CMakeLists.txt#L32-L38
        config.flag("-std=c++11");
    }

    if endianness == "big" {
        config.define("SNAPPY_IS_BIG_ENDIAN", Some("1"));
    }

    config.file("snappy/snappy.cc");
    config.file("snappy/snappy-sinksource.cc");
    config.file("snappy/snappy-c.cc");
    config.cpp(true);
    config.compile("libsnappy.a");
}

fn build_lz4() {
    let mut compiler = cc::Build::new();

    compiler
        .file("lz4/lib/lz4.c")
        .file("lz4/lib/lz4frame.c")
        .file("lz4/lib/lz4hc.c")
        .file("lz4/lib/xxhash.c");

    compiler.opt_level(3);

    let target = env::var("TARGET").unwrap();

    if &target == "i686-pc-windows-gnu" {
        compiler.flag("-fno-tree-vectorize");
    }

    compiler.compile("liblz4.a");
}

fn try_to_find_and_link_lib(lib_name: &str) -> bool {
    if let Ok(v) = env::var(&format!("{}_COMPILE", lib_name)) {
        if v.to_lowercase() == "true" || v == "1" {
            return false;
        }
    }

    if let Ok(lib_dir) = env::var(&format!("{}_LIB_DIR", lib_name)) {
        println!("cargo:rustc-link-search=native={}", lib_dir);
        let mode = match env::var_os(&format!("{}_STATIC", lib_name)) {
            Some(_) => "static",
            None => "dylib",
        };
        println!("cargo:rustc-link-lib={}={}", mode, lib_name.to_lowercase());
        return true;
    }
    false
}

fn cxx_standard() -> String {
    env::var("ROCKSDB_CXX_STD").map_or("-std=c++17".to_owned(), |cxx_std| {
        if !cxx_std.starts_with("-std=") {
            format!("-std={}", cxx_std)
        } else {
            cxx_std
        }
    })
}

fn update_submodules() {
    let program = "git";
    let dir = "../";
    let args = ["submodule", "update", "--init"];
    println!(
        "Running command: \"{} {}\" in dir: {}",
        program,
        args.join(" "),
        dir
    );
    let ret = Command::new(program).current_dir(dir).args(args).status();

    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => (),
        Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
        Ok((false, None)) => panic!("Command got killed"),
        Err(e) => panic!("Command failed with error: {}", e),
    }
}

fn main() {
    if !Path::new("rocksdb/AUTHORS").exists() {
        update_submodules();
    }
    bindgen_rocksdb();

    if !try_to_find_and_link_lib("ROCKSDB") {
        println!("cargo:rerun-if-changed=rocksdb/");
        fail_on_empty_directory("rocksdb");
        build_rocksdb();
    } else {
        let target = env::var("TARGET").unwrap();
        // according to https://github.com/alexcrichton/cc-rs/blob/master/src/lib.rs#L2189
        if target.contains("apple") || target.contains("freebsd") || target.contains("openbsd") {
            println!("cargo:rustc-link-lib=dylib=c++");
        } else if target.contains("linux") {
            println!("cargo:rustc-link-lib=dylib=stdc++");
        }
    }
    if cfg!(feature = "snappy") && !try_to_find_and_link_lib("SNAPPY") {
        println!("cargo:rerun-if-changed=snappy/");
        fail_on_empty_directory("snappy");
        build_snappy();
    }
    if cfg!(feature = "lz4") && !try_to_find_and_link_lib("LZ4") {
        println!("cargo:rerun-if-changed=lz4/");
        fail_on_empty_directory("lz4");
        build_lz4();
    }

    // Allow dependent crates to locate the sources and output directory of
    // this crate. Notably, this allows a dependent crate to locate the RocksDB
    // sources and built archive artifacts provided by this crate.
    println!(
        "cargo:cargo_manifest_dir={}",
        env::var("CARGO_MANIFEST_DIR").unwrap()
    );
    println!("cargo:out_dir={}", env::var("OUT_DIR").unwrap());
}
<commit_msg>Use more target features to build librocksdb-sys (#620)<commit_after>use std::path::Path;
use std::{env, fs, path::PathBuf, process::Command};

fn link(name: &str, bundled: bool) {
    use std::env::var;
    let target = var("TARGET").unwrap();
    let target: Vec<_> = target.split('-').collect();
    if target.get(2) == Some(&"windows") {
        println!("cargo:rustc-link-lib=dylib={}", name);
        if bundled && target.get(3) == Some(&"gnu") {
            let dir = var("CARGO_MANIFEST_DIR").unwrap();
            println!("cargo:rustc-link-search=native={}/{}", dir, target[0]);
        }
    }
}

fn fail_on_empty_directory(name: &str) {
    if fs::read_dir(name).unwrap().count() == 0 {
        println!(
            "The `{}` directory is empty, did you forget to pull the submodules?",
            name
        );
        println!("Try `git submodule update --init --recursive`");
        panic!();
    }
}

fn rocksdb_include_dir() -> String {
    match env::var("ROCKSDB_INCLUDE_DIR") {
        Ok(val) => val,
        Err(_) => "rocksdb/include".to_string(),
    }
}

fn bindgen_rocksdb() {
    let bindings = bindgen::Builder::default()
        .header(rocksdb_include_dir() + "/rocksdb/c.h")
        .derive_debug(false)
        .blocklist_type("max_align_t") // https://github.com/rust-lang-nursery/rust-bindgen/issues/550
        .ctypes_prefix("libc")
        .size_t_is_usize(true)
        .generate()
        .expect("unable to generate rocksdb bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("unable to write rocksdb bindings");
}

fn build_rocksdb() {
    let target = env::var("TARGET").unwrap();

    let mut config = cc::Build::new();
    config.include("rocksdb/include/");
    config.include("rocksdb/");
    config.include("rocksdb/third-party/gtest-1.8.1/fused-src/");

    if cfg!(feature = "snappy") {
        config.define("SNAPPY", Some("1"));
        config.include("snappy/");
    }

    if cfg!(feature = "lz4") {
        config.define("LZ4", Some("1"));
        config.include("lz4/lib/");
    }

    if cfg!(feature = "zstd") {
        config.define("ZSTD", Some("1"));
        if let Some(path) = env::var_os("DEP_ZSTD_INCLUDE") {
            config.include(path);
        }
    }

    if cfg!(feature = "zlib") {
        config.define("ZLIB", Some("1"));
        if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
            config.include(path);
        }
    }

    if cfg!(feature = "bzip2") {
        config.define("BZIP2", Some("1"));
        if let Some(path) = env::var_os("DEP_BZIP2_INCLUDE") {
            config.include(path);
        }
    }

    if cfg!(feature = "rtti") {
        config.define("USE_RTTI", Some("1"));
    }

    config.include(".");
    config.define("NDEBUG", Some("1"));

    let mut lib_sources = include_str!("rocksdb_lib_sources.txt")
        .trim()
        .split('\n')
        .map(str::trim)
        .collect::<Vec<&'static str>>();

    // We have a pregenerated a version of build_version.cc in the local directory
    lib_sources = lib_sources
        .iter()
        .cloned()
        .filter(|&file| file != "util/build_version.cc")
        .collect::<Vec<&'static str>>();

    if target.contains("x86_64") {
        // This is needed to enable hardware CRC32C. Technically, SSE 4.2 is
        // only available since Intel Nehalem (about 2010) and AMD Bulldozer
        // (about 2011).
        let target_feature = env::var("CARGO_CFG_TARGET_FEATURE").unwrap();
        let target_features: Vec<_> = target_feature.split(',').collect();
        if target_features.contains(&"sse2") {
            config.flag_if_supported("-msse2");
        }
        if target_features.contains(&"sse4.1") {
            config.flag_if_supported("-msse4.1");
        }
        if target_features.contains(&"sse4.2") {
            config.flag_if_supported("-msse4.2");
            config.define("HAVE_SSE42", Some("1"));
        }
        // Pass along additional target features as defined in
        // build_tools/build_detect_platform.
        if target_features.contains(&"avx2") {
            config.flag_if_supported("-mavx2");
            config.define("HAVE_AVX2", Some("1"));
        }
        if target_features.contains(&"bmi1") {
            config.flag_if_supported("-mbmi");
            config.define("HAVE_BMI", Some("1"));
        }
        if target_features.contains(&"lzcnt") {
            config.flag_if_supported("-mlzcnt");
            config.define("HAVE_LZCNT", Some("1"));
        }
        if !target.contains("android") && target_features.contains(&"pclmulqdq") {
            config.define("HAVE_PCLMUL", Some("1"));
            config.flag_if_supported("-mpclmul");
        }
    }

    if target.contains("aarch64") {
        lib_sources.push("util/crc32c_arm64.cc")
    }

    if target.contains("darwin") {
        config.define("OS_MACOSX", None);
        config.define("ROCKSDB_PLATFORM_POSIX", None);
        config.define("ROCKSDB_LIB_IO_POSIX", None);
    } else if target.contains("android") {
        config.define("OS_ANDROID", None);
        config.define("ROCKSDB_PLATFORM_POSIX", None);
        config.define("ROCKSDB_LIB_IO_POSIX", None);
    } else if target.contains("linux") {
        config.define("OS_LINUX", None);
        config.define("ROCKSDB_PLATFORM_POSIX", None);
        config.define("ROCKSDB_LIB_IO_POSIX", None);
    } else if target.contains("freebsd") {
        config.define("OS_FREEBSD", None);
        config.define("ROCKSDB_PLATFORM_POSIX", None);
        config.define("ROCKSDB_LIB_IO_POSIX", None);
    } else if target.contains("windows") {
        link("rpcrt4", false);
        link("shlwapi", false);
        config.define("DWIN32", None);
        config.define("OS_WIN", None);
        config.define("_MBCS", None);
        config.define("WIN64", None);
        config.define("NOMINMAX", None);
        config.define("WITH_WINDOWS_UTF8_FILENAMES", "ON");

        if &target == "x86_64-pc-windows-gnu" {
            // Tell MinGW to create localtime_r wrapper of localtime_s function.
            config.define("_POSIX_C_SOURCE", Some("1"));
            // Tell MinGW to use at least Windows Vista headers instead of the ones of Windows XP.
            // (This is minimum supported version of rocksdb)
            config.define("_WIN32_WINNT", Some("_WIN32_WINNT_VISTA"));
        }

        // Remove POSIX-specific sources
        lib_sources = lib_sources
            .iter()
            .cloned()
            .filter(|file| {
                !matches!(
                    *file,
                    "port/port_posix.cc"
                        | "env/env_posix.cc"
                        | "env/fs_posix.cc"
                        | "env/io_posix.cc"
                )
            })
            .collect::<Vec<&'static str>>();

        // Add Windows-specific sources
        lib_sources.extend([
            "port/win/env_default.cc",
            "port/win/port_win.cc",
            "port/win/xpress_win.cc",
            "port/win/io_win.cc",
            "port/win/win_thread.cc",
            "port/win/env_win.cc",
            "port/win/win_logger.cc",
        ]);

        if cfg!(feature = "jemalloc") {
            lib_sources.push("port/win/win_jemalloc.cc");
        }
    }

    config.define("ROCKSDB_SUPPORT_THREAD_LOCAL", None);

    if cfg!(feature = "jemalloc") {
        config.define("WITH_JEMALLOC", "ON");
    }

    if target.contains("msvc") {
        config.flag("-EHsc");
        config.flag("-std:c++17");
    } else {
        config.flag(&cxx_standard());
        // matches the flags in CMakeLists.txt from rocksdb
        config.define("HAVE_UINT128_EXTENSION", Some("1"));
        config.flag("-Wsign-compare");
        config.flag("-Wshadow");
        config.flag("-Wno-unused-parameter");
        config.flag("-Wno-unused-variable");
        config.flag("-Woverloaded-virtual");
        config.flag("-Wnon-virtual-dtor");
        config.flag("-Wno-missing-field-initializers");
        config.flag("-Wno-strict-aliasing");
        config.flag("-Wno-invalid-offsetof");
    }

    for file in lib_sources {
        let file = "rocksdb/".to_string() + file;
        config.file(&file);
    }

    config.file("build_version.cc");

    config.cpp(true);
    config.flag_if_supported("-std=c++17");
    config.compile("librocksdb.a");
}

fn build_snappy() {
    let target = env::var("TARGET").unwrap();
    let endianness = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap();
    let mut config = cc::Build::new();

    config.include("snappy/");
    config.include(".");
    config.define("NDEBUG", Some("1"));
    config.extra_warnings(false);

    if target.contains("msvc") {
        config.flag("-EHsc");
    } else {
        // Snappy requires C++11.
        // See: https://github.com/google/snappy/blob/master/CMakeLists.txt#L32-L38
        config.flag("-std=c++11");
    }

    if endianness == "big" {
        config.define("SNAPPY_IS_BIG_ENDIAN", Some("1"));
    }

    config.file("snappy/snappy.cc");
    config.file("snappy/snappy-sinksource.cc");
    config.file("snappy/snappy-c.cc");
    config.cpp(true);
    config.compile("libsnappy.a");
}

fn build_lz4() {
    let mut compiler = cc::Build::new();

    compiler
        .file("lz4/lib/lz4.c")
        .file("lz4/lib/lz4frame.c")
        .file("lz4/lib/lz4hc.c")
        .file("lz4/lib/xxhash.c");

    compiler.opt_level(3);

    let target = env::var("TARGET").unwrap();

    if &target == "i686-pc-windows-gnu" {
        compiler.flag("-fno-tree-vectorize");
    }

    compiler.compile("liblz4.a");
}

fn try_to_find_and_link_lib(lib_name: &str) -> bool {
    if let Ok(v) = env::var(&format!("{}_COMPILE", lib_name)) {
        if v.to_lowercase() == "true" || v == "1" {
            return false;
        }
    }

    if let Ok(lib_dir) = env::var(&format!("{}_LIB_DIR", lib_name)) {
        println!("cargo:rustc-link-search=native={}", lib_dir);
        let mode = match env::var_os(&format!("{}_STATIC", lib_name)) {
            Some(_) => "static",
            None => "dylib",
        };
        println!("cargo:rustc-link-lib={}={}", mode, lib_name.to_lowercase());
        return true;
    }
    false
}

fn cxx_standard() -> String {
    env::var("ROCKSDB_CXX_STD").map_or("-std=c++17".to_owned(), |cxx_std| {
        if !cxx_std.starts_with("-std=") {
            format!("-std={}", cxx_std)
        } else {
            cxx_std
        }
    })
}

fn update_submodules() {
    let program = "git";
    let dir = "../";
    let args = ["submodule", "update", "--init"];
    println!(
        "Running command: \"{} {}\" in dir: {}",
        program,
        args.join(" "),
        dir
    );
    let ret = Command::new(program).current_dir(dir).args(args).status();

    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => (),
        Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
        Ok((false, None)) => panic!("Command got killed"),
        Err(e) => panic!("Command failed with error: {}", e),
    }
}

fn main() {
    if !Path::new("rocksdb/AUTHORS").exists() {
        update_submodules();
    }
    bindgen_rocksdb();

    if !try_to_find_and_link_lib("ROCKSDB") {
        println!("cargo:rerun-if-changed=rocksdb/");
        fail_on_empty_directory("rocksdb");
        build_rocksdb();
    } else {
        let target = env::var("TARGET").unwrap();
        // according to https://github.com/alexcrichton/cc-rs/blob/master/src/lib.rs#L2189
        if target.contains("apple") || target.contains("freebsd") || target.contains("openbsd") {
            println!("cargo:rustc-link-lib=dylib=c++");
        } else if target.contains("linux") {
            println!("cargo:rustc-link-lib=dylib=stdc++");
        }
    }
    if cfg!(feature = "snappy") && !try_to_find_and_link_lib("SNAPPY") {
        println!("cargo:rerun-if-changed=snappy/");
        fail_on_empty_directory("snappy");
        build_snappy();
    }
    if cfg!(feature = "lz4") && !try_to_find_and_link_lib("LZ4") {
        println!("cargo:rerun-if-changed=lz4/");
        fail_on_empty_directory("lz4");
        build_lz4();
    }

    // Allow dependent crates to locate the sources and output directory of
    // this crate. Notably, this allows a dependent crate to locate the RocksDB
    // sources and built archive artifacts provided by this crate.
    println!(
        "cargo:cargo_manifest_dir={}",
        env::var("CARGO_MANIFEST_DIR").unwrap()
    );
    println!("cargo:out_dir={}", env::var("OUT_DIR").unwrap());
}
<|endoftext|>


<commit_before><commit_msg>[dictionary-std]<commit_after><|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt;
use parser::ast::*;
use evaluator::types::*;

#[derive(Eq, Debug)]
pub enum Value {
    Int(i64),
    Bool(bool),
    String(String),
    Array(Vec<Value>),
    Hash(HashMap<Hashable, Value>),
    Fn {
        params: Vec<Ident>,
        body: BlockStmt,
        env: Box<Env>,
    },
    BuiltInFn {
        name: String,
        num_params: i32,
        func: &'static BuiltInFn,
    },
    Return(Box<Value>),
    Null,
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Value::Int(i) => write!(f, "{}", i),
            &Value::Bool(b) => write!(f, "{}", b),
            &Value::String(ref s) => write!(f, "{}", s),
            &Value::Array(ref a) => unimplemented!(), // FIXME
            &Value::Hash(ref m) => unimplemented!(), // FIXME
            &Value::Fn { .. } => write!(f, "[function]"),
            &Value::BuiltInFn { ref name, .. } => write!(f, "[built-in function: {}", name),
            &Value::Return(ref o) => o.fmt(f),
            &Value::Null => write!(f, "null"),
        }
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Value::Int(i1), &Value::Int(i2)) => i1 == i2,
            (&Value::Bool(b1), &Value::Bool(b2)) => b1 == b2,
            (&Value::String(ref s1), &Value::String(ref s2)) => s1 == s2,
            (&Value::Array(ref v1), &Value::Array(ref v2)) => v1 == v2,
            (&Value::Hash(ref h1), &Value::Hash(ref h2)) => h1 == h2,
            (&Value::Fn {
                 params: ref p1,
                 body: ref b1,
                 env: ref e1,
             },
             &Value::Fn {
                 params: ref p2,
                 body: ref b2,
                 env: ref e2,
             }) => p1 == p2 && b1 == b2 && e1 == e2,
            (&Value::BuiltInFn {
                 name: ref n1,
                 num_params: ref p1,
                 ..
             },
             &Value::BuiltInFn {
                 name: ref n2,
                 num_params: ref p2,
                 ..
             }) => n1 == n2 && p1 == p2,
            (&Value::Return(ref o1), &Value::Return(ref o2)) => o1 == o2,
            (&Value::Null, &Value::Null) => true,
            _ => false,
        }
    }
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Hashable {
    Int(i64),
    Bool(bool),
    String(String),
}

impl fmt::Display for Hashable {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match self {
            &Hashable::Int(i) => Value::Int(i),
            &Hashable::Bool(b) => Value::Bool(b),
            &Hashable::String(ref s) => Value::String(s.clone()),
        })
    }
}

pub type BuiltInFn = fn(Vec<Value>) -> Result<Value>;
<commit_msg>Remove useless &'static for static function<commit_after>use std::collections::HashMap;
use std::fmt;
use parser::ast::*;
use evaluator::types::*;

#[derive(Eq, Debug)]
pub enum Value {
    Int(i64),
    Bool(bool),
    String(String),
    Array(Vec<Value>),
    Hash(HashMap<Hashable, Value>),
    Fn {
        params: Vec<Ident>,
        body: BlockStmt,
        env: Box<Env>,
    },
    BuiltInFn {
        name: String,
        num_params: i32,
        func: BuiltInFn,
    },
    Return(Box<Value>),
    Null,
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Value::Int(i) => write!(f, "{}", i),
            &Value::Bool(b) => write!(f, "{}", b),
            &Value::String(ref s) => write!(f, "{}", s),
            &Value::Array(ref a) => unimplemented!(), // FIXME
            &Value::Hash(ref m) => unimplemented!(), // FIXME
            &Value::Fn { .. } => write!(f, "[function]"),
            &Value::BuiltInFn { ref name, .. } => write!(f, "[built-in function: {}", name),
            &Value::Return(ref o) => o.fmt(f),
            &Value::Null => write!(f, "null"),
        }
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Value::Int(i1), &Value::Int(i2)) => i1 == i2,
            (&Value::Bool(b1), &Value::Bool(b2)) => b1 == b2,
            (&Value::String(ref s1), &Value::String(ref s2)) => s1 == s2,
            (&Value::Array(ref v1), &Value::Array(ref v2)) => v1 == v2,
            (&Value::Hash(ref h1), &Value::Hash(ref h2)) => h1 == h2,
            (&Value::Fn {
                 params: ref p1,
                 body: ref b1,
                 env: ref e1,
             },
             &Value::Fn {
                 params: ref p2,
                 body: ref b2,
                 env: ref e2,
             }) => p1 == p2 && b1 == b2 && e1 == e2,
            (&Value::BuiltInFn {
                 name: ref n1,
                 num_params: ref p1,
                 ..
             },
             &Value::BuiltInFn {
                 name: ref n2,
                 num_params: ref p2,
                 ..
             }) => n1 == n2 && p1 == p2,
            (&Value::Return(ref o1), &Value::Return(ref o2)) => o1 == o2,
            (&Value::Null, &Value::Null) => true,
            _ => false,
        }
    }
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Hashable {
    Int(i64),
    Bool(bool),
    String(String),
}

impl fmt::Display for Hashable {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match self {
            &Hashable::Int(i) => Value::Int(i),
            &Hashable::Bool(b) => Value::Bool(b),
            &Hashable::String(ref s) => Value::String(s.clone()),
        })
    }
}

pub type BuiltInFn = fn(Vec<Value>) -> Result<Value>;
<|endoftext|>


<commit_before>use std::collections::HashMap;

use crate::controllers::frontend_prelude::*;

use crate::controllers::helpers::*;
use crate::email;

use crate::models::{
    CrateOwner, Email, Follow, NewEmail, OwnerKind, User, Version, VersionOwnerAction,
};
use crate::schema::{crate_owners, crates, emails, follows, users, versions};
use crate::views::{EncodableMe, EncodableVersion, OwnedCrate};

/// Handles the `GET /me` route.
pub fn me(req: &mut dyn RequestExt) -> EndpointResult {
    let user_id = req.authenticate()?.user_id();
    let conn = req.db_conn()?;

    let (user, verified, email, verification_sent) = users::table
        .find(user_id)
        .left_join(emails::table)
        .select((
            users::all_columns,
            emails::verified.nullable(),
            emails::email.nullable(),
            emails::token_generated_at.nullable().is_not_null(),
        ))
        .first::<(User, Option<bool>, Option<String>, bool)>(&*conn)?;

    let owned_crates = CrateOwner::by_owner_kind(OwnerKind::User)
        .inner_join(crates::table)
        .filter(crate_owners::owner_id.eq(user_id))
        .select((crates::id, crates::name, crate_owners::email_notifications))
        .order(crates::name.asc())
        .load(&*conn)?
        .into_iter()
        .map(|(id, name, email_notifications)| OwnedCrate {
            id,
            name,
            email_notifications,
        })
        .collect();

    let verified = verified.unwrap_or(false);
    let verification_sent = verified || verification_sent;
    Ok(req.json(&EncodableMe {
        user: user.encodable_private(email, verified, verification_sent),
        owned_crates,
    }))
}

/// Handles the `GET /me/updates` route.
pub fn updates(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::any;

    let authenticated_user = req.authenticate()?;
    let conn = req.db_conn()?;
    let user = authenticated_user.user();

    let followed_crates = Follow::belonging_to(&user).select(follows::crate_id);
    let data = versions::table
        .inner_join(crates::table)
        .left_outer_join(users::table)
        .filter(crates::id.eq(any(followed_crates)))
        .order(versions::created_at.desc())
        .select((
            versions::all_columns,
            crates::name,
            users::all_columns.nullable(),
        ))
        .paginate(&req.query())?
        .load::<(Version, String, Option<User>)>(&*conn)?;
    let more = data.next_page_params().is_some();
    let versions = data.iter().map(|(v, _, _)| v).cloned().collect::<Vec<_>>();
    let data = data
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((v, cn, pb), voas)| (v, cn, pb, voas))
        .collect::<Vec<_>>();

    let versions = data
        .into_iter()
        .map(|(version, crate_name, published_by, actions)| {
            version.encodable(&crate_name, published_by, actions)
        })
        .collect();

    #[derive(Serialize)]
    struct R {
        versions: Vec<EncodableVersion>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        more: bool,
    }
    Ok(req.json(&R {
        versions,
        meta: Meta { more },
    }))
}

/// Handles the `PUT /user/:user_id` route.
pub fn update_user(req: &mut dyn RequestExt) -> EndpointResult {
    use self::emails::user_id;
    use diesel::insert_into;

    let authenticated_user = req.authenticate()?;

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let param_user_id = &req.params()["user_id"];
    let conn = req.db_conn()?;
    let user = authenticated_user.user();

    // need to check if current user matches user to be updated
    if &user.id.to_string() != param_user_id {
        return Err(bad_request("current user does not match requested user"));
    }

    #[derive(Deserialize)]
    struct UserUpdate {
        user: User,
    }

    #[derive(Deserialize)]
    struct User {
        email: Option<String>,
    }

    let user_update: UserUpdate =
        serde_json::from_str(&body).map_err(|_| bad_request("invalid json request"))?;

    let user_email = match &user_update.user.email {
        Some(email) => email.trim(),
        None => return Err(bad_request("empty email rejected")),
    };

    if user_email == "" {
        return Err(bad_request("empty email rejected"));
    }

    conn.transaction::<_, Box<dyn AppError>, _>(|| {
        let new_email = NewEmail {
            user_id: user.id,
            email: user_email,
        };

        let token = insert_into(emails::table)
            .values(&new_email)
            .on_conflict(user_id)
            .do_update()
            .set(&new_email)
            .returning(emails::token)
            .get_result::<String>(&*conn)
            .map_err(|_| server_error("Error in creating token"))?;

        crate::email::send_user_confirm_email(user_email, &user.gh_login, &token);

        Ok(())
    })?;

    ok_true()
}

/// Handles the `PUT /confirm/:email_token` route
pub fn confirm_user_email(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::update;

    let conn = req.db_conn()?;
    let req_token = &req.params()["email_token"];

    let updated_rows = update(emails::table.filter(emails::token.eq(req_token)))
        .set(emails::verified.eq(true))
        .execute(&*conn)?;

    if updated_rows == 0 {
        return Err(bad_request("Email belonging to token not found."));
    }

    ok_true()
}

/// Handles `PUT /user/:user_id/resend` route
pub fn regenerate_token_and_send(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::sql;
    use diesel::update;

    let param_user_id = req.params()["user_id"]
        .parse::<i32>()
        .chain_error(|| bad_request("invalid user_id"))?;
    let authenticated_user = req.authenticate()?;
    let conn = req.db_conn()?;
    let user = authenticated_user.user();

    // need to check if current user matches user to be updated
    if user.id != param_user_id {
        return Err(bad_request("current user does not match requested user"));
    }

    conn.transaction(|| {
        let email = update(Email::belonging_to(&user))
            .set(emails::token.eq(sql("DEFAULT")))
            .get_result::<Email>(&*conn)
            .map_err(|_| bad_request("Email could not be found"))?;

        email::try_send_user_confirm_email(&email.email, &user.gh_login, &email.token)
            .map_err(|_| server_error("Error in sending email"))
    })?;

    ok_true()
}

/// Handles `PUT /me/email_notifications` route
pub fn update_email_notifications(req: &mut dyn RequestExt) -> EndpointResult {
    use self::crate_owners::dsl::*;
    use diesel::pg::upsert::excluded;

    #[derive(Deserialize)]
    struct CrateEmailNotifications {
        id: i32,
        email_notifications: bool,
    }

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;
    let updates: HashMap<i32, bool> = serde_json::from_str::<Vec<CrateEmailNotifications>>(&body)
        .map_err(|_| bad_request("invalid json request"))?
        .iter()
        .map(|c| (c.id, c.email_notifications))
        .collect();

    let user_id = req.authenticate()?.user_id();
    let conn = req.db_conn()?;

    // Build inserts from existing crates belonging to the current user
    let to_insert = CrateOwner::by_owner_kind(OwnerKind::User)
        .filter(owner_id.eq(user_id))
        .select((crate_id, owner_id, owner_kind, email_notifications))
        .load(&*conn)?
        .into_iter()
        // Remove records whose `email_notifications` will not change from their current value
        .map(
            |(c_id, o_id, o_kind, e_notifications): (i32, i32, i32, bool)| {
                let current_e_notifications = *updates.get(&c_id).unwrap_or(&e_notifications);
                (
                    crate_id.eq(c_id),
                    owner_id.eq(o_id),
                    owner_kind.eq(o_kind),
                    email_notifications.eq(current_e_notifications),
                )
            },
        )
        .collect::<Vec<_>>();

    // Upsert crate owners; this should only actually exectute updates
    diesel::insert_into(crate_owners)
        .values(&to_insert)
        .on_conflict((crate_id, owner_id, owner_kind))
        .do_update()
        .set(email_notifications.eq(excluded(email_notifications)))
        .execute(&*conn)?;

    ok_true()
}
<commit_msg>controllers/user/me: Fix typo in `update_user()` API docs<commit_after>use std::collections::HashMap;

use crate::controllers::frontend_prelude::*;

use crate::controllers::helpers::*;
use crate::email;

use crate::models::{
    CrateOwner, Email, Follow, NewEmail, OwnerKind, User, Version, VersionOwnerAction,
};
use crate::schema::{crate_owners, crates, emails, follows, users, versions};
use crate::views::{EncodableMe, EncodableVersion, OwnedCrate};

/// Handles the `GET /me` route.
pub fn me(req: &mut dyn RequestExt) -> EndpointResult {
    let user_id = req.authenticate()?.user_id();
    let conn = req.db_conn()?;

    let (user, verified, email, verification_sent) = users::table
        .find(user_id)
        .left_join(emails::table)
        .select((
            users::all_columns,
            emails::verified.nullable(),
            emails::email.nullable(),
            emails::token_generated_at.nullable().is_not_null(),
        ))
        .first::<(User, Option<bool>, Option<String>, bool)>(&*conn)?;

    let owned_crates = CrateOwner::by_owner_kind(OwnerKind::User)
        .inner_join(crates::table)
        .filter(crate_owners::owner_id.eq(user_id))
        .select((crates::id, crates::name, crate_owners::email_notifications))
        .order(crates::name.asc())
        .load(&*conn)?
        .into_iter()
        .map(|(id, name, email_notifications)| OwnedCrate {
            id,
            name,
            email_notifications,
        })
        .collect();

    let verified = verified.unwrap_or(false);
    let verification_sent = verified || verification_sent;
    Ok(req.json(&EncodableMe {
        user: user.encodable_private(email, verified, verification_sent),
        owned_crates,
    }))
}

/// Handles the `GET /me/updates` route.
pub fn updates(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::any;

    let authenticated_user = req.authenticate()?;
    let conn = req.db_conn()?;
    let user = authenticated_user.user();

    let followed_crates = Follow::belonging_to(&user).select(follows::crate_id);
    let data = versions::table
        .inner_join(crates::table)
        .left_outer_join(users::table)
        .filter(crates::id.eq(any(followed_crates)))
        .order(versions::created_at.desc())
        .select((
            versions::all_columns,
            crates::name,
            users::all_columns.nullable(),
        ))
        .paginate(&req.query())?
        .load::<(Version, String, Option<User>)>(&*conn)?;
    let more = data.next_page_params().is_some();
    let versions = data.iter().map(|(v, _, _)| v).cloned().collect::<Vec<_>>();
    let data = data
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((v, cn, pb), voas)| (v, cn, pb, voas))
        .collect::<Vec<_>>();

    let versions = data
        .into_iter()
        .map(|(version, crate_name, published_by, actions)| {
            version.encodable(&crate_name, published_by, actions)
        })
        .collect();

    #[derive(Serialize)]
    struct R {
        versions: Vec<EncodableVersion>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        more: bool,
    }
    Ok(req.json(&R {
        versions,
        meta: Meta { more },
    }))
}

/// Handles the `PUT /users/:user_id` route.
pub fn update_user(req: &mut dyn RequestExt) -> EndpointResult {
    use self::emails::user_id;
    use diesel::insert_into;

    let authenticated_user = req.authenticate()?;

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let param_user_id = &req.params()["user_id"];
    let conn = req.db_conn()?;
    let user = authenticated_user.user();

    // need to check if current user matches user to be updated
    if &user.id.to_string() != param_user_id {
        return Err(bad_request("current user does not match requested user"));
    }

    #[derive(Deserialize)]
    struct UserUpdate {
        user: User,
    }

    #[derive(Deserialize)]
    struct User {
        email: Option<String>,
    }

    let user_update: UserUpdate =
        serde_json::from_str(&body).map_err(|_| bad_request("invalid json request"))?;

    let user_email = match &user_update.user.email {
        Some(email) => email.trim(),
        None => return Err(bad_request("empty email rejected")),
    };

    if user_email == "" {
        return Err(bad_request("empty email rejected"));
    }

    conn.transaction::<_, Box<dyn AppError>, _>(|| {
        let new_email = NewEmail {
            user_id: user.id,
            email: user_email,
        };

        let token = insert_into(emails::table)
            .values(&new_email)
            .on_conflict(user_id)
            .do_update()
            .set(&new_email)
            .returning(emails::token)
            .get_result::<String>(&*conn)
            .map_err(|_| server_error("Error in creating token"))?;

        crate::email::send_user_confirm_email(user_email, &user.gh_login, &token);

        Ok(())
    })?;

    ok_true()
}

/// Handles the `PUT /confirm/:email_token` route
pub fn confirm_user_email(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::update;

    let conn = req.db_conn()?;
    let req_token = &req.params()["email_token"];

    let updated_rows = update(emails::table.filter(emails::token.eq(req_token)))
        .set(emails::verified.eq(true))
        .execute(&*conn)?;

    if updated_rows == 0 {
        return Err(bad_request("Email belonging to token not found."));
    }

    ok_true()
}

/// Handles `PUT /user/:user_id/resend` route
pub fn regenerate_token_and_send(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::sql;
    use diesel::update;

    let param_user_id = req.params()["user_id"]
        .parse::<i32>()
        .chain_error(|| bad_request("invalid user_id"))?;
    let authenticated_user = req.authenticate()?;
    let conn = req.db_conn()?;
    let user = authenticated_user.user();

    // need to check if current user matches user to be updated
    if user.id != param_user_id {
        return Err(bad_request("current user does not match requested user"));
    }

    conn.transaction(|| {
        let email = update(Email::belonging_to(&user))
            .set(emails::token.eq(sql("DEFAULT")))
            .get_result::<Email>(&*conn)
            .map_err(|_| bad_request("Email could not be found"))?;

        email::try_send_user_confirm_email(&email.email, &user.gh_login, &email.token)
            .map_err(|_| server_error("Error in sending email"))
    })?;

    ok_true()
}

/// Handles `PUT /me/email_notifications` route
pub fn update_email_notifications(req: &mut dyn RequestExt) -> EndpointResult {
    use self::crate_owners::dsl::*;
    use diesel::pg::upsert::excluded;

    #[derive(Deserialize)]
    struct CrateEmailNotifications {
        id: i32,
        email_notifications: bool,
    }

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;
    let updates: HashMap<i32, bool> = serde_json::from_str::<Vec<CrateEmailNotifications>>(&body)
        .map_err(|_| bad_request("invalid json request"))?
        .iter()
        .map(|c| (c.id, c.email_notifications))
        .collect();

    let user_id = req.authenticate()?.user_id();
    let conn = req.db_conn()?;

    // Build inserts from existing crates belonging to the current user
    let to_insert = CrateOwner::by_owner_kind(OwnerKind::User)
        .filter(owner_id.eq(user_id))
        .select((crate_id, owner_id, owner_kind, email_notifications))
        .load(&*conn)?
        .into_iter()
        // Remove records whose `email_notifications` will not change from their current value
        .map(
            |(c_id, o_id, o_kind, e_notifications): (i32, i32, i32, bool)| {
                let current_e_notifications = *updates.get(&c_id).unwrap_or(&e_notifications);
                (
                    crate_id.eq(c_id),
                    owner_id.eq(o_id),
                    owner_kind.eq(o_kind),
                    email_notifications.eq(current_e_notifications),
                )
            },
        )
        .collect::<Vec<_>>();

    // Upsert crate owners; this should only actually exectute updates
    diesel::insert_into(crate_owners)
        .values(&to_insert)
        .on_conflict((crate_id, owner_id, owner_kind))
        .do_update()
        .set(email_notifications.eq(excluded(email_notifications)))
        .execute(&*conn)?;

    ok_true()
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;

use parse;
use enums;

/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
pub fn write_structs(doc: &parse::Spirv) -> String {
    let mut result = String::new();

    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::TypeStruct { result_id, ref member_types } => {
                result.push_str(&write_struct(doc, result_id, member_types));
                result.push_str("\n");
            },
            _ => ()
        }
    }

    result
}

/// Writes a single struct.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> String {
    let name = ::name_from_id(doc, struct_id);

    let mut members_defs = Vec::with_capacity(members.len());

    // Contains the offset of the next field.
    // Equals to `None` if there's a runtime-sized field in there.
    let mut current_rust_offset = Some(0);

    for (num, &member) in members.iter().enumerate() {
        let (ty, rust_size, rust_align) = type_from_id(doc, member);
        let member_name = ::member_name_from_id(doc, struct_id, num as u32);

        // Ignore the whole struct is a member is built in, which includes
        // `gl_Position` for example.
        if is_builtin_member(doc, struct_id, num as u32) {
            return String::new();
        }

        let spirv_offset = doc.instructions.iter().filter_map(|i| {
            match *i {
                parse::Instruction::MemberDecorate { target_id, member,
                                                   decoration: enums::Decoration::DecorationOffset,
                                                   ref params } if target_id == struct_id &&
                                                                   member as usize == num =>
                {
                    return Some(params[0]);
                },
                _ => ()
            };

            None
        }).next();

        // Some structs don't have `Offset` decorations, in the case they are used as local
        // variables only. Ignoring these.
        let spirv_offset = match spirv_offset {
            Some(o) => o as usize,
            None => return String::new()
        };

        // We need to add a dummy field if necessary.
        {
            let current_rust_offset = current_rust_offset.as_mut().expect("Found runtime-sized member in non-final position");

            // Updating current_rust_offset to take the alignment of the next field into account
            *current_rust_offset = if *current_rust_offset == 0 {
                0
            } else {
                (1 + (*current_rust_offset - 1) / rust_align) * rust_align
            };

            if spirv_offset != *current_rust_offset {
                let diff = spirv_offset.checked_sub(*current_rust_offset).unwrap();
                members_defs.push(format!("_dummy: [u8; {}]", diff));       // FIXME: fix name if there are multiple dummies
                *current_rust_offset += diff;
            }
        }

        // Updating `current_rust_offset`.
        if let Some(s) = rust_size {
            *current_rust_offset.as_mut().unwrap() += s;
        } else {
            current_rust_offset = None;
        }

        members_defs.push(format!("pub {name}: {ty}", name = member_name, ty = ty));
    }

    // We can only derive common traits if there's no unsized member in the struct.
    let derive = if current_rust_offset.is_some() {
        "#[derive(Copy, Clone, Debug, Default)]\n"
    } else {
        ""
    };

    format!("#[repr(C)]\n{derive}\
             pub struct {name} {{\n\t{members}\n}}\n",
            derive = derive, name = name, members = members_defs.join(",\n\t"))
}

/// Returns true if a `BuiltIn` decorator is applied on a struct member.
fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::MemberDecorate { target_id, member,
                                                 decoration: enums::Decoration::DecorationBuiltIn,
                                                 .. } if target_id == id && member == member_id =>
            {
                return true;
            },
            _ => ()
        }
    }

    false
}

/// Returns the type name to put in the Rust struct, and its size and alignment.
///
/// The size can be `None` if it's only known at runtime.
fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
    for instruction in doc.instructions.iter() {
        match instruction {
            &parse::Instruction::TypeBool { result_id } if result_id == searched => {
                return ("bool".to_owned(), Some(mem::size_of::<bool>()), mem::align_of::<bool>())
            },
            &parse::Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
                // FIXME: width
                return ("i32".to_owned(), Some(mem::size_of::<i32>()), mem::align_of::<i32>())
            },
            &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
                // FIXME: width
                return ("f32".to_owned(), Some(mem::size_of::<f32>()), mem::align_of::<f32>())
            },
            &parse::Instruction::TypeVector { result_id, component_id, count } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, component_id);
                return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
            },
            &parse::Instruction::TypeMatrix { result_id, column_type_id, column_count } if result_id == searched => {
                // FIXME: row-major or column-major
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, column_type_id);
                return (format!("[{}; {}]", t, column_count), t_size.map(|s| s * column_count as usize), t_align);
            },
            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, type_id);
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                return (format!("[{}; {}]", t, len), t_size.map(|s| s * len as usize), t_align);       // FIXME:
            },
            &parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, _, t_align) = type_from_id(doc, type_id);
                return (format!("[{}]", t), None, t_align);
            },
            &parse::Instruction::TypeStruct { result_id, ref member_types } if result_id == searched => {
                let name = ::name_from_id(doc, result_id);
                let size = member_types.iter().filter_map(|&t| type_from_id(doc, t).1).fold(0, |a,b|a+b);
                let align = member_types.iter().map(|&t| type_from_id(doc, t).2).max().unwrap_or(1);
                return (name, Some(size), align);
            },
            _ => ()
        }
    }

    panic!("Type #{} not found", searched)
}
<commit_msg>Print struct members offset for debugging purposes<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;

use parse;
use enums;

/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
pub fn write_structs(doc: &parse::Spirv) -> String {
    let mut result = String::new();

    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::TypeStruct { result_id, ref member_types } => {
                result.push_str(&write_struct(doc, result_id, member_types));
                result.push_str("\n");
            },
            _ => ()
        }
    }

    result
}

/// Writes a single struct.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> String {
    let name = ::name_from_id(doc, struct_id);

    let mut members_defs = Vec::with_capacity(members.len());

    // Contains the offset of the next field.
    // Equals to `None` if there's a runtime-sized field in there.
    let mut current_rust_offset = Some(0);

    for (num, &member) in members.iter().enumerate() {
        let (ty, rust_size, rust_align) = type_from_id(doc, member);
        let member_name = ::member_name_from_id(doc, struct_id, num as u32);

        // Ignore the whole struct is a member is built in, which includes
        // `gl_Position` for example.
        if is_builtin_member(doc, struct_id, num as u32) {
            return String::new();
        }

        let spirv_offset = doc.instructions.iter().filter_map(|i| {
            match *i {
                parse::Instruction::MemberDecorate { target_id, member,
                                                   decoration: enums::Decoration::DecorationOffset,
                                                   ref params } if target_id == struct_id &&
                                                                   member as usize == num =>
                {
                    return Some(params[0]);
                },
                _ => ()
            };

            None
        }).next();

        // Some structs don't have `Offset` decorations, in the case they are used as local
        // variables only. Ignoring these.
        let spirv_offset = match spirv_offset {
            Some(o) => o as usize,
            None => return String::new()
        };

        // We need to add a dummy field if necessary.
        {
            let current_rust_offset = current_rust_offset.as_mut().expect("Found runtime-sized member in non-final position");

            // Updating current_rust_offset to take the alignment of the next field into account
            *current_rust_offset = if *current_rust_offset == 0 {
                0
            } else {
                (1 + (*current_rust_offset - 1) / rust_align) * rust_align
            };

            if spirv_offset != *current_rust_offset {
                let diff = spirv_offset.checked_sub(*current_rust_offset).unwrap();
                members_defs.push(format!("_dummy: [u8; {}]", diff));       // FIXME: fix name if there are multiple dummies
                *current_rust_offset += diff;
            }
        }

        // Updating `current_rust_offset`.
        if let Some(s) = rust_size {
            *current_rust_offset.as_mut().unwrap() += s;
        } else {
            current_rust_offset = None;
        }

        members_defs.push(format!("pub {name}: {ty}  /* offset: {offset} */",
                                  name = member_name, ty = ty, offset = spirv_offset));
    }

    // We can only derive common traits if there's no unsized member in the struct.
    let derive = if current_rust_offset.is_some() {
        "#[derive(Copy, Clone, Debug, Default)]\n"
    } else {
        ""
    };

    format!("#[repr(C)]\n{derive}\
             pub struct {name} {{\n\t{members}\n}}\n",
            derive = derive, name = name, members = members_defs.join(",\n\t"))
}

/// Returns true if a `BuiltIn` decorator is applied on a struct member.
fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::MemberDecorate { target_id, member,
                                                 decoration: enums::Decoration::DecorationBuiltIn,
                                                 .. } if target_id == id && member == member_id =>
            {
                return true;
            },
            _ => ()
        }
    }

    false
}

/// Returns the type name to put in the Rust struct, and its size and alignment.
///
/// The size can be `None` if it's only known at runtime.
fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
    for instruction in doc.instructions.iter() {
        match instruction {
            &parse::Instruction::TypeBool { result_id } if result_id == searched => {
                return ("bool".to_owned(), Some(mem::size_of::<bool>()), mem::align_of::<bool>())
            },
            &parse::Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
                // FIXME: width
                return ("i32".to_owned(), Some(mem::size_of::<i32>()), mem::align_of::<i32>())
            },
            &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
                // FIXME: width
                return ("f32".to_owned(), Some(mem::size_of::<f32>()), mem::align_of::<f32>())
            },
            &parse::Instruction::TypeVector { result_id, component_id, count } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, component_id);
                return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
            },
            &parse::Instruction::TypeMatrix { result_id, column_type_id, column_count } if result_id == searched => {
                // FIXME: row-major or column-major
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, column_type_id);
                return (format!("[{}; {}]", t, column_count), t_size.map(|s| s * column_count as usize), t_align);
            },
            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, type_id);
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                return (format!("[{}; {}]", t, len), t_size.map(|s| s * len as usize), t_align);       // FIXME:
            },
            &parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, _, t_align) = type_from_id(doc, type_id);
                return (format!("[{}]", t), None, t_align);
            },
            &parse::Instruction::TypeStruct { result_id, ref member_types } if result_id == searched => {
                let name = ::name_from_id(doc, result_id);
                let size = member_types.iter().filter_map(|&t| type_from_id(doc, t).1).fold(0, |a,b|a+b);
                let align = member_types.iter().map(|&t| type_from_id(doc, t).2).max().unwrap_or(1);
                return (name, Some(size), align);
            },
            _ => ()
        }
    }

    panic!("Type #{} not found", searched)
}
<|endoftext|>