<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 std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use bindgen::cargo::{Cargo, PackageRef};
use syn;

const STD_CRATES: &'static [&'static str] = &["std",
                                              "std_unicode",
                                              "alloc",
                                              "collections",
                                              "core",
                                              "proc_macro"];

type ParseResult = Result<(), String>;

/// Parses a single rust source file, not following `mod` or `extern crate`.
pub fn parse_src<F>(src_file: &Path,
                    items_callback: &mut F) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let src_parsed = {
        let mut s = String::new();
        let mut f = File::open(src_file).map_err(|_| format!("parsing: cannot open file `{:?}`", src_file))?;
        f.read_to_string(&mut s).map_err(|_| format!("parsing: cannot open file `{:?}`", src_file))?;
        syn::parse_crate(&s).map_err(|msg| format!("parsing:\n{}", msg))?
    };

    items_callback("", &src_parsed.items);

    Ok(())
}

/// Recursively parses a rust library starting at the root crate's directory.
///
/// Inside a crate, `mod` and `extern crate` declarations are followed
/// and parsed. To find an external crate, the parser uses the `cargo metadata`
/// command to find the location of dependencies.
pub fn parse_lib<F>(lib: Cargo,
                    include: &Option<Vec<String>>,
                    exclude: &Vec<String>,
                    expand: &Vec<String>,
                    items_callback: &mut F) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let mut context = ParseLibContext {
        lib: lib,
        include: include.clone(),
        exclude: exclude.clone(),
        expand: expand.clone(),
        cache_src: HashMap::new(),
        cache_expanded_crate: HashMap::new(),
        items_callback: items_callback,
    };

    parse_crate(&context.lib.binding_crate_ref(), &mut context)
}

struct ParseLibContext<F>
    where F: FnMut(&str, &Vec<syn::Item>)
{
    lib: Cargo,
    include: Option<Vec<String>>,
    exclude: Vec<String>,
    expand: Vec<String>,
    cache_src: HashMap<PathBuf, Vec<syn::Item>>,
    cache_expanded_crate: HashMap<String, Vec<syn::Item>>,

    items_callback: F,
}

impl<F> ParseLibContext<F>
    where F: FnMut(&str, &Vec<syn::Item>)
{
    fn should_parse_crate(&self, pkg_name: &String) -> bool {
        // Skip any whitelist or blacklist for expand
        if self.expand.contains(&pkg_name) {
            return true;
        }

        // If we have a whitelist, check it
        if let Some(ref include) = self.include {
            if !include.contains(&pkg_name) {
                return false;
            }
        }

        // Check the blacklist
        return !STD_CRATES.contains(&pkg_name.as_ref()) &&
               !self.exclude.contains(&pkg_name);
    }
}

fn parse_crate<F>(pkg: &PackageRef, context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    // Check if we should use cargo expand for this crate
    if context.expand.contains(&pkg.name) {
        return parse_expand_crate(pkg, context);
    }

    // Otherwise do our normal parse
    let crate_src = context.lib.find_crate_src(pkg);

    match crate_src {
        Some(crate_src) => {
            parse_mod(pkg, crate_src.as_path(), context)
        },
        None => {
            // This should be an error, but is common enough to just elicit a warning
            warn!("parsing crate `{}`: can't find lib.rs with `cargo metadata`", pkg.name);
            Ok(())
        },
    }
}

fn parse_expand_crate<F>(pkg: &PackageRef, context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let mod_parsed = {
        if !context.cache_expanded_crate.contains_key(&pkg.name) {
            let s = context.lib.expand_crate(pkg)?;
            let i = syn::parse_crate(&s).map_err(|msg| format!("parsing crate `{}`:\n{}", pkg.name, msg))?;
            context.cache_expanded_crate.insert(pkg.name.clone(), i.items);
        }

        context.cache_expanded_crate.get(&pkg.name).unwrap().clone()
    };

    process_expanded_mod(pkg, &mod_parsed, context)
}

fn process_expanded_mod<F>(pkg: &PackageRef,
                           items: &Vec<syn::Item>,
                           context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    (context.items_callback)(&pkg.name, items);

    for item in items {
        match item.node {
            syn::ItemKind::Mod(ref inline_items) => {
                if let &Some(ref inline_items) = inline_items {
                    process_expanded_mod(pkg, inline_items, context)?;
                    continue;
                }

                return Err(format!("parsing crate `{}`: external mod found in expanded source", pkg.name));
            }
            syn::ItemKind::ExternCrate(_) => {
                let dep_pkg_name = item.ident.to_string();

                if context.should_parse_crate(&dep_pkg_name) {
                    let dep_pkg_ref = context.lib.find_dep_ref(pkg, &dep_pkg_name);

                    if let Some(dep_pkg_ref) = dep_pkg_ref {
                        parse_crate(&dep_pkg_ref, context)?;
                    } else {
                        error!("parsing crate `{}`: can't find dependency version for {}`", pkg.name, dep_pkg_name);
                    }
                }
            }
            _ => {}
        }
    }

    Ok(())
}

fn parse_mod<F>(pkg: &PackageRef,
                mod_path: &Path,
                context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let mod_parsed = {
        let owned_mod_path = mod_path.to_path_buf();

        if !context.cache_src.contains_key(&owned_mod_path) {
            let mut s = String::new();
            let mut f = File::open(mod_path).map_err(|_| format!("parsing crate `{}`: cannot open file `{:?}`", pkg.name, mod_path))?;
            f.read_to_string(&mut s).map_err(|_| format!("parsing crate `{}`: cannot open file `{:?}`", pkg.name, mod_path))?;
            let i = syn::parse_crate(&s).map_err(|msg| format!("parsing crate `{}`:\n{}", pkg.name, msg))?;
            context.cache_src.insert(owned_mod_path.clone(), i.items);
        }

        context.cache_src.get(&owned_mod_path).unwrap().clone()
    };

    let mod_dir = mod_path.parent().unwrap();

    process_mod(pkg,
                mod_dir,
                &mod_parsed,
                context)
}

fn process_mod<F>(pkg: &PackageRef,
                  mod_dir: &Path,
                  items: &Vec<syn::Item>,
                  context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    (context.items_callback)(&pkg.name, items);

    for item in items {
        match item.node {
            syn::ItemKind::Mod(ref inline_items) => {
                let next_mod_name = item.ident.to_string();

                if let &Some(ref inline_items) = inline_items {
                    process_mod(pkg,
                                mod_dir,
                                inline_items,
                                context)?;
                    continue;
                }

                let next_mod_path1 = mod_dir.join(next_mod_name.clone() + ".rs");
                let next_mod_path2 = mod_dir.join(next_mod_name.clone()).join("mod.rs");

                if next_mod_path1.exists() {
                    parse_mod(pkg,
                              next_mod_path1.as_path(),
                              context)?;
                } else if next_mod_path2.exists() {
                    parse_mod(pkg,
                              next_mod_path2.as_path(),
                              context)?;
                } else {
                    // This should be an error, but is common enough to just elicit a warning
                    warn!("parsing crate `{}`: can't find mod {}`", pkg.name, next_mod_name);
                }
            }
            syn::ItemKind::ExternCrate(_) => {
                let dep_pkg_name = item.ident.to_string();

                if context.should_parse_crate(&dep_pkg_name) {
                    let dep_pkg_ref = context.lib.find_dep_ref(pkg, &dep_pkg_name);

                    if let Some(dep_pkg_ref) = dep_pkg_ref {
                        parse_crate(&dep_pkg_ref, context)?;
                    } else {
                        error!("parsing crate `{}`: can't find dependency version for {}`", pkg.name, dep_pkg_name);
                    }
                }
            }
            _ => {}
        }
    }

    Ok(())
}
<commit_msg>Use inline mods as folders when searching for a new mod<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 std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use bindgen::cargo::{Cargo, PackageRef};
use syn;

const STD_CRATES: &'static [&'static str] = &["std",
                                              "std_unicode",
                                              "alloc",
                                              "collections",
                                              "core",
                                              "proc_macro"];

type ParseResult = Result<(), String>;

/// Parses a single rust source file, not following `mod` or `extern crate`.
pub fn parse_src<F>(src_file: &Path,
                    items_callback: &mut F) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let src_parsed = {
        let mut s = String::new();
        let mut f = File::open(src_file).map_err(|_| format!("parsing: cannot open file `{:?}`", src_file))?;
        f.read_to_string(&mut s).map_err(|_| format!("parsing: cannot open file `{:?}`", src_file))?;
        syn::parse_crate(&s).map_err(|msg| format!("parsing:\n{}", msg))?
    };

    items_callback("", &src_parsed.items);

    Ok(())
}

/// Recursively parses a rust library starting at the root crate's directory.
///
/// Inside a crate, `mod` and `extern crate` declarations are followed
/// and parsed. To find an external crate, the parser uses the `cargo metadata`
/// command to find the location of dependencies.
pub fn parse_lib<F>(lib: Cargo,
                    include: &Option<Vec<String>>,
                    exclude: &Vec<String>,
                    expand: &Vec<String>,
                    items_callback: &mut F) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let mut context = ParseLibContext {
        lib: lib,
        include: include.clone(),
        exclude: exclude.clone(),
        expand: expand.clone(),
        cache_src: HashMap::new(),
        cache_expanded_crate: HashMap::new(),
        items_callback: items_callback,
    };

    parse_crate(&context.lib.binding_crate_ref(), &mut context)
}

struct ParseLibContext<F>
    where F: FnMut(&str, &Vec<syn::Item>)
{
    lib: Cargo,
    include: Option<Vec<String>>,
    exclude: Vec<String>,
    expand: Vec<String>,
    cache_src: HashMap<PathBuf, Vec<syn::Item>>,
    cache_expanded_crate: HashMap<String, Vec<syn::Item>>,

    items_callback: F,
}

impl<F> ParseLibContext<F>
    where F: FnMut(&str, &Vec<syn::Item>)
{
    fn should_parse_crate(&self, pkg_name: &String) -> bool {
        // Skip any whitelist or blacklist for expand
        if self.expand.contains(&pkg_name) {
            return true;
        }

        // If we have a whitelist, check it
        if let Some(ref include) = self.include {
            if !include.contains(&pkg_name) {
                return false;
            }
        }

        // Check the blacklist
        return !STD_CRATES.contains(&pkg_name.as_ref()) &&
               !self.exclude.contains(&pkg_name);
    }
}

fn parse_crate<F>(pkg: &PackageRef, context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    // Check if we should use cargo expand for this crate
    if context.expand.contains(&pkg.name) {
        return parse_expand_crate(pkg, context);
    }

    // Otherwise do our normal parse
    let crate_src = context.lib.find_crate_src(pkg);

    match crate_src {
        Some(crate_src) => {
            parse_mod(pkg, crate_src.as_path(), context)
        },
        None => {
            // This should be an error, but is common enough to just elicit a warning
            warn!("parsing crate `{}`: can't find lib.rs with `cargo metadata`", pkg.name);
            Ok(())
        },
    }
}

fn parse_expand_crate<F>(pkg: &PackageRef, context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let mod_parsed = {
        if !context.cache_expanded_crate.contains_key(&pkg.name) {
            let s = context.lib.expand_crate(pkg)?;
            let i = syn::parse_crate(&s).map_err(|msg| format!("parsing crate `{}`:\n{}", pkg.name, msg))?;
            context.cache_expanded_crate.insert(pkg.name.clone(), i.items);
        }

        context.cache_expanded_crate.get(&pkg.name).unwrap().clone()
    };

    process_expanded_mod(pkg, &mod_parsed, context)
}

fn process_expanded_mod<F>(pkg: &PackageRef,
                           items: &Vec<syn::Item>,
                           context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    (context.items_callback)(&pkg.name, items);

    for item in items {
        match item.node {
            syn::ItemKind::Mod(ref inline_items) => {
                if let &Some(ref inline_items) = inline_items {
                    process_expanded_mod(pkg, inline_items, context)?;
                    continue;
                }

                return Err(format!("parsing crate `{}`: external mod found in expanded source", pkg.name));
            }
            syn::ItemKind::ExternCrate(_) => {
                let dep_pkg_name = item.ident.to_string();

                if context.should_parse_crate(&dep_pkg_name) {
                    let dep_pkg_ref = context.lib.find_dep_ref(pkg, &dep_pkg_name);

                    if let Some(dep_pkg_ref) = dep_pkg_ref {
                        parse_crate(&dep_pkg_ref, context)?;
                    } else {
                        error!("parsing crate `{}`: can't find dependency version for {}`", pkg.name, dep_pkg_name);
                    }
                }
            }
            _ => {}
        }
    }

    Ok(())
}

fn parse_mod<F>(pkg: &PackageRef,
                mod_path: &Path,
                context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    let mod_parsed = {
        let owned_mod_path = mod_path.to_path_buf();

        if !context.cache_src.contains_key(&owned_mod_path) {
            let mut s = String::new();
            let mut f = File::open(mod_path).map_err(|_| format!("parsing crate `{}`: cannot open file `{:?}`", pkg.name, mod_path))?;
            f.read_to_string(&mut s).map_err(|_| format!("parsing crate `{}`: cannot open file `{:?}`", pkg.name, mod_path))?;
            let i = syn::parse_crate(&s).map_err(|msg| format!("parsing crate `{}`:\n{}", pkg.name, msg))?;
            context.cache_src.insert(owned_mod_path.clone(), i.items);
        }

        context.cache_src.get(&owned_mod_path).unwrap().clone()
    };

    let mod_dir = mod_path.parent().unwrap();

    process_mod(pkg,
                mod_dir,
                &mod_parsed,
                context)
}

fn process_mod<F>(pkg: &PackageRef,
                  mod_dir: &Path,
                  items: &Vec<syn::Item>,
                  context: &mut ParseLibContext<F>) -> ParseResult
    where F: FnMut(&str, &Vec<syn::Item>)
{
    (context.items_callback)(&pkg.name, items);

    for item in items {
        match item.node {
            syn::ItemKind::Mod(ref inline_items) => {
                let next_mod_name = item.ident.to_string();

                if let &Some(ref inline_items) = inline_items {
                    process_mod(pkg,
                                &mod_dir.join(&next_mod_name),
                                inline_items,
                                context)?;
                    continue;
                }

                let next_mod_path1 = mod_dir.join(next_mod_name.clone() + ".rs");
                let next_mod_path2 = mod_dir.join(next_mod_name.clone()).join("mod.rs");

                if next_mod_path1.exists() {
                    parse_mod(pkg,
                              next_mod_path1.as_path(),
                              context)?;
                } else if next_mod_path2.exists() {
                    parse_mod(pkg,
                              next_mod_path2.as_path(),
                              context)?;
                } else {
                    // This should be an error, but is common enough to just elicit a warning
                    warn!("parsing crate `{}`: can't find mod {}`", pkg.name, next_mod_name);
                }
            }
            syn::ItemKind::ExternCrate(_) => {
                let dep_pkg_name = item.ident.to_string();

                if context.should_parse_crate(&dep_pkg_name) {
                    let dep_pkg_ref = context.lib.find_dep_ref(pkg, &dep_pkg_name);

                    if let Some(dep_pkg_ref) = dep_pkg_ref {
                        parse_crate(&dep_pkg_ref, context)?;
                    } else {
                        error!("parsing crate `{}`: can't find dependency version for {}`", pkg.name, dep_pkg_name);
                    }
                }
            }
            _ => {}
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! Sanity checking performed by rustbuild before actually executing anything.
//!
//! This module contains the implementation of ensuring that the build
//! environment looks reasonable before progressing. This will verify that
//! various programs like git and python exist, along with ensuring that all C
//! compilers for cross-compiling are found.
//!
//! In theory if we get past this phase it's a bug if a build fails, but in
//! practice that's likely not true!

use std::collections::HashMap;
use std::env;
use std::ffi::{OsString, OsStr};
use std::fs;
use std::process::Command;
use std::path::PathBuf;

use build_helper::output;

use Build;

struct Finder {
    cache: HashMap<OsString, Option<PathBuf>>,
    path: OsString,
}

impl Finder {
    fn new() -> Self {
        Self {
            cache: HashMap::new(),
            path: env::var_os("PATH").unwrap_or_default()
        }
    }

    fn maybe_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> Option<PathBuf> {
        let cmd: OsString = cmd.as_ref().into();
        let path = self.path.clone();
        self.cache.entry(cmd.clone()).or_insert_with(|| {
            for path in env::split_paths(&path) {
                let target = path.join(&cmd);
                let mut cmd_alt = cmd.clone();
                cmd_alt.push(".exe");
                if target.is_file() || // some/path/git
                target.with_extension("exe").exists() || // some/path/git.exe
                target.join(&cmd_alt).exists() { // some/path/git/git.exe
                    return Some(target);
                }
            }
            None
        }).clone()
    }

    fn must_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> PathBuf {
        self.maybe_have(&cmd).unwrap_or_else(|| {
            panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref());
        })
    }
}

pub fn check(build: &mut Build) {
    let path = env::var_os("PATH").unwrap_or_default();
    // On Windows, quotes are invalid characters for filename paths, and if
    // one is present as part of the PATH then that can lead to the system
    // being unable to identify the files properly. See
    // https://github.com/rust-lang/rust/issues/34959 for more details.
    if cfg!(windows) && path.to_string_lossy().contains("\"") {
        panic!("PATH contains invalid character '\"'");
    }

    let mut cmd_finder = Finder::new();
    // If we've got a git directory we're gonna need git to update
    // submodules and learn about various other aspects.
    if build.rust_info.is_git() {
        cmd_finder.must_have("git");
    }

    // We need cmake, but only if we're actually building LLVM or sanitizers.
    let building_llvm = build.hosts.iter()
        .filter_map(|host| build.config.target_config.get(host))
        .any(|config| config.llvm_config.is_none());
    if building_llvm || build.config.sanitizers {
        cmd_finder.must_have("cmake");
    }

    // Ninja is currently only used for LLVM itself.
    if building_llvm {
        if build.config.ninja {
            // Some Linux distros rename `ninja` to `ninja-build`.
            // CMake can work with either binary name.
            if cmd_finder.maybe_have("ninja-build").is_none() {
                cmd_finder.must_have("ninja");
            }
        }

        // If ninja isn't enabled but we're building for MSVC then we try
        // doubly hard to enable it. It was realized in #43767 that the msbuild
        // CMake generator for MSVC doesn't respect configuration options like
        // disabling LLVM assertions, which can often be quite important!
        //
        // In these cases we automatically enable Ninja if we find it in the
        // environment.
        if !build.config.ninja && build.config.build.contains("msvc") {
            if cmd_finder.maybe_have("ninja").is_some() {
                build.config.ninja = true;
            }
        }
    }

    build.config.python = build.config.python.take().map(|p| cmd_finder.must_have(p))
        .or_else(|| env::var_os("BOOTSTRAP_PYTHON").map(PathBuf::from)) // set by bootstrap.py
        .or_else(|| cmd_finder.maybe_have("python2.7"))
        .or_else(|| cmd_finder.maybe_have("python2"))
        .or_else(|| Some(cmd_finder.must_have("python")));

    build.config.nodejs = build.config.nodejs.take().map(|p| cmd_finder.must_have(p))
        .or_else(|| cmd_finder.maybe_have("node"))
        .or_else(|| cmd_finder.maybe_have("nodejs"));

    build.config.gdb = build.config.gdb.take().map(|p| cmd_finder.must_have(p))
        .or_else(|| cmd_finder.maybe_have("gdb"));

    // We're gonna build some custom C code here and there, host triples
    // also build some C++ shims for LLVM so we need a C++ compiler.
    for target in &build.targets {
        // On emscripten we don't actually need the C compiler to just
        // build the target artifacts, only for testing. For the sake
        // of easier bot configuration, just skip detection.
        if target.contains("emscripten") {
            continue;
        }

        cmd_finder.must_have(build.cc(*target));
        if let Some(ar) = build.ar(*target) {
            cmd_finder.must_have(ar);
        }
    }

    for host in &build.hosts {
        cmd_finder.must_have(build.cxx(*host).unwrap());

        // The msvc hosts don't use jemalloc, turn it off globally to
        // avoid packaging the dummy liballoc_jemalloc on that platform.
        if host.contains("msvc") {
            build.config.use_jemalloc = false;
        }
    }

    // Externally configured LLVM requires FileCheck to exist
    let filecheck = build.llvm_filecheck(build.build);
    if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests {
        panic!("FileCheck executable {:?} does not exist", filecheck);
    }

    for target in &build.targets {
        // Can't compile for iOS unless we're on macOS
        if target.contains("apple-ios") &&
           !build.build.contains("apple-darwin") {
            panic!("the iOS target is only supported on macOS");
        }

        // Make sure musl-root is valid
        if target.contains("musl") && !target.contains("mips") {
            // If this is a native target (host is also musl) and no musl-root is given,
            // fall back to the system toolchain in /usr before giving up
            if build.musl_root(*target).is_none() && build.config.build == *target {
                let target = build.config.target_config.entry(target.clone())
                                 .or_insert(Default::default());
                target.musl_root = Some("/usr".into());
            }
            match build.musl_root(*target) {
                Some(root) => {
                    if fs::metadata(root.join("lib/libc.a")).is_err() {
                        panic!("couldn't find libc.a in musl dir: {}",
                               root.join("lib").display());
                    }
                    if fs::metadata(root.join("lib/libunwind.a")).is_err() {
                        panic!("couldn't find libunwind.a in musl dir: {}",
                               root.join("lib").display());
                    }
                }
                None => {
                    panic!("when targeting MUSL either the rust.musl-root \
                            option or the target.$TARGET.musl-root option must \
                            be specified in config.toml")
                }
            }
        }

        if target.contains("msvc") {
            // There are three builds of cmake on windows: MSVC, MinGW, and
            // Cygwin. The Cygwin build does not have generators for Visual
            // Studio, so detect that here and error.
            let out = output(Command::new("cmake").arg("--help"));
            if !out.contains("Visual Studio") {
                panic!("
cmake does not support Visual Studio generators.

This is likely due to it being an msys/cygwin build of cmake,
rather than the required windows version, built using MinGW
or Visual Studio.

If you are building under msys2 try installing the mingw-w64-x86_64-cmake
package instead of cmake:

$ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
");
            }
        }
    }

    let run = |cmd: &mut Command| {
        cmd.output().map(|output| {
            String::from_utf8_lossy(&output.stdout)
                   .lines().next().unwrap_or_else(|| {
                       panic!("{:?} failed {:?}", cmd, output)
                   }).to_string()
        })
    };
    build.lldb_version = run(Command::new("lldb").arg("--version")).ok();
    if build.lldb_version.is_some() {
        build.lldb_python_dir = run(Command::new("lldb").arg("-P")).ok();
    }

    if let Some(ref s) = build.config.ccache {
        cmd_finder.must_have(s);
    }
}
<commit_msg>Rollup merge of #47199 - alexcrichton:stable-no-dev, r=kennytm<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! Sanity checking performed by rustbuild before actually executing anything.
//!
//! This module contains the implementation of ensuring that the build
//! environment looks reasonable before progressing. This will verify that
//! various programs like git and python exist, along with ensuring that all C
//! compilers for cross-compiling are found.
//!
//! In theory if we get past this phase it's a bug if a build fails, but in
//! practice that's likely not true!

use std::collections::HashMap;
use std::env;
use std::ffi::{OsString, OsStr};
use std::fs::{self, File};
use std::io::Read;
use std::path::PathBuf;
use std::process::Command;

use build_helper::output;

use Build;

struct Finder {
    cache: HashMap<OsString, Option<PathBuf>>,
    path: OsString,
}

impl Finder {
    fn new() -> Self {
        Self {
            cache: HashMap::new(),
            path: env::var_os("PATH").unwrap_or_default()
        }
    }

    fn maybe_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> Option<PathBuf> {
        let cmd: OsString = cmd.as_ref().into();
        let path = self.path.clone();
        self.cache.entry(cmd.clone()).or_insert_with(|| {
            for path in env::split_paths(&path) {
                let target = path.join(&cmd);
                let mut cmd_alt = cmd.clone();
                cmd_alt.push(".exe");
                if target.is_file() || // some/path/git
                target.with_extension("exe").exists() || // some/path/git.exe
                target.join(&cmd_alt).exists() { // some/path/git/git.exe
                    return Some(target);
                }
            }
            None
        }).clone()
    }

    fn must_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> PathBuf {
        self.maybe_have(&cmd).unwrap_or_else(|| {
            panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref());
        })
    }
}

pub fn check(build: &mut Build) {
    let path = env::var_os("PATH").unwrap_or_default();
    // On Windows, quotes are invalid characters for filename paths, and if
    // one is present as part of the PATH then that can lead to the system
    // being unable to identify the files properly. See
    // https://github.com/rust-lang/rust/issues/34959 for more details.
    if cfg!(windows) && path.to_string_lossy().contains("\"") {
        panic!("PATH contains invalid character '\"'");
    }

    let mut cmd_finder = Finder::new();
    // If we've got a git directory we're gonna need git to update
    // submodules and learn about various other aspects.
    if build.rust_info.is_git() {
        cmd_finder.must_have("git");
    }

    // We need cmake, but only if we're actually building LLVM or sanitizers.
    let building_llvm = build.hosts.iter()
        .filter_map(|host| build.config.target_config.get(host))
        .any(|config| config.llvm_config.is_none());
    if building_llvm || build.config.sanitizers {
        cmd_finder.must_have("cmake");
    }

    // Ninja is currently only used for LLVM itself.
    if building_llvm {
        if build.config.ninja {
            // Some Linux distros rename `ninja` to `ninja-build`.
            // CMake can work with either binary name.
            if cmd_finder.maybe_have("ninja-build").is_none() {
                cmd_finder.must_have("ninja");
            }
        }

        // If ninja isn't enabled but we're building for MSVC then we try
        // doubly hard to enable it. It was realized in #43767 that the msbuild
        // CMake generator for MSVC doesn't respect configuration options like
        // disabling LLVM assertions, which can often be quite important!
        //
        // In these cases we automatically enable Ninja if we find it in the
        // environment.
        if !build.config.ninja && build.config.build.contains("msvc") {
            if cmd_finder.maybe_have("ninja").is_some() {
                build.config.ninja = true;
            }
        }
    }

    build.config.python = build.config.python.take().map(|p| cmd_finder.must_have(p))
        .or_else(|| env::var_os("BOOTSTRAP_PYTHON").map(PathBuf::from)) // set by bootstrap.py
        .or_else(|| cmd_finder.maybe_have("python2.7"))
        .or_else(|| cmd_finder.maybe_have("python2"))
        .or_else(|| Some(cmd_finder.must_have("python")));

    build.config.nodejs = build.config.nodejs.take().map(|p| cmd_finder.must_have(p))
        .or_else(|| cmd_finder.maybe_have("node"))
        .or_else(|| cmd_finder.maybe_have("nodejs"));

    build.config.gdb = build.config.gdb.take().map(|p| cmd_finder.must_have(p))
        .or_else(|| cmd_finder.maybe_have("gdb"));

    // We're gonna build some custom C code here and there, host triples
    // also build some C++ shims for LLVM so we need a C++ compiler.
    for target in &build.targets {
        // On emscripten we don't actually need the C compiler to just
        // build the target artifacts, only for testing. For the sake
        // of easier bot configuration, just skip detection.
        if target.contains("emscripten") {
            continue;
        }

        cmd_finder.must_have(build.cc(*target));
        if let Some(ar) = build.ar(*target) {
            cmd_finder.must_have(ar);
        }
    }

    for host in &build.hosts {
        cmd_finder.must_have(build.cxx(*host).unwrap());

        // The msvc hosts don't use jemalloc, turn it off globally to
        // avoid packaging the dummy liballoc_jemalloc on that platform.
        if host.contains("msvc") {
            build.config.use_jemalloc = false;
        }
    }

    // Externally configured LLVM requires FileCheck to exist
    let filecheck = build.llvm_filecheck(build.build);
    if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests {
        panic!("FileCheck executable {:?} does not exist", filecheck);
    }

    for target in &build.targets {
        // Can't compile for iOS unless we're on macOS
        if target.contains("apple-ios") &&
           !build.build.contains("apple-darwin") {
            panic!("the iOS target is only supported on macOS");
        }

        // Make sure musl-root is valid
        if target.contains("musl") && !target.contains("mips") {
            // If this is a native target (host is also musl) and no musl-root is given,
            // fall back to the system toolchain in /usr before giving up
            if build.musl_root(*target).is_none() && build.config.build == *target {
                let target = build.config.target_config.entry(target.clone())
                                 .or_insert(Default::default());
                target.musl_root = Some("/usr".into());
            }
            match build.musl_root(*target) {
                Some(root) => {
                    if fs::metadata(root.join("lib/libc.a")).is_err() {
                        panic!("couldn't find libc.a in musl dir: {}",
                               root.join("lib").display());
                    }
                    if fs::metadata(root.join("lib/libunwind.a")).is_err() {
                        panic!("couldn't find libunwind.a in musl dir: {}",
                               root.join("lib").display());
                    }
                }
                None => {
                    panic!("when targeting MUSL either the rust.musl-root \
                            option or the target.$TARGET.musl-root option must \
                            be specified in config.toml")
                }
            }
        }

        if target.contains("msvc") {
            // There are three builds of cmake on windows: MSVC, MinGW, and
            // Cygwin. The Cygwin build does not have generators for Visual
            // Studio, so detect that here and error.
            let out = output(Command::new("cmake").arg("--help"));
            if !out.contains("Visual Studio") {
                panic!("
cmake does not support Visual Studio generators.

This is likely due to it being an msys/cygwin build of cmake,
rather than the required windows version, built using MinGW
or Visual Studio.

If you are building under msys2 try installing the mingw-w64-x86_64-cmake
package instead of cmake:

$ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
");
            }
        }
    }

    let run = |cmd: &mut Command| {
        cmd.output().map(|output| {
            String::from_utf8_lossy(&output.stdout)
                   .lines().next().unwrap_or_else(|| {
                       panic!("{:?} failed {:?}", cmd, output)
                   }).to_string()
        })
    };
    build.lldb_version = run(Command::new("lldb").arg("--version")).ok();
    if build.lldb_version.is_some() {
        build.lldb_python_dir = run(Command::new("lldb").arg("-P")).ok();
    }

    if let Some(ref s) = build.config.ccache {
        cmd_finder.must_have(s);
    }

    if build.config.channel == "stable" {
        let mut stage0 = String::new();
        t!(t!(File::open(build.src.join("src/stage0.txt")))
            .read_to_string(&mut stage0));
        if stage0.contains("\ndev:") {
            panic!("bootstrapping from a dev compiler in a stable release, but \
                    should only be bootstrapping from a released compiler!");
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::prelude::*;

use atty;
use termcolor::Color::{Cyan, Green, Red, Yellow};
use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor};

use crate::util::errors::CargoResult;

/// The requested verbosity of output.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Verbosity {
    Verbose,
    Normal,
    Quiet,
}

/// An abstraction around a `Write`able object that remembers preferences for output verbosity and
/// color.
pub struct Shell {
    /// the `Write`able object, either with or without color support (represented by different enum
    /// variants)
    err: ShellOut,
    /// How verbose messages should be
    verbosity: Verbosity,
    /// Flag that indicates the current line needs to be cleared before
    /// printing. Used when a progress bar is currently displayed.
    needs_clear: bool,
}

impl fmt::Debug for Shell {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.err {
            ShellOut::Write(_) => f
                .debug_struct("Shell")
                .field("verbosity", &self.verbosity)
                .finish(),
            ShellOut::Stream { color_choice, .. } => f
                .debug_struct("Shell")
                .field("verbosity", &self.verbosity)
                .field("color_choice", &color_choice)
                .finish(),
        }
    }
}

/// A `Write`able object, either with or without color support
enum ShellOut {
    /// A plain write object without color support
    Write(Box<dyn Write>),
    /// Color-enabled stdio, with information on whether color should be used
    Stream {
        stream: StandardStream,
        tty: bool,
        color_choice: ColorChoice,
    },
}

/// Whether messages should use color output
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ColorChoice {
    /// Force color output
    Always,
    /// Force disable color output
    Never,
    /// Intelligently guess whether to use color output
    CargoAuto,
}

impl Shell {
    /// Creates a new shell (color choice and verbosity), defaulting to 'auto' color and verbose
    /// output.
    pub fn new() -> Shell {
        Shell {
            err: ShellOut::Stream {
                stream: StandardStream::stderr(ColorChoice::CargoAuto.to_termcolor_color_choice()),
                color_choice: ColorChoice::CargoAuto,
                tty: atty::is(atty::Stream::Stderr),
            },
            verbosity: Verbosity::Verbose,
            needs_clear: false,
        }
    }

    /// Creates a shell from a plain writable object, with no color, and max verbosity.
    pub fn from_write(out: Box<dyn Write>) -> Shell {
        Shell {
            err: ShellOut::Write(out),
            verbosity: Verbosity::Verbose,
            needs_clear: false,
        }
    }

    /// Prints a message, where the status will have `color` color, and can be justified. The
    /// messages follows without color.
    fn print(
        &mut self,
        status: &dyn fmt::Display,
        message: Option<&dyn fmt::Display>,
        color: Color,
        justified: bool,
    ) -> CargoResult<()> {
        match self.verbosity {
            Verbosity::Quiet => Ok(()),
            _ => {
                if self.needs_clear {
                    self.err_erase_line();
                }
                self.err.print(status, message, color, justified)
            }
        }
    }

    /// Sets whether the next print should clear the current line.
    pub fn set_needs_clear(&mut self, needs_clear: bool) {
        self.needs_clear = needs_clear;
    }

    /// Returns `true` if the `needs_clear` flag is unset.
    pub fn is_cleared(&self) -> bool {
        !self.needs_clear
    }

    /// Returns the width of the terminal in spaces, if any.
    pub fn err_width(&self) -> Option<usize> {
        match self.err {
            ShellOut::Stream { tty: true, .. } => imp::stderr_width(),
            _ => None,
        }
    }

    /// Returns `true` if stderr is a tty.
    pub fn is_err_tty(&self) -> bool {
        match self.err {
            ShellOut::Stream { tty, .. } => tty,
            _ => false,
        }
    }

    /// Gets a reference to the underlying writer.
    pub fn err(&mut self) -> &mut dyn Write {
        if self.needs_clear {
            self.err_erase_line();
        }
        self.err.as_write()
    }

    /// Erase from cursor to end of line.
    pub fn err_erase_line(&mut self) {
        if let ShellOut::Stream { tty: true, .. } = self.err {
            imp::err_erase_line(self);
            self.needs_clear = false;
        }
    }

    /// Shortcut to right-align and color green a status message.
    pub fn status<T, U>(&mut self, status: T, message: U) -> CargoResult<()>
    where
        T: fmt::Display,
        U: fmt::Display,
    {
        self.print(&status, Some(&message), Green, true)
    }

    pub fn status_header<T>(&mut self, status: T) -> CargoResult<()>
    where
        T: fmt::Display,
    {
        self.print(&status, None, Cyan, true)
    }

    /// Shortcut to right-align a status message.
    pub fn status_with_color<T, U>(
        &mut self,
        status: T,
        message: U,
        color: Color,
    ) -> CargoResult<()>
    where
        T: fmt::Display,
        U: fmt::Display,
    {
        self.print(&status, Some(&message), color, true)
    }

    /// Runs the callback only if we are in verbose mode.
    pub fn verbose<F>(&mut self, mut callback: F) -> CargoResult<()>
    where
        F: FnMut(&mut Shell) -> CargoResult<()>,
    {
        match self.verbosity {
            Verbosity::Verbose => callback(self),
            _ => Ok(()),
        }
    }

    /// Runs the callback if we are not in verbose mode.
    pub fn concise<F>(&mut self, mut callback: F) -> CargoResult<()>
    where
        F: FnMut(&mut Shell) -> CargoResult<()>,
    {
        match self.verbosity {
            Verbosity::Verbose => Ok(()),
            _ => callback(self),
        }
    }

    /// Prints a red 'error' message.
    pub fn error<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
        if self.needs_clear {
            self.err_erase_line();
        }
        self.err.print(&"error:", Some(&message), Red, false)
    }

    /// Prints an amber 'warning' message.
    pub fn warn<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
        match self.verbosity {
            Verbosity::Quiet => Ok(()),
            _ => self.print(&"warning:", Some(&message), Yellow, false),
        }
    }

    /// Updates the verbosity of the shell.
    pub fn set_verbosity(&mut self, verbosity: Verbosity) {
        self.verbosity = verbosity;
    }

    /// Gets the verbosity of the shell.
    pub fn verbosity(&self) -> Verbosity {
        self.verbosity
    }

    /// Updates the color choice (always, never, or auto) from a string..
    pub fn set_color_choice(&mut self, color: Option<&str>) -> CargoResult<()> {
        if let ShellOut::Stream {
            ref mut stream,
            ref mut color_choice,
            ..
        } = self.err
        {
            let cfg = match color {
                Some("always") => ColorChoice::Always,
                Some("never") => ColorChoice::Never,

                Some("auto") | None => ColorChoice::CargoAuto,

                Some(arg) => failure::bail!(
                    "argument for --color must be auto, always, or \
                     never, but found `{}`",
                    arg
                ),
            };
            *color_choice = cfg;
            *stream = StandardStream::stderr(cfg.to_termcolor_color_choice());
        }
        Ok(())
    }

    /// Gets the current color choice.
    ///
    /// If we are not using a color stream, this will always return `Never`, even if the color
    /// choice has been set to something else.
    pub fn color_choice(&self) -> ColorChoice {
        match self.err {
            ShellOut::Stream { color_choice, .. } => color_choice,
            ShellOut::Write(_) => ColorChoice::Never,
        }
    }

    /// Whether the shell supports color.
    pub fn supports_color(&self) -> bool {
        match &self.err {
            ShellOut::Write(_) => false,
            ShellOut::Stream { stream, .. } => stream.supports_color(),
        }
    }

    /// Prints a message and translates ANSI escape code into console colors.
    pub fn print_ansi(&mut self, message: &[u8]) -> CargoResult<()> {
        if self.needs_clear {
            self.err_erase_line();
        }
        #[cfg(windows)]
        {
            if let ShellOut::Stream { stream, .. } = &mut self.err {
                ::fwdansi::write_ansi(stream, message)?;
                return Ok(());
            }
        }
        self.err().write_all(message)?;
        Ok(())
    }
}

impl Default for Shell {
    fn default() -> Self {
        Self::new()
    }
}

impl ShellOut {
    /// Prints out a message with a status. The status comes first, and is bold plus the given
    /// color. The status can be justified, in which case the max width that will right align is
    /// 12 chars.
    fn print(
        &mut self,
        status: &dyn fmt::Display,
        message: Option<&dyn fmt::Display>,
        color: Color,
        justified: bool,
    ) -> CargoResult<()> {
        match *self {
            ShellOut::Stream { ref mut stream, .. } => {
                stream.reset()?;
                stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
                if justified {
                    write!(stream, "{:>12}", status)?;
                } else {
                    write!(stream, "{}", status)?;
                }
                stream.reset()?;
                match message {
                    Some(message) => writeln!(stream, " {}", message)?,
                    None => write!(stream, " ")?,
                }
            }
            ShellOut::Write(ref mut w) => {
                if justified {
                    write!(w, "{:>12}", status)?;
                } else {
                    write!(w, "{}", status)?;
                }
                match message {
                    Some(message) => writeln!(w, " {}", message)?,
                    None => write!(w, " ")?,
                }
            }
        }
        Ok(())
    }

    /// Gets this object as a `io::Write`.
    fn as_write(&mut self) -> &mut dyn Write {
        match *self {
            ShellOut::Stream { ref mut stream, .. } => stream,
            ShellOut::Write(ref mut w) => w,
        }
    }
}

impl ColorChoice {
    /// Converts our color choice to termcolor's version.
    fn to_termcolor_color_choice(self) -> termcolor::ColorChoice {
        match self {
            ColorChoice::Always => termcolor::ColorChoice::Always,
            ColorChoice::Never => termcolor::ColorChoice::Never,
            ColorChoice::CargoAuto => {
                if atty::is(atty::Stream::Stderr) {
                    termcolor::ColorChoice::Auto
                } else {
                    termcolor::ColorChoice::Never
                }
            }
        }
    }
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))]
mod imp {
    use std::mem;

    use libc;

    use super::Shell;

    pub fn stderr_width() -> Option<usize> {
        unsafe {
            let mut winsize: libc::winsize = mem::zeroed();
            if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ.into(), &mut winsize) < 0 {
                return None;
            }
            if winsize.ws_col > 0 {
                Some(winsize.ws_col as usize)
            } else {
                None
            }
        }
    }

    pub fn err_erase_line(shell: &mut Shell) {
        // This is the "EL - Erase in Line" sequence. It clears from the cursor
        // to the end of line.
        // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
        let _ = shell.err.as_write().write_all(b"\x1B[K");
    }
}

#[cfg(all(
    unix,
    not(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))
))]
mod imp {
    pub(super) use super::default_err_erase_line as err_erase_line;

    pub fn stderr_width() -> Option<usize> {
        None
    }
}

#[cfg(windows)]
mod imp {
    use std::{cmp, mem, ptr};
    use winapi::um::fileapi::*;
    use winapi::um::handleapi::*;
    use winapi::um::processenv::*;
    use winapi::um::winbase::*;
    use winapi::um::wincon::*;
    use winapi::um::winnt::*;

    pub(super) use super::default_err_erase_line as err_erase_line;

    pub fn stderr_width() -> Option<usize> {
        unsafe {
            let stdout = GetStdHandle(STD_ERROR_HANDLE);
            let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
            if GetConsoleScreenBufferInfo(stdout, &mut csbi) != 0 {
                return Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize);
            }

            // On mintty/msys/cygwin based terminals, the above fails with
            // INVALID_HANDLE_VALUE. Use an alternate method which works
            // in that case as well.
            let h = CreateFileA(
                "CONOUT$\0".as_ptr() as *const CHAR,
                GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                ptr::null_mut(),
                OPEN_EXISTING,
                0,
                ptr::null_mut(),
            );
            if h == INVALID_HANDLE_VALUE {
                return None;
            }

            let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
            let rc = GetConsoleScreenBufferInfo(h, &mut csbi);
            CloseHandle(h);
            if rc != 0 {
                let width = (csbi.srWindow.Right - csbi.srWindow.Left) as usize;
                // Unfortunately cygwin/mintty does not set the size of the
                // backing console to match the actual window size. This
                // always reports a size of 80 or 120 (not sure what
                // determines that). Use a conservative max of 60 which should
                // work in most circumstances. ConEmu does some magic to
                // resize the console correctly, but there's no reasonable way
                // to detect which kind of terminal we are running in, or if
                // GetConsoleScreenBufferInfo returns accurate information.
                return Some(cmp::min(60, width));
            }
            None
        }
    }
}

#[cfg(any(
    all(
        unix,
        not(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))
    ),
    windows,
))]
fn default_err_erase_line(shell: &mut Shell) {
    if let Some(max_width) = imp::stderr_width() {
        let blank = " ".repeat(max_width);
        drop(write!(shell.err.as_write(), "{}\r", blank));
    }
}
<commit_msg>Add comment about TIOCGWINSZ .into().<commit_after>use std::fmt;
use std::io::prelude::*;

use atty;
use termcolor::Color::{Cyan, Green, Red, Yellow};
use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor};

use crate::util::errors::CargoResult;

/// The requested verbosity of output.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Verbosity {
    Verbose,
    Normal,
    Quiet,
}

/// An abstraction around a `Write`able object that remembers preferences for output verbosity and
/// color.
pub struct Shell {
    /// the `Write`able object, either with or without color support (represented by different enum
    /// variants)
    err: ShellOut,
    /// How verbose messages should be
    verbosity: Verbosity,
    /// Flag that indicates the current line needs to be cleared before
    /// printing. Used when a progress bar is currently displayed.
    needs_clear: bool,
}

impl fmt::Debug for Shell {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.err {
            ShellOut::Write(_) => f
                .debug_struct("Shell")
                .field("verbosity", &self.verbosity)
                .finish(),
            ShellOut::Stream { color_choice, .. } => f
                .debug_struct("Shell")
                .field("verbosity", &self.verbosity)
                .field("color_choice", &color_choice)
                .finish(),
        }
    }
}

/// A `Write`able object, either with or without color support
enum ShellOut {
    /// A plain write object without color support
    Write(Box<dyn Write>),
    /// Color-enabled stdio, with information on whether color should be used
    Stream {
        stream: StandardStream,
        tty: bool,
        color_choice: ColorChoice,
    },
}

/// Whether messages should use color output
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ColorChoice {
    /// Force color output
    Always,
    /// Force disable color output
    Never,
    /// Intelligently guess whether to use color output
    CargoAuto,
}

impl Shell {
    /// Creates a new shell (color choice and verbosity), defaulting to 'auto' color and verbose
    /// output.
    pub fn new() -> Shell {
        Shell {
            err: ShellOut::Stream {
                stream: StandardStream::stderr(ColorChoice::CargoAuto.to_termcolor_color_choice()),
                color_choice: ColorChoice::CargoAuto,
                tty: atty::is(atty::Stream::Stderr),
            },
            verbosity: Verbosity::Verbose,
            needs_clear: false,
        }
    }

    /// Creates a shell from a plain writable object, with no color, and max verbosity.
    pub fn from_write(out: Box<dyn Write>) -> Shell {
        Shell {
            err: ShellOut::Write(out),
            verbosity: Verbosity::Verbose,
            needs_clear: false,
        }
    }

    /// Prints a message, where the status will have `color` color, and can be justified. The
    /// messages follows without color.
    fn print(
        &mut self,
        status: &dyn fmt::Display,
        message: Option<&dyn fmt::Display>,
        color: Color,
        justified: bool,
    ) -> CargoResult<()> {
        match self.verbosity {
            Verbosity::Quiet => Ok(()),
            _ => {
                if self.needs_clear {
                    self.err_erase_line();
                }
                self.err.print(status, message, color, justified)
            }
        }
    }

    /// Sets whether the next print should clear the current line.
    pub fn set_needs_clear(&mut self, needs_clear: bool) {
        self.needs_clear = needs_clear;
    }

    /// Returns `true` if the `needs_clear` flag is unset.
    pub fn is_cleared(&self) -> bool {
        !self.needs_clear
    }

    /// Returns the width of the terminal in spaces, if any.
    pub fn err_width(&self) -> Option<usize> {
        match self.err {
            ShellOut::Stream { tty: true, .. } => imp::stderr_width(),
            _ => None,
        }
    }

    /// Returns `true` if stderr is a tty.
    pub fn is_err_tty(&self) -> bool {
        match self.err {
            ShellOut::Stream { tty, .. } => tty,
            _ => false,
        }
    }

    /// Gets a reference to the underlying writer.
    pub fn err(&mut self) -> &mut dyn Write {
        if self.needs_clear {
            self.err_erase_line();
        }
        self.err.as_write()
    }

    /// Erase from cursor to end of line.
    pub fn err_erase_line(&mut self) {
        if let ShellOut::Stream { tty: true, .. } = self.err {
            imp::err_erase_line(self);
            self.needs_clear = false;
        }
    }

    /// Shortcut to right-align and color green a status message.
    pub fn status<T, U>(&mut self, status: T, message: U) -> CargoResult<()>
    where
        T: fmt::Display,
        U: fmt::Display,
    {
        self.print(&status, Some(&message), Green, true)
    }

    pub fn status_header<T>(&mut self, status: T) -> CargoResult<()>
    where
        T: fmt::Display,
    {
        self.print(&status, None, Cyan, true)
    }

    /// Shortcut to right-align a status message.
    pub fn status_with_color<T, U>(
        &mut self,
        status: T,
        message: U,
        color: Color,
    ) -> CargoResult<()>
    where
        T: fmt::Display,
        U: fmt::Display,
    {
        self.print(&status, Some(&message), color, true)
    }

    /// Runs the callback only if we are in verbose mode.
    pub fn verbose<F>(&mut self, mut callback: F) -> CargoResult<()>
    where
        F: FnMut(&mut Shell) -> CargoResult<()>,
    {
        match self.verbosity {
            Verbosity::Verbose => callback(self),
            _ => Ok(()),
        }
    }

    /// Runs the callback if we are not in verbose mode.
    pub fn concise<F>(&mut self, mut callback: F) -> CargoResult<()>
    where
        F: FnMut(&mut Shell) -> CargoResult<()>,
    {
        match self.verbosity {
            Verbosity::Verbose => Ok(()),
            _ => callback(self),
        }
    }

    /// Prints a red 'error' message.
    pub fn error<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
        if self.needs_clear {
            self.err_erase_line();
        }
        self.err.print(&"error:", Some(&message), Red, false)
    }

    /// Prints an amber 'warning' message.
    pub fn warn<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
        match self.verbosity {
            Verbosity::Quiet => Ok(()),
            _ => self.print(&"warning:", Some(&message), Yellow, false),
        }
    }

    /// Updates the verbosity of the shell.
    pub fn set_verbosity(&mut self, verbosity: Verbosity) {
        self.verbosity = verbosity;
    }

    /// Gets the verbosity of the shell.
    pub fn verbosity(&self) -> Verbosity {
        self.verbosity
    }

    /// Updates the color choice (always, never, or auto) from a string..
    pub fn set_color_choice(&mut self, color: Option<&str>) -> CargoResult<()> {
        if let ShellOut::Stream {
            ref mut stream,
            ref mut color_choice,
            ..
        } = self.err
        {
            let cfg = match color {
                Some("always") => ColorChoice::Always,
                Some("never") => ColorChoice::Never,

                Some("auto") | None => ColorChoice::CargoAuto,

                Some(arg) => failure::bail!(
                    "argument for --color must be auto, always, or \
                     never, but found `{}`",
                    arg
                ),
            };
            *color_choice = cfg;
            *stream = StandardStream::stderr(cfg.to_termcolor_color_choice());
        }
        Ok(())
    }

    /// Gets the current color choice.
    ///
    /// If we are not using a color stream, this will always return `Never`, even if the color
    /// choice has been set to something else.
    pub fn color_choice(&self) -> ColorChoice {
        match self.err {
            ShellOut::Stream { color_choice, .. } => color_choice,
            ShellOut::Write(_) => ColorChoice::Never,
        }
    }

    /// Whether the shell supports color.
    pub fn supports_color(&self) -> bool {
        match &self.err {
            ShellOut::Write(_) => false,
            ShellOut::Stream { stream, .. } => stream.supports_color(),
        }
    }

    /// Prints a message and translates ANSI escape code into console colors.
    pub fn print_ansi(&mut self, message: &[u8]) -> CargoResult<()> {
        if self.needs_clear {
            self.err_erase_line();
        }
        #[cfg(windows)]
        {
            if let ShellOut::Stream { stream, .. } = &mut self.err {
                ::fwdansi::write_ansi(stream, message)?;
                return Ok(());
            }
        }
        self.err().write_all(message)?;
        Ok(())
    }
}

impl Default for Shell {
    fn default() -> Self {
        Self::new()
    }
}

impl ShellOut {
    /// Prints out a message with a status. The status comes first, and is bold plus the given
    /// color. The status can be justified, in which case the max width that will right align is
    /// 12 chars.
    fn print(
        &mut self,
        status: &dyn fmt::Display,
        message: Option<&dyn fmt::Display>,
        color: Color,
        justified: bool,
    ) -> CargoResult<()> {
        match *self {
            ShellOut::Stream { ref mut stream, .. } => {
                stream.reset()?;
                stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
                if justified {
                    write!(stream, "{:>12}", status)?;
                } else {
                    write!(stream, "{}", status)?;
                }
                stream.reset()?;
                match message {
                    Some(message) => writeln!(stream, " {}", message)?,
                    None => write!(stream, " ")?,
                }
            }
            ShellOut::Write(ref mut w) => {
                if justified {
                    write!(w, "{:>12}", status)?;
                } else {
                    write!(w, "{}", status)?;
                }
                match message {
                    Some(message) => writeln!(w, " {}", message)?,
                    None => write!(w, " ")?,
                }
            }
        }
        Ok(())
    }

    /// Gets this object as a `io::Write`.
    fn as_write(&mut self) -> &mut dyn Write {
        match *self {
            ShellOut::Stream { ref mut stream, .. } => stream,
            ShellOut::Write(ref mut w) => w,
        }
    }
}

impl ColorChoice {
    /// Converts our color choice to termcolor's version.
    fn to_termcolor_color_choice(self) -> termcolor::ColorChoice {
        match self {
            ColorChoice::Always => termcolor::ColorChoice::Always,
            ColorChoice::Never => termcolor::ColorChoice::Never,
            ColorChoice::CargoAuto => {
                if atty::is(atty::Stream::Stderr) {
                    termcolor::ColorChoice::Auto
                } else {
                    termcolor::ColorChoice::Never
                }
            }
        }
    }
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))]
mod imp {
    use std::mem;

    use libc;

    use super::Shell;

    pub fn stderr_width() -> Option<usize> {
        unsafe {
            let mut winsize: libc::winsize = mem::zeroed();
            // The .into() here is needed for FreeBSD which defines TIOCGWINSZ
            // as c_uint but ioctl wants c_ulong.
            if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ.into(), &mut winsize) < 0 {
                return None;
            }
            if winsize.ws_col > 0 {
                Some(winsize.ws_col as usize)
            } else {
                None
            }
        }
    }

    pub fn err_erase_line(shell: &mut Shell) {
        // This is the "EL - Erase in Line" sequence. It clears from the cursor
        // to the end of line.
        // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
        let _ = shell.err.as_write().write_all(b"\x1B[K");
    }
}

#[cfg(all(
    unix,
    not(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))
))]
mod imp {
    pub(super) use super::default_err_erase_line as err_erase_line;

    pub fn stderr_width() -> Option<usize> {
        None
    }
}

#[cfg(windows)]
mod imp {
    use std::{cmp, mem, ptr};
    use winapi::um::fileapi::*;
    use winapi::um::handleapi::*;
    use winapi::um::processenv::*;
    use winapi::um::winbase::*;
    use winapi::um::wincon::*;
    use winapi::um::winnt::*;

    pub(super) use super::default_err_erase_line as err_erase_line;

    pub fn stderr_width() -> Option<usize> {
        unsafe {
            let stdout = GetStdHandle(STD_ERROR_HANDLE);
            let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
            if GetConsoleScreenBufferInfo(stdout, &mut csbi) != 0 {
                return Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize);
            }

            // On mintty/msys/cygwin based terminals, the above fails with
            // INVALID_HANDLE_VALUE. Use an alternate method which works
            // in that case as well.
            let h = CreateFileA(
                "CONOUT$\0".as_ptr() as *const CHAR,
                GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                ptr::null_mut(),
                OPEN_EXISTING,
                0,
                ptr::null_mut(),
            );
            if h == INVALID_HANDLE_VALUE {
                return None;
            }

            let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
            let rc = GetConsoleScreenBufferInfo(h, &mut csbi);
            CloseHandle(h);
            if rc != 0 {
                let width = (csbi.srWindow.Right - csbi.srWindow.Left) as usize;
                // Unfortunately cygwin/mintty does not set the size of the
                // backing console to match the actual window size. This
                // always reports a size of 80 or 120 (not sure what
                // determines that). Use a conservative max of 60 which should
                // work in most circumstances. ConEmu does some magic to
                // resize the console correctly, but there's no reasonable way
                // to detect which kind of terminal we are running in, or if
                // GetConsoleScreenBufferInfo returns accurate information.
                return Some(cmp::min(60, width));
            }
            None
        }
    }
}

#[cfg(any(
    all(
        unix,
        not(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))
    ),
    windows,
))]
fn default_err_erase_line(shell: &mut Shell) {
    if let Some(max_width) = imp::stderr_width() {
        let blank = " ".repeat(max_width);
        drop(write!(shell.err.as_write(), "{}\r", blank));
    }
}
<|endoftext|>


<commit_before><commit_msg>impl Clone for Event<commit_after><|endoftext|>


<commit_before>//! Defines various Monte Carlo sampling functions for sampling
//! points/directions on objects and computing the corresponding pdfs

use std::f32;
use std::num::Float;

use linalg::Vector;

/// Sample a hemisphere using a cosine distribution to produce cosine weighted samples
/// `samples` should be two random samples in range [0, 1)
/// directions returned will be in the hemisphere around (0, 0, 1)
pub fn cos_sample_hemisphere(u: &[f32]) -> Vector {
	//We use Malley's method here, generate samples on a disk then project
	//them up to the hemisphere
	let d = concentric_sample_disk(u);
	return Vector::new(d[0], d[1], Float::sqrt(Float::max(0.0, 1.0 - d[0] * d[0] - d[1] * d[1])));
}
/// Compute the PDF of the cosine weighted hemisphere sampling
pub fn cos_hemisphere_pdf(cos_theta: f32) -> f32 { cos_theta * f32::consts::FRAC_1_PI }
/// Compute concentric sample positions on a unit disk mapping input from range [0, 1)
/// to sample positions on a disk
/// `samples` should be two random samples in range [0, 1)
/// See: [Shirley and Chiu, A Low Distortion Map Between Disk and Square](https://mediatech.aalto.fi/~jaakko/T111-5310/K2013/JGT-97.pdf)
pub fn concentric_sample_disk(u: &[f32]) -> [f32; 2] {
	let mut s = [2.0 * u[0] - 1.0, 2.0 * u[1] - 1.0];
	let mut radius = 0f32;
    let mut theta = 0f32;
	if s[0] == 0.0 && s[1] == 0.0 {
		return s;
	}
	if s[0] >= -s[1] {
		if s[0] > s[1] {
			radius = s[0];
            if s[1] > 0.0 {
                theta = s[1] / s[0];
            } else {
                theta = 8.0 + s[1] / s[0];
            }
		}
		else {
			radius = s[1];
			theta = 2.0 - s[0] / s[1];
		}
	}
	else {
		if s[0] <= s[1] {
			radius = -s[0];
			theta = 4.0 + s[1] / s[0];
		}
		else {
            radius = -s[1];
			if s[1] != 0.0 {
				theta = 6.0 - s[0] / s[1];
			}
			else {
				theta = 0.0;
			}
		}
	}
	theta *= f32::consts::FRAC_PI_4;
    s[0] = radius * Float::cos(theta);
    s[1] = radius * Float::sin(theta);
    s
}
/// Power heuristic for multiple importance sampling for two functions being sampled, f & g
/// where beta is hard-coded to be two following PBR & Veach
/// - `n_f`, `n_g` number of samples taken of each
/// - `pdf_f`, `pdf_g` pdf of each function
pub fn power_heuristic(n_f: f32, pdf_f: f32, n_g: f32, pdf_g: f32) -> f32 {
	let f = n_f * pdf_f;
	let g = n_g * pdf_g;
	(f * f) / (f * f + g * g)
}

<commit_msg>Fix indentation<commit_after>//! Defines various Monte Carlo sampling functions for sampling
//! points/directions on objects and computing the corresponding pdfs

use std::f32;
use std::num::Float;

use linalg::Vector;

/// Sample a hemisphere using a cosine distribution to produce cosine weighted samples
/// `samples` should be two random samples in range [0, 1)
/// directions returned will be in the hemisphere around (0, 0, 1)
pub fn cos_sample_hemisphere(u: &[f32]) -> Vector {
    //We use Malley's method here, generate samples on a disk then project
    //them up to the hemisphere
    let d = concentric_sample_disk(u);
    return Vector::new(d[0], d[1], Float::sqrt(Float::max(0.0, 1.0 - d[0] * d[0] - d[1] * d[1])));
}
/// Compute the PDF of the cosine weighted hemisphere sampling
pub fn cos_hemisphere_pdf(cos_theta: f32) -> f32 { cos_theta * f32::consts::FRAC_1_PI }
/// Compute concentric sample positions on a unit disk mapping input from range [0, 1)
/// to sample positions on a disk
/// `samples` should be two random samples in range [0, 1)
/// See: [Shirley and Chiu, A Low Distortion Map Between Disk and Square](https://mediatech.aalto.fi/~jaakko/T111-5310/K2013/JGT-97.pdf)
pub fn concentric_sample_disk(u: &[f32]) -> [f32; 2] {
    let mut s = [2.0 * u[0] - 1.0, 2.0 * u[1] - 1.0];
    let mut radius = 0f32;
    let mut theta = 0f32;
    if s[0] == 0.0 && s[1] == 0.0 {
        return s;
    }
    if s[0] >= -s[1] {
        if s[0] > s[1] {
            radius = s[0];
            if s[1] > 0.0 {
                theta = s[1] / s[0];
            } else {
                theta = 8.0 + s[1] / s[0];
            }
        }
        else {
            radius = s[1];
            theta = 2.0 - s[0] / s[1];
        }
    }
    else {
        if s[0] <= s[1] {
            radius = -s[0];
            theta = 4.0 + s[1] / s[0];
        }
        else {
            radius = -s[1];
            if s[1] != 0.0 {
                theta = 6.0 - s[0] / s[1];
            }
            else {
                theta = 0.0;
            }
        }
    }
    theta *= f32::consts::FRAC_PI_4;
    s[0] = radius * Float::cos(theta);
    s[1] = radius * Float::sin(theta);
    s
}
/// Power heuristic for multiple importance sampling for two functions being sampled, f & g
/// where beta is hard-coded to be two following PBR & Veach
/// - `n_f`, `n_g` number of samples taken of each
/// - `pdf_f`, `pdf_g` pdf of each function
pub fn power_heuristic(n_f: f32, pdf_f: f32, n_g: f32, pdf_g: f32) -> f32 {
    let f = n_f * pdf_f;
    let g = n_g * pdf_g;
    (f * f) / (f * f + g * g)
}

<|endoftext|>


<commit_before>use random::Random;
use types::Reward;

/// Model struct for an agent which can interact with an environment.
pub struct Agent {
  age: i32,
  total_reward: Reward,
  num_actions: i16,
  random: Box<Random>,
}

impl Agent {
  pub fn new(num_actions: i16, random: Box<Random>) -> Agent {
    Agent {
      age: 0,
      total_reward: Reward(0.0),
      num_actions: num_actions,
      random: random,
    }
  }

  pub fn age(&self) -> i32 {
    self.age
  }

  pub fn total_reward(&self) -> Reward {
    self.total_reward
  }

  /// Returns an action in [0, num_actions - 1].
  pub fn act(&mut self) -> i16 {
    // Picks a random legal action for now.
    return self.random.next_modulo(self.num_actions as i64) as i16;
  }

  pub fn update(&mut self, observation: i16, reward: Reward) {
    self.age = self.age + 1;
    self.total_reward = self.total_reward + reward;
  }

  pub fn average_reward(&self) -> Reward {
    return self.total_reward / (self.age as f64);
  }
}
<commit_msg>Add some more missing documentation.<commit_after>use random::Random;
use types::Reward;

/// Model struct for an agent which can interact with an environment.
pub struct Agent {
  age: i32,
  total_reward: Reward,
  num_actions: i16,
  random: Box<Random>,
}

impl Agent {
  pub fn new(num_actions: i16, random: Box<Random>) -> Agent {
    Agent {
      age: 0,
      total_reward: Reward(0.0),
      num_actions: num_actions,
      random: random,
    }
  }

  pub fn age(&self) -> i32 {
    self.age
  }

  /// Returns the total reward accumulated so far.
  pub fn total_reward(&self) -> Reward {
    self.total_reward
  }

  /// Returns the average reward accumulated so far.
  pub fn average_reward(&self) -> Reward {
    return self.total_reward / (self.age as f64);
  }

  /// Returns an action in [0, num_actions - 1].
  pub fn act(&mut self) -> i16 {
    // Picks a random legal action for now.
    return self.random.next_modulo(self.num_actions as i64) as i16;
  }

  /// Update the agent's view of the world based on a new
  /// (observation, reward) pair.
  pub fn update(&mut self, observation: i16, reward: Reward) {
    self.age = self.age + 1;
    self.total_reward = self.total_reward + reward;
  }
}
<|endoftext|>


<commit_before>use std::f64::consts::PI;
use std::cmp::Ordering;

<commit_msg>Really remove angle.rs<commit_after><|endoftext|>


<commit_before>use std::fmt;

/// Mirrors parquet::Type
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Type {
  BOOLEAN,
  INT32,
  INT64,
  INT96,
  FLOAT,
  DOUBLE,
  BYTE_ARRAY,
  FIXED_LEN_BYTE_ARRAY
}

/// Mirrors parquet::ConvertedType
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LogicalType {
  NONE,
  UTF8,
  MAP,
  MAP_KEY_VALUE,
  LIST,
  ENUM,
  DECIMAL,
  DATE,
  TIME_MILLIS,
  TIME_MICROS,
  TIMESTAMP_MILLIS,
  TIMESTAMP_MICROS,
  UINT_8,
  UINT_16,
  UINT_32,
  UINT_64,
  INT_8,
  INT_16,
  INT_32,
  INT_64,
  JSON,
  BSON,
  INTERVAL
}

/// Mirrors parquet::FieldRepetitionType
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Repetition {
  REQUIRED,
  OPTIONAL,
  REPEATED
}

/// Mirrors parquet::Encoding
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Encoding {
  PLAIN,
  PLAIN_DICTIONARY,
  RLE,
  BIT_PACKED,
  DELTA_BINARY_PACKED,
  DELTA_LENGTH_BYTE_ARRAY,
  DELTA_BYTE_ARRAY,
  RLE_DICTIONARY
}

/// Mirrors parquet::CompressionCodec
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Compression {
  UNCOMPRESSED,
  SNAPPY,
  GZIP,
  LZO,
  BROTLI
}

/// Mirrors parquet::PageType
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PageType {
  DATA_PAGE,
  INDEX_PAGE,
  DICTIONARY_PAGE,
  DATA_PAGE_V2
}

impl fmt::Display for Type {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for LogicalType {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for Repetition {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for Encoding {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for Compression {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for PageType {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

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

  #[test]
  fn test_display_type() {
    assert_eq!(Type::BOOLEAN.to_string(), "BOOLEAN");
    assert_eq!(Type::INT32.to_string(), "INT32");
    assert_eq!(Type::INT64.to_string(), "INT64");
    assert_eq!(Type::INT96.to_string(), "INT96");
    assert_eq!(Type::FLOAT.to_string(), "FLOAT");
    assert_eq!(Type::DOUBLE.to_string(), "DOUBLE");
    assert_eq!(Type::BYTE_ARRAY.to_string(), "BYTE_ARRAY");
    assert_eq!(Type::FIXED_LEN_BYTE_ARRAY.to_string(), "FIXED_LEN_BYTE_ARRAY");
  }

  #[test]
  fn test_display_logical_type() {
    assert_eq!(LogicalType::NONE.to_string(), "NONE");
    assert_eq!(LogicalType::UTF8.to_string(), "UTF8");
    assert_eq!(LogicalType::MAP.to_string(), "MAP");
    assert_eq!(LogicalType::MAP_KEY_VALUE.to_string(), "MAP_KEY_VALUE");
    assert_eq!(LogicalType::LIST.to_string(), "LIST");
    assert_eq!(LogicalType::ENUM.to_string(), "ENUM");
    assert_eq!(LogicalType::DECIMAL.to_string(), "DECIMAL");
    assert_eq!(LogicalType::DATE.to_string(), "DATE");
    assert_eq!(LogicalType::TIME_MILLIS.to_string(), "TIME_MILLIS");
    assert_eq!(LogicalType::DATE.to_string(), "DATE");
    assert_eq!(LogicalType::TIME_MICROS.to_string(), "TIME_MICROS");
    assert_eq!(LogicalType::TIMESTAMP_MILLIS.to_string(), "TIMESTAMP_MILLIS");
    assert_eq!(LogicalType::TIMESTAMP_MICROS.to_string(), "TIMESTAMP_MICROS");
    assert_eq!(LogicalType::UINT_8.to_string(), "UINT_8");
    assert_eq!(LogicalType::UINT_16.to_string(), "UINT_16");
    assert_eq!(LogicalType::UINT_32.to_string(), "UINT_32");
    assert_eq!(LogicalType::UINT_64.to_string(), "UINT_64");
    assert_eq!(LogicalType::INT_8.to_string(), "INT_8");
    assert_eq!(LogicalType::INT_16.to_string(), "INT_16");
    assert_eq!(LogicalType::INT_32.to_string(), "INT_32");
    assert_eq!(LogicalType::INT_64.to_string(), "INT_64");
    assert_eq!(LogicalType::JSON.to_string(), "JSON");
    assert_eq!(LogicalType::BSON.to_string(), "BSON");
    assert_eq!(LogicalType::INTERVAL.to_string(), "INTERVAL");
  }

  #[test]
  fn test_display_repetition() {
    assert_eq!(Repetition::REQUIRED.to_string(), "REQUIRED");
    assert_eq!(Repetition::OPTIONAL.to_string(), "OPTIONAL");
    assert_eq!(Repetition::REPEATED.to_string(), "REPEATED");
  }

  #[test]
  fn test_display_encoding() {
    assert_eq!(Encoding::PLAIN.to_string(), "PLAIN");
    assert_eq!(Encoding::PLAIN_DICTIONARY.to_string(), "PLAIN_DICTIONARY");
    assert_eq!(Encoding::RLE.to_string(), "RLE");
    assert_eq!(Encoding::BIT_PACKED.to_string(), "BIT_PACKED");
    assert_eq!(Encoding::DELTA_BINARY_PACKED.to_string(), "DELTA_BINARY_PACKED");
    assert_eq!(Encoding::DELTA_LENGTH_BYTE_ARRAY.to_string(), "DELTA_LENGTH_BYTE_ARRAY");
    assert_eq!(Encoding::DELTA_BYTE_ARRAY.to_string(), "DELTA_BYTE_ARRAY");
    assert_eq!(Encoding::RLE_DICTIONARY.to_string(), "RLE_DICTIONARY");
  }

  #[test]
  fn test_display_compression() {
    assert_eq!(Compression::UNCOMPRESSED.to_string(), "UNCOMPRESSED");
    assert_eq!(Compression::SNAPPY.to_string(), "SNAPPY");
    assert_eq!(Compression::GZIP.to_string(), "GZIP");
    assert_eq!(Compression::LZO.to_string(), "LZO");
    assert_eq!(Compression::BROTLI.to_string(), "BROTLI");
  }

  #[test]
  fn test_display_page_type() {
    assert_eq!(PageType::DATA_PAGE.to_string(), "DATA_PAGE");
    assert_eq!(PageType::INDEX_PAGE.to_string(), "INDEX_PAGE");
    assert_eq!(PageType::DICTIONARY_PAGE.to_string(), "DICTIONARY_PAGE");
    assert_eq!(PageType::DATA_PAGE_V2.to_string(), "DATA_PAGE_V2");
  }
}
<commit_msg>Adding conversion from Thrift for basic types<commit_after>use std::fmt;
use std::convert;
use parquet_thrift::parquet;

/// Mirrors parquet::Type
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Type {
  BOOLEAN,
  INT32,
  INT64,
  INT96,
  FLOAT,
  DOUBLE,
  BYTE_ARRAY,
  FIXED_LEN_BYTE_ARRAY
}

/// Mirrors parquet::ConvertedType
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LogicalType {
  NONE,
  UTF8,
  MAP,
  MAP_KEY_VALUE,
  LIST,
  ENUM,
  DECIMAL,
  DATE,
  TIME_MILLIS,
  TIME_MICROS,
  TIMESTAMP_MILLIS,
  TIMESTAMP_MICROS,
  UINT_8,
  UINT_16,
  UINT_32,
  UINT_64,
  INT_8,
  INT_16,
  INT_32,
  INT_64,
  JSON,
  BSON,
  INTERVAL
}

/// Mirrors parquet::FieldRepetitionType
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Repetition {
  REQUIRED,
  OPTIONAL,
  REPEATED
}

/// Mirrors parquet::Encoding
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Encoding {
  PLAIN,
  PLAIN_DICTIONARY,
  RLE,
  BIT_PACKED,
  DELTA_BINARY_PACKED,
  DELTA_LENGTH_BYTE_ARRAY,
  DELTA_BYTE_ARRAY,
  RLE_DICTIONARY
}

/// Mirrors parquet::CompressionCodec
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Compression {
  UNCOMPRESSED,
  SNAPPY,
  GZIP,
  LZO,
  BROTLI
}

/// Mirrors parquet::PageType
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PageType {
  DATA_PAGE,
  INDEX_PAGE,
  DICTIONARY_PAGE,
  DATA_PAGE_V2
}

impl fmt::Display for Type {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for LogicalType {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for Repetition {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for Encoding {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for Compression {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl fmt::Display for PageType {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", self)
  }
}

impl convert::From<parquet::Type> for Type {
  fn from(tp: parquet::Type) -> Self {
    match tp {
      parquet::Type::BOOLEAN => Type::BOOLEAN,
      parquet::Type::INT32 => Type::INT32,
      parquet::Type::INT64 => Type::INT64,
      parquet::Type::INT96 => Type::INT96,
      parquet::Type::FLOAT => Type::FLOAT,
      parquet::Type::DOUBLE => Type::DOUBLE,
      parquet::Type::BYTE_ARRAY => Type::BYTE_ARRAY,
      parquet::Type::FIXED_LEN_BYTE_ARRAY => Type::FIXED_LEN_BYTE_ARRAY
    }
  }
}

impl convert::From<parquet::ConvertedType> for LogicalType {
  fn from(tp: parquet::ConvertedType) -> Self {
    match tp {
      parquet::ConvertedType::UTF8 => LogicalType::UTF8,
      parquet::ConvertedType::MAP => LogicalType::MAP,
      parquet::ConvertedType::MAP_KEY_VALUE => LogicalType::MAP_KEY_VALUE,
      parquet::ConvertedType::LIST => LogicalType::LIST,
      parquet::ConvertedType::ENUM => LogicalType::ENUM,
      parquet::ConvertedType::DECIMAL => LogicalType::DECIMAL,
      parquet::ConvertedType::DATE => LogicalType::DATE,
      parquet::ConvertedType::TIME_MILLIS => LogicalType::TIME_MILLIS,
      parquet::ConvertedType::TIME_MICROS => LogicalType::TIME_MICROS,
      parquet::ConvertedType::TIMESTAMP_MILLIS => LogicalType::TIMESTAMP_MILLIS,
      parquet::ConvertedType::TIMESTAMP_MICROS => LogicalType::TIMESTAMP_MICROS,
      parquet::ConvertedType::UINT_8 => LogicalType::UINT_8,
      parquet::ConvertedType::UINT_16 => LogicalType::UINT_16,
      parquet::ConvertedType::UINT_32 => LogicalType::UINT_32,
      parquet::ConvertedType::UINT_64 => LogicalType::UINT_64,
      parquet::ConvertedType::INT_8 => LogicalType::INT_8,
      parquet::ConvertedType::INT_16 => LogicalType::INT_16,
      parquet::ConvertedType::INT_32 => LogicalType::INT_32,
      parquet::ConvertedType::INT_64 => LogicalType::INT_64,
      parquet::ConvertedType::JSON => LogicalType::JSON,
      parquet::ConvertedType::BSON => LogicalType::BSON,
      parquet::ConvertedType::INTERVAL => LogicalType::INTERVAL
    }
  }
}

impl convert::From<parquet::FieldRepetitionType> for Repetition {
  fn from(tp: parquet::FieldRepetitionType) -> Self {
    match tp {
      parquet::FieldRepetitionType::REQUIRED => Repetition::REQUIRED,
      parquet::FieldRepetitionType::OPTIONAL => Repetition::OPTIONAL,
      parquet::FieldRepetitionType::REPEATED => Repetition::REPEATED
    }
  }
}

impl convert::From<parquet::Encoding> for Encoding {
  fn from(tp: parquet::Encoding) -> Self {
    match tp {
      parquet::Encoding::PLAIN => Encoding::PLAIN,
      parquet::Encoding::PLAIN_DICTIONARY => Encoding::PLAIN_DICTIONARY,
      parquet::Encoding::RLE => Encoding::RLE,
      parquet::Encoding::BIT_PACKED => Encoding::BIT_PACKED,
      parquet::Encoding::DELTA_BINARY_PACKED => Encoding::DELTA_BINARY_PACKED,
      parquet::Encoding::DELTA_LENGTH_BYTE_ARRAY => Encoding::DELTA_LENGTH_BYTE_ARRAY,
      parquet::Encoding::DELTA_BYTE_ARRAY => Encoding::DELTA_BYTE_ARRAY,
      parquet::Encoding::RLE_DICTIONARY => Encoding::RLE_DICTIONARY
    }
  }
}

impl convert::From<parquet::CompressionCodec> for Compression {
  fn from(tp: parquet::CompressionCodec) -> Self {
    match tp {
      parquet::CompressionCodec::UNCOMPRESSED => Compression::UNCOMPRESSED,
      parquet::CompressionCodec::SNAPPY => Compression::SNAPPY,
      parquet::CompressionCodec::GZIP => Compression::GZIP,
      parquet::CompressionCodec::LZO => Compression::LZO,
      parquet::CompressionCodec::BROTLI => Compression::BROTLI
    }
  }
}

impl convert::From<parquet::PageType> for PageType {
  fn from(tp: parquet::PageType) -> Self {
    match tp {
      parquet::PageType::DATA_PAGE => PageType::DATA_PAGE,
      parquet::PageType::INDEX_PAGE => PageType::INDEX_PAGE,
      parquet::PageType::DICTIONARY_PAGE => PageType::DICTIONARY_PAGE,
      parquet::PageType::DATA_PAGE_V2 => PageType::DATA_PAGE_V2
    }
  }
}

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

  #[test]
  fn test_display_type() {
    assert_eq!(Type::BOOLEAN.to_string(), "BOOLEAN");
    assert_eq!(Type::INT32.to_string(), "INT32");
    assert_eq!(Type::INT64.to_string(), "INT64");
    assert_eq!(Type::INT96.to_string(), "INT96");
    assert_eq!(Type::FLOAT.to_string(), "FLOAT");
    assert_eq!(Type::DOUBLE.to_string(), "DOUBLE");
    assert_eq!(Type::BYTE_ARRAY.to_string(), "BYTE_ARRAY");
    assert_eq!(Type::FIXED_LEN_BYTE_ARRAY.to_string(), "FIXED_LEN_BYTE_ARRAY");
  }

  #[test]
  fn test_from_type() {
    assert_eq!(Type::from(parquet::Type::BOOLEAN) , Type::BOOLEAN);
    assert_eq!(Type::from(parquet::Type::INT32) , Type::INT32);
    assert_eq!(Type::from(parquet::Type::INT64) , Type::INT64);
    assert_eq!(Type::from(parquet::Type::INT96) , Type::INT96);
    assert_eq!(Type::from(parquet::Type::FLOAT) , Type::FLOAT);
    assert_eq!(Type::from(parquet::Type::DOUBLE) , Type::DOUBLE);
    assert_eq!(Type::from(parquet::Type::BYTE_ARRAY) , Type::BYTE_ARRAY);
    assert_eq!(Type::from(parquet::Type::FIXED_LEN_BYTE_ARRAY) , Type::FIXED_LEN_BYTE_ARRAY);
  }

  #[test]
  fn test_display_logical_type() {
    assert_eq!(LogicalType::NONE.to_string(), "NONE");
    assert_eq!(LogicalType::UTF8.to_string(), "UTF8");
    assert_eq!(LogicalType::MAP.to_string(), "MAP");
    assert_eq!(LogicalType::MAP_KEY_VALUE.to_string(), "MAP_KEY_VALUE");
    assert_eq!(LogicalType::LIST.to_string(), "LIST");
    assert_eq!(LogicalType::ENUM.to_string(), "ENUM");
    assert_eq!(LogicalType::DECIMAL.to_string(), "DECIMAL");
    assert_eq!(LogicalType::DATE.to_string(), "DATE");
    assert_eq!(LogicalType::TIME_MILLIS.to_string(), "TIME_MILLIS");
    assert_eq!(LogicalType::DATE.to_string(), "DATE");
    assert_eq!(LogicalType::TIME_MICROS.to_string(), "TIME_MICROS");
    assert_eq!(LogicalType::TIMESTAMP_MILLIS.to_string(), "TIMESTAMP_MILLIS");
    assert_eq!(LogicalType::TIMESTAMP_MICROS.to_string(), "TIMESTAMP_MICROS");
    assert_eq!(LogicalType::UINT_8.to_string(), "UINT_8");
    assert_eq!(LogicalType::UINT_16.to_string(), "UINT_16");
    assert_eq!(LogicalType::UINT_32.to_string(), "UINT_32");
    assert_eq!(LogicalType::UINT_64.to_string(), "UINT_64");
    assert_eq!(LogicalType::INT_8.to_string(), "INT_8");
    assert_eq!(LogicalType::INT_16.to_string(), "INT_16");
    assert_eq!(LogicalType::INT_32.to_string(), "INT_32");
    assert_eq!(LogicalType::INT_64.to_string(), "INT_64");
    assert_eq!(LogicalType::JSON.to_string(), "JSON");
    assert_eq!(LogicalType::BSON.to_string(), "BSON");
    assert_eq!(LogicalType::INTERVAL.to_string(), "INTERVAL");
  }

    #[test]
  fn test_from_logical_type() {
    assert_eq!(LogicalType::from(parquet::ConvertedType::UTF8),
               LogicalType::UTF8);
    assert_eq!(LogicalType::from(parquet::ConvertedType::MAP),
               LogicalType::MAP);
    assert_eq!(LogicalType::from(parquet::ConvertedType::MAP_KEY_VALUE),
               LogicalType::MAP_KEY_VALUE);
    assert_eq!(LogicalType::from(parquet::ConvertedType::LIST),
               LogicalType::LIST);
    assert_eq!(LogicalType::from(parquet::ConvertedType::ENUM),
               LogicalType::ENUM);
    assert_eq!(LogicalType::from(parquet::ConvertedType::DECIMAL),
               LogicalType::DECIMAL);
    assert_eq!(LogicalType::from(parquet::ConvertedType::DATE),
               LogicalType::DATE);
    assert_eq!(LogicalType::from(parquet::ConvertedType::TIME_MILLIS),
               LogicalType::TIME_MILLIS);
    assert_eq!(LogicalType::from(parquet::ConvertedType::TIME_MICROS),
               LogicalType::TIME_MICROS);
    assert_eq!(LogicalType::from(parquet::ConvertedType::TIMESTAMP_MILLIS),
               LogicalType::TIMESTAMP_MILLIS);
    assert_eq!(LogicalType::from(parquet::ConvertedType::TIMESTAMP_MICROS),
               LogicalType::TIMESTAMP_MICROS);
    assert_eq!(LogicalType::from(parquet::ConvertedType::UINT_8),
               LogicalType::UINT_8);
    assert_eq!(LogicalType::from(parquet::ConvertedType::UINT_16),
               LogicalType::UINT_16);
    assert_eq!(LogicalType::from(parquet::ConvertedType::UINT_32),
               LogicalType::UINT_32);
    assert_eq!(LogicalType::from(parquet::ConvertedType::UINT_64),
               LogicalType::UINT_64);
    assert_eq!(LogicalType::from(parquet::ConvertedType::INT_8),
               LogicalType::INT_8);
    assert_eq!(LogicalType::from(parquet::ConvertedType::INT_16),
               LogicalType::INT_16);
    assert_eq!(LogicalType::from(parquet::ConvertedType::INT_32),
               LogicalType::INT_32);
    assert_eq!(LogicalType::from(parquet::ConvertedType::INT_64),
               LogicalType::INT_64);
    assert_eq!(LogicalType::from(parquet::ConvertedType::JSON),
               LogicalType::JSON);
    assert_eq!(LogicalType::from(parquet::ConvertedType::BSON),
               LogicalType::BSON);
    assert_eq!(LogicalType::from(parquet::ConvertedType::INTERVAL),
               LogicalType::INTERVAL);
  }

  #[test]
  fn test_display_repetition() {
    assert_eq!(Repetition::REQUIRED.to_string(), "REQUIRED");
    assert_eq!(Repetition::OPTIONAL.to_string(), "OPTIONAL");
    assert_eq!(Repetition::REPEATED.to_string(), "REPEATED");
  }

  #[test]
  fn test_from_repetition() {
    assert_eq!(Repetition::from(parquet::FieldRepetitionType::REQUIRED),
               Repetition::REQUIRED);
    assert_eq!(Repetition::from(parquet::FieldRepetitionType::OPTIONAL),
               Repetition::OPTIONAL);
    assert_eq!(Repetition::from(parquet::FieldRepetitionType::REPEATED),
               Repetition::REPEATED);
  }

  #[test]
  fn test_display_encoding() {
    assert_eq!(Encoding::PLAIN.to_string(), "PLAIN");
    assert_eq!(Encoding::PLAIN_DICTIONARY.to_string(), "PLAIN_DICTIONARY");
    assert_eq!(Encoding::RLE.to_string(), "RLE");
    assert_eq!(Encoding::BIT_PACKED.to_string(), "BIT_PACKED");
    assert_eq!(Encoding::DELTA_BINARY_PACKED.to_string(), "DELTA_BINARY_PACKED");
    assert_eq!(Encoding::DELTA_LENGTH_BYTE_ARRAY.to_string(), "DELTA_LENGTH_BYTE_ARRAY");
    assert_eq!(Encoding::DELTA_BYTE_ARRAY.to_string(), "DELTA_BYTE_ARRAY");
    assert_eq!(Encoding::RLE_DICTIONARY.to_string(), "RLE_DICTIONARY");
  }

  #[test]
  fn test_from_encoding() {
    assert_eq!(Encoding::from(parquet::Encoding::PLAIN),
               Encoding::PLAIN);
    assert_eq!(Encoding::from(parquet::Encoding::PLAIN_DICTIONARY),
               Encoding::PLAIN_DICTIONARY);
    assert_eq!(Encoding::from(parquet::Encoding::RLE), Encoding::RLE);
    assert_eq!(Encoding::from(parquet::Encoding::BIT_PACKED), Encoding::BIT_PACKED);
    assert_eq!(Encoding::from(parquet::Encoding::DELTA_BINARY_PACKED),
               Encoding::DELTA_BINARY_PACKED);
    assert_eq!(Encoding::from(parquet::Encoding::DELTA_LENGTH_BYTE_ARRAY),
               Encoding::DELTA_LENGTH_BYTE_ARRAY);
    assert_eq!(Encoding::from(parquet::Encoding::DELTA_BYTE_ARRAY),
               Encoding::DELTA_BYTE_ARRAY);
  }

  #[test]
  fn test_display_compression() {
    assert_eq!(Compression::UNCOMPRESSED.to_string(), "UNCOMPRESSED");
    assert_eq!(Compression::SNAPPY.to_string(), "SNAPPY");
    assert_eq!(Compression::GZIP.to_string(), "GZIP");
    assert_eq!(Compression::LZO.to_string(), "LZO");
    assert_eq!(Compression::BROTLI.to_string(), "BROTLI");
  }

  #[test]
  fn test_from_compression() {
    assert_eq!(Compression::from(parquet::CompressionCodec::UNCOMPRESSED),
               Compression::UNCOMPRESSED);
    assert_eq!(Compression::from(parquet::CompressionCodec::SNAPPY),
               Compression::SNAPPY);
    assert_eq!(Compression::from(parquet::CompressionCodec::GZIP),
               Compression::GZIP);
    assert_eq!(Compression::from(parquet::CompressionCodec::LZO),
               Compression::LZO);
    assert_eq!(Compression::from(parquet::CompressionCodec::BROTLI),
               Compression::BROTLI);
  }

  #[test]
  fn test_display_page_type() {
    assert_eq!(PageType::DATA_PAGE.to_string(), "DATA_PAGE");
    assert_eq!(PageType::INDEX_PAGE.to_string(), "INDEX_PAGE");
    assert_eq!(PageType::DICTIONARY_PAGE.to_string(), "DICTIONARY_PAGE");
    assert_eq!(PageType::DATA_PAGE_V2.to_string(), "DATA_PAGE_V2");
  }

  #[test]
  fn test_from_page_type() {
    assert_eq!(PageType::from(parquet::PageType::DATA_PAGE), PageType::DATA_PAGE);
    assert_eq!(PageType::from(parquet::PageType::INDEX_PAGE), PageType::INDEX_PAGE);
    assert_eq!(PageType::from(parquet::PageType::DICTIONARY_PAGE), PageType::DICTIONARY_PAGE);
    assert_eq!(PageType::from(parquet::PageType::DATA_PAGE_V2), PageType::DATA_PAGE_V2);
  }
}
<|endoftext|>