<commit_before>// Copyright 2021 Chad Williamson <chad@dahc.us>
//
// Use of this source code is governed by an MIT-syle license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT.

// This file contains integration tests for the find command.
//
// Note: the `serial` macro is used on tests that make assumptions about the
// working directory, since we have at least one test that needs to change it.

use assert_cmd::Command;
use predicates::prelude::*;
use serial_test::serial;
use std::fs::File;
use std::io::Write;
use std::{env, io::ErrorKind};
use tempfile::Builder;

#[cfg(unix)]
use std::os::unix::fs::symlink;

#[cfg(windows)]
use std::os::windows::fs::{symlink_dir, symlink_file};

use common::test_helpers::*;

mod common;

// Variants of fix_up_slashes that properly escape the forward slashes for being
// in a regex.
#[cfg(windows)]
fn fix_up_regex_slashes(re: &str) -> String {
    re.replace("/", "\\\\")
}

#[cfg(not(windows))]
fn fix_up_regex_slashes(re: &str) -> String {
    re.to_owned()
}

#[serial(working_dir)]
#[test]
fn no_args() {
    Command::cargo_bin("find")
        .expect("found binary")
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("test_data"));
}

#[serial(working_dir)]
#[test]
fn two_matchers_both_match() {
    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-type", "d", "-name", "test_data"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("test_data"));
}

#[serial(working_dir)]
#[test]
fn two_matchers_one_matches() {
    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-type", "f", "-name", "test_data"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::is_empty());
}

#[test]
fn matcher_with_side_effects_at_end() {
    let temp_dir = Builder::new().prefix("find_cmd_").tempdir().unwrap();

    let temp_dir_path = temp_dir.path().to_string_lossy();
    let test_file = temp_dir.path().join("test");
    File::create(&test_file).expect("created test file");

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-name", "test", "-delete"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::is_empty());

    assert!(!test_file.exists(), "test file should be deleted");
    assert!(temp_dir.path().exists(), "temp dir should NOT be deleted");
}

#[test]
fn matcher_with_side_effects_in_front() {
    let temp_dir = Builder::new().prefix("find_cmd_").tempdir().unwrap();

    let temp_dir_path = temp_dir.path().to_string_lossy();
    let test_file = temp_dir.path().join("test");
    File::create(&test_file).expect("created test file");

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-delete", "-name", "test"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::is_empty());

    assert!(!test_file.exists(), "test file should be deleted");
    assert!(!temp_dir.path().exists(), "temp dir should also be deleted");
}

// This could be covered by a unit test in principle... in practice, changing
// the working dir can't be done safely in unit tests unless `--test-threads=1`
// or `serial` goes everywhere, and it doesn't seem possible to get an
// appropriate `walkdir::DirEntry` for "." without actually changing dirs
// (or risking deletion of the repo itself).
#[serial(working_dir)]
#[test]
fn delete_on_dot_dir() {
    let temp_dir = Builder::new().prefix("example").tempdir().unwrap();
    let original_dir = env::current_dir().unwrap();
    env::set_current_dir(&temp_dir.path()).expect("working dir changed");

    // "." should be matched (confirmed by the print), but not deleted.
    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[".", "-delete", "-print"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::diff(".\n"));

    env::set_current_dir(original_dir).expect("restored original working dir");

    assert!(temp_dir.path().exists(), "temp dir should still exist");
}

#[test]
fn regex_types() {
    let temp_dir = Builder::new().prefix("find_cmd_").tempdir().unwrap();

    let temp_dir_path = temp_dir.path().to_string_lossy();
    let test_file = temp_dir.path().join("teeest");
    File::create(&test_file).expect("created test file");

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-regex", &fix_up_regex_slashes(".*/tE+st")])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::is_empty());

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-iregex", &fix_up_regex_slashes(".*/tE+st")])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("teeest"));

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &temp_dir_path,
            "-regextype",
            "posix-basic",
            "-regex",
            &fix_up_regex_slashes(r".*/te\{1,3\}st"),
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("teeest"));

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &temp_dir_path,
            "-regextype",
            "posix-extended",
            "-regex",
            &fix_up_regex_slashes(".*/te{1,3}st"),
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("teeest"));
}

#[test]
fn empty_files() {
    let temp_dir = Builder::new().prefix("find_cmd_").tempdir().unwrap();
    let temp_dir_path = temp_dir.path().to_string_lossy();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-empty"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(fix_up_slashes(&format!("{}\n", temp_dir_path)));

    let test_file_path = temp_dir.path().join("test");
    let mut test_file = File::create(&test_file_path).unwrap();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-empty"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(fix_up_slashes(&format!(
            "{}\n",
            test_file_path.to_string_lossy()
        )));

    let subdir_path = temp_dir.path().join("subdir");
    std::fs::create_dir(&subdir_path).unwrap();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-empty", "-sorted"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(fix_up_slashes(&format!(
            "{}\n{}\n",
            subdir_path.to_string_lossy(),
            test_file_path.to_string_lossy()
        )));

    write!(test_file, "x").unwrap();
    test_file.sync_all().unwrap();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-empty", "-sorted"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(fix_up_slashes(&format!(
            "{}\n",
            subdir_path.to_string_lossy(),
        )));
}

#[serial(working_dir)]
#[test]
fn find_printf() {
    #[cfg(unix)]
    {
        if let Err(e) = symlink("abbbc", "test_data/links/link-f") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink("subdir", "test_data/links/link-d") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink("missing", "test_data/links/link-missing") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink("abbbc/x", "test_data/links/link-notdir") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink("link-loop", "test_data/links/link-loop") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
    }
    #[cfg(windows)]
    {
        if let Err(e) = symlink_file("abbbc", "test_data/links/link-f") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink_dir("subdir", "test_data/links/link-d") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink_file("missing", "test_data/links/link-missing") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink_file("abbbc/x", "test_data/links/link-notdir") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
    }

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &fix_up_slashes("./test_data/simple"),
            "-sorted",
            "-printf",
            "%f %d %h %H %p %P %y\n",
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::diff(fix_up_slashes(
            "simple 0 ./test_data ./test_data/simple \
            ./test_data/simple  d\n\
            abbbc 1 ./test_data/simple ./test_data/simple \
            ./test_data/simple/abbbc abbbc f\n\
            subdir 1 ./test_data/simple ./test_data/simple \
            ./test_data/simple/subdir subdir d\n\
            ABBBC 2 ./test_data/simple/subdir ./test_data/simple \
            ./test_data/simple/subdir/ABBBC subdir/ABBBC f\n",
        )));

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &fix_up_slashes("./test_data/links"),
            "-sorted",
            "-type",
            "l",
            "-printf",
            "%f %l %y %Y\n",
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::diff(
            [
                "link-d subdir l d\n",
                "link-f abbbc l f\n",
                #[cfg(unix)]
                "link-loop link-loop l L\n",
                "link-missing missing l N\n",
                // We can't detect ENOTDIR on non-unix platforms yet.
                #[cfg(not(unix))]
                "link-notdir abbbc/x l ?\n",
                #[cfg(unix)]
                "link-notdir abbbc/x l N\n",
            ]
            .join(""),
        ));
}

#[cfg(unix)]
#[serial(working_dir)]
#[test]
fn find_perm() {
    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-perm", "+rwx"])
        .assert()
        .success();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-perm", "u+rwX"])
        .assert()
        .success();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-perm", "u=g"])
        .assert()
        .success();
}
<commit_msg>tests: Add integration tests for find -regextype ed/sed<commit_after>// Copyright 2021 Chad Williamson <chad@dahc.us>
//
// Use of this source code is governed by an MIT-syle license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT.

// This file contains integration tests for the find command.
//
// Note: the `serial` macro is used on tests that make assumptions about the
// working directory, since we have at least one test that needs to change it.

use assert_cmd::Command;
use predicates::prelude::*;
use serial_test::serial;
use std::fs::File;
use std::io::Write;
use std::{env, io::ErrorKind};
use tempfile::Builder;

#[cfg(unix)]
use std::os::unix::fs::symlink;

#[cfg(windows)]
use std::os::windows::fs::{symlink_dir, symlink_file};

use common::test_helpers::*;

mod common;

// Variants of fix_up_slashes that properly escape the forward slashes for being
// in a regex.
#[cfg(windows)]
fn fix_up_regex_slashes(re: &str) -> String {
    re.replace("/", "\\\\")
}

#[cfg(not(windows))]
fn fix_up_regex_slashes(re: &str) -> String {
    re.to_owned()
}

#[serial(working_dir)]
#[test]
fn no_args() {
    Command::cargo_bin("find")
        .expect("found binary")
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("test_data"));
}

#[serial(working_dir)]
#[test]
fn two_matchers_both_match() {
    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-type", "d", "-name", "test_data"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("test_data"));
}

#[serial(working_dir)]
#[test]
fn two_matchers_one_matches() {
    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-type", "f", "-name", "test_data"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::is_empty());
}

#[test]
fn matcher_with_side_effects_at_end() {
    let temp_dir = Builder::new().prefix("find_cmd_").tempdir().unwrap();

    let temp_dir_path = temp_dir.path().to_string_lossy();
    let test_file = temp_dir.path().join("test");
    File::create(&test_file).expect("created test file");

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-name", "test", "-delete"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::is_empty());

    assert!(!test_file.exists(), "test file should be deleted");
    assert!(temp_dir.path().exists(), "temp dir should NOT be deleted");
}

#[test]
fn matcher_with_side_effects_in_front() {
    let temp_dir = Builder::new().prefix("find_cmd_").tempdir().unwrap();

    let temp_dir_path = temp_dir.path().to_string_lossy();
    let test_file = temp_dir.path().join("test");
    File::create(&test_file).expect("created test file");

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-delete", "-name", "test"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::is_empty());

    assert!(!test_file.exists(), "test file should be deleted");
    assert!(!temp_dir.path().exists(), "temp dir should also be deleted");
}

// This could be covered by a unit test in principle... in practice, changing
// the working dir can't be done safely in unit tests unless `--test-threads=1`
// or `serial` goes everywhere, and it doesn't seem possible to get an
// appropriate `walkdir::DirEntry` for "." without actually changing dirs
// (or risking deletion of the repo itself).
#[serial(working_dir)]
#[test]
fn delete_on_dot_dir() {
    let temp_dir = Builder::new().prefix("example").tempdir().unwrap();
    let original_dir = env::current_dir().unwrap();
    env::set_current_dir(&temp_dir.path()).expect("working dir changed");

    // "." should be matched (confirmed by the print), but not deleted.
    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[".", "-delete", "-print"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::diff(".\n"));

    env::set_current_dir(original_dir).expect("restored original working dir");

    assert!(temp_dir.path().exists(), "temp dir should still exist");
}

#[test]
fn regex_types() {
    let temp_dir = Builder::new().prefix("find_cmd_").tempdir().unwrap();

    let temp_dir_path = temp_dir.path().to_string_lossy();
    let test_file = temp_dir.path().join("teeest");
    File::create(&test_file).expect("created test file");

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-regex", &fix_up_regex_slashes(".*/tE+st")])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::is_empty());

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-iregex", &fix_up_regex_slashes(".*/tE+st")])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("teeest"));

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &temp_dir_path,
            "-regextype",
            "posix-basic",
            "-regex",
            &fix_up_regex_slashes(r".*/te\{1,3\}st"),
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("teeest"));

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &temp_dir_path,
            "-regextype",
            "posix-extended",
            "-regex",
            &fix_up_regex_slashes(".*/te{1,3}st"),
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("teeest"));

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &temp_dir_path,
            "-regextype",
            "ed",
            "-regex",
            &fix_up_regex_slashes(r".*/te\{1,3\}st"),
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("teeest"));

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &temp_dir_path,
            "-regextype",
            "sed",
            "-regex",
            &fix_up_regex_slashes(r".*/te\{1,3\}st"),
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::contains("teeest"));
}

#[test]
fn empty_files() {
    let temp_dir = Builder::new().prefix("find_cmd_").tempdir().unwrap();
    let temp_dir_path = temp_dir.path().to_string_lossy();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-empty"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(fix_up_slashes(&format!("{}\n", temp_dir_path)));

    let test_file_path = temp_dir.path().join("test");
    let mut test_file = File::create(&test_file_path).unwrap();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-empty"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(fix_up_slashes(&format!(
            "{}\n",
            test_file_path.to_string_lossy()
        )));

    let subdir_path = temp_dir.path().join("subdir");
    std::fs::create_dir(&subdir_path).unwrap();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-empty", "-sorted"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(fix_up_slashes(&format!(
            "{}\n{}\n",
            subdir_path.to_string_lossy(),
            test_file_path.to_string_lossy()
        )));

    write!(test_file, "x").unwrap();
    test_file.sync_all().unwrap();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[&temp_dir_path, "-empty", "-sorted"])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(fix_up_slashes(&format!(
            "{}\n",
            subdir_path.to_string_lossy(),
        )));
}

#[serial(working_dir)]
#[test]
fn find_printf() {
    #[cfg(unix)]
    {
        if let Err(e) = symlink("abbbc", "test_data/links/link-f") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink("subdir", "test_data/links/link-d") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink("missing", "test_data/links/link-missing") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink("abbbc/x", "test_data/links/link-notdir") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink("link-loop", "test_data/links/link-loop") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
    }
    #[cfg(windows)]
    {
        if let Err(e) = symlink_file("abbbc", "test_data/links/link-f") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink_dir("subdir", "test_data/links/link-d") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink_file("missing", "test_data/links/link-missing") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
        if let Err(e) = symlink_file("abbbc/x", "test_data/links/link-notdir") {
            if e.kind() != ErrorKind::AlreadyExists {
                panic!("Failed to create sym link: {:?}", e);
            }
        }
    }

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &fix_up_slashes("./test_data/simple"),
            "-sorted",
            "-printf",
            "%f %d %h %H %p %P %y\n",
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::diff(fix_up_slashes(
            "simple 0 ./test_data ./test_data/simple \
            ./test_data/simple  d\n\
            abbbc 1 ./test_data/simple ./test_data/simple \
            ./test_data/simple/abbbc abbbc f\n\
            subdir 1 ./test_data/simple ./test_data/simple \
            ./test_data/simple/subdir subdir d\n\
            ABBBC 2 ./test_data/simple/subdir ./test_data/simple \
            ./test_data/simple/subdir/ABBBC subdir/ABBBC f\n",
        )));

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&[
            &fix_up_slashes("./test_data/links"),
            "-sorted",
            "-type",
            "l",
            "-printf",
            "%f %l %y %Y\n",
        ])
        .assert()
        .success()
        .stderr(predicate::str::is_empty())
        .stdout(predicate::str::diff(
            [
                "link-d subdir l d\n",
                "link-f abbbc l f\n",
                #[cfg(unix)]
                "link-loop link-loop l L\n",
                "link-missing missing l N\n",
                // We can't detect ENOTDIR on non-unix platforms yet.
                #[cfg(not(unix))]
                "link-notdir abbbc/x l ?\n",
                #[cfg(unix)]
                "link-notdir abbbc/x l N\n",
            ]
            .join(""),
        ));
}

#[cfg(unix)]
#[serial(working_dir)]
#[test]
fn find_perm() {
    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-perm", "+rwx"])
        .assert()
        .success();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-perm", "u+rwX"])
        .assert()
        .success();

    Command::cargo_bin("find")
        .expect("found binary")
        .args(&["-perm", "u=g"])
        .assert()
        .success();
}
<|endoftext|>


<commit_before>extern crate curl;
extern crate flate2;
extern crate pkg_config;
extern crate semver;
extern crate tar;

use std::error::Error;
use std::fs::File;
use std::io::BufWriter;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process;
use std::process::Command;
use std::{env, fs};

use curl::easy::Easy;
use flate2::read::GzDecoder;
use semver::Version;
use tar::Archive;

const FRAMEWORK_LIBRARY: &'static str = "tensorflow_framework";
const LIBRARY: &'static str = "tensorflow";
const REPOSITORY: &'static str = "https://github.com/tensorflow/tensorflow.git";
const FRAMEWORK_TARGET: &'static str = "tensorflow:libtensorflow_framework.so";
const TARGET: &'static str = "tensorflow:libtensorflow.so";
// `VERSION` and `TAG` are separate because the tag is not always `'v' + VERSION`.
const VERSION: &'static str = "1.13.1";
const TAG: &'static str = "v1.13.1";
const MIN_BAZEL: &'static str = "0.5.4";

macro_rules! get(($name:expr) => (ok!(env::var($name))));
macro_rules! ok(($expression:expr) => ($expression.unwrap()));
macro_rules! log {
    ($fmt:expr) => (println!(concat!("libtensorflow-sys/build.rs:{}: ", $fmt), line!()));
    ($fmt:expr, $($arg:tt)*) => (println!(concat!("libtensorflow-sys/build.rs:{}: ", $fmt),
    line!(), $($arg)*));
}
macro_rules! log_var(($var:ident) => (log!(concat!(stringify!($var), " = {:?}"), $var)));

fn main() {
    if check_windows_lib() {
        log!("Returning early because {} was already found", LIBRARY);
        return;
    }

    if let Ok(library) = pkg_config::find_library(LIBRARY) {
        for lib in &library.libs {
            println!("cargo:rustc-link-lib=dylib={}", lib);
        }
        for path in &library.link_paths {
            println!("cargo:rustc-link-search=native={}", path.display());
        }
        log!("Returning early because {} was already found", LIBRARY);
        return;
    }

    let force_src = match env::var("TF_RUST_BUILD_FROM_SRC") {
        Ok(s) => s == "true",
        Err(_) => false,
    };

    if !force_src
        && env::consts::ARCH == "x86_64"
        && (env::consts::OS == "linux" || env::consts::OS == "macos")
    {
        install_prebuilt();
    } else {
        build_from_src();
    }
}

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

#[cfg(target_env = "msvc")]
fn check_windows_lib() -> bool {
    let windows_lib: &str = &format!("{}.lib", LIBRARY);
    if let Ok(path) = env::var("PATH") {
        for p in path.split(";") {
            let path = Path::new(p).join(windows_lib);
            if path.exists() {
                println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
                println!("cargo:rustc-link-search=native={}", p);
                return true;
            }
        }
    }
    false
}

fn remove_suffix(value: &mut String, suffix: &str) {
    if value.ends_with(suffix) {
        let n = value.len();
        value.truncate(n - suffix.len());
    }
}

fn extract<P: AsRef<Path>, P2: AsRef<Path>>(archive_path: P, extract_to: P2) {
    let file = File::open(archive_path).unwrap();
    let unzipped = GzDecoder::new(file);
    let mut a = Archive::new(unzipped);
    a.unpack(extract_to).unwrap();
}

// Downloads and unpacks a prebuilt binary. Only works for certain platforms.
fn install_prebuilt() {
    // Figure out the file names.
    let os = match env::consts::OS {
        "macos" => "darwin",
        x => x,
    };
    let proc_type = if cfg!(feature = "tensorflow_gpu") {
        "gpu"
    } else {
        "cpu"
    };
    let binary_url =
        format!(
        "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-{}-{}-{}-{}.tar.gz",
        proc_type, os, env::consts::ARCH, VERSION);
    log_var!(binary_url);
    let short_file_name = binary_url.split("/").last().unwrap();
    let mut base_name = short_file_name.to_string();
    remove_suffix(&mut base_name, ".tar.gz");
    log_var!(base_name);
    let download_dir = match env::var("TF_RUST_DOWNLOAD_DIR") {
        Ok(s) => PathBuf::from(s),
        Err(_) => PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join("target"),
    };
    if !download_dir.exists() {
        fs::create_dir(&download_dir).unwrap();
    }
    let file_name = download_dir.join(short_file_name);
    log_var!(file_name);

    // Download the tarball.
    if !file_name.exists() {
        let f = File::create(&file_name).unwrap();
        let mut writer = BufWriter::new(f);
        let mut easy = Easy::new();
        easy.url(&binary_url).unwrap();
        easy.write_function(move |data| Ok(writer.write(data).unwrap()))
            .unwrap();
        easy.perform().unwrap();

        let response_code = easy.response_code().unwrap();
        if response_code != 200 {
            panic!(
                "Unexpected response code {} for {}",
                response_code, binary_url
            );
        }
    }

    // Extract the tarball.
    let unpacked_dir = download_dir.join(base_name);
    let lib_dir = unpacked_dir.join("lib");
    let framework_library_file = format!("lib{}.so", FRAMEWORK_LIBRARY);
    let library_file = format!("lib{}.so", LIBRARY);
    let framework_library_full_path = lib_dir.join(&framework_library_file);
    let library_full_path = lib_dir.join(&library_file);
    if !framework_library_full_path.exists() || !library_full_path.exists() {
        extract(file_name, &unpacked_dir);
    }

    println!("cargo:rustc-link-lib=dylib={}", FRAMEWORK_LIBRARY);
    println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
    let output = PathBuf::from(&get!("OUT_DIR"));
    let new_framework_library_full_path = output.join(&framework_library_file);
    if new_framework_library_full_path.exists() {
        log!(
            "File {} already exists, deleting.",
            new_framework_library_full_path.display()
        );
        std::fs::remove_file(&new_framework_library_full_path).unwrap();
    }
    let new_library_full_path = output.join(&library_file);
    if new_library_full_path.exists() {
        log!(
            "File {} already exists, deleting.",
            new_library_full_path.display()
        );
        std::fs::remove_file(&new_library_full_path).unwrap();
    }

    log!(
        "Copying {} to {}...",
        library_full_path.display(),
        new_library_full_path.display()
    );
    std::fs::copy(&library_full_path, &new_library_full_path).unwrap();
    log!(
        "Copying {} to {}...",
        framework_library_full_path.display(),
        new_framework_library_full_path.display()
    );
    std::fs::copy(
        &framework_library_full_path,
        &new_framework_library_full_path,
    )
    .unwrap();
    println!("cargo:rustc-link-search={}", output.display());
}

fn build_from_src() {
    let output = PathBuf::from(&get!("OUT_DIR"));
    log_var!(output);
    let source = PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join(format!("target/source-{}", TAG));
    log_var!(source);
    let lib_dir = output.join(format!("lib-{}", TAG));
    log_var!(lib_dir);
    if lib_dir.exists() {
        log!("Directory {:?} already exists", lib_dir);
    } else {
        log!("Creating directory {:?}", lib_dir);
        fs::create_dir(lib_dir.clone()).unwrap();
    }
    let framework_library_path = lib_dir.join(format!("lib{}.so", FRAMEWORK_LIBRARY));
    log_var!(framework_library_path);
    let library_path = lib_dir.join(format!("lib{}.so", LIBRARY));
    log_var!(library_path);
    if library_path.exists() && framework_library_path.exists() {
        log!(
            "{:?} and {:?} already exist, not building",
            library_path,
            framework_library_path
        );
    } else {
        if let Err(e) = check_bazel() {
            println!(
                "cargo:error=Bazel must be installed at version {} or greater. (Error: {})",
                MIN_BAZEL, e
            );
            process::exit(1);
        }
        let framework_target_path = &FRAMEWORK_TARGET.replace(":", "/");
        log_var!(framework_target_path);
        let target_path = &TARGET.replace(":", "/");
        log_var!(target_path);
        if !Path::new(&source.join(".git")).exists() {
            run("git", |command| {
                command
                    .arg("clone")
                    .arg(format!("--branch={}", TAG))
                    .arg("--recursive")
                    .arg(REPOSITORY)
                    .arg(&source)
            });
        }
        // Only configure if not previously configured.  Configuring runs a
        // `bazel clean`, which we don't want, because we want to be able to
        // continue from a cancelled build.
        let configure_hint_file_pb = source.join(".rust-configured");
        let configure_hint_file = Path::new(&configure_hint_file_pb);
        if !configure_hint_file.exists() {
            run("bash", |command| {
                command
                    .current_dir(&source)
                    .env(
                        "TF_NEED_CUDA",
                        if cfg!(feature = "tensorflow_gpu") {
                            "1"
                        } else {
                            "0"
                        },
                    )
                    .arg("-c")
                    .arg("yes ''|./configure")
            });
            File::create(configure_hint_file).unwrap();
        }
        // Allows us to pass in --incompatible_load_argument_is_label=false
        // to work around https://github.com/tensorflow/tensorflow/issues/15492
        let bazel_args_string = if let Ok(args) = env::var("TF_RUST_BAZEL_OPTS") {
            args
        } else {
            "".to_string()
        };
        run("bazel", |command| {
            command
                .current_dir(&source)
                .arg("build")
                .arg(format!("--jobs={}", get!("NUM_JOBS")))
                .arg("--compilation_mode=opt")
                .arg("--copt=-march=native")
                .args(bazel_args_string.split_whitespace())
                .arg(TARGET)
        });
        let framework_target_bazel_bin = source.join("bazel-bin").join(framework_target_path);
        log!(
            "Copying {:?} to {:?}",
            framework_target_bazel_bin,
            framework_library_path
        );
        fs::copy(framework_target_bazel_bin, framework_library_path).unwrap();
        let target_bazel_bin = source.join("bazel-bin").join(target_path);
        log!("Copying {:?} to {:?}", target_bazel_bin, library_path);
        fs::copy(target_bazel_bin, library_path).unwrap();
    }

    println!("cargo:rustc-link-lib=dylib={}", FRAMEWORK_LIBRARY);
    println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
    println!("cargo:rustc-link-search={}", lib_dir.display());
}

fn run<F>(name: &str, mut configure: F)
where
    F: FnMut(&mut Command) -> &mut Command,
{
    let mut command = Command::new(name);
    let configured = configure(&mut command);
    log!("Executing {:?}", configured);
    if !ok!(configured.status()).success() {
        panic!("failed to execute {:?}", configured);
    }
    log!("Command {:?} finished successfully", configured);
}

// Building TF 0.11.0rc1 with Bazel 0.3.0 gives this error when running `configure`:
//   expected ConfigurationTransition or NoneType for 'cfg' while calling label_list but got
// string instead:     data.
//       ERROR: com.google.devtools.build.lib.packages.BuildFileContainsErrorsException: error
// loading package '': Extension file 'tensorflow/tensorflow.bzl' has errors.
// And the simple solution is to require Bazel 0.3.1 or higher.
fn check_bazel() -> Result<(), Box<Error>> {
    let mut command = Command::new("bazel");
    command.arg("version");
    log!("Executing {:?}", command);
    let out = command.output()?;
    log!("Command {:?} finished successfully", command);
    let stdout = String::from_utf8(out.stdout)?;
    let mut found_version = false;
    for line in stdout.lines() {
        if line.starts_with("Build label:") {
            found_version = true;
            let mut version_str = line
                .split(":")
                .nth(1)
                .unwrap()
                .split(" ")
                .nth(1)
                .unwrap()
                .trim();
            if version_str.ends_with('-') {
                // hyphen is 1 byte long, so it's safe
                version_str = &version_str[..version_str.len() - 1];
            }
            let version = Version::parse(version_str)?;
            let want = Version::parse(MIN_BAZEL)?;
            if version < want {
                return Err(format!(
                    "Installed version {} is less than required version {}",
                    version_str, MIN_BAZEL
                )
                .into());
            }
        }
    }
    if !found_version {
        return Err("Did not find version number in `bazel version` output.".into());
    }
    Ok(())
}
<commit_msg>Revert "set rustc-link-lib/search for tensorflow lib found via pkg_config"<commit_after>extern crate curl;
extern crate flate2;
extern crate pkg_config;
extern crate semver;
extern crate tar;

use std::error::Error;
use std::fs::File;
use std::io::BufWriter;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process;
use std::process::Command;
use std::{env, fs};

use curl::easy::Easy;
use flate2::read::GzDecoder;
use semver::Version;
use tar::Archive;

const FRAMEWORK_LIBRARY: &'static str = "tensorflow_framework";
const LIBRARY: &'static str = "tensorflow";
const REPOSITORY: &'static str = "https://github.com/tensorflow/tensorflow.git";
const FRAMEWORK_TARGET: &'static str = "tensorflow:libtensorflow_framework.so";
const TARGET: &'static str = "tensorflow:libtensorflow.so";
// `VERSION` and `TAG` are separate because the tag is not always `'v' + VERSION`.
const VERSION: &'static str = "1.13.1";
const TAG: &'static str = "v1.13.1";
const MIN_BAZEL: &'static str = "0.5.4";

macro_rules! get(($name:expr) => (ok!(env::var($name))));
macro_rules! ok(($expression:expr) => ($expression.unwrap()));
macro_rules! log {
    ($fmt:expr) => (println!(concat!("libtensorflow-sys/build.rs:{}: ", $fmt), line!()));
    ($fmt:expr, $($arg:tt)*) => (println!(concat!("libtensorflow-sys/build.rs:{}: ", $fmt),
    line!(), $($arg)*));
}
macro_rules! log_var(($var:ident) => (log!(concat!(stringify!($var), " = {:?}"), $var)));

fn main() {
    if check_windows_lib() {
        log!("Returning early because {} was already found", LIBRARY);
        return;
    }

    if pkg_config::find_library(LIBRARY).is_ok() {
        log!("Returning early because {} was already found", LIBRARY);
        return;
    }

    let force_src = match env::var("TF_RUST_BUILD_FROM_SRC") {
        Ok(s) => s == "true",
        Err(_) => false,
    };

    if !force_src
        && env::consts::ARCH == "x86_64"
        && (env::consts::OS == "linux" || env::consts::OS == "macos")
    {
        install_prebuilt();
    } else {
        build_from_src();
    }
}

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

#[cfg(target_env = "msvc")]
fn check_windows_lib() -> bool {
    let windows_lib: &str = &format!("{}.lib", LIBRARY);
    if let Ok(path) = env::var("PATH") {
        for p in path.split(";") {
            let path = Path::new(p).join(windows_lib);
            if path.exists() {
                println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
                println!("cargo:rustc-link-search=native={}", p);
                return true;
            }
        }
    }
    false
}

fn remove_suffix(value: &mut String, suffix: &str) {
    if value.ends_with(suffix) {
        let n = value.len();
        value.truncate(n - suffix.len());
    }
}

fn extract<P: AsRef<Path>, P2: AsRef<Path>>(archive_path: P, extract_to: P2) {
    let file = File::open(archive_path).unwrap();
    let unzipped = GzDecoder::new(file);
    let mut a = Archive::new(unzipped);
    a.unpack(extract_to).unwrap();
}

// Downloads and unpacks a prebuilt binary. Only works for certain platforms.
fn install_prebuilt() {
    // Figure out the file names.
    let os = match env::consts::OS {
        "macos" => "darwin",
        x => x,
    };
    let proc_type = if cfg!(feature = "tensorflow_gpu") {
        "gpu"
    } else {
        "cpu"
    };
    let binary_url =
        format!(
        "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-{}-{}-{}-{}.tar.gz",
        proc_type, os, env::consts::ARCH, VERSION);
    log_var!(binary_url);
    let short_file_name = binary_url.split("/").last().unwrap();
    let mut base_name = short_file_name.to_string();
    remove_suffix(&mut base_name, ".tar.gz");
    log_var!(base_name);
    let download_dir = match env::var("TF_RUST_DOWNLOAD_DIR") {
        Ok(s) => PathBuf::from(s),
        Err(_) => PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join("target"),
    };
    if !download_dir.exists() {
        fs::create_dir(&download_dir).unwrap();
    }
    let file_name = download_dir.join(short_file_name);
    log_var!(file_name);

    // Download the tarball.
    if !file_name.exists() {
        let f = File::create(&file_name).unwrap();
        let mut writer = BufWriter::new(f);
        let mut easy = Easy::new();
        easy.url(&binary_url).unwrap();
        easy.write_function(move |data| Ok(writer.write(data).unwrap()))
            .unwrap();
        easy.perform().unwrap();

        let response_code = easy.response_code().unwrap();
        if response_code != 200 {
            panic!(
                "Unexpected response code {} for {}",
                response_code, binary_url
            );
        }
    }

    // Extract the tarball.
    let unpacked_dir = download_dir.join(base_name);
    let lib_dir = unpacked_dir.join("lib");
    let framework_library_file = format!("lib{}.so", FRAMEWORK_LIBRARY);
    let library_file = format!("lib{}.so", LIBRARY);
    let framework_library_full_path = lib_dir.join(&framework_library_file);
    let library_full_path = lib_dir.join(&library_file);
    if !framework_library_full_path.exists() || !library_full_path.exists() {
        extract(file_name, &unpacked_dir);
    }

    println!("cargo:rustc-link-lib=dylib={}", FRAMEWORK_LIBRARY);
    println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
    let output = PathBuf::from(&get!("OUT_DIR"));
    let new_framework_library_full_path = output.join(&framework_library_file);
    if new_framework_library_full_path.exists() {
        log!(
            "File {} already exists, deleting.",
            new_framework_library_full_path.display()
        );
        std::fs::remove_file(&new_framework_library_full_path).unwrap();
    }
    let new_library_full_path = output.join(&library_file);
    if new_library_full_path.exists() {
        log!(
            "File {} already exists, deleting.",
            new_library_full_path.display()
        );
        std::fs::remove_file(&new_library_full_path).unwrap();
    }

    log!(
        "Copying {} to {}...",
        library_full_path.display(),
        new_library_full_path.display()
    );
    std::fs::copy(&library_full_path, &new_library_full_path).unwrap();
    log!(
        "Copying {} to {}...",
        framework_library_full_path.display(),
        new_framework_library_full_path.display()
    );
    std::fs::copy(
        &framework_library_full_path,
        &new_framework_library_full_path,
    )
    .unwrap();
    println!("cargo:rustc-link-search={}", output.display());
}

fn build_from_src() {
    let output = PathBuf::from(&get!("OUT_DIR"));
    log_var!(output);
    let source = PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join(format!("target/source-{}", TAG));
    log_var!(source);
    let lib_dir = output.join(format!("lib-{}", TAG));
    log_var!(lib_dir);
    if lib_dir.exists() {
        log!("Directory {:?} already exists", lib_dir);
    } else {
        log!("Creating directory {:?}", lib_dir);
        fs::create_dir(lib_dir.clone()).unwrap();
    }
    let framework_library_path = lib_dir.join(format!("lib{}.so", FRAMEWORK_LIBRARY));
    log_var!(framework_library_path);
    let library_path = lib_dir.join(format!("lib{}.so", LIBRARY));
    log_var!(library_path);
    if library_path.exists() && framework_library_path.exists() {
        log!(
            "{:?} and {:?} already exist, not building",
            library_path,
            framework_library_path
        );
    } else {
        if let Err(e) = check_bazel() {
            println!(
                "cargo:error=Bazel must be installed at version {} or greater. (Error: {})",
                MIN_BAZEL, e
            );
            process::exit(1);
        }
        let framework_target_path = &FRAMEWORK_TARGET.replace(":", "/");
        log_var!(framework_target_path);
        let target_path = &TARGET.replace(":", "/");
        log_var!(target_path);
        if !Path::new(&source.join(".git")).exists() {
            run("git", |command| {
                command
                    .arg("clone")
                    .arg(format!("--branch={}", TAG))
                    .arg("--recursive")
                    .arg(REPOSITORY)
                    .arg(&source)
            });
        }
        // Only configure if not previously configured.  Configuring runs a
        // `bazel clean`, which we don't want, because we want to be able to
        // continue from a cancelled build.
        let configure_hint_file_pb = source.join(".rust-configured");
        let configure_hint_file = Path::new(&configure_hint_file_pb);
        if !configure_hint_file.exists() {
            run("bash", |command| {
                command
                    .current_dir(&source)
                    .env(
                        "TF_NEED_CUDA",
                        if cfg!(feature = "tensorflow_gpu") {
                            "1"
                        } else {
                            "0"
                        },
                    )
                    .arg("-c")
                    .arg("yes ''|./configure")
            });
            File::create(configure_hint_file).unwrap();
        }
        // Allows us to pass in --incompatible_load_argument_is_label=false
        // to work around https://github.com/tensorflow/tensorflow/issues/15492
        let bazel_args_string = if let Ok(args) = env::var("TF_RUST_BAZEL_OPTS") {
            args
        } else {
            "".to_string()
        };
        run("bazel", |command| {
            command
                .current_dir(&source)
                .arg("build")
                .arg(format!("--jobs={}", get!("NUM_JOBS")))
                .arg("--compilation_mode=opt")
                .arg("--copt=-march=native")
                .args(bazel_args_string.split_whitespace())
                .arg(TARGET)
        });
        let framework_target_bazel_bin = source.join("bazel-bin").join(framework_target_path);
        log!(
            "Copying {:?} to {:?}",
            framework_target_bazel_bin,
            framework_library_path
        );
        fs::copy(framework_target_bazel_bin, framework_library_path).unwrap();
        let target_bazel_bin = source.join("bazel-bin").join(target_path);
        log!("Copying {:?} to {:?}", target_bazel_bin, library_path);
        fs::copy(target_bazel_bin, library_path).unwrap();
    }

    println!("cargo:rustc-link-lib=dylib={}", FRAMEWORK_LIBRARY);
    println!("cargo:rustc-link-lib=dylib={}", LIBRARY);
    println!("cargo:rustc-link-search={}", lib_dir.display());
}

fn run<F>(name: &str, mut configure: F)
where
    F: FnMut(&mut Command) -> &mut Command,
{
    let mut command = Command::new(name);
    let configured = configure(&mut command);
    log!("Executing {:?}", configured);
    if !ok!(configured.status()).success() {
        panic!("failed to execute {:?}", configured);
    }
    log!("Command {:?} finished successfully", configured);
}

// Building TF 0.11.0rc1 with Bazel 0.3.0 gives this error when running `configure`:
//   expected ConfigurationTransition or NoneType for 'cfg' while calling label_list but got
// string instead:     data.
//       ERROR: com.google.devtools.build.lib.packages.BuildFileContainsErrorsException: error
// loading package '': Extension file 'tensorflow/tensorflow.bzl' has errors.
// And the simple solution is to require Bazel 0.3.1 or higher.
fn check_bazel() -> Result<(), Box<Error>> {
    let mut command = Command::new("bazel");
    command.arg("version");
    log!("Executing {:?}", command);
    let out = command.output()?;
    log!("Command {:?} finished successfully", command);
    let stdout = String::from_utf8(out.stdout)?;
    let mut found_version = false;
    for line in stdout.lines() {
        if line.starts_with("Build label:") {
            found_version = true;
            let mut version_str = line
                .split(":")
                .nth(1)
                .unwrap()
                .split(" ")
                .nth(1)
                .unwrap()
                .trim();
            if version_str.ends_with('-') {
                // hyphen is 1 byte long, so it's safe
                version_str = &version_str[..version_str.len() - 1];
            }
            let version = Version::parse(version_str)?;
            let want = Version::parse(MIN_BAZEL)?;
            if version < want {
                return Err(format!(
                    "Installed version {} is less than required version {}",
                    version_str, MIN_BAZEL
                )
                .into());
            }
        }
    }
    if !found_version {
        return Err("Did not find version number in `bazel version` output.".into());
    }
    Ok(())
}
<|endoftext|>


<commit_before>/// `ArgGroup`s are a family of related arguments and way for you to say, "Any of these arguments".
/// By placing arguments in a logical group, you can make easier requirement and exclusion rules
/// instead of having to list each individually, or when you want a rule to apply "any but not all"
/// arguments.
///
/// For instance, you can make an entire ArgGroup required, this means that one (and *only* one)
/// argument. from that group must be present. Using more than one argument from an ArgGroup causes
/// a failure (graceful exit).
///
/// You can also do things such as name an ArgGroup as a confliction or requirement, meaning any
/// of the arguments that belong to that group will cause a failure if present, or must present
/// respectively.
///
/// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
/// present out of a given set. Imagine that you had multiple arguments, and you want one of them to
/// be required, but making all of them required isn't feasible because perhaps they conflict with
/// each other. For example, lets say that you were building an application where one could set a
/// given version number by supplying a string with an option argument, i.e. `--set-ver v1.2.3`, you
/// also wanted to support automatically using a previous version number and simply incrementing one
/// of the three numbers. So you create three flags `--major`, `--minor`, and `--patch`. All of
/// these arguments shouldn't be used at one time but you want to specify that *at least one* of
/// them is used. For this, you can create a group.

extern crate clap;

use clap::{App, Arg, ArgGroup};

fn main() {
    // Create application like normal
    let matches = App::new("myapp")
                      // Add the version arguments
                      .args_from_usage("--set-ver [ver] 'set version manually'
                                        --major         'auto inc major'
                                        --minor         'auto inc minor'
                                        --patch         'auto inc patch'")
                      // Create a group, make it required, and add the above arguments
                      .group(ArgGroup::with_name("vers")
                                          .required(true)
                                          .args(&["ver", "major", "minor", "patch"]))
                      // Arguments can also be added to a group individually, these two arguments
                      // are part of the "input" group which is not required
                      .arg(Arg::from_usage("[INPUT_FILE] 'some regular input'")
                        .group("input"))
                      .arg(Arg::from_usage("--spec-in [SPEC_IN] 'some special input argument'")
                        .group("input"))
                      // Now let's assume we have a -c [config] argument which requires one of
                      // (but **not** both) the "input" arguments
                      .arg(Arg::with_name("config")
                        .short("c")
                        .takes_value(true)
                        .requires("input"))
                      .get_matches();

    // Let's assume the old version 1.2.3
    let mut major = 1;
    let mut minor = 2;
    let mut patch = 3;

    // See if --set-ver was used to set the version manually
    let version = if let Some(ver) = matches.value_of("ver") {
        format!("{}", ver)
    } else {
        // Increment the one requested (in a real program, we'd reset the lower numbers)
        let (maj, min, pat) = (matches.is_present("major"),
                                     matches.is_present("minor"),
                                     matches.is_present("patch"));
        match (maj, min, pat) {
            (true, _, _) => major += 1,
            (_, true, _) => minor += 1,
            (_, _, true) => patch += 1,
            _            => unreachable!(),
        };
        format!("{}.{}.{}", major, minor, patch)
    };

    println!("Version: {}", version);

    // Check for usage of -c
    if matches.is_present("config") {
        let input = matches.value_of("INPUT_FILE").unwrap_or(matches.value_of("SPEC_IN").unwrap());
        println!("Doing work using input {} and config {}",
                                                     input,
                                                     matches.value_of("config").unwrap());
    }

}
<commit_msg>clippy: A couple of markdown fixes and a typo fix.<commit_after>/// `ArgGroup`s are a family of related arguments and way for you to say, "Any of these arguments".
/// By placing arguments in a logical group, you can make easier requirement and exclusion rules
/// instead of having to list each individually, or when you want a rule to apply "any but not all"
/// arguments.
///
/// For instance, you can make an entire `ArgGroup` required, this means that one (and *only* one)
/// argument from that group must be present. Using more than one argument from an `ArgGroup` causes
/// a failure (graceful exit).
///
/// You can also do things such as name an `ArgGroup` as a confliction or requirement, meaning any
/// of the arguments that belong to that group will cause a failure if present, or must present
/// respectively.
///
/// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
/// present out of a given set. Imagine that you had multiple arguments, and you want one of them to
/// be required, but making all of them required isn't feasible because perhaps they conflict with
/// each other. For example, lets say that you were building an application where one could set a
/// given version number by supplying a string with an option argument, i.e. `--set-ver v1.2.3`, you
/// also wanted to support automatically using a previous version number and simply incrementing one
/// of the three numbers. So you create three flags `--major`, `--minor`, and `--patch`. All of
/// these arguments shouldn't be used at one time but you want to specify that *at least one* of
/// them is used. For this, you can create a group.

extern crate clap;

use clap::{App, Arg, ArgGroup};

fn main() {
    // Create application like normal
    let matches = App::new("myapp")
                      // Add the version arguments
                      .args_from_usage("--set-ver [ver] 'set version manually'
                                        --major         'auto inc major'
                                        --minor         'auto inc minor'
                                        --patch         'auto inc patch'")
                      // Create a group, make it required, and add the above arguments
                      .group(ArgGroup::with_name("vers")
                                          .required(true)
                                          .args(&["ver", "major", "minor", "patch"]))
                      // Arguments can also be added to a group individually, these two arguments
                      // are part of the "input" group which is not required
                      .arg(Arg::from_usage("[INPUT_FILE] 'some regular input'")
                        .group("input"))
                      .arg(Arg::from_usage("--spec-in [SPEC_IN] 'some special input argument'")
                        .group("input"))
                      // Now let's assume we have a -c [config] argument which requires one of
                      // (but **not** both) the "input" arguments
                      .arg(Arg::with_name("config")
                        .short("c")
                        .takes_value(true)
                        .requires("input"))
                      .get_matches();

    // Let's assume the old version 1.2.3
    let mut major = 1;
    let mut minor = 2;
    let mut patch = 3;

    // See if --set-ver was used to set the version manually
    let version = if let Some(ver) = matches.value_of("ver") {
        format!("{}", ver)
    } else {
        // Increment the one requested (in a real program, we'd reset the lower numbers)
        let (maj, min, pat) = (matches.is_present("major"),
                                     matches.is_present("minor"),
                                     matches.is_present("patch"));
        match (maj, min, pat) {
            (true, _, _) => major += 1,
            (_, true, _) => minor += 1,
            (_, _, true) => patch += 1,
            _            => unreachable!(),
        };
        format!("{}.{}.{}", major, minor, patch)
    };

    println!("Version: {}", version);

    // Check for usage of -c
    if matches.is_present("config") {
        let input = matches.value_of("INPUT_FILE").unwrap_or(matches.value_of("SPEC_IN").unwrap());
        println!("Doing work using input {} and config {}",
                                                     input,
                                                     matches.value_of("config").unwrap());
    }

}
<|endoftext|>


<commit_before>extern crate calx;

use std::num::{FloatMath};
use calx::color;
use calx::{V2, Rgba, Fonter, CanvasUtil};
use calx::Event;

fn main() {
    let mut t = 0i;

    for evt in calx::Canvas::new().run() {
        match evt {
            Event::Render(ctx) => {
                let img = ctx.font_image('@').unwrap();

                ctx.clear(&calx::Rgb::new(t as u8, 0, 0));
                for y in range(0, 360/8) {
                    for x in range(0, 640/8) {
                        ctx.draw_image(V2(x * 8, y * 8), 0.4, img, &color::ORANGE);
                    }
                }
                let center = V2(320, 180);
                let offset = V2(
                    ((t as f32 / 16.0).cos() * 128.0) as int,
                    ((t as f32 / 16.0).sin() * 128.0) as int);

                ctx.draw_line(3, center, center + offset, 0.3, &Rgba::new(0, 255, 255, 128));

                let fps = 1.0 / ctx.render_duration;
                let _ = write!(&mut ctx.text_writer(V2(0, 8), 0.1, color::LIGHTGREEN)
                               .set_border(color::BLACK),
                    "FPS {:.0}", fps);

                t += 1;
            }
            Event::KeyPressed(calx::Key::Escape) => {
                return;
            }
            _ => ()
        }
    }
}
<commit_msg>Print keypresses in demo<commit_after>extern crate calx;

use std::num::{FloatMath};
use calx::color;
use calx::{V2, Rgba, Fonter, CanvasUtil};
use calx::Event;

fn main() {
    let mut t = 0i;

    for evt in calx::Canvas::new().run() {
        match evt {
            Event::Render(ctx) => {
                let img = ctx.font_image('@').unwrap();

                ctx.clear(&calx::Rgb::new(t as u8, 0, 0));
                for y in range(0, 360/8) {
                    for x in range(0, 640/8) {
                        ctx.draw_image(V2(x * 8, y * 8), 0.4, img, &color::ORANGE);
                    }
                }
                let center = V2(320, 180);
                let offset = V2(
                    ((t as f32 / 16.0).cos() * 128.0) as int,
                    ((t as f32 / 16.0).sin() * 128.0) as int);

                ctx.draw_line(3, center, center + offset, 0.3, &Rgba::new(0, 255, 255, 128));

                let fps = 1.0 / ctx.render_duration;
                let _ = write!(&mut ctx.text_writer(V2(0, 8), 0.1, color::LIGHTGREEN)
                               .set_border(color::BLACK),
                    "FPS {:.0}", fps);

                t += 1;
            }
            Event::KeyPressed(calx::Key::Escape) => {
                return;
            }
            Event::KeyPressed(k) => {
                println!("Pressed {}", k);
            }
            Event::Char(c) => {
                println!("Typed {}", c);
            }
            _ => ()
        }
    }
}
<|endoftext|>


<commit_before>import geom::matrix::{Matrix4, identity};
import opengles::gl2::{GLuint, delete_textures};

import std::cmp::fuzzy_eq;
import dvec::dvec;

enum Format {
    ARGB32Format,
    RGB24Format
}

enum Layer {
    ContainerLayerKind(@ContainerLayer),
    ImageLayerKind(@ImageLayer),
    TiledImageLayerKind(@TiledImageLayer)
}

class CommonLayer {
    let mut parent: option<Layer>;
    let mut prev_sibling: option<Layer>;
    let mut next_sibling: option<Layer>;

    let mut transform: Matrix4<f32>;

    new() {
        self.parent = none;
        self.prev_sibling = none;
        self.next_sibling = none;

        self.transform = identity(0.0f32);
    }

    // FIXME: Workaround for cross-crate bug regarding mutability of class fields
    fn set_transform(new_transform: Matrix4<f32>) {
        self.transform = new_transform;
    }
}

class ContainerLayer {
    let mut common: CommonLayer;
    let mut first_child: option<Layer>;
    let mut last_child: option<Layer>;

    new() {
        self.common = CommonLayer();
        self.first_child = none;
        self.last_child = none;
    }
}

class Image {
    let width: uint;
    let height: uint;
    let format: Format;
    let data: ~[u8];

    let mut texture: option<GLuint>;

    new(width: uint, height: uint, format: Format, +data: ~[u8]) {
        self.width = width;
        self.height = height;
        self.format = format;
        self.data = data;

        self.texture = none;
    }

    drop {
        match copy self.texture {
            none => {
                // Nothing to do.
            }
            some(texture) => {
                delete_textures(&[texture]);
            }
        }
    }
}

class ImageLayer {
    let mut common: CommonLayer;
    let mut image: @layers::Image;

    new(image: @layers::Image) {
        self.common = CommonLayer();
        self.image = image;
    }

    // FIXME: Workaround for cross-crate bug
    fn set_image(new_image: @layers::Image) {
        self.image = new_image;
    }
}

struct TiledImageLayer {
    mut common: CommonLayer;
    tiles: dvec<@layers::Image>;
    mut tiles_across: uint;
}

fn TiledImageLayer(in_tiles: &[@layers::Image], tiles_across: uint) -> TiledImageLayer {
    let tiles = dvec();
    for in_tiles.each |tile| {
        tiles.push(tile);
    }

    TiledImageLayer {
        common: CommonLayer(),
        tiles: tiles,
        tiles_across: tiles_across
    }
}

<commit_msg>Fix breakage<commit_after>import geom::matrix::{Matrix4, identity};
import opengles::gl2::{GLuint, delete_textures};

import std::cmp::fuzzy_eq;
import dvec::{DVec, dvec};

enum Format {
    ARGB32Format,
    RGB24Format
}

enum Layer {
    ContainerLayerKind(@ContainerLayer),
    ImageLayerKind(@ImageLayer),
    TiledImageLayerKind(@TiledImageLayer)
}

class CommonLayer {
    let mut parent: option<Layer>;
    let mut prev_sibling: option<Layer>;
    let mut next_sibling: option<Layer>;

    let mut transform: Matrix4<f32>;

    new() {
        self.parent = none;
        self.prev_sibling = none;
        self.next_sibling = none;

        self.transform = identity(0.0f32);
    }

    // FIXME: Workaround for cross-crate bug regarding mutability of class fields
    fn set_transform(new_transform: Matrix4<f32>) {
        self.transform = new_transform;
    }
}

class ContainerLayer {
    let mut common: CommonLayer;
    let mut first_child: option<Layer>;
    let mut last_child: option<Layer>;

    new() {
        self.common = CommonLayer();
        self.first_child = none;
        self.last_child = none;
    }
}

class Image {
    let width: uint;
    let height: uint;
    let format: Format;
    let data: ~[u8];

    let mut texture: option<GLuint>;

    new(width: uint, height: uint, format: Format, +data: ~[u8]) {
        self.width = width;
        self.height = height;
        self.format = format;
        self.data = data;

        self.texture = none;
    }

    drop {
        match copy self.texture {
            none => {
                // Nothing to do.
            }
            some(texture) => {
                delete_textures(&[texture]);
            }
        }
    }
}

class ImageLayer {
    let mut common: CommonLayer;
    let mut image: @layers::Image;

    new(image: @layers::Image) {
        self.common = CommonLayer();
        self.image = image;
    }

    // FIXME: Workaround for cross-crate bug
    fn set_image(new_image: @layers::Image) {
        self.image = new_image;
    }
}

struct TiledImageLayer {
    mut common: CommonLayer;
    tiles: DVec<@layers::Image>;
    mut tiles_across: uint;
}

fn TiledImageLayer(in_tiles: &[@layers::Image], tiles_across: uint) -> TiledImageLayer {
    let tiles = dvec();
    for in_tiles.each |tile| {
        tiles.push(tile);
    }

    TiledImageLayer {
        common: CommonLayer(),
        tiles: tiles,
        tiles_across: tiles_across
    }
}

<|endoftext|>


<commit_before>use std::sync::atomic::{AtomicBool, Ordering};
use std::collections::HashMap;

use hlua::any::AnyLuaValue;
use hlua::{LuaTable, function0};

use super::*;
use test_util;

const EVENT: &'static str = include_str!("event.lua");
const TEST: &'static str = include_str!("test.lua");
const REQUIRE: &'static str = include_str!("require.lua");

static CALL_FN_NO_ARGS_TEST_VAL: AtomicBool = AtomicBool::new(false);

/// Call Engine.new without any code.
#[test]
fn engine_new_no_code() {
    test_util::start_log_once();
    Engine::new(HashMap::new()).unwrap();
}

/// Tests requiring a module.
#[test]
fn load_module() {
    test_util::start_log_once();
    let mut scripts: HashMap<String, String> = HashMap::new();
    scripts.insert("test".to_owned(), TEST.to_owned());
    scripts.insert("init".to_owned(), REQUIRE.to_owned());
    Engine::new(scripts).unwrap();
}

/// Tests declaring and raising a lua event.
#[test]
fn lua_event() {
    test_util::start_log_once();
    let mut scripts: HashMap<String, String> = HashMap::new();
    scripts.insert("init".to_owned(), EVENT.to_owned());
    let mut engine = Engine::new(scripts).unwrap();
    let _ = engine.exec_event("test".to_owned(), Vec::new()).expect("failed to exec event");
    let test_val: AnyLuaValue = engine.interpreter.get("test_val").unwrap();
    assert_eq!(test_val, AnyLuaValue::LuaBoolean(true));
}

/// Tests calling a prelude function using Engine::call_prelude_fn.
///
/// Curently broken until tomaka/hlua#66
#[test]
fn call_fn_no_args() {
    test_util::start_log_once();
    let mut engine = Engine::new(HashMap::new()).unwrap();
    let fun = function0(|| {
        CALL_FN_NO_ARGS_TEST_VAL.store(true, Ordering::Relaxed);
    });
    {
        let mut prelude_table: LuaTable<_> = engine.interpreter
                                                   .get("prelude_buildengine")
                                                   .expect("failed to get prelude table.");
        prelude_table.set("test_fn", fun);
    }
    let result = engine.call_prelude_fn("test_fn", Vec::new()).expect("failed to call test_fn");
    assert!(result.is_none(),
            "Engine::call_prelude_fn returned a Some value for a function returning nil: {:?}",
            result);
}
<commit_msg>Changes the test script::test::call_fn_no_args to use tattles<commit_after>use std::collections::HashMap;

use hlua::any::AnyLuaValue;
use hlua::{LuaTable, function0};

use super::*;
use test_util;

const EVENT: &'static str = include_str!("event.lua");
const TEST: &'static str = include_str!("test.lua");
const REQUIRE: &'static str = include_str!("require.lua");

/// Call Engine.new without any code.
#[test]
fn engine_new_no_code() {
    test_util::start_log_once();
    Engine::new(HashMap::new()).unwrap();
}

/// Tests requiring a module.
#[test]
fn load_module() {
    test_util::start_log_once();
    let mut scripts: HashMap<String, String> = HashMap::new();
    scripts.insert("test".to_owned(), TEST.to_owned());
    scripts.insert("init".to_owned(), REQUIRE.to_owned());
    Engine::new(scripts).unwrap();
}

/// Tests declaring and raising a lua event.
#[test]
fn lua_event() {
    test_util::start_log_once();
    let mut scripts: HashMap<String, String> = HashMap::new();
    scripts.insert("init".to_owned(), EVENT.to_owned());
    let mut engine = Engine::new(scripts).unwrap();
    let _ = engine.exec_event("test".to_owned(), Vec::new()).expect("failed to exec event");
    let test_val: AnyLuaValue = engine.interpreter.get("test_val").unwrap();
    assert_eq!(test_val, AnyLuaValue::LuaBoolean(true));
}

/// Tests calling a prelude function using Engine::call_prelude_fn.
#[test]
fn call_fn_no_args() {
    test_util::start_log_once();
    let tattle = test_util::Tattle::new();
    let tattle_clone = tattle.clone();
    let mut engine = Engine::new(HashMap::new()).unwrap();
    let fun = function0(|| {
        tattle_clone.call();
    });
    {
        let mut prelude_table: LuaTable<_> = engine.interpreter
                                                   .get("prelude_buildengine")
                                                   .expect("failed to get prelude table.");
        prelude_table.set("test_fn", fun);
    }
    tattle.assert_changed(|| {
        let result = engine.call_prelude_fn("test_fn", Vec::new()).expect("failed to call test_fn");
        assert!(result.is_none(),
                "Engine::call_prelude_fn returned a Some value for a function returning nil: {:?}",
                result);
    });
}
<|endoftext|>


<commit_before>use futures::{Async, Future, Poll};
use futures_cpupool::{CpuFuture, CpuPool};
use postgres_protocol::message::frontend;
use state_machine_future::RentToOwn;
use std::error::Error as StdError;
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::time::{Duration, Instant};
use std::vec;
use tokio_io::io::{flush, read_exact, write_all, Flush, ReadExact, WriteAll};
use tokio_tcp::{self, TcpStream};
use tokio_timer::Delay;

#[cfg(unix)]
use tokio_uds::{self, UnixStream};

use params::{ConnectParams, Host};
use proto::socket::Socket;
use tls::{self, TlsConnect, TlsStream};
use {Error, TlsMode};

lazy_static! {
    static ref DNS_POOL: CpuPool = CpuPool::new(2);
}

#[derive(StateMachineFuture)]
pub enum Connect {
    #[state_machine_future(start)]
    #[cfg_attr(
        unix,
        state_machine_future(transitions(ResolvingDns, ConnectingUnix))
    )]
    #[cfg_attr(not(unix), state_machine_future(transitions(ResolvingDns)))]
    Start { params: ConnectParams, tls: TlsMode },
    #[state_machine_future(transitions(ConnectingTcp))]
    ResolvingDns {
        future: CpuFuture<vec::IntoIter<SocketAddr>, io::Error>,
        timeout: Option<Duration>,
        params: ConnectParams,
        tls: TlsMode,
    },
    #[state_machine_future(transitions(PreparingSsl))]
    ConnectingTcp {
        addrs: vec::IntoIter<SocketAddr>,
        future: tokio_tcp::ConnectFuture,
        timeout: Option<(Duration, Delay)>,
        params: ConnectParams,
        tls: TlsMode,
    },
    #[cfg(unix)]
    #[state_machine_future(transitions(PreparingSsl))]
    ConnectingUnix {
        future: tokio_uds::ConnectFuture,
        timeout: Option<Delay>,
        params: ConnectParams,
        tls: TlsMode,
    },
    #[state_machine_future(transitions(Ready, SendingSsl))]
    PreparingSsl {
        socket: Socket,
        params: ConnectParams,
        tls: TlsMode,
    },
    #[state_machine_future(transitions(FlushingSsl))]
    SendingSsl {
        future: WriteAll<Socket, Vec<u8>>,
        params: ConnectParams,
        connector: Box<TlsConnect>,
        required: bool,
    },
    #[state_machine_future(transitions(ReadingSsl))]
    FlushingSsl {
        future: Flush<Socket>,
        params: ConnectParams,
        connector: Box<TlsConnect>,
        required: bool,
    },
    #[state_machine_future(transitions(ConnectingTls, Ready))]
    ReadingSsl {
        future: ReadExact<Socket, [u8; 1]>,
        params: ConnectParams,
        connector: Box<TlsConnect>,
        required: bool,
    },
    #[state_machine_future(transitions(Ready))]
    ConnectingTls {
        future:
            Box<Future<Item = Box<TlsStream>, Error = Box<StdError + Sync + Send>> + Sync + Send>,
        params: ConnectParams,
    },
    #[state_machine_future(ready)]
    Ready(Box<TlsStream>),
    #[state_machine_future(error)]
    Failed(Error),
}

impl PollConnect for Connect {
    fn poll_start<'a>(state: &'a mut RentToOwn<'a, Start>) -> Poll<AfterStart, Error> {
        let state = state.take();

        let timeout = state.params.connect_timeout();
        let port = state.params.port();

        match state.params.host().clone() {
            Host::Tcp(host) => transition!(ResolvingDns {
                future: DNS_POOL.spawn_fn(move || (&*host, port).to_socket_addrs()),
                params: state.params,
                tls: state.tls,
                timeout,
            }),
            #[cfg(unix)]
            Host::Unix(mut path) => {
                path.push(format!(".s.PGSQL.{}", port));
                transition!(ConnectingUnix {
                    future: UnixStream::connect(path),
                    timeout: timeout.map(|t| Delay::new(Instant::now() + t)),
                    params: state.params,
                    tls: state.tls,
                })
            }
        }
    }

    fn poll_resolving_dns<'a>(
        state: &'a mut RentToOwn<'a, ResolvingDns>,
    ) -> Poll<AfterResolvingDns, Error> {
        let mut addrs = try_ready!(state.future.poll().map_err(Error::connect));
        let state = state.take();

        let addr = match addrs.next() {
            Some(addr) => addr,
            None => {
                return Err(Error::connect(io::Error::new(
                    io::ErrorKind::Other,
                    "resolved to 0 addresses",
                )))
            }
        };

        transition!(ConnectingTcp {
            addrs,
            future: TcpStream::connect(&addr),
            timeout: state.timeout.map(|t| (t, Delay::new(Instant::now() + t))),
            params: state.params,
            tls: state.tls,
        })
    }

    fn poll_connecting_tcp<'a>(
        state: &'a mut RentToOwn<'a, ConnectingTcp>,
    ) -> Poll<AfterConnectingTcp, Error> {
        let socket = loop {
            let error = match state.future.poll() {
                Ok(Async::Ready(socket)) => break socket,
                Ok(Async::NotReady) => match state.timeout {
                    Some((_, ref mut delay)) => {
                        try_ready!(delay.poll().map_err(Error::timer));
                        io::Error::new(io::ErrorKind::TimedOut, "connection timed out")
                    }
                    None => return Ok(Async::NotReady),
                },
                Err(e) => e,
            };

            let addr = match state.addrs.next() {
                Some(addr) => addr,
                None => return Err(Error::connect(error)),
            };

            state.future = TcpStream::connect(&addr);
            if let Some((timeout, ref mut delay)) = state.timeout {
                delay.reset(Instant::now() + timeout);
            }
        };

        // Our read/write patterns may trigger Nagle's algorithm since we're pipelining which
        // we don't want. Each individual write should be a full command we want the backend to
        // see immediately.
        socket.set_nodelay(true).map_err(Error::connect)?;

        let state = state.take();
        transition!(PreparingSsl {
            socket: Socket::Tcp(socket),
            params: state.params,
            tls: state.tls,
        })
    }

    #[cfg(unix)]
    fn poll_connecting_unix<'a>(
        state: &'a mut RentToOwn<'a, ConnectingUnix>,
    ) -> Poll<AfterConnectingUnix, Error> {
        match state.future.poll().map_err(Error::connect)? {
            Async::Ready(socket) => {
                let state = state.take();
                transition!(PreparingSsl {
                    socket: Socket::Unix(socket),
                    params: state.params,
                    tls: state.tls,
                })
            }
            Async::NotReady => match state.timeout {
                Some(ref mut delay) => {
                    try_ready!(delay.poll().map_err(Error::timer));
                    Err(Error::connect(io::Error::new(
                        io::ErrorKind::TimedOut,
                        "connection timed out",
                    )))
                }
                None => Ok(Async::NotReady),
            },
        }
    }

    fn poll_preparing_ssl<'a>(
        state: &'a mut RentToOwn<'a, PreparingSsl>,
    ) -> Poll<AfterPreparingSsl, Error> {
        let state = state.take();

        let (connector, required) = match state.tls {
            TlsMode::None => {
                transition!(Ready(Box::new(state.socket)));
            }
            TlsMode::Prefer(connector) => (connector, false),
            TlsMode::Require(connector) => (connector, true),
        };

        let mut buf = vec![];
        frontend::ssl_request(&mut buf);
        transition!(SendingSsl {
            future: write_all(state.socket, buf),
            params: state.params,
            connector,
            required,
        })
    }

    fn poll_sending_ssl<'a>(
        state: &'a mut RentToOwn<'a, SendingSsl>,
    ) -> Poll<AfterSendingSsl, Error> {
        let (stream, _) = try_ready_closed!(state.future.poll());
        let state = state.take();
        transition!(FlushingSsl {
            future: flush(stream),
            params: state.params,
            connector: state.connector,
            required: state.required,
        })
    }

    fn poll_flushing_ssl<'a>(
        state: &'a mut RentToOwn<'a, FlushingSsl>,
    ) -> Poll<AfterFlushingSsl, Error> {
        let stream = try_ready_closed!(state.future.poll());
        let state = state.take();
        transition!(ReadingSsl {
            future: read_exact(stream, [0]),
            params: state.params,
            connector: state.connector,
            required: state.required,
        })
    }

    fn poll_reading_ssl<'a>(
        state: &'a mut RentToOwn<'a, ReadingSsl>,
    ) -> Poll<AfterReadingSsl, Error> {
        let (stream, buf) = try_ready_closed!(state.future.poll());
        let state = state.take();

        match buf[0] {
            b'S' => {
                let future = match state.params.host() {
                    Host::Tcp(domain) => state.connector.connect(domain, tls::Socket(stream)),
                    Host::Unix(_) => {
                        return Err(Error::tls("TLS over unix sockets not supported".into()))
                    }
                };
                transition!(ConnectingTls {
                    future,
                    params: state.params,
                })
            }
            b'N' if !state.required => transition!(Ready(Box::new(stream))),
            b'N' => Err(Error::tls("TLS was required but not supported".into())),
            _ => Err(Error::unexpected_message()),
        }
    }

    fn poll_connecting_tls<'a>(
        state: &'a mut RentToOwn<'a, ConnectingTls>,
    ) -> Poll<AfterConnectingTls, Error> {
        let stream = try_ready!(state.future.poll().map_err(Error::tls));
        transition!(Ready(stream))
    }
}

impl ConnectFuture {
    pub fn new(params: ConnectParams, tls: TlsMode) -> ConnectFuture {
        Connect::start(params, tls)
    }
}
<commit_msg>Remove the flush step from connect future<commit_after>use futures::{Async, Future, Poll};
use futures_cpupool::{CpuFuture, CpuPool};
use postgres_protocol::message::frontend;
use state_machine_future::RentToOwn;
use std::error::Error as StdError;
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::time::{Duration, Instant};
use std::vec;
use tokio_io::io::{read_exact, write_all, ReadExact, WriteAll};
use tokio_tcp::{self, TcpStream};
use tokio_timer::Delay;

#[cfg(unix)]
use tokio_uds::{self, UnixStream};

use params::{ConnectParams, Host};
use proto::socket::Socket;
use tls::{self, TlsConnect, TlsStream};
use {Error, TlsMode};

lazy_static! {
    static ref DNS_POOL: CpuPool = CpuPool::new(2);
}

#[derive(StateMachineFuture)]
pub enum Connect {
    #[state_machine_future(start)]
    #[cfg_attr(
        unix,
        state_machine_future(transitions(ResolvingDns, ConnectingUnix))
    )]
    #[cfg_attr(not(unix), state_machine_future(transitions(ResolvingDns)))]
    Start { params: ConnectParams, tls: TlsMode },
    #[state_machine_future(transitions(ConnectingTcp))]
    ResolvingDns {
        future: CpuFuture<vec::IntoIter<SocketAddr>, io::Error>,
        timeout: Option<Duration>,
        params: ConnectParams,
        tls: TlsMode,
    },
    #[state_machine_future(transitions(PreparingSsl))]
    ConnectingTcp {
        addrs: vec::IntoIter<SocketAddr>,
        future: tokio_tcp::ConnectFuture,
        timeout: Option<(Duration, Delay)>,
        params: ConnectParams,
        tls: TlsMode,
    },
    #[cfg(unix)]
    #[state_machine_future(transitions(PreparingSsl))]
    ConnectingUnix {
        future: tokio_uds::ConnectFuture,
        timeout: Option<Delay>,
        params: ConnectParams,
        tls: TlsMode,
    },
    #[state_machine_future(transitions(Ready, SendingSsl))]
    PreparingSsl {
        socket: Socket,
        params: ConnectParams,
        tls: TlsMode,
    },
    #[state_machine_future(transitions(ReadingSsl))]
    SendingSsl {
        future: WriteAll<Socket, Vec<u8>>,
        params: ConnectParams,
        connector: Box<TlsConnect>,
        required: bool,
    },
    #[state_machine_future(transitions(ConnectingTls, Ready))]
    ReadingSsl {
        future: ReadExact<Socket, [u8; 1]>,
        params: ConnectParams,
        connector: Box<TlsConnect>,
        required: bool,
    },
    #[state_machine_future(transitions(Ready))]
    ConnectingTls {
        future:
            Box<Future<Item = Box<TlsStream>, Error = Box<StdError + Sync + Send>> + Sync + Send>,
        params: ConnectParams,
    },
    #[state_machine_future(ready)]
    Ready(Box<TlsStream>),
    #[state_machine_future(error)]
    Failed(Error),
}

impl PollConnect for Connect {
    fn poll_start<'a>(state: &'a mut RentToOwn<'a, Start>) -> Poll<AfterStart, Error> {
        let state = state.take();

        let timeout = state.params.connect_timeout();
        let port = state.params.port();

        match state.params.host().clone() {
            Host::Tcp(host) => transition!(ResolvingDns {
                future: DNS_POOL.spawn_fn(move || (&*host, port).to_socket_addrs()),
                params: state.params,
                tls: state.tls,
                timeout,
            }),
            #[cfg(unix)]
            Host::Unix(mut path) => {
                path.push(format!(".s.PGSQL.{}", port));
                transition!(ConnectingUnix {
                    future: UnixStream::connect(path),
                    timeout: timeout.map(|t| Delay::new(Instant::now() + t)),
                    params: state.params,
                    tls: state.tls,
                })
            }
        }
    }

    fn poll_resolving_dns<'a>(
        state: &'a mut RentToOwn<'a, ResolvingDns>,
    ) -> Poll<AfterResolvingDns, Error> {
        let mut addrs = try_ready!(state.future.poll().map_err(Error::connect));
        let state = state.take();

        let addr = match addrs.next() {
            Some(addr) => addr,
            None => {
                return Err(Error::connect(io::Error::new(
                    io::ErrorKind::Other,
                    "resolved to 0 addresses",
                )))
            }
        };

        transition!(ConnectingTcp {
            addrs,
            future: TcpStream::connect(&addr),
            timeout: state.timeout.map(|t| (t, Delay::new(Instant::now() + t))),
            params: state.params,
            tls: state.tls,
        })
    }

    fn poll_connecting_tcp<'a>(
        state: &'a mut RentToOwn<'a, ConnectingTcp>,
    ) -> Poll<AfterConnectingTcp, Error> {
        let socket = loop {
            let error = match state.future.poll() {
                Ok(Async::Ready(socket)) => break socket,
                Ok(Async::NotReady) => match state.timeout {
                    Some((_, ref mut delay)) => {
                        try_ready!(delay.poll().map_err(Error::timer));
                        io::Error::new(io::ErrorKind::TimedOut, "connection timed out")
                    }
                    None => return Ok(Async::NotReady),
                },
                Err(e) => e,
            };

            let addr = match state.addrs.next() {
                Some(addr) => addr,
                None => return Err(Error::connect(error)),
            };

            state.future = TcpStream::connect(&addr);
            if let Some((timeout, ref mut delay)) = state.timeout {
                delay.reset(Instant::now() + timeout);
            }
        };

        // Our read/write patterns may trigger Nagle's algorithm since we're pipelining which
        // we don't want. Each individual write should be a full command we want the backend to
        // see immediately.
        socket.set_nodelay(true).map_err(Error::connect)?;

        let state = state.take();
        transition!(PreparingSsl {
            socket: Socket::Tcp(socket),
            params: state.params,
            tls: state.tls,
        })
    }

    #[cfg(unix)]
    fn poll_connecting_unix<'a>(
        state: &'a mut RentToOwn<'a, ConnectingUnix>,
    ) -> Poll<AfterConnectingUnix, Error> {
        match state.future.poll().map_err(Error::connect)? {
            Async::Ready(socket) => {
                let state = state.take();
                transition!(PreparingSsl {
                    socket: Socket::Unix(socket),
                    params: state.params,
                    tls: state.tls,
                })
            }
            Async::NotReady => match state.timeout {
                Some(ref mut delay) => {
                    try_ready!(delay.poll().map_err(Error::timer));
                    Err(Error::connect(io::Error::new(
                        io::ErrorKind::TimedOut,
                        "connection timed out",
                    )))
                }
                None => Ok(Async::NotReady),
            },
        }
    }

    fn poll_preparing_ssl<'a>(
        state: &'a mut RentToOwn<'a, PreparingSsl>,
    ) -> Poll<AfterPreparingSsl, Error> {
        let state = state.take();

        let (connector, required) = match state.tls {
            TlsMode::None => {
                transition!(Ready(Box::new(state.socket)));
            }
            TlsMode::Prefer(connector) => (connector, false),
            TlsMode::Require(connector) => (connector, true),
        };

        let mut buf = vec![];
        frontend::ssl_request(&mut buf);
        transition!(SendingSsl {
            future: write_all(state.socket, buf),
            params: state.params,
            connector,
            required,
        })
    }

    fn poll_sending_ssl<'a>(
        state: &'a mut RentToOwn<'a, SendingSsl>,
    ) -> Poll<AfterSendingSsl, Error> {
        let (stream, _) = try_ready_closed!(state.future.poll());
        let state = state.take();
        transition!(ReadingSsl {
            future: read_exact(stream, [0]),
            params: state.params,
            connector: state.connector,
            required: state.required,
        })
    }

    fn poll_reading_ssl<'a>(
        state: &'a mut RentToOwn<'a, ReadingSsl>,
    ) -> Poll<AfterReadingSsl, Error> {
        let (stream, buf) = try_ready_closed!(state.future.poll());
        let state = state.take();

        match buf[0] {
            b'S' => {
                let future = match state.params.host() {
                    Host::Tcp(domain) => state.connector.connect(domain, tls::Socket(stream)),
                    Host::Unix(_) => {
                        return Err(Error::tls("TLS over unix sockets not supported".into()))
                    }
                };
                transition!(ConnectingTls {
                    future,
                    params: state.params,
                })
            }
            b'N' if !state.required => transition!(Ready(Box::new(stream))),
            b'N' => Err(Error::tls("TLS was required but not supported".into())),
            _ => Err(Error::unexpected_message()),
        }
    }

    fn poll_connecting_tls<'a>(
        state: &'a mut RentToOwn<'a, ConnectingTls>,
    ) -> Poll<AfterConnectingTls, Error> {
        let stream = try_ready!(state.future.poll().map_err(Error::tls));
        transition!(Ready(stream))
    }
}

impl ConnectFuture {
    pub fn new(params: ConnectParams, tls: TlsMode) -> ConnectFuture {
        Connect::start(params, tls)
    }
}
<|endoftext|>