<commit_before>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(lang_items, start, compiler_builtins_lib)]
#![no_std]

extern crate libc;

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    0
}

#[lang = "eh_personality"]
#[no_mangle]
pub extern fn rust_eh_personality() {}

#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
                               _file: &'static str,
                               _line: u32) -> ! {
    unsafe {
        libc::abort()
    }
}

//////////////////////////////////////////////////////////////////////////////

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
struct Unit;

#[derive(Serialize, Deserialize)]
struct Newtype(u8);

#[derive(Serialize, Deserialize)]
struct Tuple(u8, u8);

#[derive(Serialize, Deserialize)]
struct Struct { f: u8 }

#[derive(Serialize, Deserialize)]
enum Enum {
    Unit,
    Newtype(u8),
    Tuple(u8, u8),
    Struct { f: u8 },
}
<commit_msg>Remove unused compiler_builtins_lib feature<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(lang_items, start)]
#![no_std]

extern crate libc;

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    0
}

#[lang = "eh_personality"]
#[no_mangle]
pub extern fn rust_eh_personality() {}

#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
                               _file: &'static str,
                               _line: u32) -> ! {
    unsafe {
        libc::abort()
    }
}

//////////////////////////////////////////////////////////////////////////////

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
struct Unit;

#[derive(Serialize, Deserialize)]
struct Newtype(u8);

#[derive(Serialize, Deserialize)]
struct Tuple(u8, u8);

#[derive(Serialize, Deserialize)]
struct Struct { f: u8 }

#[derive(Serialize, Deserialize)]
enum Enum {
    Unit,
    Newtype(u8),
    Tuple(u8, u8),
    Struct { f: u8 },
}
<|endoftext|>


<commit_before>use crate::error::{Error,
                   Result};
use serde::Serialize;
use std::{self,
          collections::BTreeMap,
          env,
          fmt,
          fs::File,
          io::Read,
          iter::IntoIterator,
          path::{Path,
                 PathBuf},
          str::FromStr,
          string::ToString,
          vec::IntoIter};

#[cfg(not(windows))]
const ENV_PATH_SEPARATOR: char = ':';

#[cfg(windows)]
const ENV_PATH_SEPARATOR: char = ';';

pub fn parse_key_value(s: &str) -> Result<BTreeMap<String, String>> {
    Ok(s.lines()
        .map(|l| l.splitn(2, '=').collect::<Vec<_>>())
        .map(|kv| (kv[0].to_string(), kv[1].to_string()))
        .collect())
}

#[derive(Clone, Debug, Serialize)]
pub struct Bind {
    pub service: String,
    pub exports: Vec<String>,
}

impl FromStr for Bind {
    type Err = Error;

    fn from_str(line: &str) -> Result<Self> {
        let mut parts = line.split('=');
        let service = match parts.next() {
            None => return Err(Error::MetaFileBadBind),
            Some(service) => service.to_string(),
        };
        let exports = match parts.next() {
            None => return Err(Error::MetaFileBadBind),
            Some(exports) => exports.split_whitespace().map(str::to_string).collect(),
        };
        Ok(Bind { service, exports })
    }
}

impl fmt::Display for Bind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let formatted_exports = self.exports.join(" ");
        write!(f, "[{}]={}", self.service, formatted_exports)
    }
}

#[derive(Debug, PartialEq)]
pub struct EnvVar {
    pub key:       String,
    pub value:     String,
    pub separator: Option<char>,
}

#[derive(Debug)]
pub struct PkgEnv {
    inner: Vec<EnvVar>,
}

impl PkgEnv {
    pub fn new(values: BTreeMap<String, String>, separators: &BTreeMap<String, String>) -> Self {
        Self { inner: values.into_iter()
                            .map(|(key, value)| {
                                if let Some(sep) = separators.get(&key) {
                                    EnvVar { key,
                                             value,
                                             separator: sep.to_owned().pop() }
                                } else {
                                    EnvVar { key,
                                             value,
                                             separator: None }
                                }
                            })
                            .collect(), }
    }

    pub fn from_paths(paths: &[PathBuf]) -> Self {
        let p = env::join_paths(paths).expect("Failed to build path string");
        Self { inner: vec![EnvVar { key:       "PATH".to_string(),
                                    value:     p.into_string()
                                                .expect("Failed to convert path to utf8 string"),
                                    separator: Some(ENV_PATH_SEPARATOR), }], }
    }

    pub fn is_empty(&self) -> bool { self.inner.is_empty() }
}

impl IntoIterator for PkgEnv {
    type IntoIter = IntoIter<EnvVar>;
    type Item = EnvVar;

    fn into_iter(self) -> Self::IntoIter { self.inner.into_iter() }
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum MetaFile {
    Binds,
    BindsOptional,
    BuildDeps,
    BuildTDeps,
    CFlags,
    Config,
    Deps,
    Environment,
    EnvironmentSep,
    Exports,
    Exposes,
    Ident,
    LdFlags,
    LdRunPath,
    Manifest,
    Path,
    RuntimeEnvironment,
    RuntimeEnvironmentPaths,
    RuntimePath,
    ShutdownSignal,
    ShutdownTimeout,
    SvcGroup,
    SvcUser,
    Target,
    TDeps,
    PackageType,
}

impl fmt::Display for MetaFile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let id = match *self {
            MetaFile::Binds => "BINDS",
            MetaFile::BindsOptional => "BINDS_OPTIONAL",
            MetaFile::BuildDeps => "BUILD_DEPS",
            MetaFile::BuildTDeps => "BUILD_TDEPS",
            MetaFile::CFlags => "CFLAGS",
            MetaFile::Config => "default.toml",
            MetaFile::Deps => "DEPS",
            MetaFile::Environment => "ENVIRONMENT",
            MetaFile::EnvironmentSep => "ENVIRONMENT_SEP",
            MetaFile::Exports => "EXPORTS",
            MetaFile::Exposes => "EXPOSES",
            MetaFile::Ident => "IDENT",
            MetaFile::LdFlags => "LDFLAGS",
            MetaFile::LdRunPath => "LD_RUN_PATH",
            MetaFile::Manifest => "MANIFEST",
            MetaFile::Path => "PATH",
            MetaFile::RuntimeEnvironment => "RUNTIME_ENVIRONMENT",
            MetaFile::RuntimeEnvironmentPaths => "RUNTIME_ENVIRONMENT_PATHS",
            MetaFile::RuntimePath => "RUNTIME_PATH",
            MetaFile::ShutdownSignal => "SHUTDOWN_SIGNAL",
            MetaFile::ShutdownTimeout => "SHUTDOWN_TIMEOUT",
            MetaFile::SvcGroup => "SVC_GROUP",
            MetaFile::SvcUser => "SVC_USER",
            MetaFile::Target => "TARGET",
            MetaFile::TDeps => "TDEPS",
            MetaFile::PackageType => "PACKAGE_TYPE",
        };
        write!(f, "{}", id)
    }
}

/// Read a metadata file from within a package directory if it exists
///
/// Returns the contents of the file
pub fn read_metafile<P: AsRef<Path>>(installed_path: P, file: MetaFile) -> Result<String> {
    match existing_metafile(installed_path, file) {
        Some(filepath) => {
            match File::open(&filepath) {
                Ok(mut f) => {
                    let mut data = String::new();
                    if f.read_to_string(&mut data).is_err() {
                        return Err(Error::MetaFileMalformed(file));
                    }
                    Ok(data.trim().to_string())
                }
                Err(e) => Err(Error::MetaFileIO(e)),
            }
        }
        None => Err(Error::MetaFileNotFound(file)),
    }
}

/// Returns the path to a specified MetaFile in an installed path if it exists.
///
/// Useful for fallback logic for dealing with older Habitat packages.
fn existing_metafile<P: AsRef<Path>>(installed_path: P, file: MetaFile) -> Option<PathBuf> {
    let filepath = installed_path.as_ref().join(file.to_string());
    match std::fs::metadata(&filepath) {
        Ok(_) => Some(filepath),
        Err(_) => None,
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PackageType {
    Standard,
    Native,
}

impl fmt::Display for PackageType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let id = match *self {
            PackageType::Standard => "standard",
            PackageType::Native => "native",
        };
        write!(f, "{}", id)
    }
}

impl FromStr for PackageType {
    type Err = Error;

    fn from_str(value: &str) -> Result<Self> {
        match value {
            "standard" => Ok(PackageType::Standard),
            "native" => Ok(PackageType::Native),
            _ => Err(Error::InvalidPackageType(value.to_string())),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::io::Write;
    use tempfile::Builder;

    static ENVIRONMENT: &str = r#"PATH=/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin
PYTHONPATH=/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages
"#;
    static ENVIRONMENT_SEP: &str = r#"PATH=:
PYTHONPATH=:
"#;
    static EXPORTS: &str = r#"status-port=status.port
port=front-end.port
"#;
    static PATH: &str = "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin";

    /// Write the given contents into the specified metadata file for
    /// the package.
    fn write_metafile(install_dir: &Path, metafile: MetaFile, content: &str) {
        let path = install_dir.join(metafile.to_string());
        let mut f = File::create(path).expect("Could not create metafile");
        f.write_all(content.as_bytes())
         .expect("Could not write metafile contents");
    }

    #[test]
    #[should_panic]
    fn malformed_file() { parse_key_value("PATH").unwrap(); }

    #[test]
    fn can_parse_environment_file() {
        let mut m: BTreeMap<String, String> = BTreeMap::new();
        m.insert("PATH".to_string(),
                 "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin".to_string());
        m.insert(
            "PYTHONPATH".to_string(),
            "/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages"
                .to_string(),
        );

        assert_eq!(parse_key_value(ENVIRONMENT).unwrap(), m);
    }

    #[test]
    fn can_parse_environment_sep_file() {
        let mut m: BTreeMap<String, String> = BTreeMap::new();
        m.insert("PATH".to_string(), ":".to_string());
        m.insert("PYTHONPATH".to_string(), ":".to_string());

        assert_eq!(parse_key_value(ENVIRONMENT_SEP).unwrap(), m);
    }

    #[test]
    fn can_parse_exports_file() {
        let mut m: BTreeMap<String, String> = BTreeMap::new();
        m.insert("status-port".to_string(), "status.port".to_string());
        m.insert("port".to_string(), "front-end.port".to_string());

        assert_eq!(parse_key_value(EXPORTS).unwrap(), m);
    }

    #[test]
    fn build_pkg_env() {
        let mut result =
            PkgEnv::new(parse_key_value(ENVIRONMENT).unwrap(),
                        &parse_key_value(ENVIRONMENT_SEP).unwrap()).into_iter()
                                                                   .collect::<Vec<_>>();
        // Sort the result by key, so we have a guarantee of order
        result.sort_by_key(|v| v.key.to_owned());

        let expected =
            vec![EnvVar { key:       "PATH".to_string(),
                          value:
                              "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin".to_string(),
                          separator: Some(':'), },
                 EnvVar { key:       "PYTHONPATH".to_string(),
                          value:     "/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/\
                                      python3.6/site-packages"
                                                              .to_string(),
                          separator: Some(':'), },];

        assert_eq!(result, expected);
    }

    #[test]
    fn build_pkg_env_is_empty() {
        let result = PkgEnv::new(BTreeMap::new(), &BTreeMap::new());
        assert!(result.is_empty());
    }

    #[test]
    fn build_pkg_env_from_path() {
        let result = PkgEnv::from_paths(&[PathBuf::from(PATH)]).into_iter()
                                                               .collect::<Vec<_>>();

        let expected = vec![EnvVar { key:       "PATH".to_string(),
                                     value:     "/hab/pkgs/python/setuptools/35.0.1/\
                                                 20170424072606/bin"
                                                                    .to_string(),
                                     separator: Some(ENV_PATH_SEPARATOR), }];

        assert_eq!(result, expected);
    }

    #[test]
    fn can_read_metafile() {
        let pkg_root = Builder::new().prefix("pkg-root").tempdir().unwrap();
        let install_dir = pkg_root.path();

        let expected = "core/foo=db:core/database";
        write_metafile(install_dir, MetaFile::Binds, expected);

        let bind_map = read_metafile(install_dir, MetaFile::Binds).unwrap();

        assert_eq!(expected, bind_map);
    }

    #[test]
    fn reading_a_non_existing_metafile_is_an_error() {
        let pkg_root = Builder::new().prefix("pkg-root").tempdir().unwrap();
        let install_dir = pkg_root.path();
        let bind_map = read_metafile(install_dir, MetaFile::Binds);

        assert!(bind_map.is_err());
    }
}
<commit_msg>adds missing derives for PackageType for builder<commit_after>use crate::error::{Error,
                   Result};
use serde::{Deserialize,
            Serialize};
use std::{self,
          collections::BTreeMap,
          env,
          fmt,
          fs::File,
          io::Read,
          iter::IntoIterator,
          path::{Path,
                 PathBuf},
          str::FromStr,
          string::ToString,
          vec::IntoIter};

#[cfg(not(windows))]
const ENV_PATH_SEPARATOR: char = ':';

#[cfg(windows)]
const ENV_PATH_SEPARATOR: char = ';';

pub fn parse_key_value(s: &str) -> Result<BTreeMap<String, String>> {
    Ok(s.lines()
        .map(|l| l.splitn(2, '=').collect::<Vec<_>>())
        .map(|kv| (kv[0].to_string(), kv[1].to_string()))
        .collect())
}

#[derive(Clone, Debug, Serialize)]
pub struct Bind {
    pub service: String,
    pub exports: Vec<String>,
}

impl FromStr for Bind {
    type Err = Error;

    fn from_str(line: &str) -> Result<Self> {
        let mut parts = line.split('=');
        let service = match parts.next() {
            None => return Err(Error::MetaFileBadBind),
            Some(service) => service.to_string(),
        };
        let exports = match parts.next() {
            None => return Err(Error::MetaFileBadBind),
            Some(exports) => exports.split_whitespace().map(str::to_string).collect(),
        };
        Ok(Bind { service, exports })
    }
}

impl fmt::Display for Bind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let formatted_exports = self.exports.join(" ");
        write!(f, "[{}]={}", self.service, formatted_exports)
    }
}

#[derive(Debug, PartialEq)]
pub struct EnvVar {
    pub key:       String,
    pub value:     String,
    pub separator: Option<char>,
}

#[derive(Debug)]
pub struct PkgEnv {
    inner: Vec<EnvVar>,
}

impl PkgEnv {
    pub fn new(values: BTreeMap<String, String>, separators: &BTreeMap<String, String>) -> Self {
        Self { inner: values.into_iter()
                            .map(|(key, value)| {
                                if let Some(sep) = separators.get(&key) {
                                    EnvVar { key,
                                             value,
                                             separator: sep.to_owned().pop() }
                                } else {
                                    EnvVar { key,
                                             value,
                                             separator: None }
                                }
                            })
                            .collect(), }
    }

    pub fn from_paths(paths: &[PathBuf]) -> Self {
        let p = env::join_paths(paths).expect("Failed to build path string");
        Self { inner: vec![EnvVar { key:       "PATH".to_string(),
                                    value:     p.into_string()
                                                .expect("Failed to convert path to utf8 string"),
                                    separator: Some(ENV_PATH_SEPARATOR), }], }
    }

    pub fn is_empty(&self) -> bool { self.inner.is_empty() }
}

impl IntoIterator for PkgEnv {
    type IntoIter = IntoIter<EnvVar>;
    type Item = EnvVar;

    fn into_iter(self) -> Self::IntoIter { self.inner.into_iter() }
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum MetaFile {
    Binds,
    BindsOptional,
    BuildDeps,
    BuildTDeps,
    CFlags,
    Config,
    Deps,
    Environment,
    EnvironmentSep,
    Exports,
    Exposes,
    Ident,
    LdFlags,
    LdRunPath,
    Manifest,
    Path,
    RuntimeEnvironment,
    RuntimeEnvironmentPaths,
    RuntimePath,
    ShutdownSignal,
    ShutdownTimeout,
    SvcGroup,
    SvcUser,
    Target,
    TDeps,
    PackageType,
}

impl fmt::Display for MetaFile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let id = match *self {
            MetaFile::Binds => "BINDS",
            MetaFile::BindsOptional => "BINDS_OPTIONAL",
            MetaFile::BuildDeps => "BUILD_DEPS",
            MetaFile::BuildTDeps => "BUILD_TDEPS",
            MetaFile::CFlags => "CFLAGS",
            MetaFile::Config => "default.toml",
            MetaFile::Deps => "DEPS",
            MetaFile::Environment => "ENVIRONMENT",
            MetaFile::EnvironmentSep => "ENVIRONMENT_SEP",
            MetaFile::Exports => "EXPORTS",
            MetaFile::Exposes => "EXPOSES",
            MetaFile::Ident => "IDENT",
            MetaFile::LdFlags => "LDFLAGS",
            MetaFile::LdRunPath => "LD_RUN_PATH",
            MetaFile::Manifest => "MANIFEST",
            MetaFile::Path => "PATH",
            MetaFile::RuntimeEnvironment => "RUNTIME_ENVIRONMENT",
            MetaFile::RuntimeEnvironmentPaths => "RUNTIME_ENVIRONMENT_PATHS",
            MetaFile::RuntimePath => "RUNTIME_PATH",
            MetaFile::ShutdownSignal => "SHUTDOWN_SIGNAL",
            MetaFile::ShutdownTimeout => "SHUTDOWN_TIMEOUT",
            MetaFile::SvcGroup => "SVC_GROUP",
            MetaFile::SvcUser => "SVC_USER",
            MetaFile::Target => "TARGET",
            MetaFile::TDeps => "TDEPS",
            MetaFile::PackageType => "PACKAGE_TYPE",
        };
        write!(f, "{}", id)
    }
}

/// Read a metadata file from within a package directory if it exists
///
/// Returns the contents of the file
pub fn read_metafile<P: AsRef<Path>>(installed_path: P, file: MetaFile) -> Result<String> {
    match existing_metafile(installed_path, file) {
        Some(filepath) => {
            match File::open(&filepath) {
                Ok(mut f) => {
                    let mut data = String::new();
                    if f.read_to_string(&mut data).is_err() {
                        return Err(Error::MetaFileMalformed(file));
                    }
                    Ok(data.trim().to_string())
                }
                Err(e) => Err(Error::MetaFileIO(e)),
            }
        }
        None => Err(Error::MetaFileNotFound(file)),
    }
}

/// Returns the path to a specified MetaFile in an installed path if it exists.
///
/// Useful for fallback logic for dealing with older Habitat packages.
fn existing_metafile<P: AsRef<Path>>(installed_path: P, file: MetaFile) -> Option<PathBuf> {
    let filepath = installed_path.as_ref().join(file.to_string());
    match std::fs::metadata(&filepath) {
        Ok(_) => Some(filepath),
        Err(_) => None,
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub enum PackageType {
    Standard,
    Native,
}

impl fmt::Display for PackageType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let id = match *self {
            PackageType::Standard => "standard",
            PackageType::Native => "native",
        };
        write!(f, "{}", id)
    }
}

impl FromStr for PackageType {
    type Err = Error;

    fn from_str(value: &str) -> Result<Self> {
        match value {
            "standard" => Ok(PackageType::Standard),
            "native" => Ok(PackageType::Native),
            _ => Err(Error::InvalidPackageType(value.to_string())),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::io::Write;
    use tempfile::Builder;

    static ENVIRONMENT: &str = r#"PATH=/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin
PYTHONPATH=/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages
"#;
    static ENVIRONMENT_SEP: &str = r#"PATH=:
PYTHONPATH=:
"#;
    static EXPORTS: &str = r#"status-port=status.port
port=front-end.port
"#;
    static PATH: &str = "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin";

    /// Write the given contents into the specified metadata file for
    /// the package.
    fn write_metafile(install_dir: &Path, metafile: MetaFile, content: &str) {
        let path = install_dir.join(metafile.to_string());
        let mut f = File::create(path).expect("Could not create metafile");
        f.write_all(content.as_bytes())
         .expect("Could not write metafile contents");
    }

    #[test]
    #[should_panic]
    fn malformed_file() { parse_key_value("PATH").unwrap(); }

    #[test]
    fn can_parse_environment_file() {
        let mut m: BTreeMap<String, String> = BTreeMap::new();
        m.insert("PATH".to_string(),
                 "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin".to_string());
        m.insert(
            "PYTHONPATH".to_string(),
            "/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages"
                .to_string(),
        );

        assert_eq!(parse_key_value(ENVIRONMENT).unwrap(), m);
    }

    #[test]
    fn can_parse_environment_sep_file() {
        let mut m: BTreeMap<String, String> = BTreeMap::new();
        m.insert("PATH".to_string(), ":".to_string());
        m.insert("PYTHONPATH".to_string(), ":".to_string());

        assert_eq!(parse_key_value(ENVIRONMENT_SEP).unwrap(), m);
    }

    #[test]
    fn can_parse_exports_file() {
        let mut m: BTreeMap<String, String> = BTreeMap::new();
        m.insert("status-port".to_string(), "status.port".to_string());
        m.insert("port".to_string(), "front-end.port".to_string());

        assert_eq!(parse_key_value(EXPORTS).unwrap(), m);
    }

    #[test]
    fn build_pkg_env() {
        let mut result =
            PkgEnv::new(parse_key_value(ENVIRONMENT).unwrap(),
                        &parse_key_value(ENVIRONMENT_SEP).unwrap()).into_iter()
                                                                   .collect::<Vec<_>>();
        // Sort the result by key, so we have a guarantee of order
        result.sort_by_key(|v| v.key.to_owned());

        let expected =
            vec![EnvVar { key:       "PATH".to_string(),
                          value:
                              "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin".to_string(),
                          separator: Some(':'), },
                 EnvVar { key:       "PYTHONPATH".to_string(),
                          value:     "/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/\
                                      python3.6/site-packages"
                                                              .to_string(),
                          separator: Some(':'), },];

        assert_eq!(result, expected);
    }

    #[test]
    fn build_pkg_env_is_empty() {
        let result = PkgEnv::new(BTreeMap::new(), &BTreeMap::new());
        assert!(result.is_empty());
    }

    #[test]
    fn build_pkg_env_from_path() {
        let result = PkgEnv::from_paths(&[PathBuf::from(PATH)]).into_iter()
                                                               .collect::<Vec<_>>();

        let expected = vec![EnvVar { key:       "PATH".to_string(),
                                     value:     "/hab/pkgs/python/setuptools/35.0.1/\
                                                 20170424072606/bin"
                                                                    .to_string(),
                                     separator: Some(ENV_PATH_SEPARATOR), }];

        assert_eq!(result, expected);
    }

    #[test]
    fn can_read_metafile() {
        let pkg_root = Builder::new().prefix("pkg-root").tempdir().unwrap();
        let install_dir = pkg_root.path();

        let expected = "core/foo=db:core/database";
        write_metafile(install_dir, MetaFile::Binds, expected);

        let bind_map = read_metafile(install_dir, MetaFile::Binds).unwrap();

        assert_eq!(expected, bind_map);
    }

    #[test]
    fn reading_a_non_existing_metafile_is_an_error() {
        let pkg_root = Builder::new().prefix("pkg-root").tempdir().unwrap();
        let install_dir = pkg_root.path();
        let bind_map = read_metafile(install_dir, MetaFile::Binds);

        assert!(bind_map.is_err());
    }
}
<|endoftext|>


<commit_before>use rustc::lint::*;

use crate::utils::{in_macro, match_type, paths, span_lint_and_then, usage::is_potentially_mutated};
use rustc::hir::intravisit::*;
use rustc::hir::*;
use syntax::ast::NodeId;
use syntax::codemap::Span;

/// **What it does:** Checks for calls of unwrap[_err]() that cannot fail.
///
/// **Why is this bad?** Using `if let` or `match` is more idiomatic.
///
/// **Known problems:** Limitations of the borrow checker might make unwrap() necessary sometimes?
///
/// **Example:**
/// ```rust
/// if option.is_some() {
///     do_something_with(option.unwrap())
/// }
/// ```
///
/// Could be written:
///
/// ```rust
/// if let Some(value) = option {
///     do_something_with(value)
/// }
/// ```
declare_clippy_lint! {
    pub UNNECESSARY_UNWRAP,
    nursery,
    "checks for calls of unwrap[_err]() that cannot fail"
}

/// **What it does:** Checks for calls of unwrap[_err]() that will always fail.
///
/// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.
///
/// **Known problems:** This lint only checks `if` conditions not assignments.
/// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
///
/// **Example:**
/// ```rust
/// if option.is_none() {
///     do_something_with(option.unwrap())
/// }
/// ```
///
/// This code will always panic. The if condition should probably be inverted.
/// ```
declare_clippy_lint! {
    pub PANICKING_UNWRAP,
    nursery,
    "checks for calls of unwrap[_err]() that will always fail"
}

pub struct Pass;

/// Visitor that keeps track of which variables are unwrappable.
struct UnwrappableVariablesVisitor<'a, 'tcx: 'a> {
    unwrappables: Vec<UnwrapInfo<'tcx>>,
    cx: &'a LateContext<'a, 'tcx>,
}
/// Contains information about whether a variable can be unwrapped.
#[derive(Copy, Clone, Debug)]
struct UnwrapInfo<'tcx> {
    /// The variable that is checked
    ident: &'tcx Path,
    /// The check, like `x.is_ok()`
    check: &'tcx Expr,
    /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
    safe_to_unwrap: bool,
}

/// Collects the information about unwrappable variables from an if condition
/// The `invert` argument tells us whether the condition is negated.
fn collect_unwrap_info<'a, 'tcx: 'a>(
    cx: &'a LateContext<'a, 'tcx>,
    expr: &'tcx Expr,
    invert: bool,
) -> Vec<UnwrapInfo<'tcx>> {
    if let Expr_::ExprBinary(op, left, right) = &expr.node {
        match (invert, op.node) {
            (false, BinOp_::BiAnd) | (false, BinOp_::BiBitAnd) | (true, BinOp_::BiOr) | (true, BinOp_::BiBitOr) => {
                let mut unwrap_info = collect_unwrap_info(cx, left, invert);
                unwrap_info.append(&mut collect_unwrap_info(cx, right, invert));
                return unwrap_info;
            },
            _ => (),
        }
    } else if let Expr_::ExprUnary(UnNot, expr) = &expr.node {
        return collect_unwrap_info(cx, expr, !invert);
    } else {
        if_chain! {
            if let Expr_::ExprMethodCall(method_name, _, args) = &expr.node;
            if let Expr_::ExprPath(QPath::Resolved(None, path)) = &args[0].node;
            let ty = cx.tables.expr_ty(&args[0]);
            if match_type(cx, ty, &paths::OPTION) || match_type(cx, ty, &paths::RESULT);
            let name = method_name.name.as_str();
            if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name);
            then {
                assert!(args.len() == 1);
                let unwrappable = match name.as_ref() {
                    "is_some" | "is_ok" => true,
                    "is_err" | "is_none" => false,
                    _ => unreachable!(),
                };
                let safe_to_unwrap = unwrappable != invert;
                return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }];
            }
        }
    }
    Vec::new()
}

impl<'a, 'tcx: 'a> UnwrappableVariablesVisitor<'a, 'tcx> {
    fn visit_branch(&mut self, cond: &'tcx Expr, branch: &'tcx Expr, else_branch: bool) {
        let prev_len = self.unwrappables.len();
        for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) {
            if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
                || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
            {
                // if the variable is mutated, we don't know whether it can be unwrapped:
                continue;
            }
            self.unwrappables.push(unwrap_info);
        }
        walk_expr(self, branch);
        self.unwrappables.truncate(prev_len);
    }
}

impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx Expr) {
        if let Expr_::ExprIf(cond, then, els) = &expr.node {
            walk_expr(self, cond);
            self.visit_branch(cond, then, false);
            if let Some(els) = els {
                self.visit_branch(cond, els, true);
            }
        } else {
            // find `unwrap[_err]()` calls:
            if_chain! {
                if let Expr_::ExprMethodCall(ref method_name, _, ref args) = expr.node;
                if let Expr_::ExprPath(QPath::Resolved(None, ref path)) = args[0].node;
                if ["unwrap", "unwrap_err"].contains(&&*method_name.name.as_str());
                let call_to_unwrap = method_name.name == "unwrap";
                if let Some(unwrappable) = self.unwrappables.iter()
                    .find(|u| u.ident.def == path.def);
                then {
                    if call_to_unwrap == unwrappable.safe_to_unwrap {
                        span_lint_and_then(
                            self.cx,
                            UNNECESSARY_UNWRAP,
                            expr.span,
                            &format!("You checked before that `{}()` cannot fail. \
                            Instead of checking and unwrapping, it's better to use `if let` or `match`.",
                            method_name.name),
                            |db| { db.span_label(unwrappable.check.span, "the check is happening here"); },
                        );
                    } else {
                        span_lint_and_then(
                            self.cx,
                            PANICKING_UNWRAP,
                            expr.span,
                            &format!("This call to `{}()` will always panic.",
                            method_name.name),
                            |db| { db.span_label(unwrappable.check.span, "because of this check"); },
                        );
                    }
                }
            }
            walk_expr(self, expr);
        }
    }

    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir)
    }
}

impl<'a> LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(PANICKING_UNWRAP, UNNECESSARY_UNWRAP)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        fn_id: NodeId,
    ) {
        if in_macro(span) {
            return;
        }

        let mut v = UnwrappableVariablesVisitor {
            cx,
            unwrappables: Vec::new(),
        };

        walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
    }
}
<commit_msg>Fix markdown.<commit_after>use rustc::lint::*;

use crate::utils::{in_macro, match_type, paths, span_lint_and_then, usage::is_potentially_mutated};
use rustc::hir::intravisit::*;
use rustc::hir::*;
use syntax::ast::NodeId;
use syntax::codemap::Span;

/// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
///
/// **Why is this bad?** Using `if let` or `match` is more idiomatic.
///
/// **Known problems:** Limitations of the borrow checker might make unwrap() necessary sometimes?
///
/// **Example:**
/// ```rust
/// if option.is_some() {
///     do_something_with(option.unwrap())
/// }
/// ```
///
/// Could be written:
///
/// ```rust
/// if let Some(value) = option {
///     do_something_with(value)
/// }
/// ```
declare_clippy_lint! {
    pub UNNECESSARY_UNWRAP,
    nursery,
    "checks for calls of unwrap[_err]() that cannot fail"
}

/// **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.
///
/// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.
///
/// **Known problems:** This lint only checks `if` conditions not assignments.
/// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
///
/// **Example:**
/// ```rust
/// if option.is_none() {
///     do_something_with(option.unwrap())
/// }
/// ```
///
/// This code will always panic. The if condition should probably be inverted.
/// ```
declare_clippy_lint! {
    pub PANICKING_UNWRAP,
    nursery,
    "checks for calls of unwrap[_err]() that will always fail"
}

pub struct Pass;

/// Visitor that keeps track of which variables are unwrappable.
struct UnwrappableVariablesVisitor<'a, 'tcx: 'a> {
    unwrappables: Vec<UnwrapInfo<'tcx>>,
    cx: &'a LateContext<'a, 'tcx>,
}
/// Contains information about whether a variable can be unwrapped.
#[derive(Copy, Clone, Debug)]
struct UnwrapInfo<'tcx> {
    /// The variable that is checked
    ident: &'tcx Path,
    /// The check, like `x.is_ok()`
    check: &'tcx Expr,
    /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
    safe_to_unwrap: bool,
}

/// Collects the information about unwrappable variables from an if condition
/// The `invert` argument tells us whether the condition is negated.
fn collect_unwrap_info<'a, 'tcx: 'a>(
    cx: &'a LateContext<'a, 'tcx>,
    expr: &'tcx Expr,
    invert: bool,
) -> Vec<UnwrapInfo<'tcx>> {
    if let Expr_::ExprBinary(op, left, right) = &expr.node {
        match (invert, op.node) {
            (false, BinOp_::BiAnd) | (false, BinOp_::BiBitAnd) | (true, BinOp_::BiOr) | (true, BinOp_::BiBitOr) => {
                let mut unwrap_info = collect_unwrap_info(cx, left, invert);
                unwrap_info.append(&mut collect_unwrap_info(cx, right, invert));
                return unwrap_info;
            },
            _ => (),
        }
    } else if let Expr_::ExprUnary(UnNot, expr) = &expr.node {
        return collect_unwrap_info(cx, expr, !invert);
    } else {
        if_chain! {
            if let Expr_::ExprMethodCall(method_name, _, args) = &expr.node;
            if let Expr_::ExprPath(QPath::Resolved(None, path)) = &args[0].node;
            let ty = cx.tables.expr_ty(&args[0]);
            if match_type(cx, ty, &paths::OPTION) || match_type(cx, ty, &paths::RESULT);
            let name = method_name.name.as_str();
            if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name);
            then {
                assert!(args.len() == 1);
                let unwrappable = match name.as_ref() {
                    "is_some" | "is_ok" => true,
                    "is_err" | "is_none" => false,
                    _ => unreachable!(),
                };
                let safe_to_unwrap = unwrappable != invert;
                return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }];
            }
        }
    }
    Vec::new()
}

impl<'a, 'tcx: 'a> UnwrappableVariablesVisitor<'a, 'tcx> {
    fn visit_branch(&mut self, cond: &'tcx Expr, branch: &'tcx Expr, else_branch: bool) {
        let prev_len = self.unwrappables.len();
        for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) {
            if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
                || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
            {
                // if the variable is mutated, we don't know whether it can be unwrapped:
                continue;
            }
            self.unwrappables.push(unwrap_info);
        }
        walk_expr(self, branch);
        self.unwrappables.truncate(prev_len);
    }
}

impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx Expr) {
        if let Expr_::ExprIf(cond, then, els) = &expr.node {
            walk_expr(self, cond);
            self.visit_branch(cond, then, false);
            if let Some(els) = els {
                self.visit_branch(cond, els, true);
            }
        } else {
            // find `unwrap[_err]()` calls:
            if_chain! {
                if let Expr_::ExprMethodCall(ref method_name, _, ref args) = expr.node;
                if let Expr_::ExprPath(QPath::Resolved(None, ref path)) = args[0].node;
                if ["unwrap", "unwrap_err"].contains(&&*method_name.name.as_str());
                let call_to_unwrap = method_name.name == "unwrap";
                if let Some(unwrappable) = self.unwrappables.iter()
                    .find(|u| u.ident.def == path.def);
                then {
                    if call_to_unwrap == unwrappable.safe_to_unwrap {
                        span_lint_and_then(
                            self.cx,
                            UNNECESSARY_UNWRAP,
                            expr.span,
                            &format!("You checked before that `{}()` cannot fail. \
                            Instead of checking and unwrapping, it's better to use `if let` or `match`.",
                            method_name.name),
                            |db| { db.span_label(unwrappable.check.span, "the check is happening here"); },
                        );
                    } else {
                        span_lint_and_then(
                            self.cx,
                            PANICKING_UNWRAP,
                            expr.span,
                            &format!("This call to `{}()` will always panic.",
                            method_name.name),
                            |db| { db.span_label(unwrappable.check.span, "because of this check"); },
                        );
                    }
                }
            }
            walk_expr(self, expr);
        }
    }

    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir)
    }
}

impl<'a> LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(PANICKING_UNWRAP, UNNECESSARY_UNWRAP)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        fn_id: NodeId,
    ) {
        if in_macro(span) {
            return;
        }

        let mut v = UnwrappableVariablesVisitor {
            cx,
            unwrappables: Vec::new(),
        };

        walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
    }
}
<|endoftext|>


<commit_before>use if_chain::if_chain;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};

use crate::utils::{in_macro, match_type, paths, span_lint_and_then, usage::is_potentially_mutated};
use rustc::hir::intravisit::*;
use rustc::hir::*;
use syntax::source_map::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
    ///
    /// **Why is this bad?** Using `if let` or `match` is more idiomatic.
    ///
    /// **Known problems:** Limitations of the borrow checker might make unwrap() necessary sometimes?
    ///
    /// **Example:**
    /// ```rust
    /// if option.is_some() {
    ///     do_something_with(option.unwrap())
    /// }
    /// ```
    ///
    /// Could be written:
    ///
    /// ```rust
    /// if let Some(value) = option {
    ///     do_something_with(value)
    /// }
    /// ```
    pub UNNECESSARY_UNWRAP,
    nursery,
    "checks for calls of unwrap[_err]() that cannot fail"
}

declare_clippy_lint! {
    /// **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.
    ///
    /// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.
    ///
    /// **Known problems:** This lint only checks `if` conditions not assignments.
    /// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
    ///
    /// **Example:**
    /// ```rust
    /// if option.is_none() {
    ///     do_something_with(option.unwrap())
    /// }
    /// ```
    ///
    /// This code will always panic. The if condition should probably be inverted.
    pub PANICKING_UNWRAP,
    nursery,
    "checks for calls of unwrap[_err]() that will always fail"
}

/// Visitor that keeps track of which variables are unwrappable.
struct UnwrappableVariablesVisitor<'a, 'tcx: 'a> {
    unwrappables: Vec<UnwrapInfo<'tcx>>,
    cx: &'a LateContext<'a, 'tcx>,
}
/// Contains information about whether a variable can be unwrapped.
#[derive(Copy, Clone, Debug)]
struct UnwrapInfo<'tcx> {
    /// The variable that is checked
    ident: &'tcx Path,
    /// The check, like `x.is_ok()`
    check: &'tcx Expr,
    /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
    safe_to_unwrap: bool,
}

/// Collects the information about unwrappable variables from an if condition
/// The `invert` argument tells us whether the condition is negated.
fn collect_unwrap_info<'a, 'tcx: 'a>(
    cx: &'a LateContext<'a, 'tcx>,
    expr: &'tcx Expr,
    invert: bool,
) -> Vec<UnwrapInfo<'tcx>> {
    if let ExprKind::Binary(op, left, right) = &expr.node {
        match (invert, op.node) {
            (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => {
                let mut unwrap_info = collect_unwrap_info(cx, left, invert);
                unwrap_info.append(&mut collect_unwrap_info(cx, right, invert));
                return unwrap_info;
            },
            _ => (),
        }
    } else if let ExprKind::Unary(UnNot, expr) = &expr.node {
        return collect_unwrap_info(cx, expr, !invert);
    } else {
        if_chain! {
            if let ExprKind::MethodCall(method_name, _, args) = &expr.node;
            if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].node;
            let ty = cx.tables.expr_ty(&args[0]);
            if match_type(cx, ty, &paths::OPTION) || match_type(cx, ty, &paths::RESULT);
            let name = method_name.ident.as_str();
            if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name);
            then {
                assert!(args.len() == 1);
                let unwrappable = match name.as_ref() {
                    "is_some" | "is_ok" => true,
                    "is_err" | "is_none" => false,
                    _ => unreachable!(),
                };
                let safe_to_unwrap = unwrappable != invert;
                return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }];
            }
        }
    }
    Vec::new()
}

impl<'a, 'tcx: 'a> UnwrappableVariablesVisitor<'a, 'tcx> {
    fn visit_branch(&mut self, cond: &'tcx Expr, branch: &'tcx Expr, else_branch: bool) {
        let prev_len = self.unwrappables.len();
        for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) {
            if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
                || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
            {
                // if the variable is mutated, we don't know whether it can be unwrapped:
                continue;
            }
            self.unwrappables.push(unwrap_info);
        }
        walk_expr(self, branch);
        self.unwrappables.truncate(prev_len);
    }
}

impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx Expr) {
        if let ExprKind::If(cond, then, els) = &expr.node {
            walk_expr(self, cond);
            self.visit_branch(cond, then, false);
            if let Some(els) = els {
                self.visit_branch(cond, els, true);
            }
        } else {
            // find `unwrap[_err]()` calls:
            if_chain! {
                if let ExprKind::MethodCall(ref method_name, _, ref args) = expr.node;
                if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].node;
                if ["unwrap", "unwrap_err"].contains(&&*method_name.ident.as_str());
                let call_to_unwrap = method_name.ident.name == "unwrap";
                if let Some(unwrappable) = self.unwrappables.iter()
                    .find(|u| u.ident.res == path.res);
                then {
                    if call_to_unwrap == unwrappable.safe_to_unwrap {
                        span_lint_and_then(
                            self.cx,
                            UNNECESSARY_UNWRAP,
                            expr.span,
                            &format!("You checked before that `{}()` cannot fail. \
                            Instead of checking and unwrapping, it's better to use `if let` or `match`.",
                            method_name.ident.name),
                            |db| { db.span_label(unwrappable.check.span, "the check is happening here"); },
                        );
                    } else {
                        span_lint_and_then(
                            self.cx,
                            PANICKING_UNWRAP,
                            expr.span,
                            &format!("This call to `{}()` will always panic.",
                            method_name.ident.name),
                            |db| { db.span_label(unwrappable.check.span, "because of this check"); },
                        );
                    }
                }
            }
            walk_expr(self, expr);
        }
    }

    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir())
    }
}

declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unwrap {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        fn_id: HirId,
    ) {
        if in_macro(span) {
            return;
        }

        let mut v = UnwrappableVariablesVisitor {
            cx,
            unwrappables: Vec::new(),
        };

        walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
    }
}
<commit_msg>Fix unwrap.rs<commit_after>use if_chain::if_chain;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};

use crate::utils::{higher::if_block, in_macro, match_type, paths, span_lint_and_then, usage::is_potentially_mutated};
use rustc::hir::intravisit::*;
use rustc::hir::*;
use syntax::source_map::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
    ///
    /// **Why is this bad?** Using `if let` or `match` is more idiomatic.
    ///
    /// **Known problems:** Limitations of the borrow checker might make unwrap() necessary sometimes?
    ///
    /// **Example:**
    /// ```rust
    /// if option.is_some() {
    ///     do_something_with(option.unwrap())
    /// }
    /// ```
    ///
    /// Could be written:
    ///
    /// ```rust
    /// if let Some(value) = option {
    ///     do_something_with(value)
    /// }
    /// ```
    pub UNNECESSARY_UNWRAP,
    nursery,
    "checks for calls of unwrap[_err]() that cannot fail"
}

declare_clippy_lint! {
    /// **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.
    ///
    /// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.
    ///
    /// **Known problems:** This lint only checks `if` conditions not assignments.
    /// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
    ///
    /// **Example:**
    /// ```rust
    /// if option.is_none() {
    ///     do_something_with(option.unwrap())
    /// }
    /// ```
    ///
    /// This code will always panic. The if condition should probably be inverted.
    pub PANICKING_UNWRAP,
    nursery,
    "checks for calls of unwrap[_err]() that will always fail"
}

/// Visitor that keeps track of which variables are unwrappable.
struct UnwrappableVariablesVisitor<'a, 'tcx: 'a> {
    unwrappables: Vec<UnwrapInfo<'tcx>>,
    cx: &'a LateContext<'a, 'tcx>,
}
/// Contains information about whether a variable can be unwrapped.
#[derive(Copy, Clone, Debug)]
struct UnwrapInfo<'tcx> {
    /// The variable that is checked
    ident: &'tcx Path,
    /// The check, like `x.is_ok()`
    check: &'tcx Expr,
    /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
    safe_to_unwrap: bool,
}

/// Collects the information about unwrappable variables from an if condition
/// The `invert` argument tells us whether the condition is negated.
fn collect_unwrap_info<'a, 'tcx: 'a>(
    cx: &'a LateContext<'a, 'tcx>,
    expr: &'tcx Expr,
    invert: bool,
) -> Vec<UnwrapInfo<'tcx>> {
    if let ExprKind::Binary(op, left, right) = &expr.node {
        match (invert, op.node) {
            (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => {
                let mut unwrap_info = collect_unwrap_info(cx, left, invert);
                unwrap_info.append(&mut collect_unwrap_info(cx, right, invert));
                return unwrap_info;
            },
            _ => (),
        }
    } else if let ExprKind::Unary(UnNot, expr) = &expr.node {
        return collect_unwrap_info(cx, expr, !invert);
    } else {
        if_chain! {
            if let ExprKind::MethodCall(method_name, _, args) = &expr.node;
            if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].node;
            let ty = cx.tables.expr_ty(&args[0]);
            if match_type(cx, ty, &paths::OPTION) || match_type(cx, ty, &paths::RESULT);
            let name = method_name.ident.as_str();
            if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name);
            then {
                assert!(args.len() == 1);
                let unwrappable = match name.as_ref() {
                    "is_some" | "is_ok" => true,
                    "is_err" | "is_none" => false,
                    _ => unreachable!(),
                };
                let safe_to_unwrap = unwrappable != invert;
                return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }];
            }
        }
    }
    Vec::new()
}

impl<'a, 'tcx: 'a> UnwrappableVariablesVisitor<'a, 'tcx> {
    fn visit_branch(&mut self, cond: &'tcx Expr, branch: &'tcx Expr, else_branch: bool) {
        let prev_len = self.unwrappables.len();
        for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) {
            if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
                || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
            {
                // if the variable is mutated, we don't know whether it can be unwrapped:
                continue;
            }
            self.unwrappables.push(unwrap_info);
        }
        walk_expr(self, branch);
        self.unwrappables.truncate(prev_len);
    }
}

impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx Expr) {
        if let Some((cond, then, els)) = if_block(&expr) {
            walk_expr(self, cond);
            self.visit_branch(cond, then, false);
            if let Some(els) = els {
                self.visit_branch(cond, els, true);
            }
        } else {
            // find `unwrap[_err]()` calls:
            if_chain! {
                if let ExprKind::MethodCall(ref method_name, _, ref args) = expr.node;
                if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].node;
                if ["unwrap", "unwrap_err"].contains(&&*method_name.ident.as_str());
                let call_to_unwrap = method_name.ident.name == "unwrap";
                if let Some(unwrappable) = self.unwrappables.iter()
                    .find(|u| u.ident.res == path.res);
                then {
                    if call_to_unwrap == unwrappable.safe_to_unwrap {
                        span_lint_and_then(
                            self.cx,
                            UNNECESSARY_UNWRAP,
                            expr.span,
                            &format!("You checked before that `{}()` cannot fail. \
                            Instead of checking and unwrapping, it's better to use `if let` or `match`.",
                            method_name.ident.name),
                            |db| { db.span_label(unwrappable.check.span, "the check is happening here"); },
                        );
                    } else {
                        span_lint_and_then(
                            self.cx,
                            PANICKING_UNWRAP,
                            expr.span,
                            &format!("This call to `{}()` will always panic.",
                            method_name.ident.name),
                            |db| { db.span_label(unwrappable.check.span, "because of this check"); },
                        );
                    }
                }
            }
            walk_expr(self, expr);
        }
    }

    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir())
    }
}

declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unwrap {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        fn_id: HirId,
    ) {
        if in_macro(span) {
            return;
        }

        let mut v = UnwrappableVariablesVisitor {
            cx,
            unwrappables: Vec::new(),
        };

        walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::sync::{mpsc, Arc, Mutex};
use std::thread;

use futures::stream::{self, Stream, Receiver};
use futures::{Future, failed, finished, oneshot};
use support::{self, mock};
use tokio_proto::pipeline::{self, Frame, Message};
use tokio_proto;
use tokio_core::Loop;

// The message type is a static string for both the request and response
type Msg = &'static str;

// The body stream is a stream of u32 values
type Body = Receiver<u32, io::Error>;

// Frame written to the transport
type InFrame = Frame<Msg, io::Error, u32>;
type OutFrame = Frame<pipeline::Message<Msg, Body>, io::Error, u32>;

#[test]
fn test_immediate_done() {
    let service = tokio_proto::simple_service(|req| {
        finished(req)
    });

    run(service, |mock| {
        mock.send(pipeline::Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_immediate_writable_echo() {
    let service = tokio_proto::simple_service(|req| {
        assert_eq!(req, "hello");
        finished((req))
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg("hello"));
        assert_eq!(mock.next_write().unwrap_msg(), "hello");

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_immediate_writable_delayed_response_echo() {
    let (c, fut) = oneshot();
    let fut = Mutex::new(Some(fut));

    let service = tokio_proto::simple_service(move |req| {
        assert_eq!(req, "hello");
        fut.lock().unwrap().take().unwrap().then(|r| r.unwrap())
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg("hello"));

        support::sleep_ms(20);
        c.complete(Ok(Message::WithoutBody("goodbye")));

        assert_eq!(mock.next_write().unwrap_msg(), "goodbye");

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_delayed_writable_immediate_response_echo() {
    let service = tokio_proto::simple_service(|req| {
        assert_eq!(req, "hello");
        finished((req))
    });

    run(service, |mock| {
        mock.send(msg("hello"));

        support::sleep_ms(20);

        mock.allow_write();
        assert_eq!(mock.next_write().unwrap_msg(), "hello");
    });
}

#[test]
fn test_pipelining_while_service_is_processing() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |_| {
        let (c, fut) = oneshot();
        tx.lock().unwrap().send(c).unwrap();
        fut.then(|r| r.unwrap())
    });

    run(service, |mock| {
        // Allow all the writes
        for _ in 0..3 { mock.allow_write() };

        mock.send(msg("hello"));
        let c1 = rx.recv().unwrap();

        mock.send(msg("hello"));
        let c2 = rx.recv().unwrap();

        mock.send(msg("hello"));
        let c3 = rx.recv().unwrap();

        mock.assert_no_write(20);
        c3.complete(Ok(Message::WithoutBody("three")));

        mock.assert_no_write(20);
        c2.complete(Ok(Message::WithoutBody("two")));

        mock.assert_no_write(20);
        c1.complete(Ok(Message::WithoutBody("one")));

        assert_eq!("one", mock.next_write().unwrap_msg());
        assert_eq!("two", mock.next_write().unwrap_msg());
        assert_eq!("three", mock.next_write().unwrap_msg());
    });
}

#[test]
fn test_pipelining_while_transport_not_writable() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |req: Message<&'static str, Body>| {
        tx.lock().unwrap().send(req.clone()).unwrap();
        finished(req)
    });

    run(service, |mock| {
        mock.send(msg("one"));
        mock.send(msg("two"));
        mock.send(msg("three"));

        // Assert the service received all the requests before they are written
        // to the transport
        assert_eq!("one", rx.recv().unwrap());
        assert_eq!("two", rx.recv().unwrap());
        assert_eq!("three", rx.recv().unwrap());

        mock.allow_write();
        assert_eq!("one", mock.next_write().unwrap_msg());

        mock.allow_write();
        assert_eq!("two", mock.next_write().unwrap_msg());

        mock.allow_write();
        assert_eq!("three", mock.next_write().unwrap_msg());
    });
}

#[test]
fn test_repeatedly_flushes_messages() {
    let service = tokio_proto::simple_service(move |req| {
        finished(req)
    });

    run(service, |mock| {
        mock.send(msg("hello"));

        mock.allow_and_assert_flush();
        mock.allow_and_assert_flush();

        mock.allow_write();
        assert_eq!("hello", mock.next_write().unwrap_msg());

        mock.send(Frame::Done);
        mock.assert_drop();
    });
}

#[test]
fn test_returning_error_from_service() {
    let service = tokio_proto::simple_service(move |_| {
        failed(io::Error::new(io::ErrorKind::Other, "nope"))
    });

    run(service, |mock| {
        mock.send(msg("hello"));

        mock.allow_write();
        assert_eq!(io::ErrorKind::Other, mock.next_write().unwrap_err().kind());

        mock.assert_no_write(20);

        mock.send(Frame::Done);
        mock.assert_drop();
    });
}

#[test]
fn test_reading_error_frame_from_transport() {
    let service = tokio_proto::simple_service(move |_| {
        finished(Message::WithoutBody("omg no"))
    });

    run(service, |mock| {
        mock.send(Frame::Error(io::Error::new(io::ErrorKind::Other, "mock transport error frame")));
        mock.assert_drop();
    });
}

#[test]
fn test_reading_io_error_from_transport() {
    let service = tokio_proto::simple_service(move |_| {
        finished(Message::WithoutBody("omg no"))
    });

    run(service, |mock| {
        mock.error(io::Error::new(io::ErrorKind::Other, "mock transport error frame"));
        mock.assert_drop();
    });
}

#[test]
#[ignore]
fn test_reading_error_while_pipelining_from_transport() {
    unimplemented!();
}

#[test]
#[ignore]
fn test_returning_would_block_from_service() {
    // Because... it could happen
}

#[test]
fn test_streaming_request_body_then_responding() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |mut req: Message<&'static str, Body>| {
        assert_eq!(req, "omg");

        let body = req.take_body().unwrap();
        let tx = tx.clone();

        body.for_each(move |chunk| {
                tx.lock().unwrap().send(chunk).unwrap();
                Ok(())
            })
            .and_then(|_| finished(Message::WithoutBody("hi2u")))
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg_with_body("omg"));

        for i in 0..5 {
            mock.send(Frame::Body(Some(i)));
            assert_eq!(i, rx.recv().unwrap());
        }

        // Send end-of-stream notification
        mock.send(Frame::Body(None));

        assert_eq!(mock.next_write().unwrap_msg(), "hi2u");

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_responding_then_streaming_request_body() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |mut req: Message<&'static str, Body>| {
        assert_eq!(req, "omg");

        let body = req.take_body().unwrap();
        let tx = tx.clone();

        thread::spawn(|| {
            body.for_each(move |chunk| {
                    tx.lock().unwrap().send(chunk).unwrap();
                    Ok(())
                })
                .wait()
                .unwrap();
        });

        finished(Message::WithoutBody("hi2u"))
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg_with_body("omg"));

        assert_eq!(mock.next_write().unwrap_msg(), "hi2u");

        for i in 0..5 {
            mock.send(Frame::Body(Some(i)));
            assert_eq!(i, rx.recv().unwrap());
        }

        // Send end-of-stream notification
        mock.send(Frame::Body(None));

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_pipeline_streaming_body_without_consuming() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |mut req: Message<&'static str, Body>| {
        let body = req.take_body().unwrap();

        if req == "one" {
            debug!("drop body");
            finished(Message::WithoutBody("resp-one")).boxed()
        } else {
            let tx = tx.clone();

            body.for_each(move |chunk| {
                    tx.lock().unwrap().send(chunk).unwrap();
                    Ok(())
                })
                .and_then(|_| finished(Message::WithoutBody("resp-two")))
                .boxed()
        }
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg_with_body("one"));

        for i in 0..5 {
            mock.send(Frame::Body(Some(i)));
            support::sleep_ms(20);
            assert!(rx.try_recv().is_err());
        }

        assert_eq!(mock.next_write().unwrap_msg(), "resp-one");

        // Send the next request
        println!("sending two");
        mock.send(msg_with_body("two"));

        for i in 0..5 {
            println!("sendering: {}", i);
            mock.send(Frame::Body(Some(i)));
            println!("waiting");
            assert_eq!(i, rx.recv().unwrap());
        }

        mock.send(Frame::Body(None));

        mock.allow_write();
        assert_eq!(mock.next_write().unwrap_msg(), "resp-two");

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
#[ignore]
fn test_transport_error_during_body_stream() {
}

#[test]
fn test_streaming_response_body() {
    let (tx, rx) = stream::channel::<u32, io::Error>();
    let rx = Mutex::new(Some(rx));

    let service = tokio_proto::simple_service(move |req| {
        assert_eq!(req, "omg");
        finished(Message::WithBody("hi2u", rx.lock().unwrap().take().unwrap()))
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg("omg"));

        assert_eq!(mock.next_write().unwrap_msg(), "hi2u");

        mock.assert_no_write(20);

        mock.allow_write();
        let tx = tx.send(Ok(1)).wait().ok().unwrap();
        assert_eq!(Some(1), mock.next_write().unwrap_body());

        let _ = tx.send(Ok(2)).wait().ok().unwrap();
        mock.assert_no_write(20);
        mock.allow_write();
        assert_eq!(Some(2), mock.next_write().unwrap_body());

        mock.allow_write();
        assert_eq!(None, mock.next_write().unwrap_body());

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

fn channel<T>() -> (Arc<Mutex<mpsc::Sender<T>>>, mpsc::Receiver<T>) {
    let (tx, rx) = mpsc::channel();
    let tx = Arc::new(Mutex::new(tx));
    (tx, rx)
}

fn msg(msg: Msg) -> OutFrame {
    Frame::Message(Message::WithoutBody(msg))
}

fn msg_with_body(msg: Msg) -> OutFrame {
    let (tx, rx) = stream::channel();
    Frame::MessageWithBody(Message::WithBody(msg, rx), tx)
}

/// Setup a reactor running a pipeline::Server with the given service and a
/// mock transport. Yields the mock transport handle to the function.
fn run<S, F>(service: S, f: F)
    where S: pipeline::ServerService<Req = pipeline::Message<Msg, Body>, Resp = Msg, Body = u32, BodyStream = Body, Error = io::Error>,
          F: FnOnce(mock::TransportHandle<InFrame, OutFrame>),
{
    drop(::env_logger::init());
    let (tx, rx) = oneshot();
    let (tx2, rx2) = mpsc::channel();
    let t = thread::spawn(move || {
        let mut lp = Loop::new().unwrap();
        tx2.send(lp.handle()).unwrap();
        lp.run(rx)
    });
    let handle = rx2.recv().unwrap();

    let (mock, new_transport) = mock::transport::<InFrame, OutFrame>(handle.clone());

    let transport = new_transport.new_transport().wait().unwrap();
    let dispatch = pipeline::Server::new(service, transport).unwrap();
    handle.spawn(|_| {
        dispatch.map_err(|e| error!("error: {}", e))
    });

    f(mock);

    tx.complete(());
    t.join().unwrap().unwrap();
}
<commit_msg>Fix tests<commit_after>use std::io;
use std::sync::{mpsc, Arc, Mutex};
use std::thread;

use futures::stream::{self, Stream, Receiver};
use futures::{Future, failed, finished, oneshot};
use support::{self, mock};
use tokio_proto::pipeline::{self, Frame, Message};
use tokio_proto;
use tokio_core::Loop;

// The message type is a static string for both the request and response
type Msg = &'static str;

// The body stream is a stream of u32 values
type Body = Receiver<u32, io::Error>;

// Frame written to the transport
type InFrame = Frame<Msg, io::Error, u32>;
type OutFrame = Frame<pipeline::Message<Msg, Body>, io::Error, u32>;

#[test]
fn test_immediate_done() {
    let service = tokio_proto::simple_service(|req| {
        finished(req)
    });

    run(service, |mock| {
        mock.send(pipeline::Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_immediate_writable_echo() {
    let service = tokio_proto::simple_service(|req| {
        assert_eq!(req, "hello");
        finished((req))
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg("hello"));
        assert_eq!(mock.next_write().unwrap_msg(), "hello");

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_immediate_writable_delayed_response_echo() {
    let (c, fut) = oneshot();
    let fut = Mutex::new(Some(fut));

    let service = tokio_proto::simple_service(move |req| {
        assert_eq!(req, "hello");
        fut.lock().unwrap().take().unwrap().then(|r| r.unwrap())
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg("hello"));

        support::sleep_ms(20);
        c.complete(Ok(Message::WithoutBody("goodbye")));

        assert_eq!(mock.next_write().unwrap_msg(), "goodbye");

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_delayed_writable_immediate_response_echo() {
    let service = tokio_proto::simple_service(|req| {
        assert_eq!(req, "hello");
        finished((req))
    });

    run(service, |mock| {
        mock.send(msg("hello"));

        support::sleep_ms(20);

        mock.allow_write();
        assert_eq!(mock.next_write().unwrap_msg(), "hello");
    });
}

#[test]
fn test_pipelining_while_service_is_processing() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |_| {
        let (c, fut) = oneshot();
        tx.lock().unwrap().send(c).unwrap();
        fut.then(|r| r.unwrap())
    });

    run(service, |mock| {
        // Allow all the writes
        for _ in 0..3 { mock.allow_write() };

        mock.send(msg("hello"));
        let c1 = rx.recv().unwrap();

        mock.send(msg("hello"));
        let c2 = rx.recv().unwrap();

        mock.send(msg("hello"));
        let c3 = rx.recv().unwrap();

        mock.assert_no_write(20);
        c3.complete(Ok(Message::WithoutBody("three")));

        mock.assert_no_write(20);
        c2.complete(Ok(Message::WithoutBody("two")));

        mock.assert_no_write(20);
        c1.complete(Ok(Message::WithoutBody("one")));

        assert_eq!("one", mock.next_write().unwrap_msg());
        assert_eq!("two", mock.next_write().unwrap_msg());
        assert_eq!("three", mock.next_write().unwrap_msg());
    });
}

#[test]
fn test_pipelining_while_transport_not_writable() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |req: Message<&'static str, Body>| {
        tx.lock().unwrap().send(req.clone()).unwrap();
        finished(req)
    });

    run(service, |mock| {
        mock.send(msg("one"));
        mock.send(msg("two"));
        mock.send(msg("three"));

        // Assert the service received all the requests before they are written
        // to the transport
        assert_eq!("one", rx.recv().unwrap());
        assert_eq!("two", rx.recv().unwrap());
        assert_eq!("three", rx.recv().unwrap());

        mock.allow_write();
        assert_eq!("one", mock.next_write().unwrap_msg());

        mock.allow_write();
        assert_eq!("two", mock.next_write().unwrap_msg());

        mock.allow_write();
        assert_eq!("three", mock.next_write().unwrap_msg());
    });
}

#[test]
fn test_repeatedly_flushes_messages() {
    let service = tokio_proto::simple_service(move |req| {
        finished(req)
    });

    run(service, |mock| {
        mock.send(msg("hello"));

        mock.allow_and_assert_flush();
        mock.allow_and_assert_flush();

        mock.allow_write();
        assert_eq!("hello", mock.next_write().unwrap_msg());

        mock.send(Frame::Done);
        mock.assert_drop();
    });
}

#[test]
fn test_returning_error_from_service() {
    let service = tokio_proto::simple_service(move |_| {
        failed(io::Error::new(io::ErrorKind::Other, "nope"))
    });

    run(service, |mock| {
        mock.send(msg("hello"));

        mock.allow_write();
        assert_eq!(io::ErrorKind::Other, mock.next_write().unwrap_err().kind());

        mock.assert_no_write(20);

        mock.send(Frame::Done);
        mock.assert_drop();
    });
}

#[test]
fn test_reading_error_frame_from_transport() {
    let service = tokio_proto::simple_service(move |_| {
        finished(Message::WithoutBody("omg no"))
    });

    run(service, |mock| {
        mock.send(Frame::Error(io::Error::new(io::ErrorKind::Other, "mock transport error frame")));
        mock.assert_drop();
    });
}

#[test]
fn test_reading_io_error_from_transport() {
    let service = tokio_proto::simple_service(move |_| {
        finished(Message::WithoutBody("omg no"))
    });

    run(service, |mock| {
        mock.error(io::Error::new(io::ErrorKind::Other, "mock transport error frame"));
        mock.assert_drop();
    });
}

#[test]
#[ignore]
fn test_reading_error_while_pipelining_from_transport() {
    unimplemented!();
}

#[test]
#[ignore]
fn test_returning_would_block_from_service() {
    // Because... it could happen
}

#[test]
fn test_streaming_request_body_then_responding() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |mut req: Message<&'static str, Body>| {
        assert_eq!(req, "omg");

        let body = req.take_body().unwrap();
        let tx = tx.clone();

        body.for_each(move |chunk| {
                tx.lock().unwrap().send(chunk).unwrap();
                Ok(())
            })
            .and_then(|_| finished(Message::WithoutBody("hi2u")))
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg_with_body("omg"));

        for i in 0..5 {
            mock.send(Frame::Body(Some(i)));
            assert_eq!(i, rx.recv().unwrap());
        }

        // Send end-of-stream notification
        mock.send(Frame::Body(None));

        assert_eq!(mock.next_write().unwrap_msg(), "hi2u");

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_responding_then_streaming_request_body() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |mut req: Message<&'static str, Body>| {
        assert_eq!(req, "omg");

        let body = req.take_body().unwrap();
        let tx = tx.clone();

        thread::spawn(|| {
            body.for_each(move |chunk| {
                    tx.lock().unwrap().send(chunk).unwrap();
                    Ok(())
                })
                .wait()
                .unwrap();
        });

        finished(Message::WithoutBody("hi2u"))
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg_with_body("omg"));

        assert_eq!(mock.next_write().unwrap_msg(), "hi2u");

        for i in 0..5 {
            mock.send(Frame::Body(Some(i)));
            assert_eq!(i, rx.recv().unwrap());
        }

        // Send end-of-stream notification
        mock.send(Frame::Body(None));

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
fn test_pipeline_streaming_body_without_consuming() {
    let (tx, rx) = channel();

    let service = tokio_proto::simple_service(move |mut req: Message<&'static str, Body>| {
        let body = req.take_body().unwrap();

        if req == "one" {
            debug!("drop body");
            finished(Message::WithoutBody("resp-one")).boxed()
        } else {
            let tx = tx.clone();

            body.for_each(move |chunk| {
                    tx.lock().unwrap().send(chunk).unwrap();
                    Ok(())
                })
                .and_then(|_| finished(Message::WithoutBody("resp-two")))
                .boxed()
        }
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg_with_body("one"));

        for i in 0..5 {
            mock.send(Frame::Body(Some(i)));
            support::sleep_ms(20);
            assert!(rx.try_recv().is_err());
        }

        assert_eq!(mock.next_write().unwrap_msg(), "resp-one");

        // Send the next request
        println!("sending two");
        mock.send(msg_with_body("two"));

        for i in 0..5 {
            println!("sendering: {}", i);
            mock.send(Frame::Body(Some(i)));
            println!("waiting");
            assert_eq!(i, rx.recv().unwrap());
        }

        mock.send(Frame::Body(None));

        mock.allow_write();
        assert_eq!(mock.next_write().unwrap_msg(), "resp-two");

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

#[test]
#[ignore]
fn test_transport_error_during_body_stream() {
}

#[test]
fn test_streaming_response_body() {
    let (tx, rx) = stream::channel::<u32, io::Error>();
    let rx = Mutex::new(Some(rx));

    let service = tokio_proto::simple_service(move |req| {
        assert_eq!(req, "omg");
        finished(Message::WithBody("hi2u", rx.lock().unwrap().take().unwrap()))
    });

    run(service, |mock| {
        mock.allow_write();
        mock.send(msg("omg"));

        assert_eq!(mock.next_write().unwrap_msg(), "hi2u");

        mock.assert_no_write(20);

        mock.allow_write();
        let tx = tx.send(Ok(1)).wait().ok().unwrap();
        assert_eq!(Some(1), mock.next_write().unwrap_body());

        let _ = tx.send(Ok(2)).wait().ok().unwrap();
        mock.assert_no_write(20);
        mock.allow_write();
        assert_eq!(Some(2), mock.next_write().unwrap_body());

        mock.allow_write();
        assert_eq!(None, mock.next_write().unwrap_body());

        mock.send(Frame::Done);
        mock.allow_and_assert_drop();
    });
}

fn channel<T>() -> (Arc<Mutex<mpsc::Sender<T>>>, mpsc::Receiver<T>) {
    let (tx, rx) = mpsc::channel();
    let tx = Arc::new(Mutex::new(tx));
    (tx, rx)
}

fn msg(msg: Msg) -> OutFrame {
    Frame::Message(Message::WithoutBody(msg))
}

fn msg_with_body(msg: Msg) -> OutFrame {
    let (tx, rx) = stream::channel();
    Frame::MessageWithBody(Message::WithBody(msg, rx), tx)
}

/// Setup a reactor running a pipeline::Server with the given service and a
/// mock transport. Yields the mock transport handle to the function.
fn run<S, F>(service: S, f: F)
    where S: pipeline::ServerService<Req = pipeline::Message<Msg, Body>, Resp = Msg, Body = u32, BodyStream = Body, Error = io::Error> + Send + 'static,
          S::Fut: Send + 'static,
          F: FnOnce(mock::TransportHandle<InFrame, OutFrame>),
{
    drop(::env_logger::init());
    let (tx, rx) = oneshot();
    let (tx2, rx2) = mpsc::channel();
    let t = thread::spawn(move || {
        let mut lp = Loop::new().unwrap();
        tx2.send(lp.handle()).unwrap();
        lp.run(rx)
    });
    let handle = rx2.recv().unwrap();

    let (mock, new_transport) = mock::transport::<InFrame, OutFrame>(handle.clone());

    let transport = new_transport.new_transport().wait().unwrap();
    let dispatch = pipeline::Server::new(service, transport).unwrap();
    handle.spawn(|_| {
        dispatch.map_err(|e| error!("error: {}", e))
    });

    f(mock);

    tx.complete(());
    t.join().unwrap().unwrap();
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::path::PathBuf;

use cargo_edit::{resolve_manifests, shell_status, shell_warn, upgrade_requirement, LocalManifest};
use clap::Args;

use crate::errors::*;
use crate::version::BumpLevel;
use crate::version::TargetVersion;

/// Change a package's version in the local manifest file (i.e. Cargo.toml).
#[derive(Debug, Args)]
#[command(version)]
#[command(group = clap::ArgGroup::new("ver").multiple(false))]
pub struct VersionArgs {
    /// Version to change manifests to
    #[arg(group = "ver")]
    target: Option<semver::Version>,

    /// Increment manifest version
    #[arg(long, group = "ver")]
    bump: Option<BumpLevel>,

    /// Specify the version metadata field (e.g. a wrapped libraries version)
    #[arg(short, long)]
    pub metadata: Option<String>,

    /// Path to the manifest to upgrade
    #[arg(long, value_name = "PATH")]
    manifest_path: Option<PathBuf>,

    /// Package id of the crate to change the version of.
    #[arg(
        long = "package",
        short = 'p',
        value_name = "PKGID",
        conflicts_with = "all",
        conflicts_with = "workspace"
    )]
    pkgid: Option<String>,

    /// Modify all packages in the workspace.
    #[arg(
        long,
        help = "[deprecated in favor of `--workspace`]",
        conflicts_with = "workspace",
        conflicts_with = "pkgid"
    )]
    all: bool,

    /// Modify all packages in the workspace.
    #[arg(long, conflicts_with = "all", conflicts_with = "pkgid")]
    workspace: bool,

    /// Print changes to be made without making them.
    #[arg(long)]
    dry_run: bool,

    /// Crates to exclude and not modify.
    #[arg(long)]
    exclude: Vec<String>,

    /// Run without accessing the network
    #[arg(long)]
    offline: bool,

    /// Require `Cargo.toml` to be up to date
    #[arg(long)]
    locked: bool,

    /// Unstable (nightly-only) flags
    #[arg(short = 'Z', value_name = "FLAG", global = true, value_enum)]
    unstable_features: Vec<UnstableOptions>,
}

impl VersionArgs {
    pub fn exec(self) -> CargoResult<()> {
        exec(self)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ValueEnum)]
enum UnstableOptions {}

/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error
/// messages.
fn exec(args: VersionArgs) -> CargoResult<()> {
    let VersionArgs {
        target,
        bump,
        metadata,
        manifest_path,
        pkgid,
        all,
        dry_run,
        workspace,
        exclude,
        locked,
        offline,
        unstable_features: _,
    } = args;

    let target = match (target, bump) {
        (None, None) => TargetVersion::Relative(BumpLevel::Release),
        (None, Some(level)) => TargetVersion::Relative(level),
        (Some(version), None) => TargetVersion::Absolute(version),
        (Some(_), Some(_)) => unreachable!("clap groups should prevent this"),
    };

    if all {
        shell_warn("The flag `--all` has been deprecated in favor of `--workspace`")?;
    }
    let all = workspace || all;
    let manifests = resolve_manifests(
        manifest_path.as_deref(),
        all,
        pkgid.as_deref().into_iter().collect::<Vec<_>>(),
    )?;

    let ws_metadata = resolve_ws(manifest_path.as_deref(), locked, offline)?;
    let root_manifest_path = ws_metadata.workspace_root.as_std_path().join("Cargo.toml");
    let workspace_members = find_ws_members(&ws_metadata);

    for package in manifests {
        if exclude.contains(&package.name) {
            continue;
        }
        let current = &package.version;
        let next = target.bump(current, metadata.as_deref())?;
        if let Some(next) = next {
            {
                let mut manifest = LocalManifest::try_new(Path::new(&package.manifest_path))?;
                manifest.set_package_version(&next);

                shell_status(
                    "Upgrading",
                    &format!("{} from {} to {}", package.name, current, next),
                )?;
                if !dry_run {
                    manifest.write()?;
                }
            }

            let crate_root =
                dunce::canonicalize(package.manifest_path.parent().expect("at least a parent"))?;
            update_member_dependents(
                &root_manifest_path,
                &workspace_members,
                &crate_root,
                &next,
                dry_run,
            )?
        }
    }

    if args.dry_run {
        shell_warn("aborting set-version due to dry run")?;
    }

    Ok(())
}

fn update_member_dependents(
    root_manifest_path: &Path,
    workspace_members: &[cargo_metadata::Package],
    crate_root: &Path,
    next: &semver::Version,
    dry_run: bool,
) -> CargoResult<()> {
    // This is redundant with `workspace_members`
    // - As `get_dependency_tables_mut` returns workspace dependencies
    // - If there is a root package
    //
    // But split this out for
    // - Virtual manifests
    // - Nicer message to the user
    {
        let mut dep_manifest = LocalManifest::try_new(root_manifest_path)?;
        let mut changed = false;
        let dep_crate_root = dep_manifest
            .path
            .parent()
            .expect("at least a parent")
            .to_owned();

        if let Some(workspace_deps) = dep_manifest.get_workspace_dependency_table_mut() {
            for dep in workspace_deps
                .iter_mut()
                .filter_map(|(_, d)| d.as_table_like_mut())
                .filter(|d| is_relevant(*d, &dep_crate_root, crate_root))
            {
                let old_req = dep
                    .get("version")
                    .expect("filter ensures this")
                    .as_str()
                    .unwrap_or("*");
                if let Some(new_req) = upgrade_requirement(old_req, &next)? {
                    shell_status(
                        "Updating",
                        &format!("workspace dependency from {} to {}", old_req, new_req),
                    )?;
                    dep.insert("version", toml_edit::value(new_req));
                    changed = true;
                }
            }
        }

        if changed && !dry_run {
            dep_manifest.write()?;
        }
    }

    for member in workspace_members.iter() {
        let mut dep_manifest = LocalManifest::try_new(member.manifest_path.as_std_path())?;
        let mut changed = false;
        let dep_crate_root = dep_manifest
            .path
            .parent()
            .expect("at least a parent")
            .to_owned();

        for dep in dep_manifest
            .get_dependency_tables_mut()
            .flat_map(|t| t.iter_mut().filter_map(|(_, d)| d.as_table_like_mut()))
            .filter(|d| is_relevant(*d, &dep_crate_root, crate_root))
        {
            let old_req = dep
                .get("version")
                .expect("filter ensures this")
                .as_str()
                .unwrap_or("*");
            if let Some(new_req) = upgrade_requirement(old_req, &next)? {
                shell_status(
                    "Updating",
                    &format!(
                        "{}'s dependency from {} to {}",
                        member.name, old_req, new_req
                    ),
                )?;
                dep.insert("version", toml_edit::value(new_req));
                changed = true;
            }
        }
        if changed && !dry_run {
            dep_manifest.write()?;
        }
    }

    Ok(())
}

fn is_relevant(d: &dyn toml_edit::TableLike, dep_crate_root: &Path, crate_root: &Path) -> bool {
    if !d.contains_key("version") {
        return false;
    }
    match d
        .get("path")
        .and_then(|i| i.as_str())
        .and_then(|relpath| dunce::canonicalize(dep_crate_root.join(relpath)).ok())
    {
        Some(dep_path) => dep_path == crate_root,
        None => false,
    }
}

fn resolve_ws(
    manifest_path: Option<&Path>,
    locked: bool,
    offline: bool,
) -> CargoResult<cargo_metadata::Metadata> {
    let mut cmd = cargo_metadata::MetadataCommand::new();
    if let Some(manifest_path) = manifest_path {
        cmd.manifest_path(manifest_path);
    }
    cmd.features(cargo_metadata::CargoOpt::AllFeatures);
    let mut other = Vec::new();
    if locked {
        other.push("--locked".to_owned());
    }
    if offline {
        other.push("--offline".to_owned());
    }
    cmd.other_options(other);

    let ws = cmd.exec().or_else(|_| {
        cmd.no_deps();
        cmd.exec()
    })?;
    Ok(ws)
}

fn find_ws_members(ws: &cargo_metadata::Metadata) -> Vec<cargo_metadata::Package> {
    let workspace_members: std::collections::HashSet<_> = ws.workspace_members.iter().collect();
    ws.packages
        .iter()
        .filter(|p| workspace_members.contains(&p.id))
        .cloned()
        .collect()
}
<commit_msg>refactor(version): Put what we are acting on first<commit_after>use std::path::Path;
use std::path::PathBuf;

use cargo_edit::{resolve_manifests, shell_status, shell_warn, upgrade_requirement, LocalManifest};
use clap::Args;

use crate::errors::*;
use crate::version::BumpLevel;
use crate::version::TargetVersion;

/// Change a package's version in the local manifest file (i.e. Cargo.toml).
#[derive(Debug, Args)]
#[command(version)]
#[command(group = clap::ArgGroup::new("ver").multiple(false))]
pub struct VersionArgs {
    /// Version to change manifests to
    #[arg(group = "ver")]
    target: Option<semver::Version>,

    /// Increment manifest version
    #[arg(long, group = "ver")]
    bump: Option<BumpLevel>,

    /// Specify the version metadata field (e.g. a wrapped libraries version)
    #[arg(short, long)]
    pub metadata: Option<String>,

    /// Path to the manifest to upgrade
    #[arg(long, value_name = "PATH")]
    manifest_path: Option<PathBuf>,

    /// Package id of the crate to change the version of.
    #[arg(
        long = "package",
        short = 'p',
        value_name = "PKGID",
        conflicts_with = "all",
        conflicts_with = "workspace"
    )]
    pkgid: Option<String>,

    /// Modify all packages in the workspace.
    #[arg(
        long,
        help = "[deprecated in favor of `--workspace`]",
        conflicts_with = "workspace",
        conflicts_with = "pkgid"
    )]
    all: bool,

    /// Modify all packages in the workspace.
    #[arg(long, conflicts_with = "all", conflicts_with = "pkgid")]
    workspace: bool,

    /// Print changes to be made without making them.
    #[arg(long)]
    dry_run: bool,

    /// Crates to exclude and not modify.
    #[arg(long)]
    exclude: Vec<String>,

    /// Run without accessing the network
    #[arg(long)]
    offline: bool,

    /// Require `Cargo.toml` to be up to date
    #[arg(long)]
    locked: bool,

    /// Unstable (nightly-only) flags
    #[arg(short = 'Z', value_name = "FLAG", global = true, value_enum)]
    unstable_features: Vec<UnstableOptions>,
}

impl VersionArgs {
    pub fn exec(self) -> CargoResult<()> {
        exec(self)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ValueEnum)]
enum UnstableOptions {}

/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error
/// messages.
fn exec(args: VersionArgs) -> CargoResult<()> {
    let VersionArgs {
        target,
        bump,
        metadata,
        manifest_path,
        pkgid,
        all,
        dry_run,
        workspace,
        exclude,
        locked,
        offline,
        unstable_features: _,
    } = args;

    let target = match (target, bump) {
        (None, None) => TargetVersion::Relative(BumpLevel::Release),
        (None, Some(level)) => TargetVersion::Relative(level),
        (Some(version), None) => TargetVersion::Absolute(version),
        (Some(_), Some(_)) => unreachable!("clap groups should prevent this"),
    };

    if all {
        shell_warn("The flag `--all` has been deprecated in favor of `--workspace`")?;
    }
    let all = workspace || all;
    let manifests = resolve_manifests(
        manifest_path.as_deref(),
        all,
        pkgid.as_deref().into_iter().collect::<Vec<_>>(),
    )?;

    let ws_metadata = resolve_ws(manifest_path.as_deref(), locked, offline)?;
    let root_manifest_path = ws_metadata.workspace_root.as_std_path().join("Cargo.toml");
    let workspace_members = find_ws_members(&ws_metadata);

    for package in manifests {
        if exclude.contains(&package.name) {
            continue;
        }
        let current = &package.version;
        let next = target.bump(current, metadata.as_deref())?;
        if let Some(next) = next {
            {
                let mut manifest = LocalManifest::try_new(Path::new(&package.manifest_path))?;
                manifest.set_package_version(&next);

                shell_status(
                    "Upgrading",
                    &format!("{} from {} to {}", package.name, current, next),
                )?;
                if !dry_run {
                    manifest.write()?;
                }
            }

            let crate_root =
                dunce::canonicalize(package.manifest_path.parent().expect("at least a parent"))?;
            update_member_dependents(
                &crate_root,
                &next,
                &root_manifest_path,
                &workspace_members,
                dry_run,
            )?
        }
    }

    if args.dry_run {
        shell_warn("aborting set-version due to dry run")?;
    }

    Ok(())
}

fn update_member_dependents(
    crate_root: &Path,
    next: &semver::Version,
    root_manifest_path: &Path,
    workspace_members: &[cargo_metadata::Package],
    dry_run: bool,
) -> CargoResult<()> {
    // This is redundant with `workspace_members`
    // - As `get_dependency_tables_mut` returns workspace dependencies
    // - If there is a root package
    //
    // But split this out for
    // - Virtual manifests
    // - Nicer message to the user
    {
        let mut dep_manifest = LocalManifest::try_new(root_manifest_path)?;
        let mut changed = false;
        let dep_crate_root = dep_manifest
            .path
            .parent()
            .expect("at least a parent")
            .to_owned();

        if let Some(workspace_deps) = dep_manifest.get_workspace_dependency_table_mut() {
            for dep in workspace_deps
                .iter_mut()
                .filter_map(|(_, d)| d.as_table_like_mut())
                .filter(|d| is_relevant(*d, &dep_crate_root, crate_root))
            {
                let old_req = dep
                    .get("version")
                    .expect("filter ensures this")
                    .as_str()
                    .unwrap_or("*");
                if let Some(new_req) = upgrade_requirement(old_req, &next)? {
                    shell_status(
                        "Updating",
                        &format!("workspace dependency from {} to {}", old_req, new_req),
                    )?;
                    dep.insert("version", toml_edit::value(new_req));
                    changed = true;
                }
            }
        }

        if changed && !dry_run {
            dep_manifest.write()?;
        }
    }

    for member in workspace_members.iter() {
        let mut dep_manifest = LocalManifest::try_new(member.manifest_path.as_std_path())?;
        let mut changed = false;
        let dep_crate_root = dep_manifest
            .path
            .parent()
            .expect("at least a parent")
            .to_owned();

        for dep in dep_manifest
            .get_dependency_tables_mut()
            .flat_map(|t| t.iter_mut().filter_map(|(_, d)| d.as_table_like_mut()))
            .filter(|d| is_relevant(*d, &dep_crate_root, crate_root))
        {
            let old_req = dep
                .get("version")
                .expect("filter ensures this")
                .as_str()
                .unwrap_or("*");
            if let Some(new_req) = upgrade_requirement(old_req, &next)? {
                shell_status(
                    "Updating",
                    &format!(
                        "{}'s dependency from {} to {}",
                        member.name, old_req, new_req
                    ),
                )?;
                dep.insert("version", toml_edit::value(new_req));
                changed = true;
            }
        }
        if changed && !dry_run {
            dep_manifest.write()?;
        }
    }

    Ok(())
}

fn is_relevant(d: &dyn toml_edit::TableLike, dep_crate_root: &Path, crate_root: &Path) -> bool {
    if !d.contains_key("version") {
        return false;
    }
    match d
        .get("path")
        .and_then(|i| i.as_str())
        .and_then(|relpath| dunce::canonicalize(dep_crate_root.join(relpath)).ok())
    {
        Some(dep_path) => dep_path == crate_root,
        None => false,
    }
}

fn resolve_ws(
    manifest_path: Option<&Path>,
    locked: bool,
    offline: bool,
) -> CargoResult<cargo_metadata::Metadata> {
    let mut cmd = cargo_metadata::MetadataCommand::new();
    if let Some(manifest_path) = manifest_path {
        cmd.manifest_path(manifest_path);
    }
    cmd.features(cargo_metadata::CargoOpt::AllFeatures);
    let mut other = Vec::new();
    if locked {
        other.push("--locked".to_owned());
    }
    if offline {
        other.push("--offline".to_owned());
    }
    cmd.other_options(other);

    let ws = cmd.exec().or_else(|_| {
        cmd.no_deps();
        cmd.exec()
    })?;
    Ok(ws)
}

fn find_ws_members(ws: &cargo_metadata::Metadata) -> Vec<cargo_metadata::Package> {
    let workspace_members: std::collections::HashSet<_> = ws.workspace_members.iter().collect();
    ws.packages
        .iter()
        .filter(|p| workspace_members.contains(&p.id))
        .cloned()
        .collect()
}
<|endoftext|>