<commit_before>#![allow(clippy::large_enum_variant)]
#![allow(deprecated)] // because of `Error::description` deprecation in `error_chain`

use crate::dist::dist::Profile;
use crate::dist::manifest::{Component, Manifest};
use crate::dist::temp;
use crate::{component_for_bin, Toolchain};
use error_chain::error_chain;
use std::ffi::OsString;
use std::io::{self, Write};
use std::path::PathBuf;
use url::Url;

pub const TOOLSTATE_MSG: &str =
    "If you require these components, please install and use the latest successful build version,\n\
     which you can find at <https://rust-lang.github.io/rustup-components-history>.\n\nAfter determining \
     the correct date, install it with a command such as:\n\n    \
     rustup toolchain install nightly-2018-12-27\n\n\
     Then you can use the toolchain with commands such as:\n\n    \
     cargo +nightly-2018-12-27 build";

error_chain! {
    links {
        Download(download::Error, download::ErrorKind);
    }

    foreign_links {
        Temp(temp::Error);
        Io(io::Error);
        Open(opener::OpenError);
        Thread(std::sync::mpsc::RecvError);
    }

    errors {
        LocatingWorkingDir {
            description("Unable to proceed. Could not locate working directory.")
        }
        ReadingFile {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not read file")
            display("could not read {} file: '{}'", name, path.display())
        }
        ReadingDirectory {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not read directory")
            display("could not read {} directory: '{}'", name, path.display())
        }
        WritingFile {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not write file")
            display("could not write {} file: '{}'", name, path.display())
        }
        CreatingDirectory {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not create directory")
            display("could not create {} directory: '{}'", name, path.display())
        }
        ExpectedType(t: &'static str, n: String) {
            description("expected type")
            display("expected type: '{}' for '{}'", t, n)
        }
        FilteringFile {
            name: &'static str,
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not copy file")
            display("could not copy {} file from '{}' to '{}'", name, src.display(), dest.display())
        }
        RenamingFile {
            name: &'static str,
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not rename file")
            display("could not rename {} file from '{}' to '{}'",
                name, src.display(), dest.display())
        }
        RenamingDirectory {
            name: &'static str,
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not rename directory")
            display("could not rename {} directory from '{}' to '{}'", name, src.display(), dest.display())
        }
        DownloadingFile {
            url: Url,
            path: PathBuf,
        } {
            description("could not download file")
            display("could not download file from '{}' to '{}'", url, path.display())
        }
        DownloadNotExists {
            url: Url,
            path: PathBuf,
        } {
            description("could not download file")
            display("could not download file from '{}' to '{}'", url, path.display())
        }
        InvalidUrl {
            url: String,
        } {
            description("invalid url")
            display("invalid url: {}", url)
        }
        RunningCommand {
            name: OsString,
        } {
            description("command failed")
            display("command failed: '{}'", PathBuf::from(name).display())
        }
        NotAFile {
            path: PathBuf,
        } {
            description("not a file")
            display("not a file: '{}'", path.display())
        }
        NotADirectory {
            path: PathBuf,
        } {
            description("not a directory")
            display("not a directory: '{}'", path.display())
        }
        LinkingFile {
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not link file")
            display("could not create link from '{}' to '{}'", src.display(), dest.display())
        }
        LinkingDirectory {
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not symlink directory")
            display("could not create link from '{}' to '{}'", src.display(), dest.display())
        }
        CopyingDirectory {
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not copy directory")
            display("could not copy directory from '{}' to '{}'", src.display(), dest.display())
        }
        CopyingFile {
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not copy file")
            display("could not copy file from '{}' to '{}'", src.display(), dest.display())
        }
        RemovingFile {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not remove file")
            display("could not remove '{}' file: '{}'", name, path.display())
        }
        RemovingDirectory {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not remove directory")
            display("could not remove '{}' directory: '{}'", name, path.display())
        }
        SettingPermissions {
            path: PathBuf,
        } {
            description("failed to set permissions")
            display("failed to set permissions for '{}'", path.display())
        }
        CargoHome {
            description("couldn't find value of CARGO_HOME")
        }
        RustupHome {
            description("couldn't find value of RUSTUP_HOME")
        }
        InvalidToolchainName(t: String) {
            description("invalid toolchain name")
            display("invalid toolchain name: '{}'", t)
        }
        InvalidProfile(t: String) {
            description("invalid profile name")
            display("invalid profile name: '{}'; valid names are: {}", t, valid_profile_names())
        }
        ChecksumFailed {
            url: String,
            expected: String,
            calculated: String,
        } {
            description("checksum failed")
            display("checksum failed, expected: '{}', calculated: '{}'",
                    expected,
                    calculated)
        }
        SignatureVerificationInternalError(msg: String) {
            description("internal error verifying signature")
            display("internal error verifying signature: {}", msg)
        }
        SignatureVerificationFailed {
            url: String,
        } {
            description("signature verification failed")
            display("signature verification failed for {}", url)
        }
        ComponentConflict {
            name: String,
            path: PathBuf,
        } {
            description("conflicting component")
            display("failed to install component: '{}', detected conflict: '{:?}'",
                    name,
                    path)
        }
        ComponentMissingFile {
            name: String,
            path: PathBuf,
        } {
            description("missing file in component")
            display("failure removing component '{}', directory does not exist: '{:?}'",
                    name,
                    path)
        }
        ComponentMissingDir {
            name: String,
            path: PathBuf,
        } {
            description("missing directory in component")
            display("failure removing component '{}', directory does not exist: '{:?}'",
                    name,
                    path)
        }
        CorruptComponent(name: String) {
            description("corrupt component manifest")
            display("component manifest for '{}' is corrupt", name)
        }
        ExtractingPackage {
            description("failed to extract package (perhaps you ran out of disk space?)")
        }
        BadInstallerVersion(v: String) {
            description("unsupported installer version")
            display("unsupported installer version: {}", v)
        }
        BadInstalledMetadataVersion(v: String) {
            description("unsupported metadata version in existing installation")
            display("unsupported metadata version in existing installation: {}", v)
        }
        ComponentDirPermissionsFailed {
            description("I/O error walking directory during install")
        }
        ComponentFilePermissionsFailed {
            description("error setting file permissions during install")
        }
        ComponentDownloadFailed(c: String) {
            description("component download failed")
            display("component download failed for {}", c)
        }
        Parsing(e: toml::de::Error) {
            description("error parsing manifest")
        }
        UnsupportedVersion(v: String) {
            description("unsupported manifest version")
            display("manifest version '{}' is not supported", v)
        }
        MissingPackageForComponent(name: String) {
            description("missing package for component")
            display("server sent a broken manifest: missing package for component {}", name)
        }
        MissingPackageForRename(name: String) {
            description("missing package for the target of a rename")
            display("server sent a broken manifest: missing package for the target of a rename {}", name)
        }
        MissingReleaseForToolchain(name: String) {
            description("missing release for a toolchain")
            display("no release found for '{}'", name)
        }
        RequestedComponentsUnavailable(c: Vec<Component>, manifest: Manifest, toolchain: String) {
            description("some requested components are unavailable to download")
            display("{}", component_unavailable_msg(&c, &manifest, &toolchain))
        }
        UnknownMetadataVersion(v: String) {
            description("unknown metadata version")
            display("unknown metadata version: '{}'", v)
        }
        ToolchainNotInstalled(t: String) {
            description("toolchain is not installed")
            display("toolchain '{}' is not installed", t)
        }
        ToolchainNotSelected {
            description("toolchain is not selected")
            display("no override and no default toolchain set")
        }
        OverrideToolchainNotInstalled(t: String) {
            description("override toolchain is not installed")
            display("override toolchain '{}' is not installed", t)
        }
        BinaryNotFound(bin: String, t: String, is_default: bool) {
            description("toolchain does not contain binary")
            display("'{}' is not installed for the toolchain '{}'{}", bin, t, install_msg(bin, t, *is_default))
        }
        BinaryProvidedByUnavailableComponent(component: String, bin: String, toolchain: String) {
            description("binary is provided by a component which is not available in current toolchain")
            display("the '{}' component which provides the command '{}' is not available for the '{}' toolchain", component, bin, toolchain)
        }
        BinaryNotProvidedByComponent(component: String, bin: String, toolchain: String) {
            description("binary should be provided by component but isn't in current toolchain")
            display("the '{}' binary, normally provided by the '{}' component, is not applicable to the '{}' toolchain", bin, component, toolchain)
        }
        NeedMetadataUpgrade {
            description("rustup's metadata is out of date. run `rustup self upgrade-data`")
        }
        UpgradeIoError {
            description("I/O error during upgrade")
        }
        BadInstallerType(s: String) {
            description("invalid extension for installer")
            display("invalid extension for installer: '{}'", s)
        }
        ComponentsUnsupported(t: String) {
            description("toolchain does not support components")
            display("toolchain '{}' does not support components", t)
        }
        UnknownComponent(t: String, c: String, s: Option<String>) {
            description("toolchain does not contain component")
            display("toolchain '{}' does not contain component {}{}", t, c, if let Some(suggestion) = s {
                format!("; did you mean '{}'?", suggestion)
            } else {
                "".to_string()
            })
        }
        UnknownProfile(p: String) {
            description("unknown profile name")
            display(
                "unknown profile name: '{}'; valid profile names are {}",
                p,
                valid_profile_names(),
            )
        }
        AddingRequiredComponent(t: String, c: String) {
            description("required component cannot be added")
            display("component {} was automatically added because it is required for toolchain '{}'",
                    c, t)
        }
        ParsingFallbackSettings(e: toml::de::Error) {
            description("error parsing settings")
        }
        ParsingSettings(e: toml::de::Error) {
            description("error parsing settings")
        }
        NoExeName {
            description("couldn't determine self executable name")
        }
        UnsupportedKind(v: String) {
            description("unsupported tar entry")
            display("tar entry kind '{}' is not supported", v)
        }
        BadPath(v: PathBuf) {
            description("bad path in tar")
            display("tar path '{}' is not supported", v.display())
        }
        InvalidPgpKey(v: PathBuf, error: pgp::errors::Error) {
            description("invalid PGP key"),
            display("unable to read the PGP key '{}'", v.display())
        }
        BrokenPartialFile {
            description("partially downloaded file may have been damaged and was removed, please try again")
        }
        EmptyOverrideFile {
            description("empty toolchain override file detected. Please remove it, or else specify the desired toolchain properties in the file")
        }
        InvalidOverrideFile {
            description("missing toolchain properties in toolchain override file")
        }
        ParsingOverrideFile(e: toml::de::Error) {
            description("error parsing override file")
        }
    }
}

fn valid_profile_names() -> String {
    Profile::names()
        .iter()
        .map(|s| format!("'{}'", s))
        .collect::<Vec<_>>()
        .join(", ")
}

fn remove_component_msg(cs: &Component, manifest: &Manifest, toolchain: &str) -> String {
    if cs.short_name_in_manifest() == "rust-std" {
        // We special-case rust-std as it's the stdlib so really you want to do
        // rustup target remove
        format!(
            "    rustup target remove --toolchain {} {}",
            toolchain,
            cs.target.as_deref().unwrap_or(toolchain)
        )
    } else {
        format!(
            "    rustup component remove --toolchain {}{} {}",
            toolchain,
            if let Some(target) = cs.target.as_ref() {
                format!(" --target {}", target)
            } else {
                String::default()
            },
            cs.short_name(manifest)
        )
    }
}

fn component_unavailable_msg(cs: &[Component], manifest: &Manifest, toolchain: &str) -> String {
    assert!(!cs.is_empty());

    let mut buf = vec![];

    if cs.len() == 1 {
        let _ = write!(
            buf,
            "component {} is unavailable for download for channel {}{}",
            &cs[0].description(manifest),
            toolchain,
            if toolchain.starts_with("nightly") {
                "\nSometimes not all components are available in any given nightly. "
            } else {
                " "
            }
        );

        let _ = write!(
            buf,
            "If you don't need the component, you can remove it with:\n\n{}",
            remove_component_msg(&cs[0], manifest, toolchain)
        );
    } else {
        let same_target = cs
            .iter()
            .all(|c| c.target == cs[0].target || c.target.is_none());
        if same_target {
            let cs_str = cs
                .iter()
                .map(|c| format!("'{}'", c.short_name(manifest)))
                .collect::<Vec<_>>()
                .join(", ");
            let remove_msg = cs
                .iter()
                .map(|c| remove_component_msg(c, manifest, toolchain))
                .collect::<Vec<_>>()
                .join("\n");
            let _ = write!(
                buf,
                "some components unavailable for download for channel {}: {}
                 If you don't need the components, you can remove them with:\n\n{}\n\n{}",
                toolchain, cs_str, remove_msg, TOOLSTATE_MSG,
            );
        } else {
            let cs_str = cs
                .iter()
                .map(|c| c.description(manifest))
                .collect::<Vec<_>>()
                .join(", ");
            let remove_msg = cs
                .iter()
                .map(|c| remove_component_msg(c, manifest, toolchain))
                .collect::<Vec<_>>()
                .join("\n");
            let _ = write!(
                buf,
                "some components unavailable for download for channel {}: {}
                If you don't need the components, you can remove them with:\n{}\n{}",
                toolchain, cs_str, remove_msg, TOOLSTATE_MSG,
            );
        }
    }

    String::from_utf8(buf).unwrap()
}

fn install_msg(bin: &str, toolchain: &str, is_default: bool) -> String {
    if Toolchain::is_custom_name(toolchain) {
        return "\nnote: this is a custom toolchain, which cannot use `rustup component add`\n\
        help: if you built this toolchain from source, and used `rustup toolchain link`, then you may be able to build the component with `x.py`".to_string();
    }
    match component_for_bin(bin) {
        Some(c) => format!("\nTo install, run `rustup component add {}{}`", c, {
            if is_default {
                String::new()
            } else {
                format!(" --toolchain {}", toolchain)
            }
        }),
        None => String::new(),
    }
}
<commit_msg>Make formatting of error messages more consistent<commit_after>#![allow(clippy::large_enum_variant)]
#![allow(deprecated)] // because of `Error::description` deprecation in `error_chain`

use crate::dist::dist::Profile;
use crate::dist::manifest::{Component, Manifest};
use crate::dist::temp;
use crate::{component_for_bin, Toolchain};
use error_chain::error_chain;
use std::ffi::OsString;
use std::io::{self, Write};
use std::path::PathBuf;
use url::Url;

pub const TOOLSTATE_MSG: &str =
    "If you require these components, please install and use the latest successful build version,\n\
     which you can find at <https://rust-lang.github.io/rustup-components-history>.\n\nAfter determining \
     the correct date, install it with a command such as:\n\n    \
     rustup toolchain install nightly-2018-12-27\n\n\
     Then you can use the toolchain with commands such as:\n\n    \
     cargo +nightly-2018-12-27 build";

error_chain! {
    links {
        Download(download::Error, download::ErrorKind);
    }

    foreign_links {
        Temp(temp::Error);
        Io(io::Error);
        Open(opener::OpenError);
        Thread(std::sync::mpsc::RecvError);
    }

    errors {
        LocatingWorkingDir {
            description("Unable to proceed. Could not locate working directory.")
        }
        ReadingFile {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not read file")
            display("could not read {} file: '{}'", name, path.display())
        }
        ReadingDirectory {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not read directory")
            display("could not read {} directory: '{}'", name, path.display())
        }
        WritingFile {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not write file")
            display("could not write {} file: '{}'", name, path.display())
        }
        CreatingDirectory {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not create directory")
            display("could not create {} directory: '{}'", name, path.display())
        }
        ExpectedType(t: &'static str, n: String) {
            description("expected type")
            display("expected type: '{}' for '{}'", t, n)
        }
        FilteringFile {
            name: &'static str,
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not copy file")
            display("could not copy {} file from '{}' to '{}'", name, src.display(), dest.display())
        }
        RenamingFile {
            name: &'static str,
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not rename file")
            display("could not rename {} file from '{}' to '{}'",
                name, src.display(), dest.display())
        }
        RenamingDirectory {
            name: &'static str,
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not rename directory")
            display("could not rename {} directory from '{}' to '{}'", name, src.display(), dest.display())
        }
        DownloadingFile {
            url: Url,
            path: PathBuf,
        } {
            description("could not download file")
            display("could not download file from '{}' to '{}'", url, path.display())
        }
        DownloadNotExists {
            url: Url,
            path: PathBuf,
        } {
            description("could not download file")
            display("could not download file from '{}' to '{}'", url, path.display())
        }
        InvalidUrl {
            url: String,
        } {
            description("invalid url")
            display("invalid url: {}", url)
        }
        RunningCommand {
            name: OsString,
        } {
            description("command failed")
            display("command failed: '{}'", PathBuf::from(name).display())
        }
        NotAFile {
            path: PathBuf,
        } {
            description("not a file")
            display("not a file: '{}'", path.display())
        }
        NotADirectory {
            path: PathBuf,
        } {
            description("not a directory")
            display("not a directory: '{}'", path.display())
        }
        LinkingFile {
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not link file")
            display("could not create link from '{}' to '{}'", src.display(), dest.display())
        }
        LinkingDirectory {
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not symlink directory")
            display("could not create link from '{}' to '{}'", src.display(), dest.display())
        }
        CopyingDirectory {
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not copy directory")
            display("could not copy directory from '{}' to '{}'", src.display(), dest.display())
        }
        CopyingFile {
            src: PathBuf,
            dest: PathBuf,
        } {
            description("could not copy file")
            display("could not copy file from '{}' to '{}'", src.display(), dest.display())
        }
        RemovingFile {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not remove file")
            display("could not remove '{}' file: '{}'", name, path.display())
        }
        RemovingDirectory {
            name: &'static str,
            path: PathBuf,
        } {
            description("could not remove directory")
            display("could not remove '{}' directory: '{}'", name, path.display())
        }
        SettingPermissions {
            path: PathBuf,
        } {
            description("failed to set permissions")
            display("failed to set permissions for '{}'", path.display())
        }
        CargoHome {
            description("couldn't find value of CARGO_HOME")
        }
        RustupHome {
            description("couldn't find value of RUSTUP_HOME")
        }
        InvalidToolchainName(t: String) {
            description("invalid toolchain name")
            display("invalid toolchain name: '{}'", t)
        }
        InvalidProfile(t: String) {
            description("invalid profile name")
            display("invalid profile name: '{}'; valid names are: {}", t, valid_profile_names())
        }
        ChecksumFailed {
            url: String,
            expected: String,
            calculated: String,
        } {
            description("checksum failed")
            display("checksum failed, expected: '{}', calculated: '{}'",
                    expected,
                    calculated)
        }
        SignatureVerificationInternalError(msg: String) {
            description("internal error verifying signature")
            display("internal error verifying signature: {}", msg)
        }
        SignatureVerificationFailed {
            url: String,
        } {
            description("signature verification failed")
            display("signature verification failed for {}", url)
        }
        ComponentConflict {
            name: String,
            path: PathBuf,
        } {
            description("conflicting component")
            display("failed to install component: '{}', detected conflict: '{:?}'",
                    name,
                    path)
        }
        ComponentMissingFile {
            name: String,
            path: PathBuf,
        } {
            description("missing file in component")
            display("failure removing component '{}', directory does not exist: '{:?}'",
                    name,
                    path)
        }
        ComponentMissingDir {
            name: String,
            path: PathBuf,
        } {
            description("missing directory in component")
            display("failure removing component '{}', directory does not exist: '{:?}'",
                    name,
                    path)
        }
        CorruptComponent(name: String) {
            description("corrupt component manifest")
            display("component manifest for '{}' is corrupt", name)
        }
        ExtractingPackage {
            description("failed to extract package (perhaps you ran out of disk space?)")
        }
        BadInstallerVersion(v: String) {
            description("unsupported installer version")
            display("unsupported installer version: {}", v)
        }
        BadInstalledMetadataVersion(v: String) {
            description("unsupported metadata version in existing installation")
            display("unsupported metadata version in existing installation: {}", v)
        }
        ComponentDirPermissionsFailed {
            description("I/O error walking directory during install")
        }
        ComponentFilePermissionsFailed {
            description("error setting file permissions during install")
        }
        ComponentDownloadFailed(c: String) {
            description("component download failed")
            display("component download failed for {}", c)
        }
        Parsing(e: toml::de::Error) {
            description("error parsing manifest")
        }
        UnsupportedVersion(v: String) {
            description("unsupported manifest version")
            display("manifest version '{}' is not supported", v)
        }
        MissingPackageForComponent(name: String) {
            description("missing package for component")
            display("server sent a broken manifest: missing package for component {}", name)
        }
        MissingPackageForRename(name: String) {
            description("missing package for the target of a rename")
            display("server sent a broken manifest: missing package for the target of a rename {}", name)
        }
        MissingReleaseForToolchain(name: String) {
            description("missing release for a toolchain")
            display("no release found for '{}'", name)
        }
        RequestedComponentsUnavailable(c: Vec<Component>, manifest: Manifest, toolchain: String) {
            description("some requested components are unavailable to download")
            display("{}", component_unavailable_msg(&c, &manifest, &toolchain))
        }
        UnknownMetadataVersion(v: String) {
            description("unknown metadata version")
            display("unknown metadata version: '{}'", v)
        }
        ToolchainNotInstalled(t: String) {
            description("toolchain is not installed")
            display("toolchain '{}' is not installed", t)
        }
        ToolchainNotSelected {
            description("toolchain is not selected")
            display("no override and no default toolchain set")
        }
        OverrideToolchainNotInstalled(t: String) {
            description("override toolchain is not installed")
            display("override toolchain '{}' is not installed", t)
        }
        BinaryNotFound(bin: String, t: String, is_default: bool) {
            description("toolchain does not contain binary")
            display("'{}' is not installed for the toolchain '{}'{}", bin, t, install_msg(bin, t, *is_default))
        }
        BinaryProvidedByUnavailableComponent(component: String, bin: String, toolchain: String) {
            description("binary is provided by a component which is not available in current toolchain")
            display("the '{}' component which provides the command '{}' is not available for the '{}' toolchain", component, bin, toolchain)
        }
        BinaryNotProvidedByComponent(component: String, bin: String, toolchain: String) {
            description("binary should be provided by component but isn't in current toolchain")
            display("the '{}' binary, normally provided by the '{}' component, is not applicable to the '{}' toolchain", bin, component, toolchain)
        }
        NeedMetadataUpgrade {
            description("rustup's metadata is out of date. run `rustup self upgrade-data`")
        }
        UpgradeIoError {
            description("I/O error during upgrade")
        }
        BadInstallerType(s: String) {
            description("invalid extension for installer")
            display("invalid extension for installer: '{}'", s)
        }
        ComponentsUnsupported(t: String) {
            description("toolchain does not support components")
            display("toolchain '{}' does not support components", t)
        }
        UnknownComponent(t: String, c: String, s: Option<String>) {
            description("toolchain does not contain component")
            display("toolchain '{}' does not contain component {}{}", t, c, if let Some(suggestion) = s {
                format!("; did you mean '{}'?", suggestion)
            } else {
                "".to_string()
            })
        }
        UnknownProfile(p: String) {
            description("unknown profile name")
            display(
                "unknown profile name: '{}'; valid profile names are {}",
                p,
                valid_profile_names(),
            )
        }
        AddingRequiredComponent(t: String, c: String) {
            description("required component cannot be added")
            display("component {} was automatically added because it is required for toolchain '{}'",
                    c, t)
        }
        ParsingFallbackSettings(e: toml::de::Error) {
            description("error parsing settings")
        }
        ParsingSettings(e: toml::de::Error) {
            description("error parsing settings")
        }
        NoExeName {
            description("couldn't determine self executable name")
        }
        UnsupportedKind(v: String) {
            description("unsupported tar entry")
            display("tar entry kind '{}' is not supported", v)
        }
        BadPath(v: PathBuf) {
            description("bad path in tar")
            display("tar path '{}' is not supported", v.display())
        }
        InvalidPgpKey(v: PathBuf, error: pgp::errors::Error) {
            description("invalid PGP key"),
            display("unable to read the PGP key '{}'", v.display())
        }
        BrokenPartialFile {
            description("partially downloaded file may have been damaged and was removed, please try again")
        }
        EmptyOverrideFile {
            description("empty toolchain override file detected. Please remove it, or else specify the desired toolchain properties in the file")
        }
        InvalidOverrideFile {
            description("missing toolchain properties in toolchain override file")
        }
        ParsingOverrideFile(e: toml::de::Error) {
            description("error parsing override file")
        }
    }
}

fn valid_profile_names() -> String {
    Profile::names()
        .iter()
        .map(|s| format!("'{}'", s))
        .collect::<Vec<_>>()
        .join(", ")
}

fn remove_component_msg(cs: &Component, manifest: &Manifest, toolchain: &str) -> String {
    if cs.short_name_in_manifest() == "rust-std" {
        // We special-case rust-std as it's the stdlib so really you want to do
        // rustup target remove
        format!(
            "    rustup target remove --toolchain {} {}",
            toolchain,
            cs.target.as_deref().unwrap_or(toolchain)
        )
    } else {
        format!(
            "    rustup component remove --toolchain {}{} {}",
            toolchain,
            if let Some(target) = cs.target.as_ref() {
                format!(" --target {}", target)
            } else {
                String::default()
            },
            cs.short_name(manifest)
        )
    }
}

fn component_unavailable_msg(cs: &[Component], manifest: &Manifest, toolchain: &str) -> String {
    assert!(!cs.is_empty());

    let mut buf = vec![];

    if cs.len() == 1 {
        let _ = write!(
            buf,
            "component {} is unavailable for download for channel '{}'\n",
            &cs[0].description(manifest),
            toolchain,
        );
        if toolchain.starts_with("nightly") {
            let _ = write!(
                buf,
                "Sometimes not all components are available in any given nightly. "
            );
        }
        let _ = write!(
            buf,
            "If you don't need the component, you can remove it with:\n\n{}",
            remove_component_msg(&cs[0], manifest, toolchain)
        );
    } else {
        // More than one component

        let same_target = cs
            .iter()
            .all(|c| c.target == cs[0].target || c.target.is_none());

        let cs_str = if same_target {
            cs.iter()
                .map(|c| format!("'{}'", c.short_name(manifest)))
                .collect::<Vec<_>>()
                .join(", ")
        } else {
            cs.iter()
                .map(|c| c.description(manifest))
                .collect::<Vec<_>>()
                .join(", ")
        };

        let remove_msg = cs
            .iter()
            .map(|c| remove_component_msg(c, manifest, toolchain))
            .collect::<Vec<_>>()
            .join("\n");
        let _ = write!(
            buf,
            "some components unavailable for download for channel '{}': {}\n\
            If you don't need the components, you can remove them with:\n\n{}\n\n{}",
            toolchain, cs_str, remove_msg, TOOLSTATE_MSG,
        );
    }

    String::from_utf8(buf).unwrap()
}

fn install_msg(bin: &str, toolchain: &str, is_default: bool) -> String {
    if Toolchain::is_custom_name(toolchain) {
        return "\nnote: this is a custom toolchain, which cannot use `rustup component add`\n\
        help: if you built this toolchain from source, and used `rustup toolchain link`, then you may be able to build the component with `x.py`".to_string();
    }
    match component_for_bin(bin) {
        Some(c) => format!("\nTo install, run `rustup component add {}{}`", c, {
            if is_default {
                String::new()
            } else {
                format!(" --toolchain {}", toolchain)
            }
        }),
        None => String::new(),
    }
}
<|endoftext|>


<commit_before>#[derive(Clone, Debug, Copy)]
pub enum Event {
    /// The size of the window has changed.
    Resized(u32, u32),

    /// The position of the window has changed.
    Moved(i32, i32),

    /// The window has been closed.
    Closed,

    /// The window received a unicode character.
    ReceivedCharacter(char),

    /// The window gained or lost focus.
    ///
    /// The parameter is true if the window has gained focus, and false if it has lost focus.
    Focused(bool),

    /// An event from the keyboard has been received.
    KeyboardInput(ElementState, ScanCode, Option<VirtualKeyCode>),

    /// The cursor has moved on the window.
    ///
    /// The parameter are the (x,y) coords in pixels relative to the top-left corner of the window.
    MouseMoved((i32, i32)),

    /// A mouse wheel or touchpad scroll occurred. Depending on whether the
    ///
    /// A positive value indicates that the wheel was rotated forward, away from the user;
    /// a negative value indicates that the wheel was rotated backward, toward the user.
    MouseWheel(MouseScrollDelta),

    /// An event from the mouse has been received.
    MouseInput(ElementState, MouseButton),

    /// The event loop was woken up by another thread.
    Awakened,

    /// The window needs to be redrawn.
    Refresh,
}

pub type ScanCode = u8;

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum ElementState {
    Pressed,
    Released,
}

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum MouseButton {
    Left,
    Right,
    Middle,
    Other(u8),
}

#[derive(Debug, Clone, Copy)]
pub enum MouseScrollDelta {
	/// Amount in lines or rows to scroll in the horizontal
	/// and vertical directions.
	///
	/// Positive values indicate movement forward
	/// (away from the user) or rightwards.
	LineDelta(f32, f32),
	/// Amount in pixels to scroll in the horizontal and
	/// vertical direction.
	///
	/// Scroll events are expressed as a PixelDelta if
	/// supported by the device (eg. a touchpad) and
	/// platform.
	PixelDelta(f32, f32)
}

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum VirtualKeyCode {
    /// The '1' key over the letters.
    Key1,
    /// The '2' key over the letters.
    Key2,
    /// The '3' key over the letters.
    Key3,
    /// The '4' key over the letters.
    Key4,
    /// The '5' key over the letters.
    Key5,
    /// The '6' key over the letters.
    Key6,
    /// The '7' key over the letters.
    Key7,
    /// The '8' key over the letters.
    Key8,
    /// The '9' key over the letters.
    Key9,
    /// The '0' key over the 'O' and 'P' keys.
    Key0,

    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,

    /// The Escape key, next to F1.
    Escape,

    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,

    /// Print Screen/SysRq.
    Snapshot,
    /// Scroll Lock.
    Scroll,
    /// Pause/Break key, next to Scroll lock.
    Pause,

    /// `Insert`, next to Backspace.
    Insert,
    Home,
    Delete,
    End,
    PageDown,
    PageUp,

    Left,
    Up,
    Right,
    Down,

    /// The Backspace key, right over Enter.
    // TODO: rename
    Back,
    /// The Enter key.
    Return,
    /// The space bar.
    Space,

    Numlock,
    Numpad0,
    Numpad1,
    Numpad2,
    Numpad3,
    Numpad4,
    Numpad5,
    Numpad6,
    Numpad7,
    Numpad8,
    Numpad9,

    AbntC1,
    AbntC2,
    Add,
    Apostrophe,
    Apps,
    At,
    Ax,
    Backslash,
    Calculator,
    Capital,
    Colon,
    Comma,
    Convert,
    Decimal,
    Divide,
    Equals,
    Grave,
    Kana,
    Kanji,
    LAlt,
    LBracket,
    LControl,
    LMenu,
    LShift,
    LWin,
    Mail,
    MediaSelect,
    MediaStop,
    Minus,
    Multiply,
    Mute,
    MyComputer,
    NextTrack,
    NoConvert,
    NumpadComma,
    NumpadEnter,
    NumpadEquals,
    OEM102,
    Period,
    Playpause,
    Power,
    Prevtrack,
    RAlt,
    RBracket,
    RControl,
    RMenu,
    RShift,
    RWin,
    Semicolon,
    Slash,
    Sleep,
    Stop,
    Subtract,
    Sysrq,
    Tab,
    Underline,
    Unlabeled,
    VolumeDown,
    VolumeUp,
    Wake,
    Webback,
    WebFavorites,
    WebForward,
    WebHome,
    WebRefresh,
    WebSearch,
    WebStop,
    Yen,
}
<commit_msg>Correct MouseWheel doc comment.<commit_after>#[derive(Clone, Debug, Copy)]
pub enum Event {
    /// The size of the window has changed.
    Resized(u32, u32),

    /// The position of the window has changed.
    Moved(i32, i32),

    /// The window has been closed.
    Closed,

    /// The window received a unicode character.
    ReceivedCharacter(char),

    /// The window gained or lost focus.
    ///
    /// The parameter is true if the window has gained focus, and false if it has lost focus.
    Focused(bool),

    /// An event from the keyboard has been received.
    KeyboardInput(ElementState, ScanCode, Option<VirtualKeyCode>),

    /// The cursor has moved on the window.
    ///
    /// The parameter are the (x,y) coords in pixels relative to the top-left corner of the window.
    MouseMoved((i32, i32)),

    /// A mouse wheel movement or touchpad scroll occurred.
    MouseWheel(MouseScrollDelta),

    /// An event from the mouse has been received.
    MouseInput(ElementState, MouseButton),

    /// The event loop was woken up by another thread.
    Awakened,

    /// The window needs to be redrawn.
    Refresh,
}

pub type ScanCode = u8;

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum ElementState {
    Pressed,
    Released,
}

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum MouseButton {
    Left,
    Right,
    Middle,
    Other(u8),
}

#[derive(Debug, Clone, Copy)]
pub enum MouseScrollDelta {
	/// Amount in lines or rows to scroll in the horizontal
	/// and vertical directions.
	///
	/// Positive values indicate movement forward
	/// (away from the user) or rightwards.
	LineDelta(f32, f32),
	/// Amount in pixels to scroll in the horizontal and
	/// vertical direction.
	///
	/// Scroll events are expressed as a PixelDelta if
	/// supported by the device (eg. a touchpad) and
	/// platform.
	PixelDelta(f32, f32)
}

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum VirtualKeyCode {
    /// The '1' key over the letters.
    Key1,
    /// The '2' key over the letters.
    Key2,
    /// The '3' key over the letters.
    Key3,
    /// The '4' key over the letters.
    Key4,
    /// The '5' key over the letters.
    Key5,
    /// The '6' key over the letters.
    Key6,
    /// The '7' key over the letters.
    Key7,
    /// The '8' key over the letters.
    Key8,
    /// The '9' key over the letters.
    Key9,
    /// The '0' key over the 'O' and 'P' keys.
    Key0,

    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,

    /// The Escape key, next to F1.
    Escape,

    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,

    /// Print Screen/SysRq.
    Snapshot,
    /// Scroll Lock.
    Scroll,
    /// Pause/Break key, next to Scroll lock.
    Pause,

    /// `Insert`, next to Backspace.
    Insert,
    Home,
    Delete,
    End,
    PageDown,
    PageUp,

    Left,
    Up,
    Right,
    Down,

    /// The Backspace key, right over Enter.
    // TODO: rename
    Back,
    /// The Enter key.
    Return,
    /// The space bar.
    Space,

    Numlock,
    Numpad0,
    Numpad1,
    Numpad2,
    Numpad3,
    Numpad4,
    Numpad5,
    Numpad6,
    Numpad7,
    Numpad8,
    Numpad9,

    AbntC1,
    AbntC2,
    Add,
    Apostrophe,
    Apps,
    At,
    Ax,
    Backslash,
    Calculator,
    Capital,
    Colon,
    Comma,
    Convert,
    Decimal,
    Divide,
    Equals,
    Grave,
    Kana,
    Kanji,
    LAlt,
    LBracket,
    LControl,
    LMenu,
    LShift,
    LWin,
    Mail,
    MediaSelect,
    MediaStop,
    Minus,
    Multiply,
    Mute,
    MyComputer,
    NextTrack,
    NoConvert,
    NumpadComma,
    NumpadEnter,
    NumpadEquals,
    OEM102,
    Period,
    Playpause,
    Power,
    Prevtrack,
    RAlt,
    RBracket,
    RControl,
    RMenu,
    RShift,
    RWin,
    Semicolon,
    Slash,
    Sleep,
    Stop,
    Subtract,
    Sysrq,
    Tab,
    Underline,
    Unlabeled,
    VolumeDown,
    VolumeUp,
    Wake,
    Webback,
    WebFavorites,
    WebForward,
    WebHome,
    WebRefresh,
    WebSearch,
    WebStop,
    Yen,
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{App, SubCommand, ErrorKind, Arg};

#[test]
fn help_short() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "-h"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn help_long() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "--help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn help_no_subcommand() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::UnknownArgument);
}

#[test]
fn help_subcommand() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .subcommand(SubCommand::with_name("test")
            .about("tests things")
            .arg_from_usage("-v --verbose 'with verbosity'"))
        .get_matches_from_safe(vec!["myprog", "help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn print_app_help() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .args_from_usage("-f, --flag 'some flag'
                          --option [opt] 'some option'");
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).ok().expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [FLAGS] [OPTIONS]

FLAGS:
    -f, --flag       some flag
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
        --option <opt>    some option\n"));
}

#[test]
fn possible_values() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .args(&[Arg::from_usage("-o, --opt [opt] 'some option'").possible_values(&["one", "two"]),
                Arg::from_usage("[arg1] 'some pos arg'").possible_values(&["three", "four"])]);
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [FLAGS] [OPTIONS] [ARGS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --opt <opt>    some option [values: one, two]

ARGS:
    [arg1]    some pos arg [values: three, four]\n"));
}
<commit_msg>test(Help Message): adds test to prevent issue-472<commit_after>extern crate clap;

use clap::{App, SubCommand, ErrorKind, Arg};

#[test]
fn help_short() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "-h"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn help_long() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "--help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn help_no_subcommand() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::UnknownArgument);
}

#[test]
fn help_subcommand() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .subcommand(SubCommand::with_name("test")
            .about("tests things")
            .arg_from_usage("-v --verbose 'with verbosity'"))
        .get_matches_from_safe(vec!["myprog", "help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn print_app_help() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .args_from_usage("-f, --flag 'some flag'
                          --option [opt] 'some option'")
        .arg(Arg::with_name("other")
            .short("O")
            .long("other-opt")
            .takes_value(true)
            .help("some other opt"));
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).ok().expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [FLAGS] [OPTIONS]

FLAGS:
    -f, --flag       some flag
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
        --option <opt>         some option
    -O, --other-opt <other>    some other opt\n"));
}

#[test]
fn possible_values() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .args(&[Arg::from_usage("-o, --opt [opt] 'some option'").possible_values(&["one", "two"]),
                Arg::from_usage("[arg1] 'some pos arg'").possible_values(&["three", "four"])]);
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [FLAGS] [OPTIONS] [ARGS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --opt <opt>    some option [values: one, two]

ARGS:
    [arg1]    some pos arg [values: three, four]\n"));
}
<|endoftext|>


<commit_before>#![cfg(feature = "encoding")]

extern crate lambda_calculus as lambda;

use lambda::*;
use lambda::data::list::*;

macro_rules! test_list {
    ($name:ident, $function:ident, $($($n:expr),+ => $result:expr),+) => (
        #[test]
        fn $name() {
            $(assert_eq!(beta(app!($function(), $($n.into_church()),*), HAP, 0), $result.into_church());)*
            $(assert_eq!(beta(app!($function(), $($n.into_scott()),*), HAP, 0), $result.into_scott());)*
            $(assert_eq!(beta(app!($function(), $($n.into_parigot()),*), HAP, 0), $result.into_parigot());)*
            $(assert_eq!(beta(app!($function(), $($n.into_stumpfu()),*), HAP, 0), $result.into_stumpfu());)*
        }
    );
}

macro_rules! test_list_enc {
    ($name:ident, $function:ident, $($($n:expr),+ => $result:expr),+) => (
        #[test]
        fn $name() {
            $(assert_eq!(beta(app!($function(Church),  $($n.into_church()),*), HAP, 0),  $result.into_church());)*
            $(assert_eq!(beta(app!($function(Scott),   $($n.into_scott()),*), HAP, 0),   $result.into_scott());)*
            $(assert_eq!(beta(app!($function(Parigot), $($n.into_parigot()),*), HAP, 0), $result.into_parigot());)*
            $(assert_eq!(beta(app!($function(StumpFu), $($n.into_stumpfu()),*), HAP, 0), $result.into_stumpfu());)*
        }
    );
}

fn empty() -> Vec<usize> { vec![] } // a workaround to pass vec![] to the testing macro

test_list!(list_last, last,
    vec![1] => 1,
    vec![1, 2, 3] => 3
);

test_list_enc!(list_length, length,
    empty() => 0,
    vec![1] => 1,
    vec![1, 2] => 2,
    vec![1, 2, 3] => 3
);

test_list!(list_init, init,
    vec![1] => empty(),
    vec![1, 2] => vec![1],
    vec![1, 2, 3] => vec![1, 2]
);

test_list!(list_zip, zip,
    empty(),
    vec![1], vec![1] => vec![(1, 1)],
    vec![1, 2], vec![1] => vec![(1, 1)],
    vec![1], vec![1, 2] => vec![(1, 1)],
    vec![1, 2], vec![3, 4] => vec![(1, 3), (2, 4)]
);

test_list_enc!(list_take, take,
    0, empty() => empty(),
    1, empty() => empty(),
    0, vec![1] => empty(),
    1, vec![1] => vec![1],
    2, vec![1] => vec![1],
    1, vec![1, 2] => vec![1],
    2, vec![1, 2] => vec![1, 2],
    3, vec![1, 2] => vec![1, 2]
);

/* TODO: figure out passing multiple functions (inconsistent lockstep iteration)
test_list!(list_zip, zip_with, add,
    // TODO: test empty list
    vec![1], vec![1] => vec![2],
    vec![1, 2], vec![1] => vec![2],
    vec![1], vec![1, 2] => vec![2],
    vec![1, 2], vec![3, 4] => vec![4, 6]
);
#[test]
fn list_take_while() {
    let l1 = || { vec![0, 0, 2, 3].into_scott() };
    let l2 = || { vec![0, 0].into_scott() };
    let l3 = || { vec![1, 4, 2, 3].into_scott() };
    let l4 = || { vec![0, 4, 0, 0].into_scott() };
    let l5 = || { vec![0].into_scott() };

    assert_eq!(beta(app!(take_while(), is_zero(), nil()), HAP, 0), nil());
    assert_eq!(beta(app!(take_while(), is_zero(), l1()), HAP, 0), l2());
    assert_eq!(beta(app!(take_while(), is_zero(), l2()), HAP, 0), l2());
    assert_eq!(beta(app!(take_while(), is_zero(), l3()), HAP, 0), nil());
    assert_eq!(beta(app!(take_while(), is_zero(), l4()), HAP, 0), l5());
}
*/
<commit_msg>fix forgotten zip test, test head<commit_after>#![cfg(feature = "encoding")]

extern crate lambda_calculus as lambda;

use lambda::*;
use lambda::data::list::*;

macro_rules! test_list {
    ($name:ident, $function:ident, $($($n:expr),+ => $result:expr),+) => (
        #[test]
        fn $name() {
            $(assert_eq!(beta(app!($function(), $($n.into_church()),*), HAP, 0), $result.into_church());)*
            $(assert_eq!(beta(app!($function(), $($n.into_scott()),*), HAP, 0), $result.into_scott());)*
            $(assert_eq!(beta(app!($function(), $($n.into_parigot()),*), HAP, 0), $result.into_parigot());)*
            $(assert_eq!(beta(app!($function(), $($n.into_stumpfu()),*), HAP, 0), $result.into_stumpfu());)*
        }
    );
}

macro_rules! test_list_enc {
    ($name:ident, $function:ident, $($($n:expr),+ => $result:expr),+) => (
        #[test]
        fn $name() {
            $(assert_eq!(beta(app!($function(Church),  $($n.into_church()),*), HAP, 0),  $result.into_church());)*
            $(assert_eq!(beta(app!($function(Scott),   $($n.into_scott()),*), HAP, 0),   $result.into_scott());)*
            $(assert_eq!(beta(app!($function(Parigot), $($n.into_parigot()),*), HAP, 0), $result.into_parigot());)*
            $(assert_eq!(beta(app!($function(StumpFu), $($n.into_stumpfu()),*), HAP, 0), $result.into_stumpfu());)*
        }
    );
}

fn empty() -> Vec<usize> { vec![] } // a workaround to pass vec![] to the testing macro

test_list!(list_head, head,
    vec![1] => 1,
    vec![1, 2] => 1,
    vec![1, 2, 3] => 1
);

test_list!(list_last, last,
    vec![1] => 1,
    vec![1, 2, 3] => 3
);

test_list_enc!(list_length, length,
    empty() => 0,
    vec![1] => 1,
    vec![1, 2] => 2,
    vec![1, 2, 3] => 3
);

test_list!(list_init, init,
    vec![1] => empty(),
    vec![1, 2] => vec![1],
    vec![1, 2, 3] => vec![1, 2]
);

test_list!(list_zip, zip,
    empty(), vec![1] => empty(),
    vec![1], empty() => empty(),
    vec![1], vec![1] => vec![(1, 1)],
    vec![1, 2], vec![1] => vec![(1, 1)],
    vec![1], vec![1, 2] => vec![(1, 1)],
    vec![1, 2], vec![3, 4] => vec![(1, 3), (2, 4)]
);

test_list_enc!(list_take, take,
    0, empty() => empty(),
    1, empty() => empty(),
    0, vec![1] => empty(),
    1, vec![1] => vec![1],
    2, vec![1] => vec![1],
    1, vec![1, 2] => vec![1],
    2, vec![1, 2] => vec![1, 2],
    3, vec![1, 2] => vec![1, 2]
);

/* TODO: figure out passing multiple functions (inconsistent lockstep iteration)
test_list!(list_zip, zip_with, add,
    // TODO: test empty list
    vec![1], vec![1] => vec![2],
    vec![1, 2], vec![1] => vec![2],
    vec![1], vec![1, 2] => vec![2],
    vec![1, 2], vec![3, 4] => vec![4, 6]
);
#[test]
fn list_take_while() {
    let l1 = || { vec![0, 0, 2, 3].into_scott() };
    let l2 = || { vec![0, 0].into_scott() };
    let l3 = || { vec![1, 4, 2, 3].into_scott() };
    let l4 = || { vec![0, 4, 0, 0].into_scott() };
    let l5 = || { vec![0].into_scott() };

    assert_eq!(beta(app!(take_while(), is_zero(), nil()), HAP, 0), nil());
    assert_eq!(beta(app!(take_while(), is_zero(), l1()), HAP, 0), l2());
    assert_eq!(beta(app!(take_while(), is_zero(), l2()), HAP, 0), l2());
    assert_eq!(beta(app!(take_while(), is_zero(), l3()), HAP, 0), nil());
    assert_eq!(beta(app!(take_while(), is_zero(), l4()), HAP, 0), l5());
}
*/
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The main parser interface

use ast::node_id;
use ast;
use codemap::{span, CodeMap, FileMap, CharPos, BytePos};
use codemap;
use diagnostic::{span_handler, mk_span_handler, mk_handler, Emitter};
use parse::attr::parser_attr;
use parse::lexer::{reader, StringReader};
use parse::parser::Parser;
use parse::token::{ident_interner, mk_ident_interner};

use core::io;
use core::option::{None, Option, Some};
use core::path::Path;
use core::result::{Err, Ok, Result};

pub mod lexer;
pub mod parser;
pub mod token;
pub mod comments;
pub mod attr;

/// Common routines shared by parser mods
pub mod common;

/// Functions dealing with operator precedence
pub mod prec;

/// Routines the parser uses to classify AST nodes
pub mod classify;

/// Reporting obsolete syntax
pub mod obsolete;

pub struct ParseSess {
    cm: @codemap::CodeMap,
    next_id: node_id,
    span_diagnostic: @span_handler,
    interner: @ident_interner,
}

pub fn new_parse_sess(demitter: Option<Emitter>) -> @mut ParseSess {
    let cm = @CodeMap::new();
    @mut ParseSess {
        cm: cm,
        next_id: 1,
        span_diagnostic: mk_span_handler(mk_handler(demitter), cm),
        interner: mk_ident_interner(),
    }
}

pub fn new_parse_sess_special_handler(sh: @span_handler,
                                      cm: @codemap::CodeMap)
                                   -> @mut ParseSess {
    @mut ParseSess {
        cm: cm,
        next_id: 1,
        span_diagnostic: sh,
        interner: mk_ident_interner(),
    }
}

// a bunch of utility functions of the form parse_<thing>_from_<source>
// where <thing> includes crate, expr, item, stmt, tts, and one that
// uses a HOF to parse anything, and <source> includes file and
// source_str.

// this appears to be the main entry point for rust parsing by
// rustc and crate:
pub fn parse_crate_from_file(
    input: &Path,
    cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> @ast::crate {
    let p = new_parser_from_file(sess, /*bad*/ copy cfg, input);
    p.parse_crate_mod(/*bad*/ copy cfg)
    // why is there no p.abort_if_errors here?
}

pub fn parse_crate_from_source_str(
    name: ~str,
    source: @~str,
    cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> @ast::crate {
    let p = new_parser_from_source_str(
        sess,
        /*bad*/ copy cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    maybe_aborted(p.parse_crate_mod(/*bad*/ copy cfg),p)
}

pub fn parse_expr_from_source_str(
    name: ~str,
    source: @~str,
    +cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> @ast::expr {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    maybe_aborted(p.parse_expr(), p)
}

pub fn parse_item_from_source_str(
    name: ~str,
    source: @~str,
    +cfg: ast::crate_cfg,
    +attrs: ~[ast::attribute],
    sess: @mut ParseSess
) -> Option<@ast::item> {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    maybe_aborted(p.parse_item(attrs),p)
}

pub fn parse_stmt_from_source_str(
    name: ~str,
    source: @~str,
    +cfg: ast::crate_cfg,
    +attrs: ~[ast::attribute],
    sess: @mut ParseSess
) -> @ast::stmt {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    maybe_aborted(p.parse_stmt(attrs),p)
}

pub fn parse_tts_from_source_str(
    name: ~str,
    source: @~str,
    +cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> ~[ast::token_tree] {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    *p.quote_depth += 1u;
    maybe_aborted(p.parse_all_token_trees(),p)
}

pub fn parse_from_source_str<T>(
    f: &fn (Parser) -> T,
    name: ~str, ss: codemap::FileSubstr,
    source: @~str,
    +cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> T {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        /*bad*/ copy ss,
        source
    );
    let r = f(p);
    if !p.reader.is_eof() {
        p.reader.fatal(~"expected end-of-string");
    }
    maybe_aborted(r,p)
}

pub fn next_node_id(sess: @mut ParseSess) -> node_id {
    let rv = sess.next_id;
    sess.next_id += 1;
    // ID 0 is reserved for the crate and doesn't actually exist in the AST
    fail_unless!(rv != 0);
    return rv;
}

pub fn new_parser_from_source_str(sess: @mut ParseSess,
                                  +cfg: ast::crate_cfg,
                                  +name: ~str,
                                  +ss: codemap::FileSubstr,
                                  source: @~str)
                               -> Parser {
    let filemap = sess.cm.new_filemap_w_substr(name, ss, source);
    let srdr = lexer::new_string_reader(
        copy sess.span_diagnostic,
        filemap,
        sess.interner
    );
    Parser(sess, cfg, srdr as @reader)
}

/// Read the entire source file, return a parser
/// that draws from that string
pub fn new_parser_result_from_file(
    sess: @mut ParseSess,
    +cfg: ast::crate_cfg,
    path: &Path
) -> Result<Parser, ~str> {
    match io::read_whole_file_str(path) {
        Ok(src) => {
            let filemap = sess.cm.new_filemap(path.to_str(), @src);
            let srdr = lexer::new_string_reader(copy sess.span_diagnostic,
                                                filemap,
                                                sess.interner);
            Ok(Parser(sess, cfg, srdr as @reader))

        }
        Err(e) => Err(e)
    }
}

/// Create a new parser, handling errors as appropriate
/// if the file doesn't exist
pub fn new_parser_from_file(
    sess: @mut ParseSess,
    +cfg: ast::crate_cfg,
    path: &Path
) -> Parser {
    match new_parser_result_from_file(sess, cfg, path) {
        Ok(parser) => parser,
        Err(e) => {
            sess.span_diagnostic.handler().fatal(e)
        }
    }
}

/// Create a new parser based on a span from an existing parser. Handles
/// error messages correctly when the file does not exist.
pub fn new_sub_parser_from_file(
    sess: @mut ParseSess,
    +cfg: ast::crate_cfg,
    path: &Path,
    sp: span
) -> Parser {
    match new_parser_result_from_file(sess, cfg, path) {
        Ok(parser) => parser,
        Err(e) => {
            sess.span_diagnostic.span_fatal(sp, e)
        }
    }
}

pub fn new_parser_from_tts(
    sess: @mut ParseSess,
    +cfg: ast::crate_cfg,
    +tts: ~[ast::token_tree]
) -> Parser {
    let trdr = lexer::new_tt_reader(
        copy sess.span_diagnostic,
        sess.interner,
        None,
        tts
    );
    Parser(sess, cfg, trdr as @reader)
}

// abort if necessary
pub fn maybe_aborted<T>(+result : T, p: Parser) -> T {
    p.abort_if_errors();
    result
}

#[cfg(test)]
mod test {
    use super::*;
    use std::serialize::Encodable;
    use std;
    use core::io;
    use core::option::None;
    use core::str;
    use util::testing::*;

    #[test] fn to_json_str (val: @Encodable<std::json::Encoder>) -> ~str {
        do io::with_str_writer |writer| {
            val.encode(~std::json::Encoder(writer));
        }
    }

    #[test] fn alltts () {
        let tts = parse_tts_from_source_str(
            ~"bogofile",
            @~"fn foo (x : int) { x; }",
            ~[],
            new_parse_sess(None));
        check_equal(to_json_str(@tts as Encodable::<std::json::Encoder>),
                    ~"[[\"tt_tok\",[null,[\"IDENT\",[\"fn\",false]]]],\
                      [\"tt_tok\",[null,[\"IDENT\",[\"foo\",false]]]],\
                      [\"tt_delim\",[[[\"tt_tok\",[null,[\"LPAREN\",[]]]],\
                      [\"tt_tok\",[null,[\"IDENT\",[\"x\",false]]]],\
                      [\"tt_tok\",[null,[\"COLON\",[]]]],\
                      [\"tt_tok\",[null,[\"IDENT\",[\"int\",false]]]],\
                      [\"tt_tok\",[null,[\"RPAREN\",[]]]]]]],\
                      [\"tt_delim\",[[[\"tt_tok\",[null,[\"LBRACE\",[]]]],\
                      [\"tt_tok\",[null,[\"IDENT\",[\"x\",false]]]],\
                      [\"tt_tok\",[null,[\"SEMI\",[]]]],\
                      [\"tt_tok\",[null,[\"RBRACE\",[]]]]]]]]"
                   );
        let ast1 = new_parser_from_tts(new_parse_sess(None),~[],tts)
            .parse_item(~[]);
        let ast2 = parse_item_from_source_str(
            ~"bogofile",
            @~"fn foo (x : int) { x; }",
            ~[],~[],
            new_parse_sess(None));
        check_equal(ast1,ast2);
    }
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<commit_msg>Update test case to conform to new restrictions on casting<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The main parser interface

use ast::node_id;
use ast;
use codemap::{span, CodeMap, FileMap, CharPos, BytePos};
use codemap;
use diagnostic::{span_handler, mk_span_handler, mk_handler, Emitter};
use parse::attr::parser_attr;
use parse::lexer::{reader, StringReader};
use parse::parser::Parser;
use parse::token::{ident_interner, mk_ident_interner};

use core::io;
use core::option::{None, Option, Some};
use core::path::Path;
use core::result::{Err, Ok, Result};

pub mod lexer;
pub mod parser;
pub mod token;
pub mod comments;
pub mod attr;

/// Common routines shared by parser mods
pub mod common;

/// Functions dealing with operator precedence
pub mod prec;

/// Routines the parser uses to classify AST nodes
pub mod classify;

/// Reporting obsolete syntax
pub mod obsolete;

pub struct ParseSess {
    cm: @codemap::CodeMap,
    next_id: node_id,
    span_diagnostic: @span_handler,
    interner: @ident_interner,
}

pub fn new_parse_sess(demitter: Option<Emitter>) -> @mut ParseSess {
    let cm = @CodeMap::new();
    @mut ParseSess {
        cm: cm,
        next_id: 1,
        span_diagnostic: mk_span_handler(mk_handler(demitter), cm),
        interner: mk_ident_interner(),
    }
}

pub fn new_parse_sess_special_handler(sh: @span_handler,
                                      cm: @codemap::CodeMap)
                                   -> @mut ParseSess {
    @mut ParseSess {
        cm: cm,
        next_id: 1,
        span_diagnostic: sh,
        interner: mk_ident_interner(),
    }
}

// a bunch of utility functions of the form parse_<thing>_from_<source>
// where <thing> includes crate, expr, item, stmt, tts, and one that
// uses a HOF to parse anything, and <source> includes file and
// source_str.

// this appears to be the main entry point for rust parsing by
// rustc and crate:
pub fn parse_crate_from_file(
    input: &Path,
    cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> @ast::crate {
    let p = new_parser_from_file(sess, /*bad*/ copy cfg, input);
    p.parse_crate_mod(/*bad*/ copy cfg)
    // why is there no p.abort_if_errors here?
}

pub fn parse_crate_from_source_str(
    name: ~str,
    source: @~str,
    cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> @ast::crate {
    let p = new_parser_from_source_str(
        sess,
        /*bad*/ copy cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    maybe_aborted(p.parse_crate_mod(/*bad*/ copy cfg),p)
}

pub fn parse_expr_from_source_str(
    name: ~str,
    source: @~str,
    +cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> @ast::expr {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    maybe_aborted(p.parse_expr(), p)
}

pub fn parse_item_from_source_str(
    name: ~str,
    source: @~str,
    +cfg: ast::crate_cfg,
    +attrs: ~[ast::attribute],
    sess: @mut ParseSess
) -> Option<@ast::item> {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    maybe_aborted(p.parse_item(attrs),p)
}

pub fn parse_stmt_from_source_str(
    name: ~str,
    source: @~str,
    +cfg: ast::crate_cfg,
    +attrs: ~[ast::attribute],
    sess: @mut ParseSess
) -> @ast::stmt {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    maybe_aborted(p.parse_stmt(attrs),p)
}

pub fn parse_tts_from_source_str(
    name: ~str,
    source: @~str,
    +cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> ~[ast::token_tree] {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        codemap::FssNone,
        source
    );
    *p.quote_depth += 1u;
    maybe_aborted(p.parse_all_token_trees(),p)
}

pub fn parse_from_source_str<T>(
    f: &fn (Parser) -> T,
    name: ~str, ss: codemap::FileSubstr,
    source: @~str,
    +cfg: ast::crate_cfg,
    sess: @mut ParseSess
) -> T {
    let p = new_parser_from_source_str(
        sess,
        cfg,
        /*bad*/ copy name,
        /*bad*/ copy ss,
        source
    );
    let r = f(p);
    if !p.reader.is_eof() {
        p.reader.fatal(~"expected end-of-string");
    }
    maybe_aborted(r,p)
}

pub fn next_node_id(sess: @mut ParseSess) -> node_id {
    let rv = sess.next_id;
    sess.next_id += 1;
    // ID 0 is reserved for the crate and doesn't actually exist in the AST
    fail_unless!(rv != 0);
    return rv;
}

pub fn new_parser_from_source_str(sess: @mut ParseSess,
                                  +cfg: ast::crate_cfg,
                                  +name: ~str,
                                  +ss: codemap::FileSubstr,
                                  source: @~str)
                               -> Parser {
    let filemap = sess.cm.new_filemap_w_substr(name, ss, source);
    let srdr = lexer::new_string_reader(
        copy sess.span_diagnostic,
        filemap,
        sess.interner
    );
    Parser(sess, cfg, srdr as @reader)
}

/// Read the entire source file, return a parser
/// that draws from that string
pub fn new_parser_result_from_file(
    sess: @mut ParseSess,
    +cfg: ast::crate_cfg,
    path: &Path
) -> Result<Parser, ~str> {
    match io::read_whole_file_str(path) {
        Ok(src) => {
            let filemap = sess.cm.new_filemap(path.to_str(), @src);
            let srdr = lexer::new_string_reader(copy sess.span_diagnostic,
                                                filemap,
                                                sess.interner);
            Ok(Parser(sess, cfg, srdr as @reader))

        }
        Err(e) => Err(e)
    }
}

/// Create a new parser, handling errors as appropriate
/// if the file doesn't exist
pub fn new_parser_from_file(
    sess: @mut ParseSess,
    +cfg: ast::crate_cfg,
    path: &Path
) -> Parser {
    match new_parser_result_from_file(sess, cfg, path) {
        Ok(parser) => parser,
        Err(e) => {
            sess.span_diagnostic.handler().fatal(e)
        }
    }
}

/// Create a new parser based on a span from an existing parser. Handles
/// error messages correctly when the file does not exist.
pub fn new_sub_parser_from_file(
    sess: @mut ParseSess,
    +cfg: ast::crate_cfg,
    path: &Path,
    sp: span
) -> Parser {
    match new_parser_result_from_file(sess, cfg, path) {
        Ok(parser) => parser,
        Err(e) => {
            sess.span_diagnostic.span_fatal(sp, e)
        }
    }
}

pub fn new_parser_from_tts(
    sess: @mut ParseSess,
    +cfg: ast::crate_cfg,
    +tts: ~[ast::token_tree]
) -> Parser {
    let trdr = lexer::new_tt_reader(
        copy sess.span_diagnostic,
        sess.interner,
        None,
        tts
    );
    Parser(sess, cfg, trdr as @reader)
}

// abort if necessary
pub fn maybe_aborted<T>(+result : T, p: Parser) -> T {
    p.abort_if_errors();
    result
}

#[cfg(test)]
mod test {
    use super::*;
    use std::serialize::Encodable;
    use std;
    use core::io;
    use core::option::None;
    use util::testing::*;

    #[test] fn to_json_str<E : Encodable<std::json::Encoder>>(val: @E) -> ~str {
        do io::with_str_writer |writer| {
            val.encode(~std::json::Encoder(writer));
        }
    }

    #[test] fn alltts () {
        let tts = parse_tts_from_source_str(
            ~"bogofile",
            @~"fn foo (x : int) { x; }",
            ~[],
            new_parse_sess(None));
        check_equal(to_json_str(@tts),
                    ~"[[\"tt_tok\",[null,[\"IDENT\",[\"fn\",false]]]],\
                      [\"tt_tok\",[null,[\"IDENT\",[\"foo\",false]]]],\
                      [\"tt_delim\",[[[\"tt_tok\",[null,[\"LPAREN\",[]]]],\
                      [\"tt_tok\",[null,[\"IDENT\",[\"x\",false]]]],\
                      [\"tt_tok\",[null,[\"COLON\",[]]]],\
                      [\"tt_tok\",[null,[\"IDENT\",[\"int\",false]]]],\
                      [\"tt_tok\",[null,[\"RPAREN\",[]]]]]]],\
                      [\"tt_delim\",[[[\"tt_tok\",[null,[\"LBRACE\",[]]]],\
                      [\"tt_tok\",[null,[\"IDENT\",[\"x\",false]]]],\
                      [\"tt_tok\",[null,[\"SEMI\",[]]]],\
                      [\"tt_tok\",[null,[\"RBRACE\",[]]]]]]]]"
                   );
        let ast1 = new_parser_from_tts(new_parse_sess(None),~[],tts)
            .parse_item(~[]);
        let ast2 = parse_item_from_source_str(
            ~"bogofile",
            @~"fn foo (x : int) { x; }",
            ~[],~[],
            new_parse_sess(None));
        check_equal(ast1,ast2);
    }
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use bincode::{self, SizeLimit};
use bincode::serde::{deserialize_from, serialize_into};
use serde;
use std::io::{self, Read, Write};
use std::convert;
use std::sync::Arc;
use std::time::Duration;

mod client;
mod server;
mod packet;

pub use self::packet::Packet;
pub use self::client::{Client, Future};
pub use self::server::{Serve, ServeHandle};

/// Client errors that can occur during rpc calls
#[derive(Debug, Clone)]
pub enum Error {
    /// An IO-related error
    Io(Arc<io::Error>),
    /// The server hung up.
    ConnectionBroken,
}

impl convert::From<bincode::serde::SerializeError> for Error {
    fn from(err: bincode::serde::SerializeError) -> Error {
        match err {
            bincode::serde::SerializeError::IoError(err) => Error::Io(Arc::new(err)),
            err => panic!("Unexpected error during serialization: {:?}", err),
        }
    }
}

impl convert::From<bincode::serde::DeserializeError> for Error {
    fn from(err: bincode::serde::DeserializeError) -> Error {
        match err {
            bincode::serde::DeserializeError::IoError(err) => {
                match err.kind() {
                    io::ErrorKind::ConnectionReset |
                    io::ErrorKind::UnexpectedEof => Error::ConnectionBroken,
                    _ => Error::Io(Arc::new(err)),
                }
            }
            err => panic!("Unexpected error during deserialization: {:?}", err),
        }
    }
}

impl convert::From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(Arc::new(err))
    }
}

/// Configuration for client and server.
#[derive(Debug, Default)]
pub struct Config {
    /// Request/Response timeout between packet delivery.
    pub timeout: Option<Duration>,
}

/// Return type of rpc calls: either the successful return value, or a client error.
pub type Result<T> = ::std::result::Result<T, Error>;

trait Deserialize: Read + Sized {
    fn deserialize<T: serde::Deserialize>(&mut self) -> Result<T> {
        deserialize_from(self, SizeLimit::Infinite).map_err(Error::from)
    }
}

impl<R: Read> Deserialize for R {}

trait Serialize: Write + Sized {
    fn serialize<T: serde::Serialize>(&mut self, value: &T) -> Result<()> {
        try!(serialize_into(self, value, SizeLimit::Infinite));
        try!(self.flush());
        Ok(())
    }
}

impl<W: Write> Serialize for W {}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::{Client, Config, Serve};
    use scoped_pool::Pool;
    use std::sync::{Arc, Barrier, Mutex};
    use std::thread;
    use std::time::Duration;

    fn test_timeout() -> Option<Duration> {
        Some(Duration::from_secs(1))
    }

    struct Server {
        counter: Mutex<u64>,
    }

    impl Serve for Server {
        type Request = ();
        type Reply = u64;

        fn serve(&self, _: ()) -> u64 {
            let mut counter = self.counter.lock().unwrap();
            let reply = *counter;
            *counter += 1;
            reply
        }
    }

    impl Server {
        fn new() -> Server {
            Server { counter: Mutex::new(0) }
        }

        fn count(&self) -> u64 {
            *self.counter.lock().unwrap()
        }
    }

    #[test]
    fn handle() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn("localhost:0").unwrap();
        let client: Client<(), u64> = Client::new(serve_handle.local_addr()).unwrap();
        drop(client);
        serve_handle.shutdown();
    }

    #[test]
    fn simple() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.clone().spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        // The explicit type is required so that it doesn't deserialize a u32 instead of u64
        let client: Client<(), u64> = Client::new(addr).unwrap();
        assert_eq!(0, client.rpc(()).unwrap());
        assert_eq!(1, server.count());
        assert_eq!(1, client.rpc(()).unwrap());
        assert_eq!(2, server.count());
        drop(client);
        serve_handle.shutdown();
    }

    struct BarrierServer {
        barrier: Barrier,
        inner: Server,
    }

    impl Serve for BarrierServer {
        type Request = ();
        type Reply = u64;
        fn serve(&self, request: ()) -> u64 {
            self.barrier.wait();
            self.inner.serve(request)
        }
    }

    impl BarrierServer {
        fn new(n: usize) -> BarrierServer {
            BarrierServer {
                barrier: Barrier::new(n),
                inner: Server::new(),
            }
        }

        fn count(&self) -> u64 {
            self.inner.count()
        }
    }

    #[test]
    fn force_shutdown() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn_with_config("localhost:0",
                                                    Config { timeout: Some(Duration::new(0, 10)) })
                                 .unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();
        let thread = thread::spawn(move || serve_handle.shutdown());
        info!("force_shutdown:: rpc1: {:?}", client.rpc(()));
        thread.join().unwrap();
    }

    #[test]
    fn client_failed_rpc() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn_with_config("localhost:0",
                                                    Config { timeout: test_timeout() })
                                 .unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Arc<Client<(), u64>> = Arc::new(Client::new(addr).unwrap());
        client.rpc(()).unwrap();
        serve_handle.shutdown();
        match client.rpc(()) {
            Err(super::Error::ConnectionBroken) => {} // success
            otherwise => panic!("Expected Err(ConnectionBroken), got {:?}", otherwise),
        }
        let _ = client.rpc(()); // Test whether second failure hangs
    }

    #[test]
    fn concurrent() {
        let _ = env_logger::init();
        let concurrency = 10;
        let pool = Pool::new(concurrency);
        let server = Arc::new(BarrierServer::new(concurrency));
        let serve_handle = server.clone().spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();
        pool.scoped(|scope| {
            for _ in 0..concurrency {
                let client = client.try_clone().unwrap();
                scope.execute(move || {
                    client.rpc(()).unwrap();
                });
            }
        });
        assert_eq!(concurrency as u64, server.count());
        drop(client);
        serve_handle.shutdown();
    }

    #[test]
    fn async() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();

        // Drop future immediately; does the reader channel panic when sending?
        client.rpc_async(());
        // If the reader panicked, this won't succeed
        client.rpc_async(());

        drop(client);
        serve_handle.shutdown();
    }
}
<commit_msg>Handle Serde(EndOfStream) error as ConnectionBroken<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use bincode::{self, SizeLimit};
use bincode::serde::{deserialize_from, serialize_into};
use serde;
use serde::de::value::Error::EndOfStream;
use std::io::{self, Read, Write};
use std::convert;
use std::sync::Arc;
use std::time::Duration;

mod client;
mod server;
mod packet;

pub use self::packet::Packet;
pub use self::client::{Client, Future};
pub use self::server::{Serve, ServeHandle};

/// Client errors that can occur during rpc calls
#[derive(Debug, Clone)]
pub enum Error {
    /// An IO-related error
    Io(Arc<io::Error>),
    /// The server hung up.
    ConnectionBroken,
}

impl convert::From<bincode::serde::SerializeError> for Error {
    fn from(err: bincode::serde::SerializeError) -> Error {
        match err {
            bincode::serde::SerializeError::IoError(err) => Error::Io(Arc::new(err)),
            err => panic!("Unexpected error during serialization: {:?}", err),
        }
    }
}

impl convert::From<bincode::serde::DeserializeError> for Error {
    fn from(err: bincode::serde::DeserializeError) -> Error {
        match err {
            bincode::serde::DeserializeError::Serde(EndOfStream) => Error::ConnectionBroken,
            bincode::serde::DeserializeError::IoError(err) => {
                match err.kind() {
                    io::ErrorKind::ConnectionReset |
                    io::ErrorKind::UnexpectedEof => Error::ConnectionBroken,
                    _ => Error::Io(Arc::new(err)),
                }
            }
            err => panic!("Unexpected error during deserialization: {:?}", err),
        }
    }
}

impl convert::From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(Arc::new(err))
    }
}

/// Configuration for client and server.
#[derive(Debug, Default)]
pub struct Config {
    /// Request/Response timeout between packet delivery.
    pub timeout: Option<Duration>,
}

/// Return type of rpc calls: either the successful return value, or a client error.
pub type Result<T> = ::std::result::Result<T, Error>;

trait Deserialize: Read + Sized {
    fn deserialize<T: serde::Deserialize>(&mut self) -> Result<T> {
        deserialize_from(self, SizeLimit::Infinite).map_err(Error::from)
    }
}

impl<R: Read> Deserialize for R {}

trait Serialize: Write + Sized {
    fn serialize<T: serde::Serialize>(&mut self, value: &T) -> Result<()> {
        try!(serialize_into(self, value, SizeLimit::Infinite));
        try!(self.flush());
        Ok(())
    }
}

impl<W: Write> Serialize for W {}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::{Client, Config, Serve};
    use scoped_pool::Pool;
    use std::sync::{Arc, Barrier, Mutex};
    use std::thread;
    use std::time::Duration;

    fn test_timeout() -> Option<Duration> {
        Some(Duration::from_secs(1))
    }

    struct Server {
        counter: Mutex<u64>,
    }

    impl Serve for Server {
        type Request = ();
        type Reply = u64;

        fn serve(&self, _: ()) -> u64 {
            let mut counter = self.counter.lock().unwrap();
            let reply = *counter;
            *counter += 1;
            reply
        }
    }

    impl Server {
        fn new() -> Server {
            Server { counter: Mutex::new(0) }
        }

        fn count(&self) -> u64 {
            *self.counter.lock().unwrap()
        }
    }

    #[test]
    fn handle() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn("localhost:0").unwrap();
        let client: Client<(), u64> = Client::new(serve_handle.local_addr()).unwrap();
        drop(client);
        serve_handle.shutdown();
    }

    #[test]
    fn simple() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.clone().spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        // The explicit type is required so that it doesn't deserialize a u32 instead of u64
        let client: Client<(), u64> = Client::new(addr).unwrap();
        assert_eq!(0, client.rpc(()).unwrap());
        assert_eq!(1, server.count());
        assert_eq!(1, client.rpc(()).unwrap());
        assert_eq!(2, server.count());
        drop(client);
        serve_handle.shutdown();
    }

    struct BarrierServer {
        barrier: Barrier,
        inner: Server,
    }

    impl Serve for BarrierServer {
        type Request = ();
        type Reply = u64;
        fn serve(&self, request: ()) -> u64 {
            self.barrier.wait();
            self.inner.serve(request)
        }
    }

    impl BarrierServer {
        fn new(n: usize) -> BarrierServer {
            BarrierServer {
                barrier: Barrier::new(n),
                inner: Server::new(),
            }
        }

        fn count(&self) -> u64 {
            self.inner.count()
        }
    }

    #[test]
    fn force_shutdown() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn_with_config("localhost:0",
                                                    Config { timeout: Some(Duration::new(0, 10)) })
                                 .unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();
        let thread = thread::spawn(move || serve_handle.shutdown());
        info!("force_shutdown:: rpc1: {:?}", client.rpc(()));
        thread.join().unwrap();
    }

    #[test]
    fn client_failed_rpc() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn_with_config("localhost:0",
                                                    Config { timeout: test_timeout() })
                                 .unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Arc<Client<(), u64>> = Arc::new(Client::new(addr).unwrap());
        client.rpc(()).unwrap();
        serve_handle.shutdown();
        match client.rpc(()) {
            Err(super::Error::ConnectionBroken) => {} // success
            otherwise => panic!("Expected Err(ConnectionBroken), got {:?}", otherwise),
        }
        let _ = client.rpc(()); // Test whether second failure hangs
    }

    #[test]
    fn concurrent() {
        let _ = env_logger::init();
        let concurrency = 10;
        let pool = Pool::new(concurrency);
        let server = Arc::new(BarrierServer::new(concurrency));
        let serve_handle = server.clone().spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();
        pool.scoped(|scope| {
            for _ in 0..concurrency {
                let client = client.try_clone().unwrap();
                scope.execute(move || {
                    client.rpc(()).unwrap();
                });
            }
        });
        assert_eq!(concurrency as u64, server.count());
        drop(client);
        serve_handle.shutdown();
    }

    #[test]
    fn async() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();

        // Drop future immediately; does the reader channel panic when sending?
        client.rpc_async(());
        // If the reader panicked, this won't succeed
        client.rpc_async(());

        drop(client);
        serve_handle.shutdown();
    }
}
<|endoftext|>


<commit_before>use serde::{Deserialize, Serialize};

use super::MembershipState;
use crate::{MxcUri, UserId};

/// The details of a member event required to calculate a [`MembershipChange`].
pub struct MembershipDetails<'a> {
    pub(crate) avatar_url: Option<&'a MxcUri>,
    pub(crate) displayname: Option<&'a str>,
    pub(crate) membership: &'a MembershipState,
}

/// Translation of the membership change in `m.room.member` event.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum MembershipChange {
    /// No change.
    None,

    /// Must never happen.
    Error,

    /// User joined the room.
    Joined,

    /// User left the room.
    Left,

    /// User was banned.
    Banned,

    /// User was unbanned.
    Unbanned,

    /// User was kicked.
    Kicked,

    /// User was invited.
    Invited,

    /// User was kicked and banned.
    KickedAndBanned,

    /// User rejected the invite.
    InvitationRejected,

    /// User had their invite revoked.
    InvitationRevoked,

    /// `displayname` or `avatar_url` changed.
    ProfileChanged {
        /// Whether the `displayname` changed.
        displayname_changed: bool,

        /// Whether the `avatar_url` changed.
        avatar_url_changed: bool,
    },

    /// Not implemented.
    NotImplemented,
}

/// Internal function so all `RoomMemberEventContent` state event kinds can share the same
/// implementation.
pub(super) fn membership_change(
    details: MembershipDetails<'_>,
    prev_details: Option<MembershipDetails<'_>>,
    sender: &UserId,
    state_key: &UserId,
) -> MembershipChange {
    use MembershipChange as Ch;
    use MembershipState as St;

    let prev_details = match prev_details {
        Some(prev) => prev,
        None => MembershipDetails { avatar_url: None, displayname: None, membership: &St::Leave },
    };

    match (&prev_details.membership, &details.membership) {
        (St::Invite, St::Invite) | (St::Leave, St::Leave) | (St::Ban, St::Ban) => Ch::None,
        (St::Invite, St::Join) | (St::Leave, St::Join) => Ch::Joined,
        (St::Invite, St::Leave) if sender == state_key => Ch::InvitationRevoked,
        (St::Invite, St::Leave) => Ch::InvitationRejected,
        (St::Invite, St::Ban) | (St::Leave, St::Ban) => Ch::Banned,
        (St::Join, St::Invite) | (St::Ban, St::Invite) | (St::Ban, St::Join) => Ch::Error,
        (St::Join, St::Join) => Ch::ProfileChanged {
            displayname_changed: prev_details.displayname != details.displayname,
            avatar_url_changed: prev_details.avatar_url != details.avatar_url,
        },
        (St::Join, St::Leave) if sender == state_key => Ch::Left,
        (St::Join, St::Leave) => Ch::Kicked,
        (St::Join, St::Ban) => Ch::KickedAndBanned,
        (St::Leave, St::Invite) => Ch::Invited,
        (St::Ban, St::Leave) => Ch::Unbanned,
        _ => Ch::NotImplemented,
    }
}
<commit_msg>events: Remove Deserialize and Serialize impls for MembershipChange<commit_after>use super::MembershipState;
use crate::{MxcUri, UserId};

/// The details of a member event required to calculate a [`MembershipChange`].
pub struct MembershipDetails<'a> {
    pub(crate) avatar_url: Option<&'a MxcUri>,
    pub(crate) displayname: Option<&'a str>,
    pub(crate) membership: &'a MembershipState,
}

/// Translation of the membership change in `m.room.member` event.
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum MembershipChange {
    /// No change.
    None,

    /// Must never happen.
    Error,

    /// User joined the room.
    Joined,

    /// User left the room.
    Left,

    /// User was banned.
    Banned,

    /// User was unbanned.
    Unbanned,

    /// User was kicked.
    Kicked,

    /// User was invited.
    Invited,

    /// User was kicked and banned.
    KickedAndBanned,

    /// User rejected the invite.
    InvitationRejected,

    /// User had their invite revoked.
    InvitationRevoked,

    /// `displayname` or `avatar_url` changed.
    ProfileChanged {
        /// Whether the `displayname` changed.
        displayname_changed: bool,

        /// Whether the `avatar_url` changed.
        avatar_url_changed: bool,
    },

    /// Not implemented.
    NotImplemented,
}

/// Internal function so all `RoomMemberEventContent` state event kinds can share the same
/// implementation.
pub(super) fn membership_change(
    details: MembershipDetails<'_>,
    prev_details: Option<MembershipDetails<'_>>,
    sender: &UserId,
    state_key: &UserId,
) -> MembershipChange {
    use MembershipChange as Ch;
    use MembershipState as St;

    let prev_details = match prev_details {
        Some(prev) => prev,
        None => MembershipDetails { avatar_url: None, displayname: None, membership: &St::Leave },
    };

    match (&prev_details.membership, &details.membership) {
        (St::Invite, St::Invite) | (St::Leave, St::Leave) | (St::Ban, St::Ban) => Ch::None,
        (St::Invite, St::Join) | (St::Leave, St::Join) => Ch::Joined,
        (St::Invite, St::Leave) if sender == state_key => Ch::InvitationRevoked,
        (St::Invite, St::Leave) => Ch::InvitationRejected,
        (St::Invite, St::Ban) | (St::Leave, St::Ban) => Ch::Banned,
        (St::Join, St::Invite) | (St::Ban, St::Invite) | (St::Ban, St::Join) => Ch::Error,
        (St::Join, St::Join) => Ch::ProfileChanged {
            displayname_changed: prev_details.displayname != details.displayname,
            avatar_url_changed: prev_details.avatar_url != details.avatar_url,
        },
        (St::Join, St::Leave) if sender == state_key => Ch::Left,
        (St::Join, St::Leave) => Ch::Kicked,
        (St::Join, St::Ban) => Ch::KickedAndBanned,
        (St::Leave, St::Invite) => Ch::Invited,
        (St::Ban, St::Leave) => Ch::Unbanned,
        _ => Ch::NotImplemented,
    }
}
<|endoftext|>