<commit_before>extern crate pkg_config;
#[cfg(target_env = "msvc")]
extern crate vcpkg;
extern crate cc;

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

fn main() {
    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");

    // OSX and Haiku ships libcurl by default, so we just use that version
    // unconditionally.
    if target.contains("apple") || target.contains("haiku") {
        return println!("cargo:rustc-flags=-l curl");
    }

    // Next, fall back and try to use pkg-config if its available.
    if windows {
        if try_vcpkg() {
            return
        }
    } else {
        if try_pkg_config() {
            return
        }
    }

    if !Path::new("curl/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let include = dst.join("include");
    let build = dst.join("build");
    println!("cargo:root={}", dst.display());
    println!("cargo:include={}", include.display());
    fs::create_dir_all(include.join("curl")).unwrap();
    fs::copy("curl/include/curl/curl.h", include.join("curl/curl.h")).unwrap();
    fs::copy("curl/include/curl/curlver.h", include.join("curl/curlver.h")).unwrap();
    fs::copy("curl/include/curl/easy.h", include.join("curl/easy.h")).unwrap();
    fs::copy("curl/include/curl/mprintf.h", include.join("curl/mprintf.h")).unwrap();
    fs::copy("curl/include/curl/multi.h", include.join("curl/multi.h")).unwrap();
    fs::copy("curl/include/curl/stdcheaders.h", include.join("curl/stdcheaders.h")).unwrap();
    fs::copy("curl/include/curl/system.h", include.join("curl/system.h")).unwrap();
    fs::copy("curl/include/curl/typecheck-gcc.h", include.join("curl/typecheck-gcc.h")).unwrap();
    let mut cfg = cc::Build::new();
    cfg.out_dir(&build)
        .include("curl/lib")
        .include("curl/include")
        .define("BUILDING_LIBCURL", None)
        .define("CURL_DISABLE_CRYPTO_AUTH", None)
        .define("CURL_DISABLE_DICT", None)
        .define("CURL_DISABLE_FTP", None)
        .define("CURL_DISABLE_GOPHER", None)
        .define("CURL_DISABLE_IMAP", None)
        .define("CURL_DISABLE_LDAP", None)
        .define("CURL_DISABLE_LDAPS", None)
        .define("CURL_DISABLE_NTLM", None)
        .define("CURL_DISABLE_POP3", None)
        .define("CURL_DISABLE_RTSP", None)
        .define("CURL_DISABLE_SMB", None)
        .define("CURL_DISABLE_SMTP", None)
        .define("CURL_DISABLE_TELNET", None)
        .define("CURL_DISABLE_TFTP", None)
        .define("CURL_STATICLIB", None)
        .define("ENABLE_IPV6", None)
        .define("HAVE_ASSERT_H", None)
        .define("OS", "\"unknown\"") // TODO
        .define("HAVE_ZLIB_H", None)
        .define("HAVE_LIBZ", None)

        .file("curl/lib/asyn-thread.c")
        .file("curl/lib/base64.c")
        .file("curl/lib/conncache.c")
        .file("curl/lib/connect.c")
        .file("curl/lib/content_encoding.c")
        .file("curl/lib/cookie.c")
        .file("curl/lib/curl_addrinfo.c")
        .file("curl/lib/curl_ctype.c")
        .file("curl/lib/curl_memrchr.c")
        .file("curl/lib/curl_range.c")
        .file("curl/lib/curl_threads.c")
        .file("curl/lib/dotdot.c")
        .file("curl/lib/easy.c")
        .file("curl/lib/escape.c")
        .file("curl/lib/file.c")
        .file("curl/lib/fileinfo.c")
        .file("curl/lib/formdata.c")
        .file("curl/lib/getenv.c")
        .file("curl/lib/getinfo.c")
        .file("curl/lib/hash.c")
        .file("curl/lib/hostasyn.c")
        .file("curl/lib/hostcheck.c")
        .file("curl/lib/hostip.c")
        .file("curl/lib/hostip6.c")
        .file("curl/lib/http.c")
        .file("curl/lib/http2.c")
        .file("curl/lib/http_chunks.c")
        .file("curl/lib/http_proxy.c")
        .file("curl/lib/if2ip.c")
        .file("curl/lib/inet_ntop.c")
        .file("curl/lib/inet_pton.c")
        .file("curl/lib/llist.c")
        .file("curl/lib/mime.c")
        .file("curl/lib/mprintf.c")
        .file("curl/lib/multi.c")
        .file("curl/lib/netrc.c")
        .file("curl/lib/nonblock.c")
        .file("curl/lib/parsedate.c")
        .file("curl/lib/pipeline.c")
        .file("curl/lib/progress.c")
        .file("curl/lib/rand.c")
        .file("curl/lib/select.c")
        .file("curl/lib/sendf.c")
        .file("curl/lib/setopt.c")
        .file("curl/lib/share.c")
        .file("curl/lib/slist.c")
        .file("curl/lib/socks.c")
        .file("curl/lib/speedcheck.c")
        .file("curl/lib/splay.c")
        .file("curl/lib/strcase.c")
        .file("curl/lib/strdup.c")
        .file("curl/lib/strerror.c")
        .file("curl/lib/strtok.c")
        .file("curl/lib/strtoofft.c")
        .file("curl/lib/timeval.c")
        .file("curl/lib/transfer.c")
        .file("curl/lib/url.c")
        .file("curl/lib/version.c")
        .file("curl/lib/vtls/vtls.c")
        .file("curl/lib/warnless.c")
        .file("curl/lib/wildcard.c")

        .define("HAVE_GETADDRINFO", None)

        .warnings(false);

    if cfg!(feature = "http2") {
        cfg.define("USE_NGHTTP2", None)
            .define("NGHTTP2_STATICLIB", None);

        if let Some(path) = env::var_os("DEP_NGHTTP2_ROOT") {
            let path = PathBuf::from(path);
            cfg.include(path.join("include"));
        }
    }

    if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
        cfg.include(path);
    }

    if windows {
        cfg.define("USE_THREADS_WIN32", None)
            .define("HAVE_IOCTLSOCKET_FIONBIO", None)
            .define("USE_WINSOCK", None)
            .define("USE_WINDOWS_SSPI", None)
            .define("USE_SCHANNEL", None)
            .file("curl/lib/x509asn1.c")
            .file("curl/lib/curl_sspi.c")
            .file("curl/lib/socks_sspi.c")
            .file("curl/lib/system_win32.c")
            .file("curl/lib/vtls/schannel.c")
            .file("curl/lib/vtls/schannel_verify.c");
    } else {
        cfg.define("RECV_TYPE_ARG1", "int")
            .define("HAVE_PTHREAD_H", None)
            .define("HAVE_ARPA_INET_H", None)
            .define("HAVE_ERRNO_H", None)
            .define("HAVE_FCNTL_H", None)
            .define("HAVE_NETDB_H", None)
            .define("HAVE_NETINET_IN_H", None)
            .define("HAVE_POLL_FINE", None)
            .define("HAVE_POLL_H", None)
            .define("HAVE_FCNTL_O_NONBLOCK", None)
            .define("HAVE_SYS_SELECT_H", None)
            .define("HAVE_SYS_STAT_H", None)
            .define("HAVE_UNISTD_H", None)
            .define("HAVE_RECV", None)
            .define("HAVE_SELECT", None)
            .define("HAVE_SEND", None)
            .define("HAVE_SOCKET", None)
            .define("HAVE_STERRROR_R", None)
            .define("HAVE_STRUCT_TIMEVAL", None)
            .define("USE_THREADS_POSIX", None)
            .define("USE_OPENSSL", None)
            .define("RECV_TYPE_ARG2", "void*")
            .define("RECV_TYPE_ARG3", "size_t")
            .define("RECV_TYPE_ARG4", "int")
            .define("RECV_TYPE_RETV", "ssize_t")
            .define("SEND_QUAL_ARG2", "const")
            .define("SEND_TYPE_ARG1", "int")
            .define("SEND_TYPE_ARG2", "void*")
            .define("SEND_TYPE_ARG3", "size_t")
            .define("SEND_TYPE_ARG4", "int")
            .define("SEND_TYPE_RETV", "ssize_t")
            .define("SIZEOF_CURL_OFF_T", "8")
            .define("SIZEOF_INT", "4")
            .define("SIZEOF_SHORT", "2")
            .file("curl/lib/vtls/openssl.c");

        let width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
            .unwrap()
            .parse::<usize>()
            .unwrap();
        cfg.define("SIZEOF_SSIZE_T", Some(&(width / 8).to_string()[..]));
        cfg.define("SIZEOF_SIZE_T", Some(&(width / 8).to_string()[..]));
        cfg.define("SIZEOF_LONG", Some(&(width / 8).to_string()[..]));

        if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") {
            cfg.include(path);
        }

        cfg.flag("-fvisibility=hidden");
    }

    cfg.compile("curl");

    if windows {
        println!("cargo:rustc-link-lib=ws2_32");
        println!("cargo:rustc-link-lib=crypt32");
    }

    // Illumos/Solaris requires explicit linking with libnsl
    if target.contains("solaris") {
        println!("cargo:rustc-link-lib=nsl");
    }
}

#[cfg(not(target_env = "msvc"))]
fn try_vcpkg() -> bool {
    false
}

#[cfg(target_env = "msvc")]
fn try_vcpkg() -> bool {

    // the import library for the dll is called libcurl_imp
    let mut successful_probe_details =
        match vcpkg::Config::new().lib_names("libcurl_imp", "libcurl")
                .emit_includes(true).probe("curl") {
            Ok(details) => Some(details),
            Err(e) => {
                println!("first run of vcpkg did not find libcurl: {}", e);
                None
            }
        };

    if successful_probe_details.is_none() {
        match vcpkg::Config::new().lib_name("libcurl")
                .emit_includes(true).probe("curl") {
            Ok(details) => successful_probe_details = Some(details),
            Err(e) => println!("second run of vcpkg did not find libcurl: {}", e),
        }
    }

    if successful_probe_details.is_some() {
        // Found libcurl which depends on openssl, libssh2 and zlib
        // in the a default vcpkg installation. Probe for them
        // but do not fail if they are not present as we may be working
        // with a customized vcpkg installation.
        vcpkg::Config::new()
            .lib_name("libeay32")
            .lib_name("ssleay32")
            .probe("openssl").ok();

        vcpkg::probe_package("libssh2").ok();

        vcpkg::Config::new()
            .lib_names("zlib", "zlib1")
            .probe("zlib").ok();

        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=gdi32");
        println!("cargo:rustc-link-lib=user32");
        println!("cargo:rustc-link-lib=wldap32");
        return true;
    }
    false
}

fn try_pkg_config() -> bool {
    let mut cfg = pkg_config::Config::new();
    cfg.cargo_metadata(false);
    let lib = match cfg.probe("libcurl") {
        Ok(lib) => lib,
        Err(e) => {
            println!("Couldn't find libcurl from pkgconfig ({:?}), \
                      compiling it from source...", e);
            return false
        }
    };

    // Not all system builds of libcurl have http2 features enabled, so if we've
    // got a http2-requested build then we may fall back to a build from source.
    if cfg!(feature = "http2") {
        let output = Command::new("curl-config")
            .arg("--features")
            .output();
        let output = match output {
            Ok(out) => out,
            Err(e) => {
                println!("failed to run curl-config ({}), building from source", e);
                return false
            }
        };
        if !output.status.success() {
            println!("curl-config failed: {}", output.status);
            return false
        }
        let stdout = String::from_utf8_lossy(&output.stdout);
        if !stdout.contains("HTTP2") {
            println!("failed to find http-2 feature enabled in pkg-config-found \
                      libcurl, building from source");
            return false
        }
    }

    // Re-find the library to print cargo's metadata, then print some extra
    // metadata as well.
    cfg.cargo_metadata(true)
        .probe("libcurl")
        .unwrap();
    for path in lib.include_paths.iter() {
        println!("cargo:include={}", path.display());
    }
    return true
}
<commit_msg>Add best-effort inclusion of curl's pkg-config file<commit_after>extern crate pkg_config;
#[cfg(target_env = "msvc")]
extern crate vcpkg;
extern crate cc;

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

fn main() {
    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");

    // OSX and Haiku ships libcurl by default, so we just use that version
    // unconditionally.
    if target.contains("apple") || target.contains("haiku") {
        return println!("cargo:rustc-flags=-l curl");
    }

    // Next, fall back and try to use pkg-config if its available.
    if windows {
        if try_vcpkg() {
            return
        }
    } else {
        if try_pkg_config() {
            return
        }
    }

    if !Path::new("curl/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let include = dst.join("include");
    let build = dst.join("build");
    println!("cargo:root={}", dst.display());
    println!("cargo:include={}", include.display());
    fs::create_dir_all(include.join("curl")).unwrap();
    fs::copy("curl/include/curl/curl.h", include.join("curl/curl.h")).unwrap();
    fs::copy("curl/include/curl/curlver.h", include.join("curl/curlver.h")).unwrap();
    fs::copy("curl/include/curl/easy.h", include.join("curl/easy.h")).unwrap();
    fs::copy("curl/include/curl/mprintf.h", include.join("curl/mprintf.h")).unwrap();
    fs::copy("curl/include/curl/multi.h", include.join("curl/multi.h")).unwrap();
    fs::copy("curl/include/curl/stdcheaders.h", include.join("curl/stdcheaders.h")).unwrap();
    fs::copy("curl/include/curl/system.h", include.join("curl/system.h")).unwrap();
    fs::copy("curl/include/curl/typecheck-gcc.h", include.join("curl/typecheck-gcc.h")).unwrap();

    let pkgconfig = dst.join("lib/pkgconfig");
    fs::create_dir_all(&pkgconfig).unwrap();
    let contents = fs::read_to_string("curl/libcurl.pc.in").unwrap();
    fs::write(
        pkgconfig.join("libcurl.pc"),
        contents
            .replace("@prefix@", dst.to_str().unwrap())
            .replace("@exec_prefix@", "")
            .replace("@libdir@", dst.join("lib").to_str().unwrap())
            .replace("@includedir@", include.to_str().unwrap())
            .replace("@CPPFLAG_CURL_STATICLIB@", "")
            .replace("@LIBCURL_LIBS@", "")
            .replace("@SUPPORT_FEATURES@", "")
            .replace("@SUPPORT_PROTOCOLS@", "")
            .replace("@CURLVERSION@", "7.61.1"),
    ).unwrap();

    let mut cfg = cc::Build::new();
    cfg.out_dir(&build)
        .include("curl/lib")
        .include("curl/include")
        .define("BUILDING_LIBCURL", None)
        .define("CURL_DISABLE_CRYPTO_AUTH", None)
        .define("CURL_DISABLE_DICT", None)
        .define("CURL_DISABLE_FTP", None)
        .define("CURL_DISABLE_GOPHER", None)
        .define("CURL_DISABLE_IMAP", None)
        .define("CURL_DISABLE_LDAP", None)
        .define("CURL_DISABLE_LDAPS", None)
        .define("CURL_DISABLE_NTLM", None)
        .define("CURL_DISABLE_POP3", None)
        .define("CURL_DISABLE_RTSP", None)
        .define("CURL_DISABLE_SMB", None)
        .define("CURL_DISABLE_SMTP", None)
        .define("CURL_DISABLE_TELNET", None)
        .define("CURL_DISABLE_TFTP", None)
        .define("CURL_STATICLIB", None)
        .define("ENABLE_IPV6", None)
        .define("HAVE_ASSERT_H", None)
        .define("OS", "\"unknown\"") // TODO
        .define("HAVE_ZLIB_H", None)
        .define("HAVE_LIBZ", None)

        .file("curl/lib/asyn-thread.c")
        .file("curl/lib/base64.c")
        .file("curl/lib/conncache.c")
        .file("curl/lib/connect.c")
        .file("curl/lib/content_encoding.c")
        .file("curl/lib/cookie.c")
        .file("curl/lib/curl_addrinfo.c")
        .file("curl/lib/curl_ctype.c")
        .file("curl/lib/curl_memrchr.c")
        .file("curl/lib/curl_range.c")
        .file("curl/lib/curl_threads.c")
        .file("curl/lib/dotdot.c")
        .file("curl/lib/easy.c")
        .file("curl/lib/escape.c")
        .file("curl/lib/file.c")
        .file("curl/lib/fileinfo.c")
        .file("curl/lib/formdata.c")
        .file("curl/lib/getenv.c")
        .file("curl/lib/getinfo.c")
        .file("curl/lib/hash.c")
        .file("curl/lib/hostasyn.c")
        .file("curl/lib/hostcheck.c")
        .file("curl/lib/hostip.c")
        .file("curl/lib/hostip6.c")
        .file("curl/lib/http.c")
        .file("curl/lib/http2.c")
        .file("curl/lib/http_chunks.c")
        .file("curl/lib/http_proxy.c")
        .file("curl/lib/if2ip.c")
        .file("curl/lib/inet_ntop.c")
        .file("curl/lib/inet_pton.c")
        .file("curl/lib/llist.c")
        .file("curl/lib/mime.c")
        .file("curl/lib/mprintf.c")
        .file("curl/lib/multi.c")
        .file("curl/lib/netrc.c")
        .file("curl/lib/nonblock.c")
        .file("curl/lib/parsedate.c")
        .file("curl/lib/pipeline.c")
        .file("curl/lib/progress.c")
        .file("curl/lib/rand.c")
        .file("curl/lib/select.c")
        .file("curl/lib/sendf.c")
        .file("curl/lib/setopt.c")
        .file("curl/lib/share.c")
        .file("curl/lib/slist.c")
        .file("curl/lib/socks.c")
        .file("curl/lib/speedcheck.c")
        .file("curl/lib/splay.c")
        .file("curl/lib/strcase.c")
        .file("curl/lib/strdup.c")
        .file("curl/lib/strerror.c")
        .file("curl/lib/strtok.c")
        .file("curl/lib/strtoofft.c")
        .file("curl/lib/timeval.c")
        .file("curl/lib/transfer.c")
        .file("curl/lib/url.c")
        .file("curl/lib/version.c")
        .file("curl/lib/vtls/vtls.c")
        .file("curl/lib/warnless.c")
        .file("curl/lib/wildcard.c")

        .define("HAVE_GETADDRINFO", None)

        .warnings(false);

    if cfg!(feature = "http2") {
        cfg.define("USE_NGHTTP2", None)
            .define("NGHTTP2_STATICLIB", None);

        if let Some(path) = env::var_os("DEP_NGHTTP2_ROOT") {
            let path = PathBuf::from(path);
            cfg.include(path.join("include"));
        }
    }

    if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
        cfg.include(path);
    }

    if windows {
        cfg.define("USE_THREADS_WIN32", None)
            .define("HAVE_IOCTLSOCKET_FIONBIO", None)
            .define("USE_WINSOCK", None)
            .define("USE_WINDOWS_SSPI", None)
            .define("USE_SCHANNEL", None)
            .file("curl/lib/x509asn1.c")
            .file("curl/lib/curl_sspi.c")
            .file("curl/lib/socks_sspi.c")
            .file("curl/lib/system_win32.c")
            .file("curl/lib/vtls/schannel.c")
            .file("curl/lib/vtls/schannel_verify.c");
    } else {
        cfg.define("RECV_TYPE_ARG1", "int")
            .define("HAVE_PTHREAD_H", None)
            .define("HAVE_ARPA_INET_H", None)
            .define("HAVE_ERRNO_H", None)
            .define("HAVE_FCNTL_H", None)
            .define("HAVE_NETDB_H", None)
            .define("HAVE_NETINET_IN_H", None)
            .define("HAVE_POLL_FINE", None)
            .define("HAVE_POLL_H", None)
            .define("HAVE_FCNTL_O_NONBLOCK", None)
            .define("HAVE_SYS_SELECT_H", None)
            .define("HAVE_SYS_STAT_H", None)
            .define("HAVE_UNISTD_H", None)
            .define("HAVE_RECV", None)
            .define("HAVE_SELECT", None)
            .define("HAVE_SEND", None)
            .define("HAVE_SOCKET", None)
            .define("HAVE_STERRROR_R", None)
            .define("HAVE_STRUCT_TIMEVAL", None)
            .define("USE_THREADS_POSIX", None)
            .define("USE_OPENSSL", None)
            .define("RECV_TYPE_ARG2", "void*")
            .define("RECV_TYPE_ARG3", "size_t")
            .define("RECV_TYPE_ARG4", "int")
            .define("RECV_TYPE_RETV", "ssize_t")
            .define("SEND_QUAL_ARG2", "const")
            .define("SEND_TYPE_ARG1", "int")
            .define("SEND_TYPE_ARG2", "void*")
            .define("SEND_TYPE_ARG3", "size_t")
            .define("SEND_TYPE_ARG4", "int")
            .define("SEND_TYPE_RETV", "ssize_t")
            .define("SIZEOF_CURL_OFF_T", "8")
            .define("SIZEOF_INT", "4")
            .define("SIZEOF_SHORT", "2")
            .file("curl/lib/vtls/openssl.c");

        let width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
            .unwrap()
            .parse::<usize>()
            .unwrap();
        cfg.define("SIZEOF_SSIZE_T", Some(&(width / 8).to_string()[..]));
        cfg.define("SIZEOF_SIZE_T", Some(&(width / 8).to_string()[..]));
        cfg.define("SIZEOF_LONG", Some(&(width / 8).to_string()[..]));

        if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") {
            cfg.include(path);
        }

        cfg.flag("-fvisibility=hidden");
    }

    cfg.compile("curl");

    if windows {
        println!("cargo:rustc-link-lib=ws2_32");
        println!("cargo:rustc-link-lib=crypt32");
    }

    // Illumos/Solaris requires explicit linking with libnsl
    if target.contains("solaris") {
        println!("cargo:rustc-link-lib=nsl");
    }
}

#[cfg(not(target_env = "msvc"))]
fn try_vcpkg() -> bool {
    false
}

#[cfg(target_env = "msvc")]
fn try_vcpkg() -> bool {

    // the import library for the dll is called libcurl_imp
    let mut successful_probe_details =
        match vcpkg::Config::new().lib_names("libcurl_imp", "libcurl")
                .emit_includes(true).probe("curl") {
            Ok(details) => Some(details),
            Err(e) => {
                println!("first run of vcpkg did not find libcurl: {}", e);
                None
            }
        };

    if successful_probe_details.is_none() {
        match vcpkg::Config::new().lib_name("libcurl")
                .emit_includes(true).probe("curl") {
            Ok(details) => successful_probe_details = Some(details),
            Err(e) => println!("second run of vcpkg did not find libcurl: {}", e),
        }
    }

    if successful_probe_details.is_some() {
        // Found libcurl which depends on openssl, libssh2 and zlib
        // in the a default vcpkg installation. Probe for them
        // but do not fail if they are not present as we may be working
        // with a customized vcpkg installation.
        vcpkg::Config::new()
            .lib_name("libeay32")
            .lib_name("ssleay32")
            .probe("openssl").ok();

        vcpkg::probe_package("libssh2").ok();

        vcpkg::Config::new()
            .lib_names("zlib", "zlib1")
            .probe("zlib").ok();

        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=gdi32");
        println!("cargo:rustc-link-lib=user32");
        println!("cargo:rustc-link-lib=wldap32");
        return true;
    }
    false
}

fn try_pkg_config() -> bool {
    let mut cfg = pkg_config::Config::new();
    cfg.cargo_metadata(false);
    let lib = match cfg.probe("libcurl") {
        Ok(lib) => lib,
        Err(e) => {
            println!("Couldn't find libcurl from pkgconfig ({:?}), \
                      compiling it from source...", e);
            return false
        }
    };

    // Not all system builds of libcurl have http2 features enabled, so if we've
    // got a http2-requested build then we may fall back to a build from source.
    if cfg!(feature = "http2") {
        let output = Command::new("curl-config")
            .arg("--features")
            .output();
        let output = match output {
            Ok(out) => out,
            Err(e) => {
                println!("failed to run curl-config ({}), building from source", e);
                return false
            }
        };
        if !output.status.success() {
            println!("curl-config failed: {}", output.status);
            return false
        }
        let stdout = String::from_utf8_lossy(&output.stdout);
        if !stdout.contains("HTTP2") {
            println!("failed to find http-2 feature enabled in pkg-config-found \
                      libcurl, building from source");
            return false
        }
    }

    // Re-find the library to print cargo's metadata, then print some extra
    // metadata as well.
    cfg.cargo_metadata(true)
        .probe("libcurl")
        .unwrap();
    for path in lib.include_paths.iter() {
        println!("cargo:include={}", path.display());
    }
    return true
}
<|endoftext|>


<commit_before>use crate::crypto::PublicKey;
use std::borrow::Cow;
use std::ops::Deref;

pub mod builder;
pub mod engine;
pub mod error;
pub mod op;
mod stack;

pub use self::builder::*;
pub use self::engine::*;
pub use self::error::*;
pub use self::op::*;

pub const MAX_FRAME_STACK: usize = 64;
pub const MAX_BYTE_SIZE: usize = 2048;

#[derive(Debug, Clone, PartialEq)]
pub struct Script(Vec<u8>);

impl Script {
    #[inline]
    pub fn new(byte_code: Vec<u8>) -> Self {
        Script(byte_code)
    }
}

impl From<&[u8]> for Script {
    #[inline]
    fn from(slice: &[u8]) -> Self {
        Script::new(slice.to_owned())
    }
}

impl From<Vec<u8>> for Script {
    #[inline]
    fn from(vec: Vec<u8>) -> Self {
        Script::new(vec)
    }
}

impl From<Builder> for Script {
    #[inline]
    fn from(b: Builder) -> Self {
        b.build()
    }
}

impl From<PublicKey> for Script {
    fn from(key: PublicKey) -> Self {
        let builder = Builder::new()
            .push(OpFrame::PubKey(key))
            .push(OpFrame::OpCheckSigFastFail);
        builder.build()
    }
}

impl<'a> Into<Cow<'a, Script>> for Script {
    #[inline]
    fn into(self) -> Cow<'a, Script> {
        Cow::Owned(self)
    }
}

impl<'a> Into<Cow<'a, Script>> for &'a Script {
    #[inline]
    fn into(self) -> Cow<'a, Script> {
        Cow::Borrowed(self)
    }
}

impl Deref for Script {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        &self.0
    }
}
<commit_msg>Implement AsRef for Script<commit_after>use crate::crypto::PublicKey;
use std::{
    borrow::Cow,
    ops::Deref,
};

pub mod builder;
pub mod engine;
pub mod error;
pub mod op;
mod stack;

pub use self::builder::*;
pub use self::engine::*;
pub use self::error::*;
pub use self::op::*;

pub const MAX_FRAME_STACK: usize = 64;
pub const MAX_BYTE_SIZE: usize = 2048;

#[derive(Debug, Clone, PartialEq)]
pub struct Script(Vec<u8>);

impl Script {
    #[inline]
    pub fn new(byte_code: Vec<u8>) -> Self {
        Script(byte_code)
    }
}

impl From<&[u8]> for Script {
    #[inline]
    fn from(slice: &[u8]) -> Self {
        Script::new(slice.to_owned())
    }
}

impl From<Vec<u8>> for Script {
    #[inline]
    fn from(vec: Vec<u8>) -> Self {
        Script::new(vec)
    }
}

impl From<Builder> for Script {
    #[inline]
    fn from(b: Builder) -> Self {
        b.build()
    }
}

impl From<PublicKey> for Script {
    fn from(key: PublicKey) -> Self {
        let builder = Builder::new()
            .push(OpFrame::PubKey(key))
            .push(OpFrame::OpCheckSigFastFail);
        builder.build()
    }
}

impl<'a> Into<Cow<'a, Script>> for Script {
    #[inline]
    fn into(self) -> Cow<'a, Script> {
        Cow::Owned(self)
    }
}

impl<'a> Into<Cow<'a, Script>> for &'a Script {
    #[inline]
    fn into(self) -> Cow<'a, Script> {
        Cow::Borrowed(self)
    }
}

impl AsRef<[u8]> for Script {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Deref for Script {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        &self.0
    }
}
<|endoftext|>


<commit_before>//! Types for the *m.key.verification.accept* event.

use std::collections::BTreeMap;

use ruma_events_macros::BasicEventContent;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;

use super::{
    HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, ShortAuthenticationString,
};
use crate::BasicEvent;

/// Accepts a previously sent *m.key.verification.start* message.
///
/// Typically sent as a to-device event.
pub type AcceptEvent = BasicEvent<AcceptEventContent>;

/// The payload for `AcceptEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)]
#[ruma_event(type = "m.key.verification.accept")]
pub struct AcceptEventContent {
    /// An opaque identifier for the verification process.
    ///
    /// Must be the same as the one used for the *m.key.verification.start*
    /// message.
    pub transaction_id: String,

    /// The method specific content.
    #[serde(flatten)]
    pub method: AcceptMethod,
}

/// An enum representing the different method specific
/// *m.key.verification.accept* content.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
#[serde(untagged)]
pub enum AcceptMethod {
    /// The *m.sas.v1* verification method.
    MSasV1(MSasV1Content),

    /// Any unknown accept method.
    Custom(CustomContent),
}

/// Method specific content of a unknown key verification method.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CustomContent {
    /// The name of the method.
    pub method: String,

    /// The additional fields that the method contains.
    #[serde(flatten)]
    pub fields: BTreeMap<String, JsonValue>,
}

/// The payload of an *m.key.verification.accept* event using the *m.sas.v1* method.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
#[serde(rename = "m.sas.v1", tag = "method")]
pub struct MSasV1Content {
    /// The key agreement protocol the device is choosing to use, out of the
    /// options in the *m.key.verification.start* message.
    pub key_agreement_protocol: KeyAgreementProtocol,

    /// The hash method the device is choosing to use, out of the options in the
    /// *m.key.verification.start* message.
    pub hash: HashAlgorithm,

    /// The message authentication code the device is choosing to use, out of
    /// the options in the *m.key.verification.start* message.
    pub message_authentication_code: MessageAuthenticationCode,

    /// The SAS methods both devices involved in the verification process
    /// understand.
    ///
    /// Must be a subset of the options in the *m.key.verification.start*
    /// message.
    pub short_authentication_string: Vec<ShortAuthenticationString>,

    /// The hash (encoded as unpadded base64) of the concatenation of the
    /// device's ephemeral public key (encoded as unpadded base64) and the
    /// canonical JSON representation of the *m.key.verification.start* message.
    pub commitment: String,
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use matches::assert_matches;
    use serde_json::{
        from_value as from_json_value, json, to_value as to_json_value, Value as JsonValue,
    };

    use super::{
        AcceptEvent, AcceptEventContent, AcceptMethod, CustomContent, HashAlgorithm,
        KeyAgreementProtocol, MSasV1Content, MessageAuthenticationCode, ShortAuthenticationString,
    };
    use ruma_common::Raw;

    #[test]
    fn serialization() {
        let key_verification_accept_content = AcceptEventContent {
            transaction_id: "456".into(),
            method: AcceptMethod::MSasV1(MSasV1Content {
                hash: HashAlgorithm::Sha256,
                key_agreement_protocol: KeyAgreementProtocol::Curve25519,
                message_authentication_code: MessageAuthenticationCode::HkdfHmacSha256,
                short_authentication_string: vec![ShortAuthenticationString::Decimal],
                commitment: "test_commitment".into(),
            }),
        };

        let key_verification_accept = AcceptEvent { content: key_verification_accept_content };

        let json_data = json!({
            "content": {
                "transaction_id": "456",
                "method": "m.sas.v1",
                "commitment": "test_commitment",
                "key_agreement_protocol": "curve25519",
                "hash": "sha256",
                "message_authentication_code": "hkdf-hmac-sha256",
                "short_authentication_string": ["decimal"]
            },
            "type": "m.key.verification.accept"
        });

        assert_eq!(to_json_value(&key_verification_accept).unwrap(), json_data);

        let json_data = json!({
            "content": {
                "transaction_id": "456",
                "method": "m.sas.custom",
                "test": "field",
            },
            "type": "m.key.verification.accept"
        });

        let key_verification_accept_content = AcceptEventContent {
            transaction_id: "456".into(),
            method: AcceptMethod::Custom(CustomContent {
                method: "m.sas.custom".to_owned(),
                fields: vec![("test".to_string(), JsonValue::from("field"))]
                    .into_iter()
                    .collect::<BTreeMap<String, JsonValue>>(),
            }),
        };

        let key_verification_accept = AcceptEvent { content: key_verification_accept_content };

        assert_eq!(to_json_value(&key_verification_accept).unwrap(), json_data);
    }

    #[test]
    fn deserialization() {
        let json = json!({
            "transaction_id": "456",
            "commitment": "test_commitment",
            "method": "m.sas.v1",
            "hash": "sha256",
            "key_agreement_protocol": "curve25519",
            "message_authentication_code": "hkdf-hmac-sha256",
            "short_authentication_string": ["decimal"]
        });

        // Deserialize the content struct separately to verify `TryFromRaw` is implemented for it.
        assert_matches!(
            from_json_value::<Raw<AcceptEventContent>>(json)
                .unwrap()
                .deserialize()
                .unwrap(),
            AcceptEventContent {
                transaction_id,
                method: AcceptMethod::MSasV1(MSasV1Content {
                    commitment,
                    hash,
                    key_agreement_protocol,
                    message_authentication_code,
                    short_authentication_string,
                })
            } if commitment == "test_commitment"
                && transaction_id == "456"
                && hash == HashAlgorithm::Sha256
                && key_agreement_protocol == KeyAgreementProtocol::Curve25519
                && message_authentication_code == MessageAuthenticationCode::HkdfHmacSha256
                && short_authentication_string == vec![ShortAuthenticationString::Decimal]
        );

        let json = json!({
            "content": {
                "commitment": "test_commitment",
                "transaction_id": "456",
                "method": "m.sas.v1",
                "key_agreement_protocol": "curve25519",
                "hash": "sha256",
                "message_authentication_code": "hkdf-hmac-sha256",
                "short_authentication_string": ["decimal"]
            },
            "type": "m.key.verification.accept"
        });

        assert_matches!(
            from_json_value::<Raw<AcceptEvent>>(json)
                .unwrap()
                .deserialize()
                .unwrap(),
            AcceptEvent {
                content: AcceptEventContent {
                    transaction_id,
                    method: AcceptMethod::MSasV1(MSasV1Content {
                        commitment,
                        hash,
                        key_agreement_protocol,
                        message_authentication_code,
                        short_authentication_string,
                    })
                }
            } if commitment == "test_commitment"
                && transaction_id == "456"
                && hash == HashAlgorithm::Sha256
                && key_agreement_protocol == KeyAgreementProtocol::Curve25519
                && message_authentication_code == MessageAuthenticationCode::HkdfHmacSha256
                && short_authentication_string == vec![ShortAuthenticationString::Decimal]
        );

        let json = json!({
            "content": {
                "from_device": "123",
                "transaction_id": "456",
                "method": "m.sas.custom",
                "test": "field",
            },
            "type": "m.key.verification.accept"
        });

        assert_matches!(
            from_json_value::<Raw<AcceptEvent>>(json)
                .unwrap()
                .deserialize()
                .unwrap(),
            AcceptEvent {
                content: AcceptEventContent {
                    transaction_id,
                    method: AcceptMethod::Custom(CustomContent {
                        method,
                        fields,
                    })
                }
            } if transaction_id == "456"
                && method == "m.sas.custom"
                && fields.get("test").unwrap() == &JsonValue::from("field")
        );
    }
}
<commit_msg>ruma-events: Add an init struct for the m.sas.v1 content of the accept event.<commit_after>//! Types for the *m.key.verification.accept* event.

use std::collections::BTreeMap;

use ruma_events_macros::BasicEventContent;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;

use super::{
    HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, ShortAuthenticationString,
};
use crate::BasicEvent;

/// Accepts a previously sent *m.key.verification.start* message.
///
/// Typically sent as a to-device event.
pub type AcceptEvent = BasicEvent<AcceptEventContent>;

/// The payload for `AcceptEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)]
#[ruma_event(type = "m.key.verification.accept")]
pub struct AcceptEventContent {
    /// An opaque identifier for the verification process.
    ///
    /// Must be the same as the one used for the *m.key.verification.start*
    /// message.
    pub transaction_id: String,

    /// The method specific content.
    #[serde(flatten)]
    pub method: AcceptMethod,
}

/// An enum representing the different method specific
/// *m.key.verification.accept* content.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
#[serde(untagged)]
pub enum AcceptMethod {
    /// The *m.sas.v1* verification method.
    MSasV1(MSasV1Content),

    /// Any unknown accept method.
    Custom(CustomContent),
}

/// Method specific content of a unknown key verification method.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CustomContent {
    /// The name of the method.
    pub method: String,

    /// The additional fields that the method contains.
    #[serde(flatten)]
    pub fields: BTreeMap<String, JsonValue>,
}

/// The payload of an *m.key.verification.accept* event using the *m.sas.v1* method.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
#[serde(rename = "m.sas.v1", tag = "method")]
pub struct MSasV1Content {
    /// The key agreement protocol the device is choosing to use, out of the
    /// options in the *m.key.verification.start* message.
    pub key_agreement_protocol: KeyAgreementProtocol,

    /// The hash method the device is choosing to use, out of the options in the
    /// *m.key.verification.start* message.
    pub hash: HashAlgorithm,

    /// The message authentication code the device is choosing to use, out of
    /// the options in the *m.key.verification.start* message.
    pub message_authentication_code: MessageAuthenticationCode,

    /// The SAS methods both devices involved in the verification process
    /// understand.
    ///
    /// Must be a subset of the options in the *m.key.verification.start*
    /// message.
    pub short_authentication_string: Vec<ShortAuthenticationString>,

    /// The hash (encoded as unpadded base64) of the concatenation of the
    /// device's ephemeral public key (encoded as unpadded base64) and the
    /// canonical JSON representation of the *m.key.verification.start* message.
    pub commitment: String,
}

/// Mandatory initial set of fields for creating an accept `MSasV1Content`.
#[derive(Clone, Debug, Deserialize)]
pub struct MSasV1ContentInit {
    /// The key agreement protocol the device is choosing to use, out of the
    /// options in the *m.key.verification.start* message.
    pub key_agreement_protocol: KeyAgreementProtocol,

    /// The hash method the device is choosing to use, out of the options in the
    /// *m.key.verification.start* message.
    pub hash: HashAlgorithm,

    /// The message authentication codes that the accepting device understands.
    pub message_authentication_code: MessageAuthenticationCode,

    /// The SAS methods both devices involved in the verification process
    /// understand.
    ///
    /// Must be a subset of the options in the *m.key.verification.start*
    /// message.
    pub short_authentication_string: Vec<ShortAuthenticationString>,

    /// The hash (encoded as unpadded base64) of the concatenation of the
    /// device's ephemeral public key (encoded as unpadded base64) and the
    /// canonical JSON representation of the *m.key.verification.start* message.
    pub commitment: String,
}

impl From<MSasV1ContentInit> for MSasV1Content {
    /// Creates a new `MSasV1Content` from the given init struct.
    fn from(init: MSasV1ContentInit) -> Self {
        MSasV1Content {
            hash: init.hash,
            key_agreement_protocol: init.key_agreement_protocol,
            message_authentication_code: init.message_authentication_code,
            short_authentication_string: init.short_authentication_string,
            commitment: init.commitment,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use matches::assert_matches;
    use serde_json::{
        from_value as from_json_value, json, to_value as to_json_value, Value as JsonValue,
    };

    use super::{
        AcceptEvent, AcceptEventContent, AcceptMethod, CustomContent, HashAlgorithm,
        KeyAgreementProtocol, MSasV1Content, MessageAuthenticationCode, ShortAuthenticationString,
    };
    use ruma_common::Raw;

    #[test]
    fn serialization() {
        let key_verification_accept_content = AcceptEventContent {
            transaction_id: "456".into(),
            method: AcceptMethod::MSasV1(MSasV1Content {
                hash: HashAlgorithm::Sha256,
                key_agreement_protocol: KeyAgreementProtocol::Curve25519,
                message_authentication_code: MessageAuthenticationCode::HkdfHmacSha256,
                short_authentication_string: vec![ShortAuthenticationString::Decimal],
                commitment: "test_commitment".into(),
            }),
        };

        let key_verification_accept = AcceptEvent { content: key_verification_accept_content };

        let json_data = json!({
            "content": {
                "transaction_id": "456",
                "method": "m.sas.v1",
                "commitment": "test_commitment",
                "key_agreement_protocol": "curve25519",
                "hash": "sha256",
                "message_authentication_code": "hkdf-hmac-sha256",
                "short_authentication_string": ["decimal"]
            },
            "type": "m.key.verification.accept"
        });

        assert_eq!(to_json_value(&key_verification_accept).unwrap(), json_data);

        let json_data = json!({
            "content": {
                "transaction_id": "456",
                "method": "m.sas.custom",
                "test": "field",
            },
            "type": "m.key.verification.accept"
        });

        let key_verification_accept_content = AcceptEventContent {
            transaction_id: "456".into(),
            method: AcceptMethod::Custom(CustomContent {
                method: "m.sas.custom".to_owned(),
                fields: vec![("test".to_string(), JsonValue::from("field"))]
                    .into_iter()
                    .collect::<BTreeMap<String, JsonValue>>(),
            }),
        };

        let key_verification_accept = AcceptEvent { content: key_verification_accept_content };

        assert_eq!(to_json_value(&key_verification_accept).unwrap(), json_data);
    }

    #[test]
    fn deserialization() {
        let json = json!({
            "transaction_id": "456",
            "commitment": "test_commitment",
            "method": "m.sas.v1",
            "hash": "sha256",
            "key_agreement_protocol": "curve25519",
            "message_authentication_code": "hkdf-hmac-sha256",
            "short_authentication_string": ["decimal"]
        });

        // Deserialize the content struct separately to verify `TryFromRaw` is implemented for it.
        assert_matches!(
            from_json_value::<Raw<AcceptEventContent>>(json)
                .unwrap()
                .deserialize()
                .unwrap(),
            AcceptEventContent {
                transaction_id,
                method: AcceptMethod::MSasV1(MSasV1Content {
                    commitment,
                    hash,
                    key_agreement_protocol,
                    message_authentication_code,
                    short_authentication_string,
                })
            } if commitment == "test_commitment"
                && transaction_id == "456"
                && hash == HashAlgorithm::Sha256
                && key_agreement_protocol == KeyAgreementProtocol::Curve25519
                && message_authentication_code == MessageAuthenticationCode::HkdfHmacSha256
                && short_authentication_string == vec![ShortAuthenticationString::Decimal]
        );

        let json = json!({
            "content": {
                "commitment": "test_commitment",
                "transaction_id": "456",
                "method": "m.sas.v1",
                "key_agreement_protocol": "curve25519",
                "hash": "sha256",
                "message_authentication_code": "hkdf-hmac-sha256",
                "short_authentication_string": ["decimal"]
            },
            "type": "m.key.verification.accept"
        });

        assert_matches!(
            from_json_value::<Raw<AcceptEvent>>(json)
                .unwrap()
                .deserialize()
                .unwrap(),
            AcceptEvent {
                content: AcceptEventContent {
                    transaction_id,
                    method: AcceptMethod::MSasV1(MSasV1Content {
                        commitment,
                        hash,
                        key_agreement_protocol,
                        message_authentication_code,
                        short_authentication_string,
                    })
                }
            } if commitment == "test_commitment"
                && transaction_id == "456"
                && hash == HashAlgorithm::Sha256
                && key_agreement_protocol == KeyAgreementProtocol::Curve25519
                && message_authentication_code == MessageAuthenticationCode::HkdfHmacSha256
                && short_authentication_string == vec![ShortAuthenticationString::Decimal]
        );

        let json = json!({
            "content": {
                "from_device": "123",
                "transaction_id": "456",
                "method": "m.sas.custom",
                "test": "field",
            },
            "type": "m.key.verification.accept"
        });

        assert_matches!(
            from_json_value::<Raw<AcceptEvent>>(json)
                .unwrap()
                .deserialize()
                .unwrap(),
            AcceptEvent {
                content: AcceptEventContent {
                    transaction_id,
                    method: AcceptMethod::Custom(CustomContent {
                        method,
                        fields,
                    })
                }
            } if transaction_id == "456"
                && method == "m.sas.custom"
                && fields.get("test").unwrap() == &JsonValue::from("field")
        );
    }
}
<|endoftext|>


<commit_before>//! Matrix room version identifiers.

use std::{
    cmp::Ordering,
    convert::TryFrom,
    fmt::{self, Display, Formatter},
};

use matches::matches;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::error::Error;

/// Room version identifiers cannot be more than 32 code points.
const MAX_CODE_POINTS: usize = 32;

/// A Matrix room version ID.
///
/// A `RoomVersionId` can be or converted or deserialized from a string slice, and can be converted
/// or serialized back into a string as needed.
///
/// ```
/// # use std::convert::TryFrom;
/// # use ruma_identifiers::RoomVersionId;
/// assert_eq!(RoomVersionId::try_from("1").unwrap().as_ref(), "1");
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RoomVersionId(InnerRoomVersionId);

/// Possibile values for room version, distinguishing between official Matrix versions and custom
/// versions.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum InnerRoomVersionId {
    /// A version 1 room.
    Version1,

    /// A version 2 room.
    Version2,

    /// A version 3 room.
    Version3,

    /// A version 4 room.
    Version4,

    /// A version 5 room.
    Version5,

    /// A version 6 room.
    Version6,

    /// A custom room version.
    Custom(Box<str>),
}

impl RoomVersionId {
    /// Creates a version 1 room ID.
    pub fn version_1() -> Self {
        Self(InnerRoomVersionId::Version1)
    }

    /// Creates a version 2 room ID.
    pub fn version_2() -> Self {
        Self(InnerRoomVersionId::Version2)
    }

    /// Creates a version 3 room ID.
    pub fn version_3() -> Self {
        Self(InnerRoomVersionId::Version3)
    }

    /// Creates a version 4 room ID.
    pub fn version_4() -> Self {
        Self(InnerRoomVersionId::Version4)
    }

    /// Creates a version 5 room ID.
    pub fn version_5() -> Self {
        Self(InnerRoomVersionId::Version5)
    }

    /// Creates a version 6 room ID.
    pub fn version_6() -> Self {
        Self(InnerRoomVersionId::Version6)
    }

    /// Whether or not this room version is an official one specified by the Matrix protocol.
    pub fn is_official(&self) -> bool {
        !self.is_custom()
    }

    /// Whether or not this is a custom room version.
    pub fn is_custom(&self) -> bool {
        matches!(self.0, InnerRoomVersionId::Custom(_))
    }

    /// Whether or not this is a version 1 room.
    pub fn is_version_1(&self) -> bool {
        self.0 == InnerRoomVersionId::Version1
    }

    /// Whether or not this is a version 2 room.
    pub fn is_version_2(&self) -> bool {
        self.0 == InnerRoomVersionId::Version2
    }

    /// Whether or not this is a version 3 room.
    pub fn is_version_3(&self) -> bool {
        self.0 == InnerRoomVersionId::Version3
    }

    /// Whether or not this is a version 4 room.
    pub fn is_version_4(&self) -> bool {
        self.0 == InnerRoomVersionId::Version4
    }

    /// Whether or not this is a version 5 room.
    pub fn is_version_5(&self) -> bool {
        self.0 == InnerRoomVersionId::Version5
    }

    /// Whether or not this is a version 6 room.
    pub fn is_version_6(&self) -> bool {
        self.0 == InnerRoomVersionId::Version5
    }
}

impl From<RoomVersionId> for String {
    fn from(id: RoomVersionId) -> Self {
        match id.0 {
            InnerRoomVersionId::Version1 => "1".to_owned(),
            InnerRoomVersionId::Version2 => "2".to_owned(),
            InnerRoomVersionId::Version3 => "3".to_owned(),
            InnerRoomVersionId::Version4 => "4".to_owned(),
            InnerRoomVersionId::Version5 => "5".to_owned(),
            InnerRoomVersionId::Version6 => "6".to_owned(),
            InnerRoomVersionId::Custom(version) => version.into(),
        }
    }
}

impl AsRef<str> for RoomVersionId {
    fn as_ref(&self) -> &str {
        match &self.0 {
            InnerRoomVersionId::Version1 => "1",
            InnerRoomVersionId::Version2 => "2",
            InnerRoomVersionId::Version3 => "3",
            InnerRoomVersionId::Version4 => "4",
            InnerRoomVersionId::Version5 => "5",
            InnerRoomVersionId::Version6 => "6",
            InnerRoomVersionId::Custom(version) => version,
        }
    }
}

impl Display for RoomVersionId {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_ref())
    }
}

impl PartialOrd for RoomVersionId {
    fn partial_cmp(&self, other: &RoomVersionId) -> Option<Ordering> {
        self.as_ref().partial_cmp(other.as_ref())
    }
}

impl Ord for RoomVersionId {
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

#[cfg(feature = "serde")]
impl Serialize for RoomVersionId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.as_ref())
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for RoomVersionId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        crate::deserialize_id(deserializer, "a Matrix room version ID as a string")
    }
}

/// Attempts to create a new Matrix room version ID from a string representation.
fn try_from<S>(room_version_id: S) -> Result<RoomVersionId, Error>
where
    S: AsRef<str> + Into<Box<str>>,
{
    let version = match room_version_id.as_ref() {
        "1" => RoomVersionId(InnerRoomVersionId::Version1),
        "2" => RoomVersionId(InnerRoomVersionId::Version2),
        "3" => RoomVersionId(InnerRoomVersionId::Version3),
        "4" => RoomVersionId(InnerRoomVersionId::Version4),
        "5" => RoomVersionId(InnerRoomVersionId::Version5),
        custom => {
            if custom.is_empty() {
                return Err(Error::MinimumLengthNotSatisfied);
            } else if custom.chars().count() > MAX_CODE_POINTS {
                return Err(Error::MaximumLengthExceeded);
            } else {
                RoomVersionId(InnerRoomVersionId::Custom(room_version_id.into()))
            }
        }
    };

    Ok(version)
}

impl TryFrom<&str> for RoomVersionId {
    type Error = crate::error::Error;

    fn try_from(s: &str) -> Result<Self, Error> {
        try_from(s)
    }
}

impl TryFrom<String> for RoomVersionId {
    type Error = crate::error::Error;

    fn try_from(s: String) -> Result<Self, Error> {
        try_from(s)
    }
}

impl PartialEq<&str> for RoomVersionId {
    fn eq(&self, other: &&str) -> bool {
        self.as_ref() == *other
    }
}

impl PartialEq<RoomVersionId> for &str {
    fn eq(&self, other: &RoomVersionId) -> bool {
        *self == other.as_ref()
    }
}

impl PartialEq<String> for RoomVersionId {
    fn eq(&self, other: &String) -> bool {
        self.as_ref() == other
    }
}

impl PartialEq<RoomVersionId> for String {
    fn eq(&self, other: &RoomVersionId) -> bool {
        self == other.as_ref()
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;

    #[cfg(feature = "serde")]
    use serde_json::{from_str, to_string};

    use super::RoomVersionId;
    use crate::error::Error;

    #[test]
    fn valid_version_1_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.").as_ref(),
            "1"
        );
    }

    #[test]
    fn valid_version_2_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.").as_ref(),
            "2"
        );
    }

    #[test]
    fn valid_version_3_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.").as_ref(),
            "3"
        );
    }

    #[test]
    fn valid_version_4_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.").as_ref(),
            "4"
        );
    }

    #[test]
    fn valid_version_5_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.").as_ref(),
            "5"
        );
    }

    #[test]
    fn valid_custom_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.").as_ref(),
            "io.ruma.1"
        );
    }

    #[test]
    fn empty_room_version_id() {
        assert_eq!(RoomVersionId::try_from(""), Err(Error::MinimumLengthNotSatisfied));
    }

    #[test]
    fn over_max_code_point_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("0123456789012345678901234567890123456789"),
            Err(Error::MaximumLengthExceeded)
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_official_room_id() {
        assert_eq!(
            to_string(&RoomVersionId::try_from("1").expect("Failed to create RoomVersionId."))
                .expect("Failed to convert RoomVersionId to JSON."),
            r#""1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_official_room_id() {
        let deserialized =
            from_str::<RoomVersionId>(r#""1""#).expect("Failed to convert RoomVersionId to JSON.");

        assert!(deserialized.is_version_1());
        assert!(deserialized.is_official());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.")
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_custom_room_id() {
        assert_eq!(
            to_string(
                &RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
            )
            .expect("Failed to convert RoomVersionId to JSON."),
            r#""io.ruma.1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_custom_room_id() {
        let deserialized = from_str::<RoomVersionId>(r#""io.ruma.1""#)
            .expect("Failed to convert RoomVersionId to JSON.");

        assert!(deserialized.is_custom());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
        );
    }

    #[test]
    fn constructors() {
        assert!(RoomVersionId::version_1().is_version_1());
        assert!(RoomVersionId::version_2().is_version_2());
        assert!(RoomVersionId::version_3().is_version_3());
        assert!(RoomVersionId::version_4().is_version_4());
        assert!(RoomVersionId::version_5().is_version_5());
    }

    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn predicate_methods() {
        let version_1 = RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.");
        let version_2 = RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.");
        let version_3 = RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.");
        let version_4 = RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.");
        let version_5 = RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.");
        let custom = RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.");

        assert!(version_1.is_version_1());
        assert!(version_2.is_version_2());
        assert!(version_3.is_version_3());
        assert!(version_4.is_version_4());
        assert!(version_5.is_version_5());

        assert!(!version_1.is_version_2());
        assert!(!version_1.is_version_3());
        assert!(!version_1.is_version_4());
        assert!(!version_1.is_version_5());

        assert!(version_1.is_official());
        assert!(version_2.is_official());
        assert!(version_3.is_official());
        assert!(version_4.is_official());
        assert!(version_5.is_official());

        assert!(!version_1.is_custom());
        assert!(!version_2.is_custom());
        assert!(!version_3.is_custom());
        assert!(!version_4.is_custom());
        assert!(!version_5.is_custom());

        assert!(custom.is_custom());
        assert!(!custom.is_official());
        assert!(!custom.is_version_1());
        assert!(!custom.is_version_2());
        assert!(!custom.is_version_3());
        assert!(!custom.is_version_4());
        assert!(!custom.is_version_5());
    }
}
<commit_msg>Add documentation to RoomVersionId's PartialOrd, Ord impls<commit_after>//! Matrix room version identifiers.

use std::{
    cmp::Ordering,
    convert::TryFrom,
    fmt::{self, Display, Formatter},
};

use matches::matches;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::error::Error;

/// Room version identifiers cannot be more than 32 code points.
const MAX_CODE_POINTS: usize = 32;

/// A Matrix room version ID.
///
/// A `RoomVersionId` can be or converted or deserialized from a string slice, and can be converted
/// or serialized back into a string as needed.
///
/// ```
/// # use std::convert::TryFrom;
/// # use ruma_identifiers::RoomVersionId;
/// assert_eq!(RoomVersionId::try_from("1").unwrap().as_ref(), "1");
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RoomVersionId(InnerRoomVersionId);

/// Possibile values for room version, distinguishing between official Matrix versions and custom
/// versions.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum InnerRoomVersionId {
    /// A version 1 room.
    Version1,

    /// A version 2 room.
    Version2,

    /// A version 3 room.
    Version3,

    /// A version 4 room.
    Version4,

    /// A version 5 room.
    Version5,

    /// A version 6 room.
    Version6,

    /// A custom room version.
    Custom(Box<str>),
}

impl RoomVersionId {
    /// Creates a version 1 room ID.
    pub fn version_1() -> Self {
        Self(InnerRoomVersionId::Version1)
    }

    /// Creates a version 2 room ID.
    pub fn version_2() -> Self {
        Self(InnerRoomVersionId::Version2)
    }

    /// Creates a version 3 room ID.
    pub fn version_3() -> Self {
        Self(InnerRoomVersionId::Version3)
    }

    /// Creates a version 4 room ID.
    pub fn version_4() -> Self {
        Self(InnerRoomVersionId::Version4)
    }

    /// Creates a version 5 room ID.
    pub fn version_5() -> Self {
        Self(InnerRoomVersionId::Version5)
    }

    /// Creates a version 6 room ID.
    pub fn version_6() -> Self {
        Self(InnerRoomVersionId::Version6)
    }

    /// Whether or not this room version is an official one specified by the Matrix protocol.
    pub fn is_official(&self) -> bool {
        !self.is_custom()
    }

    /// Whether or not this is a custom room version.
    pub fn is_custom(&self) -> bool {
        matches!(self.0, InnerRoomVersionId::Custom(_))
    }

    /// Whether or not this is a version 1 room.
    pub fn is_version_1(&self) -> bool {
        self.0 == InnerRoomVersionId::Version1
    }

    /// Whether or not this is a version 2 room.
    pub fn is_version_2(&self) -> bool {
        self.0 == InnerRoomVersionId::Version2
    }

    /// Whether or not this is a version 3 room.
    pub fn is_version_3(&self) -> bool {
        self.0 == InnerRoomVersionId::Version3
    }

    /// Whether or not this is a version 4 room.
    pub fn is_version_4(&self) -> bool {
        self.0 == InnerRoomVersionId::Version4
    }

    /// Whether or not this is a version 5 room.
    pub fn is_version_5(&self) -> bool {
        self.0 == InnerRoomVersionId::Version5
    }

    /// Whether or not this is a version 6 room.
    pub fn is_version_6(&self) -> bool {
        self.0 == InnerRoomVersionId::Version5
    }
}

impl From<RoomVersionId> for String {
    fn from(id: RoomVersionId) -> Self {
        match id.0 {
            InnerRoomVersionId::Version1 => "1".to_owned(),
            InnerRoomVersionId::Version2 => "2".to_owned(),
            InnerRoomVersionId::Version3 => "3".to_owned(),
            InnerRoomVersionId::Version4 => "4".to_owned(),
            InnerRoomVersionId::Version5 => "5".to_owned(),
            InnerRoomVersionId::Version6 => "6".to_owned(),
            InnerRoomVersionId::Custom(version) => version.into(),
        }
    }
}

impl AsRef<str> for RoomVersionId {
    fn as_ref(&self) -> &str {
        match &self.0 {
            InnerRoomVersionId::Version1 => "1",
            InnerRoomVersionId::Version2 => "2",
            InnerRoomVersionId::Version3 => "3",
            InnerRoomVersionId::Version4 => "4",
            InnerRoomVersionId::Version5 => "5",
            InnerRoomVersionId::Version6 => "6",
            InnerRoomVersionId::Custom(version) => version,
        }
    }
}

impl Display for RoomVersionId {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_ref())
    }
}

impl PartialOrd for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn partial_cmp(&self, other: &RoomVersionId) -> Option<Ordering> {
        self.as_ref().partial_cmp(other.as_ref())
    }
}

impl Ord for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

#[cfg(feature = "serde")]
impl Serialize for RoomVersionId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.as_ref())
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for RoomVersionId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        crate::deserialize_id(deserializer, "a Matrix room version ID as a string")
    }
}

/// Attempts to create a new Matrix room version ID from a string representation.
fn try_from<S>(room_version_id: S) -> Result<RoomVersionId, Error>
where
    S: AsRef<str> + Into<Box<str>>,
{
    let version = match room_version_id.as_ref() {
        "1" => RoomVersionId(InnerRoomVersionId::Version1),
        "2" => RoomVersionId(InnerRoomVersionId::Version2),
        "3" => RoomVersionId(InnerRoomVersionId::Version3),
        "4" => RoomVersionId(InnerRoomVersionId::Version4),
        "5" => RoomVersionId(InnerRoomVersionId::Version5),
        custom => {
            if custom.is_empty() {
                return Err(Error::MinimumLengthNotSatisfied);
            } else if custom.chars().count() > MAX_CODE_POINTS {
                return Err(Error::MaximumLengthExceeded);
            } else {
                RoomVersionId(InnerRoomVersionId::Custom(room_version_id.into()))
            }
        }
    };

    Ok(version)
}

impl TryFrom<&str> for RoomVersionId {
    type Error = crate::error::Error;

    fn try_from(s: &str) -> Result<Self, Error> {
        try_from(s)
    }
}

impl TryFrom<String> for RoomVersionId {
    type Error = crate::error::Error;

    fn try_from(s: String) -> Result<Self, Error> {
        try_from(s)
    }
}

impl PartialEq<&str> for RoomVersionId {
    fn eq(&self, other: &&str) -> bool {
        self.as_ref() == *other
    }
}

impl PartialEq<RoomVersionId> for &str {
    fn eq(&self, other: &RoomVersionId) -> bool {
        *self == other.as_ref()
    }
}

impl PartialEq<String> for RoomVersionId {
    fn eq(&self, other: &String) -> bool {
        self.as_ref() == other
    }
}

impl PartialEq<RoomVersionId> for String {
    fn eq(&self, other: &RoomVersionId) -> bool {
        self == other.as_ref()
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;

    #[cfg(feature = "serde")]
    use serde_json::{from_str, to_string};

    use super::RoomVersionId;
    use crate::error::Error;

    #[test]
    fn valid_version_1_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.").as_ref(),
            "1"
        );
    }

    #[test]
    fn valid_version_2_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.").as_ref(),
            "2"
        );
    }

    #[test]
    fn valid_version_3_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.").as_ref(),
            "3"
        );
    }

    #[test]
    fn valid_version_4_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.").as_ref(),
            "4"
        );
    }

    #[test]
    fn valid_version_5_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.").as_ref(),
            "5"
        );
    }

    #[test]
    fn valid_custom_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.").as_ref(),
            "io.ruma.1"
        );
    }

    #[test]
    fn empty_room_version_id() {
        assert_eq!(RoomVersionId::try_from(""), Err(Error::MinimumLengthNotSatisfied));
    }

    #[test]
    fn over_max_code_point_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("0123456789012345678901234567890123456789"),
            Err(Error::MaximumLengthExceeded)
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_official_room_id() {
        assert_eq!(
            to_string(&RoomVersionId::try_from("1").expect("Failed to create RoomVersionId."))
                .expect("Failed to convert RoomVersionId to JSON."),
            r#""1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_official_room_id() {
        let deserialized =
            from_str::<RoomVersionId>(r#""1""#).expect("Failed to convert RoomVersionId to JSON.");

        assert!(deserialized.is_version_1());
        assert!(deserialized.is_official());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.")
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_custom_room_id() {
        assert_eq!(
            to_string(
                &RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
            )
            .expect("Failed to convert RoomVersionId to JSON."),
            r#""io.ruma.1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_custom_room_id() {
        let deserialized = from_str::<RoomVersionId>(r#""io.ruma.1""#)
            .expect("Failed to convert RoomVersionId to JSON.");

        assert!(deserialized.is_custom());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
        );
    }

    #[test]
    fn constructors() {
        assert!(RoomVersionId::version_1().is_version_1());
        assert!(RoomVersionId::version_2().is_version_2());
        assert!(RoomVersionId::version_3().is_version_3());
        assert!(RoomVersionId::version_4().is_version_4());
        assert!(RoomVersionId::version_5().is_version_5());
    }

    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn predicate_methods() {
        let version_1 = RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.");
        let version_2 = RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.");
        let version_3 = RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.");
        let version_4 = RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.");
        let version_5 = RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.");
        let custom = RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.");

        assert!(version_1.is_version_1());
        assert!(version_2.is_version_2());
        assert!(version_3.is_version_3());
        assert!(version_4.is_version_4());
        assert!(version_5.is_version_5());

        assert!(!version_1.is_version_2());
        assert!(!version_1.is_version_3());
        assert!(!version_1.is_version_4());
        assert!(!version_1.is_version_5());

        assert!(version_1.is_official());
        assert!(version_2.is_official());
        assert!(version_3.is_official());
        assert!(version_4.is_official());
        assert!(version_5.is_official());

        assert!(!version_1.is_custom());
        assert!(!version_2.is_custom());
        assert!(!version_3.is_custom());
        assert!(!version_4.is_custom());
        assert!(!version_5.is_custom());

        assert!(custom.is_custom());
        assert!(!custom.is_official());
        assert!(!custom.is_version_1());
        assert!(!custom.is_version_2());
        assert!(!custom.is_version_3());
        assert!(!custom.is_version_4());
        assert!(!custom.is_version_5());
    }
}
<|endoftext|>


<commit_before>use std::slice::Iter;
use std::iter::Filter;
use rect::*;

fn make_vec() -> Vec<Rect> {
    vec![
        Rect::new(1, 2, "first"),
        Rect::new(10, 20, "second"),
        Rect::new(3, 5, "third"),
        Rect::new(12, 222, "fourth")
    ]
}

pub fn run() {
    println!("********* Iterator<Rect> examples *********");
    demo_skip_take();
    demo_partition();
    demo_find();
    demo_enumerate();
    demo_chain();
    demo_filter();
    demo_reduction();
    demo_map();
}

fn demo_skip_take() {
    println!(">>> demo_skip_take");

    let v = make_vec();
    let result : Vec<_> = v.iter().take(2).collect();
    println!("v.take(2) = {:?}", result);

    let v = make_vec();
    let result : Vec<_> = v.iter().skip(3).take(5).collect();
    println!("v.skip(3).take(5) = {:?}", result);

    // Note that when using structs (rather than ints) we don't need to add &&
    // because the compiler can infer what we want automatically.
    let v = make_vec();
    let result : Vec<_> = v.iter().take_while(|x| x.w < 10).collect();
    println!("v.take_while(x.w < 10) = {:?}", result);

    let v = make_vec();
    let result : Vec<_> = v.iter().skip_while(|x| x.w < 10).collect();
    println!("v.skip_while(x.w < 10) = {:?}", result);
}

// Note we are returning Vec<&Rect> rather than Vec<Rect>. This is to avoid copying the Rects,
// which is actually impossible because they do not implement the Copy trait.
fn demo_partition() {
    println!(">>> demo_partition (like filter, but returns both sets of things)");

    let v = make_vec();
    let (a, b) : (Vec<&Rect>, Vec<&Rect>) = v.iter().partition(|x| x.w < 10);
    println!("v.partition(x < 100) (a,b) = ({:?}, {:?})", a, b);
}

fn demo_find() {
    println!(">>> demo_find");

    // find() gives us an Option<T> to the first match, if any.
    let v = make_vec();
    let result = v.iter().find(|x| x.w == 1000);
    println!("v.find(x.w == 1000) = {:?}", result);        // None
    let result = v.iter().find(|x| x.w > 10);
    println!("v.find(x.w > 10) = {:?}", result);         // Some(Rect...)

    // position() and rposition() return an Option<usize> where the usize is the index of the element.
    let v = make_vec();
    let result = v.iter().position(|x| x.w == 987);
    println!("v.position(x.w == 987) = {:?}", result);    // None
    let result = v.iter().position(|x| x.w > 10);
    println!("v.position(x.w > 10) = {:?}", result);     // Some(3)
}

fn demo_enumerate() {
    println!(">>> demo_enumerate");

    // elem is &Rect.
    let v = make_vec();
    for (idx, elem) in v.iter().take(3).enumerate() {
        println!("v[{}] = {}", idx, elem);
    }
}

fn demo_chain() {
    println!(">>> demo_chain");

    let v = make_vec();
    let num_to_skip = v.len() - 1;
    let beginning = v.iter().take(1);
    let end = v.iter().skip(num_to_skip);

    for (idx, elem) in beginning.chain(end).enumerate() {
        println!("(1st chain): v[{}] = {}", idx, elem);
    }

    // Better, only a single pass. len() is O(1) because a Vec is a triple (ptr, capacity, length).
    let v = make_vec();
    let end_index = v.len() - 1;
    for (idx, elem) in v.iter().enumerate().filter(|&(idx, _)| idx < 1 || idx >= end_index) {
        println!("(2nd chain): v[{}] = {}", idx, elem);
    }
}

// /*
// // This is HARD: see https://stackoverflow.com/questions/27535289/what-is-the-correct-way-to-return-an-iterator
// fn return_iterator_from_function() -> Filter<Iter<i32>, fn(&i32) -> bool> {
//     let v = make_vec();
//     v.iter().filter(|&x| x % 2 == 0)
// }
// */

fn demo_filter() {
    println!(">>> demo_filter");

    let v = make_vec();
    let result : Vec<_> = v.iter().filter(|x| x.title.contains("i")).collect();
    println!("v.filter(x.title.contains(\"i\")) = {:?}", result);
}

fn demo_reduction() {
    println!(">>> demo_reduction");

    let v = make_vec();
    let result : i32 = v.iter().map(|x| x.w).sum();
    println!("v.sum(of w) = {:?}", result);

    let v = make_vec();
    let result = v.iter().fold(100000, |mut agg, x| { agg += x.w; agg });
    println!("v.fold(100000 + w) = {:?}", result);
}

fn demo_map() {
    println!(">>> demo_map");

    let v = make_vec();
    let result : Vec<_> = v.iter().map(|x| x.w + 1000).collect();
    println!("v.map (original vector is unchanged) = {:?}", result);

    let mut v = make_vec();
    v.iter_mut().map(|x| x.w += 1000).count();
    println!("v.map (using iter_mut) = {:?}", v);

    // step_by() requires `#![feature(iterator_step_by)]` in main.rs.
    let mut v = make_vec();
    v.iter_mut().step_by(2).map(|x| x.w += 5000).count();
    println!("v.map (using iter_mut and step_by) = {:?}", v);
}
<commit_msg>Rust: more Iterator<Rect> examples.<commit_after>use std::slice::Iter;
use std::iter::Filter;
use rect::*;

fn make_vec() -> Vec<Rect> {
    vec![
        Rect::new(1, 2, "first"),
        Rect::new(10, 20, "second"),
        Rect::new(3, 5, "third"),
        Rect::new(12, 222, "fourth")
    ]
}

pub fn run() {
    println!("********* Iterator<Rect> examples *********");
    demo_skip_take();
    demo_partition();
    demo_find();
    demo_enumerate();
    demo_chain();
    demo_filter();
    demo_reduction();
    demo_map();
    demo_max();
}

fn demo_skip_take() {
    println!(">>> demo_skip_take");

    let v = make_vec();
    let result : Vec<_> = v.iter().take(2).collect();
    println!("v.take(2) = {:?}", result);

    let v = make_vec();
    let result : Vec<_> = v.iter().skip(3).take(5).collect();
    println!("v.skip(3).take(5) = {:?}", result);

    // Note that when using structs (rather than ints) we don't need to add &&
    // because the compiler can infer what we want automatically.
    let v = make_vec();
    let result : Vec<_> = v.iter().take_while(|x| x.w < 10).collect();
    println!("v.take_while(x.w < 10) = {:?}", result);

    let v = make_vec();
    let result : Vec<_> = v.iter().skip_while(|x| x.w < 10).collect();
    println!("v.skip_while(x.w < 10) = {:?}", result);
}

// Note we are returning Vec<&Rect> rather than Vec<Rect>. This is to avoid copying the Rects,
// which is actually impossible because they do not implement the Copy trait.
fn demo_partition() {
    println!(">>> demo_partition (like filter, but returns both sets of things)");

    let v = make_vec();
    let (a, b) : (Vec<&Rect>, Vec<&Rect>) = v.iter().partition(|x| x.w < 10);
    println!("v.partition(x < 100) (a,b) = ({:?}, {:?})", a, b);
}

fn demo_find() {
    println!(">>> demo_find");

    // find() gives us an Option<T> to the first match, if any.
    let v = make_vec();
    let result = v.iter().find(|x| x.w == 1000);
    println!("v.find(x.w == 1000) = {:?}", result);        // None
    let result = v.iter().find(|x| x.w > 10);
    println!("v.find(x.w > 10) = {:?}", result);         // Some(Rect...)

    // position() and rposition() return an Option<usize> where the usize is the index of the element.
    let v = make_vec();
    let result = v.iter().position(|x| x.w == 987);
    println!("v.position(x.w == 987) = {:?}", result);    // None
    let result = v.iter().position(|x| x.w > 10);
    println!("v.position(x.w > 10) = {:?}", result);     // Some(3)
}

fn demo_enumerate() {
    println!(">>> demo_enumerate");

    // elem is &Rect.
    let v = make_vec();
    for (idx, elem) in v.iter().take(3).enumerate() {
        println!("v[{}] = {}", idx, elem);
    }
}

fn demo_chain() {
    println!(">>> demo_chain");

    let v = make_vec();
    let num_to_skip = v.len() - 1;
    let beginning = v.iter().take(1);
    let end = v.iter().skip(num_to_skip);

    for (idx, elem) in beginning.chain(end).enumerate() {
        println!("(1st chain): v[{}] = {}", idx, elem);
    }

    // Better, only a single pass. len() is O(1) because a Vec is a triple (ptr, capacity, length).
    let v = make_vec();
    let end_index = v.len() - 1;
    for (idx, elem) in v.iter().enumerate().filter(|&(idx, _)| idx < 1 || idx >= end_index) {
        println!("(2nd chain): v[{}] = {}", idx, elem);
    }
}

// /*
// // This is HARD: see https://stackoverflow.com/questions/27535289/what-is-the-correct-way-to-return-an-iterator
// fn return_iterator_from_function() -> Filter<Iter<i32>, fn(&i32) -> bool> {
//     let v = make_vec();
//     v.iter().filter(|&x| x % 2 == 0)
// }
// */

fn demo_filter() {
    println!(">>> demo_filter");

    let v = make_vec();
    let result : Vec<_> = v.iter().filter(|x| x.title.contains("i")).collect();
    println!("v.filter(x.title.contains(\"i\")) = {:?}", result);
}

fn demo_reduction() {
    println!(">>> demo_reduction");

    let v = make_vec();
    let result : i32 = v.iter().map(|x| x.w).sum();
    println!("v.sum(of w) = {:?}", result);

    let v = make_vec();
    let result = v.iter().fold(100000, |mut agg, x| { agg += x.w; agg });
    println!("v.fold(100000 + w) = {:?}", result);
}

fn demo_map() {
    println!(">>> demo_map");

    let v = make_vec();
    let result : Vec<_> = v.iter().map(|x| x.w + 1000).collect();
    println!("v.map (original vector is unchanged) = {:?}", result);

    let mut v = make_vec();
    v.iter_mut().map(|x| x.w += 1000).count();
    println!("v.map (using iter_mut) = {:?}", v);

    // step_by() requires `#![feature(iterator_step_by)]` in main.rs.
    let mut v = make_vec();
    v.iter_mut().step_by(2).map(|x| x.w += 5000).count();
    println!("v.map (using iter_mut and step_by) = {:?}", v);
}

fn demo_max() {
    println!(">>> demo_max");

    let v = make_vec();
    let result = v.iter().max_by_key(|x| x.h);
    println!("v.max_by_key(by x.h) = {:?}", result);

    // max_by is a little harder to use than max_by_key, which will probably do what you
    // want in most circumstances, unless you need to bind F as a variable.
    let result = v.iter().max_by(|a, b| a.title.cmp(&b.title));
    println!("v.max_by = {:?}", result);
}<|endoftext|>