<commit_before>// Dogfood cannot run on Windows
#![cfg(not(windows))]
#![feature(once_cell)]

use std::lazy::SyncLazy;
use std::path::{Path, PathBuf};
use std::process::Command;

mod cargo;

static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy"));

#[test]
fn dogfood_clippy() {
    // run clippy on itself and fail the test if lint warnings are reported
    if cargo::is_rustc_test_suite() {
        return;
    }
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

    let mut command = Command::new(&*CLIPPY_PATH);
    command
        .current_dir(root_dir)
        .env("CLIPPY_DOGFOOD", "1")
        .env("CARGO_INCREMENTAL", "0")
        .arg("clippy-preview")
        .arg("--all-targets")
        .arg("--all-features")
        .arg("--")
        .args(&["-D", "clippy::all"])
        .args(&["-D", "clippy::pedantic"])
        .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir

    // internal lints only exist if we build with the internal-lints feature
    if cfg!(feature = "internal-lints") {
        command.args(&["-D", "clippy::internal"]);
    }

    let output = command.output().unwrap();

    println!("status: {}", output.status);
    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

    assert!(output.status.success());
}

#[test]
fn dogfood_subprojects() {
    fn test_no_deps_ignores_path_deps_in_workspaces() {
        fn clean(cwd: &Path, target_dir: &Path) {
            Command::new("cargo")
                .current_dir(cwd)
                .env("CARGO_TARGET_DIR", target_dir)
                .arg("clean")
                .args(&["-p", "subcrate"])
                .args(&["-p", "path_dep"])
                .output()
                .unwrap();
        }

        if cargo::is_rustc_test_suite() {
            return;
        }
        let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        let target_dir = root.join("target").join("dogfood");
        let cwd = root.join("clippy_workspace_tests");

        // Make sure we start with a clean state
        clean(&cwd, &target_dir);

        // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
        // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
        let output = Command::new(&*CLIPPY_PATH)
            .current_dir(&cwd)
            .env("CLIPPY_DOGFOOD", "1")
            .env("CARGO_INCREMENTAL", "0")
            .arg("clippy")
            .args(&["-p", "subcrate"])
            .arg("--")
            .arg("--no-deps")
            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
            .args(&["--cfg", r#"feature="primary_package_test""#])
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(output.status.success());

        // Make sure we start with a clean state
        clean(&cwd, &target_dir);

        // Test that without the `--no-deps` argument, `path_dep` is linted.
        let output = Command::new(&*CLIPPY_PATH)
            .current_dir(&cwd)
            .env("CLIPPY_DOGFOOD", "1")
            .env("CARGO_INCREMENTAL", "0")
            .arg("clippy")
            .args(&["-p", "subcrate"])
            .arg("--")
            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
            .args(&["--cfg", r#"feature="primary_package_test""#])
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(!output.status.success());
    }

    // run clippy on remaining subprojects and fail the test if lint warnings are reported
    if cargo::is_rustc_test_suite() {
        return;
    }
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

    // NOTE: `path_dep` crate is omitted on purpose here
    for d in &[
        "clippy_workspace_tests",
        "clippy_workspace_tests/src",
        "clippy_workspace_tests/subcrate",
        "clippy_workspace_tests/subcrate/src",
        "clippy_dev",
        "rustc_tools_util",
    ] {
        let output = Command::new(&*CLIPPY_PATH)
            .current_dir(root_dir.join(d))
            .env("CLIPPY_DOGFOOD", "1")
            .env("CARGO_INCREMENTAL", "0")
            .arg("clippy")
            .arg("--")
            .args(&["-D", "clippy::all"])
            .args(&["-D", "clippy::pedantic"])
            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(output.status.success());
    }

    // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
    // same time, so we test this immediately after the dogfood for workspaces.
    test_no_deps_ignores_path_deps_in_workspaces();
}
<commit_msg>Add package arguments to dogfood test<commit_after>// Dogfood cannot run on Windows
#![cfg(not(windows))]
#![feature(once_cell)]

use std::lazy::SyncLazy;
use std::path::{Path, PathBuf};
use std::process::Command;

mod cargo;

static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy"));

#[test]
fn dogfood_clippy() {
    // run clippy on itself and fail the test if lint warnings are reported
    if cargo::is_rustc_test_suite() {
        return;
    }
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

    let mut command = Command::new(&*CLIPPY_PATH);
    command
        .current_dir(root_dir)
        .env("CLIPPY_DOGFOOD", "1")
        .env("CARGO_INCREMENTAL", "0")
        .arg("clippy-preview")
        .arg("--all-targets")
        .arg("--all-features")
        .args(&["-p", "clippy_lints", "-p", "clippy_utils", "-p", "rustc_tools_util"])
        .arg("--")
        .args(&["-D", "clippy::all"])
        .args(&["-D", "clippy::pedantic"])
        .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir

    // internal lints only exist if we build with the internal-lints feature
    if cfg!(feature = "internal-lints") {
        command.args(&["-D", "clippy::internal"]);
    }

    let output = command.output().unwrap();

    println!("status: {}", output.status);
    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

    assert!(output.status.success());
}

#[test]
fn dogfood_subprojects() {
    fn test_no_deps_ignores_path_deps_in_workspaces() {
        fn clean(cwd: &Path, target_dir: &Path) {
            Command::new("cargo")
                .current_dir(cwd)
                .env("CARGO_TARGET_DIR", target_dir)
                .arg("clean")
                .args(&["-p", "subcrate"])
                .args(&["-p", "path_dep"])
                .output()
                .unwrap();
        }

        if cargo::is_rustc_test_suite() {
            return;
        }
        let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        let target_dir = root.join("target").join("dogfood");
        let cwd = root.join("clippy_workspace_tests");

        // Make sure we start with a clean state
        clean(&cwd, &target_dir);

        // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
        // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
        let output = Command::new(&*CLIPPY_PATH)
            .current_dir(&cwd)
            .env("CLIPPY_DOGFOOD", "1")
            .env("CARGO_INCREMENTAL", "0")
            .arg("clippy")
            .args(&["-p", "subcrate"])
            .arg("--")
            .arg("--no-deps")
            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
            .args(&["--cfg", r#"feature="primary_package_test""#])
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(output.status.success());

        // Make sure we start with a clean state
        clean(&cwd, &target_dir);

        // Test that without the `--no-deps` argument, `path_dep` is linted.
        let output = Command::new(&*CLIPPY_PATH)
            .current_dir(&cwd)
            .env("CLIPPY_DOGFOOD", "1")
            .env("CARGO_INCREMENTAL", "0")
            .arg("clippy")
            .args(&["-p", "subcrate"])
            .arg("--")
            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
            .args(&["--cfg", r#"feature="primary_package_test""#])
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(!output.status.success());
    }

    // run clippy on remaining subprojects and fail the test if lint warnings are reported
    if cargo::is_rustc_test_suite() {
        return;
    }
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

    // NOTE: `path_dep` crate is omitted on purpose here
    for d in &[
        "clippy_workspace_tests",
        "clippy_workspace_tests/src",
        "clippy_workspace_tests/subcrate",
        "clippy_workspace_tests/subcrate/src",
        "clippy_dev",
        "rustc_tools_util",
    ] {
        let output = Command::new(&*CLIPPY_PATH)
            .current_dir(root_dir.join(d))
            .env("CLIPPY_DOGFOOD", "1")
            .env("CARGO_INCREMENTAL", "0")
            .arg("clippy")
            .arg("--")
            .args(&["-D", "clippy::all"])
            .args(&["-D", "clippy::pedantic"])
            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(output.status.success());
    }

    // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
    // same time, so we test this immediately after the dogfood for workspaces.
    test_no_deps_ignores_path_deps_in_workspaces();
}
<|endoftext|>


<commit_before>extern crate mpd;
extern crate time;

mod helpers;
use helpers::connect;

#[test]
fn status() {
    let mut mpd = connect();
    let status = mpd.status().unwrap();
    println!("{:?}", status);
}

#[test]
fn stats() {
    let mut mpd = connect();
    let stats = mpd.stats().unwrap();
    println!("{:?}", stats);
}

macro_rules! test_options_impl {
    ($name:ident, $val1:expr, $tval1:expr, $val2:expr, $tval2:expr) => {
        #[test]
        fn $name() {
            let mut mpd = connect();
            mpd.$name($val1).unwrap();
            assert_eq!(mpd.status().unwrap().$name, $tval1);
            mpd.$name($val2).unwrap();
            assert_eq!(mpd.status().unwrap().$name, $tval2);
        }
    }
}

macro_rules! test_option {
    ($name:ident, $val1:expr, $val2:expr) => {
        test_options_impl!($name, $val1, $val1, $val2, $val2);
    };
    ($name:ident, $val1:expr => $tval1:expr, $val2:expr => $tval2:expr) => {
        test_options_impl!($name, $val1, $tval1, $val2, $tval2);
    };
}

test_option!(consume, true, false);
test_option!(single, true, false);
test_option!(random, true, false);
test_option!(repeat, true, false);
//test_option!(mixrampdb, 1.0f32, 0.0f32);
//test_option!(mixrampdelay, 1 => Some(Duration::seconds(1)), 0 => None);

#[test]
fn volume() {
    let mut mpd = connect();
    if mpd.status().unwrap().volume >= 0 {
        mpd.volume(100).unwrap();
        assert_eq!(mpd.status().unwrap().volume, 100);
        mpd.volume(0).unwrap();
        assert_eq!(mpd.status().unwrap().volume, 0);
    }
}

#[test]
fn crossfade() {
    let mut mpd = connect();
    mpd.crossfade(1000).unwrap();
    assert_eq!(mpd.status().unwrap().crossfade, Some(1000));
    mpd.crossfade(0).unwrap();
    assert_eq!(mpd.status().unwrap().crossfade,
        if mpd.version >= mpd::Version(0, 19, 0) {
            None
        } else {
            Some(0)
        });
}
<commit_msg>update options tests<commit_after>extern crate mpd;
extern crate time;

mod helpers;
use helpers::connect;
use time::Duration;

#[test]
fn status() {
    let mut mpd = connect();
    let status = mpd.status().unwrap();
    println!("{:?}", status);
}

#[test]
fn stats() {
    let mut mpd = connect();
    let stats = mpd.stats().unwrap();
    println!("{:?}", stats);
}

macro_rules! test_options_impl {
    ($name:ident, $val1:expr, $tval1:expr, $val2:expr, $tval2:expr) => {
        #[test]
        fn $name() {
            let mut mpd = connect();
            mpd.$name($val1).unwrap();
            assert_eq!(mpd.status().unwrap().$name, $tval1);
            mpd.$name($val2).unwrap();
            assert_eq!(mpd.status().unwrap().$name, $tval2);
        }
    }
}

macro_rules! test_option {
    ($name:ident, $val1:expr, $val2:expr) => {
        test_options_impl!($name, $val1, $val1, $val2, $val2);
    };
    ($name:ident, $val1:expr => $tval1:expr, $val2:expr => $tval2:expr) => {
        test_options_impl!($name, $val1, $tval1, $val2, $tval2);
    };
}

test_option!(consume, true, false);
test_option!(single, true, false);
test_option!(random, true, false);
test_option!(repeat, true, false);
//test_option!(mixrampdb, 1.0f32, 0.0f32);
//test_option!(mixrampdelay, 1 => Some(Duration::seconds(1)), 0 => None);

#[test]
fn volume() {
    let mut mpd = connect();
    if mpd.status().unwrap().volume >= 0 {
        mpd.volume(100).unwrap();
        assert_eq!(mpd.status().unwrap().volume, 100);
        mpd.volume(0).unwrap();
        assert_eq!(mpd.status().unwrap().volume, 0);
    }
}

#[test]
fn crossfade() {
    let mut mpd = connect();
    mpd.crossfade(1000).unwrap();
    assert_eq!(mpd.status().unwrap().crossfade, Some(Duration::seconds(1000)));
    mpd.crossfade(0).unwrap();
    assert_eq!(mpd.status().unwrap().crossfade,
        if mpd.version >= mpd::Version(0, 19, 0) {
            None
        } else {
            Some(Duration::zero())
        });
}
<|endoftext|>


<commit_before>// use std::io::fs::{mkdir_recursive,rmdir_recursive};
use std::io::fs;
use std::io::process::{ProcessOutput,ProcessExit};
use std::os;
use std::path::{Path};
use std::str;
use std::vec::Vec;
use ham = hamcrest;
use cargo::util::{process,ProcessBuilder};

static CARGO_INTEGRATION_TEST_DIR : &'static str = "cargo-integration-tests";
static MKDIR_PERM : u32 = 0o755;

/*
 *
 * ===== Builders =====
 *
 */

#[deriving(Eq,Clone)]
struct FileBuilder {
    path: Path,
    body: ~str
}

impl FileBuilder {
    pub fn new(path: Path, body: &str) -> FileBuilder {
        FileBuilder { path: path, body: body.to_owned() }
    }

    fn mk(&self) -> Result<(), ~str> {
        try!(mkdir_recursive(&self.dirname()));

        let mut file = try!(
            fs::File::create(&self.path)
                .with_err_msg(format!("Could not create file; path={}", self.path.display())));

        file.write_str(self.body.as_slice())
            .with_err_msg(format!("Could not write to file; path={}", self.path.display()))
    }

    fn dirname(&self) -> Path {
        Path::new(self.path.dirname())
    }
}

#[deriving(Eq,Clone)]
struct ProjectBuilder {
    name: ~str,
    root: Path,
    files: Vec<FileBuilder>
}

impl ProjectBuilder {
    pub fn new(name: &str, root: Path) -> ProjectBuilder {
        ProjectBuilder {
            name: name.to_owned(),
            root: root,
            files: vec!()
        }
    }

    pub fn root(&self) -> Path {
      self.root.clone()
    }

    pub fn cargo_process(&self, program: &str) -> ProcessBuilder {
      process(program)
        .cwd(self.root())
        .extra_path(cargo_dir())
    }

    pub fn file(mut self, path: &str, body: &str) -> ProjectBuilder {
        self.files.push(FileBuilder::new(self.root.join(path), body));
        self
    }

    // TODO: return something different than a ProjectBuilder
    pub fn build(self) -> ProjectBuilder {
        match self.build_with_result() {
            Err(e) => fail!(e),
            _ => return self
        }
    }

    pub fn build_with_result(&self) -> Result<(), ~str> {
        // First, clean the directory if it already exists
        try!(self.rm_root());

        // Create the empty directory
        try!(mkdir_recursive(&self.root));

        for file in self.files.iter() {
          try!(file.mk());
        }

        println!("{}", self.root.display());
        println!("{:?}", self);
        Ok(())
    }

    fn rm_root(&self) -> Result<(), ~str> {
        if self.root.exists() {
            rmdir_recursive(&self.root)
        }
        else {
            Ok(())
        }
    }
}

// Generates a project layout
pub fn project(name: &str) -> ProjectBuilder {
    ProjectBuilder::new(name, os::tmpdir().join(CARGO_INTEGRATION_TEST_DIR))
}

// === Helpers ===

pub fn mkdir_recursive(path: &Path) -> Result<(), ~str> {
    fs::mkdir_recursive(path, MKDIR_PERM)
        .with_err_msg(format!("could not create directory; path={}", path.display()))
}

pub fn rmdir_recursive(path: &Path) -> Result<(), ~str> {
    fs::rmdir_recursive(path)
        .with_err_msg(format!("could not rm directory; path={}", path.display()))
}

trait ErrMsg<T> {
    fn with_err_msg(self, val: ~str) -> Result<T, ~str>;
}

impl<T, E> ErrMsg<T> for Result<T, E> {
    fn with_err_msg(self, val: ~str) -> Result<T, ~str> {
        match self {
            Ok(val) => Ok(val),
            Err(_) => Err(val)
        }
    }
}

// Path to cargo executables
pub fn cargo_dir() -> Path {
  os::getenv("CARGO_BIN_PATH")
    .map(|s| Path::new(s))
    .unwrap_or_else(|| fail!("CARGO_BIN_PATH wasn't set. Cannot continue running test"))
}

/*
 *
 * ===== Matchers =====
 *
 */

#[deriving(Clone,Eq)]
struct Execs {
  expect_stdout: Option<~str>,
  expect_stdin: Option<~str>,
  expect_exit_code: Option<int>
}

impl Execs {

  pub fn with_stdout(mut ~self, expected: &str) -> ~Execs {
    self.expect_stdout = Some(expected.to_owned());
    self
  }

  fn match_output(&self, actual: &ProcessOutput) -> ham::MatchResult {
    self.match_status(actual.status)
      .and(self.match_stdout(&actual.output))
  }

  fn match_status(&self, actual: ProcessExit) -> ham::MatchResult {
    match self.expect_exit_code {
      None => ham::success(),
      Some(code) => {
        ham::expect(
          actual.matches_exit_status(code),
          format!("exited with {}", actual))
      }
    }
  }

  fn match_stdout(&self, actual: &Vec<u8>) -> ham::MatchResult {
    match self.expect_stdout.as_ref().map(|s| s.as_slice()) {
      None => ham::success(),
      Some(out) => {
        match str::from_utf8(actual.as_slice()) {
          None => Err("stdout was not utf8 encoded".to_owned()),
          Some(actual) => {
            ham::expect(actual == out, format!("stdout was `{}`", actual))
          }
        }
      }
    }
  }
}

impl ham::SelfDescribing for Execs {
  fn describe(&self) -> ~str {
    "execs".to_owned()
  }
}

impl ham::Matcher<ProcessBuilder> for Execs {
  fn matches(&self, process: ProcessBuilder) -> ham::MatchResult {
    let res = process.exec_with_output();

    match res {
      Ok(out) => self.match_output(&out),
      Err(_) => Err("could not exec process".to_owned())
    }
  }
}

pub fn execs() -> ~Execs {
  ~Execs {
    expect_stdout: None,
    expect_stdin: None,
    expect_exit_code: None
  }
}
<commit_msg>Track Rust master changes<commit_after>// use std::io::fs::{mkdir_recursive,rmdir_recursive};
use std::io;
use std::io::fs;
use std::io::process::{ProcessOutput,ProcessExit};
use std::os;
use std::path::{Path};
use std::str;
use std::vec::Vec;
use ham = hamcrest;
use cargo::util::{process,ProcessBuilder};

static CARGO_INTEGRATION_TEST_DIR : &'static str = "cargo-integration-tests";

/*
 *
 * ===== Builders =====
 *
 */

#[deriving(Eq,Clone)]
struct FileBuilder {
    path: Path,
    body: ~str
}

impl FileBuilder {
    pub fn new(path: Path, body: &str) -> FileBuilder {
        FileBuilder { path: path, body: body.to_owned() }
    }

    fn mk(&self) -> Result<(), ~str> {
        try!(mkdir_recursive(&self.dirname()));

        let mut file = try!(
            fs::File::create(&self.path)
                .with_err_msg(format!("Could not create file; path={}", self.path.display())));

        file.write_str(self.body.as_slice())
            .with_err_msg(format!("Could not write to file; path={}", self.path.display()))
    }

    fn dirname(&self) -> Path {
        Path::new(self.path.dirname())
    }
}

#[deriving(Eq,Clone)]
struct ProjectBuilder {
    name: ~str,
    root: Path,
    files: Vec<FileBuilder>
}

impl ProjectBuilder {
    pub fn new(name: &str, root: Path) -> ProjectBuilder {
        ProjectBuilder {
            name: name.to_owned(),
            root: root,
            files: vec!()
        }
    }

    pub fn root(&self) -> Path {
      self.root.clone()
    }

    pub fn cargo_process(&self, program: &str) -> ProcessBuilder {
      process(program)
        .cwd(self.root())
        .extra_path(cargo_dir())
    }

    pub fn file(mut self, path: &str, body: &str) -> ProjectBuilder {
        self.files.push(FileBuilder::new(self.root.join(path), body));
        self
    }

    // TODO: return something different than a ProjectBuilder
    pub fn build(self) -> ProjectBuilder {
        match self.build_with_result() {
            Err(e) => fail!(e),
            _ => return self
        }
    }

    pub fn build_with_result(&self) -> Result<(), ~str> {
        // First, clean the directory if it already exists
        try!(self.rm_root());

        // Create the empty directory
        try!(mkdir_recursive(&self.root));

        for file in self.files.iter() {
          try!(file.mk());
        }

        println!("{}", self.root.display());
        println!("{:?}", self);
        Ok(())
    }

    fn rm_root(&self) -> Result<(), ~str> {
        if self.root.exists() {
            rmdir_recursive(&self.root)
        }
        else {
            Ok(())
        }
    }
}

// Generates a project layout
pub fn project(name: &str) -> ProjectBuilder {
    ProjectBuilder::new(name, os::tmpdir().join(CARGO_INTEGRATION_TEST_DIR))
}

// === Helpers ===

pub fn mkdir_recursive(path: &Path) -> Result<(), ~str> {
    fs::mkdir_recursive(path, io::UserDir)
        .with_err_msg(format!("could not create directory; path={}", path.display()))
}

pub fn rmdir_recursive(path: &Path) -> Result<(), ~str> {
    fs::rmdir_recursive(path)
        .with_err_msg(format!("could not rm directory; path={}", path.display()))
}

trait ErrMsg<T> {
    fn with_err_msg(self, val: ~str) -> Result<T, ~str>;
}

impl<T, E> ErrMsg<T> for Result<T, E> {
    fn with_err_msg(self, val: ~str) -> Result<T, ~str> {
        match self {
            Ok(val) => Ok(val),
            Err(_) => Err(val)
        }
    }
}

// Path to cargo executables
pub fn cargo_dir() -> Path {
  os::getenv("CARGO_BIN_PATH")
    .map(|s| Path::new(s))
    .unwrap_or_else(|| fail!("CARGO_BIN_PATH wasn't set. Cannot continue running test"))
}

/*
 *
 * ===== Matchers =====
 *
 */

#[deriving(Clone,Eq)]
struct Execs {
  expect_stdout: Option<~str>,
  expect_stdin: Option<~str>,
  expect_exit_code: Option<int>
}

impl Execs {

  pub fn with_stdout(mut ~self, expected: &str) -> Box<Execs> {
    self.expect_stdout = Some(expected.to_owned());
    self
  }

  fn match_output(&self, actual: &ProcessOutput) -> ham::MatchResult {
    self.match_status(actual.status)
      .and(self.match_stdout(&actual.output))
  }

  fn match_status(&self, actual: ProcessExit) -> ham::MatchResult {
    match self.expect_exit_code {
      None => ham::success(),
      Some(code) => {
        ham::expect(
          actual.matches_exit_status(code),
          format!("exited with {}", actual))
      }
    }
  }

  fn match_stdout(&self, actual: &Vec<u8>) -> ham::MatchResult {
    match self.expect_stdout.as_ref().map(|s| s.as_slice()) {
      None => ham::success(),
      Some(out) => {
        match str::from_utf8(actual.as_slice()) {
          None => Err("stdout was not utf8 encoded".to_owned()),
          Some(actual) => {
            ham::expect(actual == out, format!("stdout was `{}`", actual))
          }
        }
      }
    }
  }
}

impl ham::SelfDescribing for Execs {
  fn describe(&self) -> ~str {
    "execs".to_owned()
  }
}

impl ham::Matcher<ProcessBuilder> for Execs {
  fn matches(&self, process: ProcessBuilder) -> ham::MatchResult {
    let res = process.exec_with_output();

    match res {
      Ok(out) => self.match_output(&out),
      Err(_) => Err("could not exec process".to_owned())
    }
  }
}

pub fn execs() -> Box<Execs> {
  box Execs {
    expect_stdout: None,
    expect_stdin: None,
    expect_exit_code: None
  }
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use mio::*;
use mio::tcp::{TcpStream, TcpSocket};
use mio::buf::ByteBuf;
use std::net::SocketAddr;
use std::net::lookup_host;
use std::collections::VecMap;
use url::Url;

use eventual;

#[derive(Debug, Clone)]
pub enum HttpAction {
    Get(Arc<Url>),
}

pub struct Echo {
    non_block_client: VecMap<TcpStream>,
    action: VecMap<HttpAction>,
    sender: VecMap<eventual::Complete<Box<Vec<u8>>, &'static str>>,
    mut_buf: VecMap<Vec<u8>>,
    buf: Option<ByteBuf>,
    interest: VecMap<Interest>
}

impl Echo {
    pub fn new() -> Echo {
        Echo {
            non_block_client: VecMap::new(),
            action: VecMap::new(),
            sender: VecMap::new(),
            mut_buf: VecMap::new(),
            interest: VecMap::new(),
            buf: None,
        }
    }
}

impl Handler for Echo {
    type Timeout = usize;
    type Message = (String, eventual::Complete<Box<Vec<u8>>, &'static str>);

    fn readable(&mut self, event_loop: &mut EventLoop<Echo>, token: Token, hint: ReadHint) {
        println!("Read");

        let mut buf = ByteBuf::mut_with_capacity(4096 * 16);

        let ref mut non_block_client = match self.non_block_client.get_mut(&token.as_usize()) {
            Some(client) => client,
            None => panic!("Error finding the associated non blocking client {:?}", token)
        };
        let ref mut interest = match self.interest.get_mut(&token.as_usize()) {
            Some(interest) => interest,
            None => panic!("Error finding the associated interest {:?}", token)
        };
        match non_block_client.try_read_buf(&mut buf) {

            Ok(None) => {
                panic!("We just got readable, but were unable to read from the socket?");
            }
            Ok(Some(r)) => {
                interest.remove(Interest::readable());
                if r == 0 {
                	println!("DONE");
                    event_loop.deregister(*non_block_client).unwrap();
                    let mut_buf = match self.mut_buf.remove(&token.as_usize()) {
                        Some(mut_buf) => mut_buf,
                        None =>
                            panic!("Error finding the mut_buf for {:?} {:?}", token, self.mut_buf)
                    };

                    match self.sender.remove(&token.as_usize()) {
                        Some(completer) => {
                            completer.complete(Box::new(mut_buf));
                            interest.remove(Interest::none());
                        }
                        None => panic!("Error finding the mut_buf for {:?}", token)
                    };

                } else {
        event_loop.reregister(*non_block_client, token, **interest,
                              PollOpt::edge() | PollOpt::oneshot()).unwrap();

                    match self.mut_buf.get_mut(&token.as_usize()) {
                        Some(mut_buf) => mut_buf.push_all(buf.flip().bytes()),
                        None => panic!("Error finding the mut_buf for {:?}", token)
                    }

                    interest.insert(Interest::readable());
                }
            }
            Err(e) => {
                panic!("not implemented; client err={:?}", e);
            }
        }
    }

    fn writable(&mut self, event_loop: &mut EventLoop<Echo>, token: Token) {
        let ref mut action = match self.action.get_mut(&token.as_usize()) {
            Some(action) => action,
            None => panic!("Error finding the associated action {:?}", token)
        };
        let get_command: String = body(action.clone());

        let ref mut non_block_client = match self.non_block_client.get_mut(&token.as_usize()) {
            Some(client) => client,
            None => panic!("Error finding the associated non blocking client {:?}", token)
        };

        let ref mut interest = match self.interest.get_mut(&token.as_usize()) {
            Some(interest) => interest,
            None => panic!("Error finding the associated interest {:?}", token)
        };

        match **action {
            HttpAction::Get(_) => {
                let mut buf = ByteBuf::from_slice(get_command.as_bytes());
                match non_block_client.try_write_buf(&mut buf) {
                    Ok(None) => {
                        println!("client flushing buf; WOULDBLOCK");
                        self.buf = Some(buf);
                        interest.insert(Interest::writable());
                    }
                    Ok(Some(_)) => {
                        interest.insert(Interest::readable());
                        interest.remove(Interest::writable());
                        event_loop.reregister(*non_block_client, token, **interest,
                                              PollOpt::edge() | PollOpt::oneshot()).unwrap();
                    }
                    Err(e) => panic!("not implemented; client err={:?}", e),
                }
            }
        }
    }
    fn notify(&mut self,
              event_loop: &mut EventLoop<Echo>,
              tuple: (String, eventual::Complete<Box<Vec<u8>>, &'static str>)) {
        let token = Token(self.action.len() + 1);
        let action = get_action(tuple.0);
        self.action.insert(token.as_usize(), action.clone());

        match action {
            HttpAction::Get(url_p) => {
                let url: Url = (*url_p).clone();
                let ip = lookup_host(url.domain().unwrap()).unwrap().next().unwrap().unwrap();
                let port = url.port_or_default().unwrap();
                let address = SocketAddr::new(ip.ip(), port);
                let (sock, _) = TcpSocket::v4().unwrap().connect(&address).unwrap();
                self.non_block_client.insert(token.as_usize(), sock);
                self.sender.insert(token.as_usize(), tuple.1);
                self.mut_buf.insert(token.as_usize(), Vec::new());
                self.interest.insert(token.as_usize(), Interest::hup());
                event_loop.register_opt(self.non_block_client.get(&token.as_usize()).unwrap(),
                                        token, Interest::writable(),
                                        PollOpt::edge() | PollOpt::oneshot()).unwrap();
            }
        }
    }
}

fn body(action: HttpAction) -> String {
    match action {
        HttpAction::Get(ref resource) => {
            format!(
                "GET {}  HTTP/1.1\r\nHost: {}\r\nUser-Agent: curl/7.37.1\r\nAccept */*\r\n\r\n",
                resource.serialize_path().unwrap(), resource.domain().unwrap())
        }
    }
}
fn get_action(url_s: String) -> HttpAction {
    let url = Url::parse(url_s.as_str()).unwrap();
    HttpAction::Get(Arc::new(url))
}<commit_msg>Simplify one shot logic<commit_after>use std::sync::Arc;
use mio::*;
use mio::tcp::{TcpStream, TcpSocket};
use mio::buf::ByteBuf;
use std::net::SocketAddr;
use std::net::lookup_host;
use std::collections::VecMap;
use url::Url;

use eventual;

#[derive(Debug, Clone)]
pub enum HttpAction {
    Get(Arc<Url>),
}

pub struct Echo {
    non_block_client: VecMap<TcpStream>,
    action: VecMap<HttpAction>,
    sender: VecMap<eventual::Complete<Box<Vec<u8>>, &'static str>>,
    mut_buf: VecMap<Vec<u8>>,
    buf: Option<ByteBuf>,
    interest: VecMap<Interest>
}

impl Echo {
    pub fn new() -> Echo {
        Echo {
            non_block_client: VecMap::new(),
            action: VecMap::new(),
            sender: VecMap::new(),
            mut_buf: VecMap::new(),
            interest: VecMap::new(),
            buf: None,
        }
    }
}

impl Handler for Echo {
    type Timeout = usize;
    type Message = (String, eventual::Complete<Box<Vec<u8>>, &'static str>);

    fn readable(&mut self, event_loop: &mut EventLoop<Echo>, token: Token, hint: ReadHint) {
        println!("Read {:?} {:?}", hint, token);

        let mut buf = ByteBuf::mut_with_capacity(4096 * 16);

        let ref mut non_block_client = match self.non_block_client.get_mut(&token.as_usize()) {
            Some(client) => client,
            None => panic!("Error finding the associated non blocking client {:?}", token)
        };
        let ref mut interest = match self.interest.get_mut(&token.as_usize()) {
            Some(interest) => interest,
            None => panic!("Error finding the associated interest {:?}", token)
        };
        match non_block_client.try_read_buf(&mut buf) {

            Ok(None) => {
                panic!("We just got readable, but were unable to read from the socket?");
            }
            Ok(Some(r)) => {
            	println!("r {} ", r);
                interest.remove(Interest::readable());
                if r == 0 {
                    println!("DONE");
                    //event_loop.deregister(*non_block_client).unwrap();
                    let mut_buf = match self.mut_buf.remove(&token.as_usize()) {
                        Some(mut_buf) => mut_buf,
                        None =>
                            panic!("Error finding the mut_buf for {:?} {:?}", token, self.mut_buf)
                    };

                    match self.sender.remove(&token.as_usize()) {
                        Some(completer) => {
                            completer.complete(Box::new(mut_buf));
                            interest.remove(Interest::none());
                        }
                        None => panic!("Error finding the mut_buf for {:?}", token)
                    };
                } else {
                    match self.mut_buf.get_mut(&token.as_usize()) {
                        Some(mut_buf) => mut_buf.push_all(buf.flip().bytes()),
                        None => panic!("Error finding the mut_buf for {:?}", token)
                    }

                    interest.insert(Interest::readable());
                    println!("1");
                    event_loop.reregister(*non_block_client, token, **interest, PollOpt::edge()).unwrap();
                    println!("2");
                }
            }
            Err(e) => {
                panic!("not implemented; client err={:?}", e);
            }
        }
    }

    fn writable(&mut self, event_loop: &mut EventLoop<Echo>, token: Token) {
        let ref mut action = match self.action.get_mut(&token.as_usize()) {
            Some(action) => action,
            None => panic!("Error finding the associated action {:?}", token)
        };
        let get_command: String = body(action.clone());

        let ref mut non_block_client = match self.non_block_client.get_mut(&token.as_usize()) {
            Some(client) => client,
            None => panic!("Error finding the associated non blocking client {:?}", token)
        };

        let ref mut interest = match self.interest.get_mut(&token.as_usize()) {
            Some(interest) => interest,
            None => panic!("Error finding the associated interest {:?}", token)
        };

        match **action {
            HttpAction::Get(_) => {
                let mut buf = ByteBuf::from_slice(get_command.as_bytes());
                match non_block_client.try_write_buf(&mut buf) {
                    Ok(None) => {
                        println!("client flushing buf; WOULDBLOCK");
                        self.buf = Some(buf);
                        interest.insert(Interest::writable());
                    }
                    Ok(Some(_)) => {
                        interest.insert(Interest::readable());
                        interest.remove(Interest::writable());
                        event_loop.reregister(*non_block_client, token, **interest,
                                              PollOpt::edge() | PollOpt::oneshot()).unwrap();
                    }
                    Err(e) => panic!("not implemented; client err={:?}", e),
                }
            }
        }
    }
    fn notify(&mut self,
              event_loop: &mut EventLoop<Echo>,
              tuple: (String, eventual::Complete<Box<Vec<u8>>, &'static str>)) {
        let token = Token(self.action.len() + 1);
        let action = get_action(tuple.0);
        self.action.insert(token.as_usize(), action.clone());

        match action {
            HttpAction::Get(url_p) => {
                let url: Url = (*url_p).clone();
                let ip = lookup_host(url.domain().unwrap()).unwrap().next().unwrap().unwrap();
                let port = url.port_or_default().unwrap();
                let address = SocketAddr::new(ip.ip(), port);
                let (sock, _) = TcpSocket::v4().unwrap().connect(&address).unwrap();
                self.non_block_client.insert(token.as_usize(), sock);
                self.sender.insert(token.as_usize(), tuple.1);
                self.mut_buf.insert(token.as_usize(), Vec::new());
                self.interest.insert(token.as_usize(), Interest::hup());
                event_loop.register_opt(self.non_block_client.get(&token.as_usize()).unwrap(),
                                        token, Interest::writable(),
                                        PollOpt::edge() ).unwrap();
            }
        }
    }
}

fn body(action: HttpAction) -> String {
    match action {
        HttpAction::Get(ref resource) => {
            format!(
                "GET {}  HTTP/1.1\r\nHost: {}\r\nUser-Agent: curl/7.37.1\r\nAccept */*\r\n\r\n",
                resource.serialize_path().unwrap(), resource.domain().unwrap())
        }
    }
}
fn get_action(url_s: String) -> HttpAction {
    let url = Url::parse(url_s.as_str()).unwrap();
    HttpAction::Get(Arc::new(url))
}<|endoftext|>


<commit_before>/* This file is part of Grust, GObject introspection bindings for Rust
 *
 * Copyright (C) 2013  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

extern mod grust (name="grust", vers="0.1");
extern mod gio (name="grust-Gio", vers="2.0");

extern mod std;

use core::result::{Result,Ok};
use core::comm::{Port,stream};
use core::task::TaskResult;
use core::util;
use std::timer::sleep;
// use std::timer::recv_timeout;
use std::uv_global_loop;
use grust::eventloop::EventLoop;

// We have to do this because of an rpath problem with crates linking to
// foreign libraries
extern mod grustna {
}

// Test timeout in milliseconds
static TEST_TIMEOUT: uint = 3000u;

fn spawn_with_future(func: ~fn()) -> Port<TaskResult> {
    let mut (port, _) = stream::<TaskResult>();
    task::task()
        .future_result(|p| {
            port = p;
        })
        .spawn(func);
    port
}

fn tcase(test: ~fn()) {
    grust::init();

    let port = spawn_with_future(test);

    match Some(port.recv()) /* recv_timeout(&uv_global_loop::get(), TEST_TIMEOUT, &port) */ {
        Some(task::Success) => {}
        Some(task::Failure) => {
            fail!(~"test failed");
        }
        None => {
            error!("test timed out");
            unsafe { libc::abort(); }
        }
    }
}

fn tcase_result(test: ~fn() -> Result<(),()>) {
    do tcase {
        let result = test();
        assert!(result == Ok(()));
    }
}

#[test]
fn simple() {
    do tcase {
        let fobj = gio::File::new_for_path("/dev/null");
        let f = fobj.interface() as &gio::File;
        assert!(f.get_path().to_str() == ~"/dev/null");
    }
}

#[test]
fn as_interface() {
    do tcase {
        gio::File::new_for_path("/dev/null").as_interface(|fobj| {
                let f = fobj as &gio::File;
                assert!(f.get_path().to_str() == ~"/dev/null");
            });
    }
}

#[test]
fn new_ref() {
    do tcase {
        let fobj = gio::File::new_for_path("/dev/null");
        let gobj = fobj.interface().new_ref();
        let g = gobj.interface() as &gio::File;
        assert!(g.get_path().to_str() == ~"/dev/null");
    }
}

#[test]
fn clone() {
    do tcase {
        let fobj = gio::File::new_for_path("/dev/null");
        fobj.clone().as_interface(|gobj| {
                let g = gobj as &gio::File;
                assert!(g.get_path().to_str() == ~"/dev/null");
            });
    }
}

#[test]
fn off_task() {
    do tcase_result {
        let f = ~gio::File::new_for_path("/dev/null");
        do task::try {
            let f = f.interface() as &gio::File;
            let p = f.get_path();
            assert!(p.to_str() == ~"/dev/null");
        }
    }
}

#[test]
fn off_task_as_interface() {
    do tcase_result {
        let fobj = ~gio::File::new_for_path("/dev/null");
        do task::try {
            fobj.as_interface(|fifa| {
                    let f = fifa as &gio::File;
                    let p = f.get_path();
                    assert!(p.to_str() == ~"/dev/null");
                });
        }
    }
}

#[test]
fn async() {
    do tcase {
        let fobj = gio::File::new_for_path("/dev/null");
        let f = fobj.interface() as &gio::File;
        let el = EventLoop::new();
        let elo = ~el.clone();
        f.read_async(0, None,
                |obj, res| {
                    let f: &gio::File = obj.cast::<gio::raw::GFile>()
                                        as &gio::File;
                    let in = f.read_finish(res);
                    util::ignore(in);
                    elo.quit();
                });
        el.run();
    }
}

#[test]
fn async_off_stack() {
    do tcase {
        let fobj = ~gio::File::new_for_path("/dev/null");
        let el = EventLoop::new();
        let elo = ~el.clone();
        task::spawn_sched(task::ThreadPerCore, || {
            let f = fobj.interface() as &gio::File;

            // Give the event loop time to start
            sleep(&uv_global_loop::get(), 100);

            let elo2 = ~elo.clone();
            f.read_async(0, None, |obj, res| {
                    let f = obj.cast::<gio::raw::GFile>() as &gio::File;
                    f.read_finish(res);
                    elo2.quit();
                });
        });
        el.run();
    }
}
<commit_msg>Document a test limitation<commit_after>/* This file is part of Grust, GObject introspection bindings for Rust
 *
 * Copyright (C) 2013  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

extern mod grust (name="grust", vers="0.1");
extern mod gio (name="grust-Gio", vers="2.0");

extern mod std;

use core::result::{Result,Ok};
use core::comm::{Port,stream};
use core::task::TaskResult;
use core::util;
use std::timer::sleep;
// use std::timer::recv_timeout;
use std::uv_global_loop;
use grust::eventloop::EventLoop;

// We have to do this because of an rpath problem with crates linking to
// foreign libraries
extern mod grustna {
}

// Test timeout in milliseconds
static TEST_TIMEOUT: uint = 3000u;

fn spawn_with_future(func: ~fn()) -> Port<TaskResult> {
    let mut (port, _) = stream::<TaskResult>();
    task::task()
        .future_result(|p| {
            port = p;
        })
        .spawn(func);
    port
}

fn tcase(test: ~fn()) {
    grust::init();

    let port = spawn_with_future(test);

    // recv_timeout is broken, see https://github.com/mozilla/rust/issues/6089
    match Some(port.recv()) /* recv_timeout(&uv_global_loop::get(), TEST_TIMEOUT, &port) */ {
        Some(task::Success) => {}
        Some(task::Failure) => {
            fail!(~"test failed");
        }
        None => {
            error!("test timed out");
            unsafe { libc::abort(); }
        }
    }
}

fn tcase_result(test: ~fn() -> Result<(),()>) {
    do tcase {
        let result = test();
        assert!(result == Ok(()));
    }
}

#[test]
fn simple() {
    do tcase {
        let fobj = gio::File::new_for_path("/dev/null");
        let f = fobj.interface() as &gio::File;
        assert!(f.get_path().to_str() == ~"/dev/null");
    }
}

#[test]
fn as_interface() {
    do tcase {
        gio::File::new_for_path("/dev/null").as_interface(|fobj| {
                let f = fobj as &gio::File;
                assert!(f.get_path().to_str() == ~"/dev/null");
            });
    }
}

#[test]
fn new_ref() {
    do tcase {
        let fobj = gio::File::new_for_path("/dev/null");
        let gobj = fobj.interface().new_ref();
        let g = gobj.interface() as &gio::File;
        assert!(g.get_path().to_str() == ~"/dev/null");
    }
}

#[test]
fn clone() {
    do tcase {
        let fobj = gio::File::new_for_path("/dev/null");
        fobj.clone().as_interface(|gobj| {
                let g = gobj as &gio::File;
                assert!(g.get_path().to_str() == ~"/dev/null");
            });
    }
}

#[test]
fn off_task() {
    do tcase_result {
        let f = ~gio::File::new_for_path("/dev/null");
        do task::try {
            let f = f.interface() as &gio::File;
            let p = f.get_path();
            assert!(p.to_str() == ~"/dev/null");
        }
    }
}

#[test]
fn off_task_as_interface() {
    do tcase_result {
        let fobj = ~gio::File::new_for_path("/dev/null");
        do task::try {
            fobj.as_interface(|fifa| {
                    let f = fifa as &gio::File;
                    let p = f.get_path();
                    assert!(p.to_str() == ~"/dev/null");
                });
        }
    }
}

#[test]
fn async() {
    do tcase {
        let fobj = gio::File::new_for_path("/dev/null");
        let f = fobj.interface() as &gio::File;
        let el = EventLoop::new();
        let elo = ~el.clone();
        f.read_async(0, None,
                |obj, res| {
                    let f: &gio::File = obj.cast::<gio::raw::GFile>()
                                        as &gio::File;
                    let in = f.read_finish(res);
                    util::ignore(in);
                    elo.quit();
                });
        el.run();
    }
}

#[test]
fn async_off_stack() {
    do tcase {
        let fobj = ~gio::File::new_for_path("/dev/null");
        let el = EventLoop::new();
        let elo = ~el.clone();
        task::spawn_sched(task::ThreadPerCore, || {
            let f = fobj.interface() as &gio::File;

            // Give the event loop time to start
            sleep(&uv_global_loop::get(), 100);

            let elo2 = ~elo.clone();
            f.read_async(0, None, |obj, res| {
                    let f = obj.cast::<gio::raw::GFile>() as &gio::File;
                    f.read_finish(res);
                    elo2.quit();
                });
        });
        el.run();
    }
}
<|endoftext|>


<commit_before>use std::net::IpAddr;
use std::io::{Write, ErrorKind};
use std::io::Read;

use mio::*;
use mio::tcp::TcpStream;
use mio::unix::UnixReady;
use nom::IResult::*;
use nom::Offset;
use network::protocol::proxy_protocol::header;
use network::{Protocol, ClientResult};
use network::Readiness;
use network::protocol::ProtocolResult;
use network::socket::{SocketHandler, SocketResult};
use network::buffer_queue::BufferQueue;
use network::SessionMetrics;
use network::protocol::pipe::Pipe;
use network::pool::Checkout;
use parser::proxy_protocol::parse_v2_header;
use super::header::ProxyAddr;

pub struct ExpectProxyProtocol<Front:SocketHandler> {
  pub frontend:       Front,
  pub frontend_token: Token,
  pub front_buf:      Checkout<BufferQueue>,
  pub readiness:      Readiness,
  pub addresses:      Option<ProxyAddr>,
}

impl <Front:SocketHandler + Read>ExpectProxyProtocol<Front> {
  pub fn new(frontend: Front, frontend_token: Token, front_buf: Checkout<BufferQueue>) -> Self {
    println!("expect starting, connection from {:?}", frontend.socket_ref().peer_addr());
    ExpectProxyProtocol {
      frontend,
      frontend_token,
      front_buf,
      readiness: Readiness {
        front_interest:  UnixReady::from(Ready::readable()) | UnixReady::hup() | UnixReady::error(),
        back_interest:   UnixReady::hup() | UnixReady::error(),
        front_readiness: UnixReady::from(Ready::empty()),
        back_readiness:  UnixReady::from(Ready::empty()),
      },
      addresses: None,
    }
  }

  pub fn readable(&mut self, metrics: &mut SessionMetrics) -> (ProtocolResult, ClientResult) {
    let (sz, res) = self.frontend.socket_read(self.front_buf.buffer.space());
    info!("FRONT proxy protocol [{:?}]: read {} bytes and res={:?}", self.frontend_token, sz, res);

    if sz > 0 {
      self.front_buf.buffer.fill(sz);
      self.front_buf.sliced_input(sz);

      count!("bytes_in", sz as i64);
      metrics.bin += sz;

      if res == SocketResult::Error {
        error!("[{:?}] front socket error, closing the connection", self.frontend_token);
        metrics.service_stop();
        incr!("proxy_protocol.errors");
        self.readiness.reset();
        return (ProtocolResult::Continue, ClientResult::CloseClient);
      }

      if res == SocketResult::WouldBlock {
        self.readiness.front_readiness.remove(Ready::readable());
      }

      let read_sz = match parse_v2_header(self.front_buf.unparsed_data()) {
        Done(rest, header) => {
          self.addresses = Some(header.addr);
          self.front_buf.next_output_data().offset(rest)
        },
        Incomplete(_) => {
          return (ProtocolResult::Continue, ClientResult::Continue)
        },
        Error(e) => {
          return (ProtocolResult::Continue, ClientResult::CloseClient)
        }
      };

      self.front_buf.consume_parsed_data(read_sz);
      self.front_buf.delete_output(read_sz);
      info!("read {} bytes of proxy protocol, {} remaining", read_sz, self.front_buf.available_input_data());
      return (ProtocolResult::Upgrade, ClientResult::Continue)
    }

    return (ProtocolResult::Continue, ClientResult::Continue);
  }

  pub fn front_socket(&self) -> &TcpStream {
    self.frontend.socket_ref()
  }

  pub fn readiness(&mut self) -> &mut Readiness {
    &mut self.readiness
  }

  pub fn into_pipe(self, back_buf: Checkout<BufferQueue>, backend_socket: Option<TcpStream>, backend_token: Option<Token>) -> Pipe<Front> {
    let addr = if let Some(ref backend_socket) = backend_socket {
      backend_socket.peer_addr().map(|s| s.ip()).ok()
    } else {
      None
    };

    let mut pipe = Pipe::new(
      self.frontend,
      self.frontend_token,
      backend_socket,
      self.front_buf,
      back_buf,
      addr,
    );

    pipe.readiness.front_readiness = self.readiness.front_readiness;
    pipe.readiness.back_readiness  = self.readiness.back_readiness;

    if let Some(backend_token) = backend_token {
      pipe.set_back_token(backend_token);
    }

    pipe
  }
}

#[cfg(test)]
mod expect_test {

 use super::*;

  use parser::proxy_protocol::parse_v2_header;
  use nom::IResult::Done;
  use network::pool::Pool;

  use std::{sync::{Arc, Barrier}, thread::{self, JoinHandle}, time::Duration, net::{SocketAddr, IpAddr, Ipv4Addr}};
  use mio::net::{TcpListener, TcpStream};
  use std::net::{TcpListener as StdTcpListener, TcpStream as StdTcpStream};

  use network::protocol::proxy_protocol::header::*;

  // Flow diagram of the test below
  //                [connect]   [send proxy protocol]
  //upfront proxy  ----------------------X
  //              /     |           |
  //  sozu     ---------v-----------v----X
  #[test]
  fn middleware_should_receive_proxy_protocol_header_from_an_upfront_middleware() {
    setup_test_logger!();
    let middleware_addr: SocketAddr = "127.0.0.1:3500".parse().expect("parse address error");
    let barrier = Arc::new(Barrier::new(2));

    let upfront = start_upfront_middleware(middleware_addr.clone(), barrier.clone());
    start_middleware(middleware_addr, barrier);

    upfront.join().expect("should join");
  }

  // Accept connection from an upfront proxy and expect to read a proxy protocol header in this stream.
  fn start_middleware(middleware_addr: SocketAddr, barrier: Arc<Barrier>) {
    let upfront_middleware_conn_listener = TcpListener::bind(&middleware_addr).expect("could not accept upfront middleware connection");
    let mut client_stream: Option<TcpStream> = None;
    barrier.wait();

    // mio::TcpListener use a nonblocking mode so we have to loop on accept
    loop {
      if let Ok((stream, _addr)) = upfront_middleware_conn_listener.accept() {
        client_stream = Some(stream);
        break;
      }
    }

    let mut session_metrics = SessionMetrics::new();
    let mut pool = Pool::with_capacity(1, 0, || BufferQueue::with_capacity(16384));
    let mut front_buf = pool.checkout().unwrap();
    let mut expect_pp = ExpectProxyProtocol::new(client_stream.unwrap(), Token(0), front_buf);

    if (ProtocolResult::Upgrade, ClientResult::Continue) != expect_pp.readable(&mut session_metrics) {
      panic!("Should receive a complete proxy protocol header");
    };
  }

  // Connect to the next middleware and send a proxy protocol header
  fn start_upfront_middleware(next_middleware_addr: SocketAddr, barrier: Arc<Barrier>) -> JoinHandle<()> {
    thread::spawn(move|| {
      let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
      let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
      let proxy_protocol = HeaderV2::new(Command::Local, src_addr, dst_addr).into_bytes();

      barrier.wait();
      match StdTcpStream::connect(&next_middleware_addr) {
        Ok(mut stream) => {
          stream.write(&proxy_protocol).unwrap();
        },
        Err(e) => panic!("could not connect to the next middleware: {}", e),
      };
    })
  }
}
<commit_msg>fix IO handling in expect proxy protocol<commit_after>use std::net::IpAddr;
use std::io::{Write, ErrorKind};
use std::io::Read;

use mio::*;
use mio::tcp::TcpStream;
use mio::unix::UnixReady;
use nom::IResult::*;
use nom::Offset;
use network::protocol::proxy_protocol::header;
use network::{Protocol, ClientResult};
use network::Readiness;
use network::protocol::ProtocolResult;
use network::socket::{SocketHandler, SocketResult};
use network::buffer_queue::BufferQueue;
use network::SessionMetrics;
use network::protocol::pipe::Pipe;
use network::pool::Checkout;
use parser::proxy_protocol::parse_v2_header;
use super::header::ProxyAddr;

pub struct ExpectProxyProtocol<Front:SocketHandler> {
  pub frontend:       Front,
  pub frontend_token: Token,
  pub front_buf:      Checkout<BufferQueue>,
  pub readiness:      Readiness,
  pub addresses:      Option<ProxyAddr>,
}

impl <Front:SocketHandler + Read>ExpectProxyProtocol<Front> {
  pub fn new(frontend: Front, frontend_token: Token, front_buf: Checkout<BufferQueue>) -> Self {
    println!("expect starting, connection from {:?}", frontend.socket_ref().peer_addr());
    ExpectProxyProtocol {
      frontend,
      frontend_token,
      front_buf,
      readiness: Readiness {
        front_interest:  UnixReady::from(Ready::readable()) | UnixReady::hup() | UnixReady::error(),
        back_interest:   UnixReady::hup() | UnixReady::error(),
        front_readiness: UnixReady::from(Ready::empty()),
        back_readiness:  UnixReady::from(Ready::empty()),
      },
      addresses: None,
    }
  }

  pub fn readable(&mut self, metrics: &mut SessionMetrics) -> (ProtocolResult, ClientResult) {
    let (sz, res) = self.frontend.socket_read(self.front_buf.buffer.space());
    info!("FRONT proxy protocol [{:?}]: read {} bytes and res={:?}", self.frontend_token, sz, res);

    if sz > 0 {
      self.front_buf.buffer.fill(sz);
      self.front_buf.sliced_input(sz);

      count!("bytes_in", sz as i64);
      metrics.bin += sz;

      if self.front_buf.buffer.available_space() == 0 {
        self.readiness.front_interest.remove(Ready::readable());
      }
    }

    if res == SocketResult::Error {
      error!("[{:?}] front socket error, closing the connection", self.frontend_token);
      metrics.service_stop();
      incr!("proxy_protocol.errors");
      self.readiness.reset();
      return (ProtocolResult::Continue, ClientResult::CloseClient);
    }

    if res == SocketResult::WouldBlock {
      self.readiness.front_readiness.remove(Ready::readable());
    }

    let read_sz = match parse_v2_header(self.front_buf.unparsed_data()) {
      Done(rest, header) => {
        self.addresses = Some(header.addr);
        self.front_buf.next_output_data().offset(rest)
      },
      Incomplete(_) => {
        return (ProtocolResult::Continue, ClientResult::Continue)
      },
      Error(e) => {
        error!("[{:?}] front socket parse error, closing the connection", self.frontend_token);
        metrics.service_stop();
        incr!("proxy_protocol.errors");
        self.readiness.reset();
        return (ProtocolResult::Continue, ClientResult::CloseClient)
      }
    };

    self.front_buf.consume_parsed_data(read_sz);
    self.front_buf.delete_output(read_sz);
    info!("read {} bytes of proxy protocol, {} remaining", read_sz, self.front_buf.available_input_data());
    (ProtocolResult::Upgrade, ClientResult::Continue)
  }

  pub fn front_socket(&self) -> &TcpStream {
    self.frontend.socket_ref()
  }

  pub fn readiness(&mut self) -> &mut Readiness {
    &mut self.readiness
  }

  pub fn into_pipe(self, back_buf: Checkout<BufferQueue>, backend_socket: Option<TcpStream>, backend_token: Option<Token>) -> Pipe<Front> {
    let addr = if let Some(ref backend_socket) = backend_socket {
      backend_socket.peer_addr().map(|s| s.ip()).ok()
    } else {
      None
    };

    let mut pipe = Pipe::new(
      self.frontend,
      self.frontend_token,
      backend_socket,
      self.front_buf,
      back_buf,
      addr,
    );

    pipe.readiness.front_readiness = self.readiness.front_readiness;
    pipe.readiness.back_readiness  = self.readiness.back_readiness;

    if let Some(backend_token) = backend_token {
      pipe.set_back_token(backend_token);
    }

    pipe
  }
}

#[cfg(test)]
mod expect_test {

 use super::*;

  use parser::proxy_protocol::parse_v2_header;
  use nom::IResult::Done;
  use network::pool::Pool;

  use std::{sync::{Arc, Barrier}, thread::{self, JoinHandle}, time::Duration, net::{SocketAddr, IpAddr, Ipv4Addr}};
  use mio::net::{TcpListener, TcpStream};
  use std::net::{TcpListener as StdTcpListener, TcpStream as StdTcpStream};

  use network::protocol::proxy_protocol::header::*;

  // Flow diagram of the test below
  //                [connect]   [send proxy protocol]
  //upfront proxy  ----------------------X
  //              /     |           |
  //  sozu     ---------v-----------v----X
  #[test]
  fn middleware_should_receive_proxy_protocol_header_from_an_upfront_middleware() {
    setup_test_logger!();
    let middleware_addr: SocketAddr = "127.0.0.1:3500".parse().expect("parse address error");
    let barrier = Arc::new(Barrier::new(2));

    let upfront = start_upfront_middleware(middleware_addr.clone(), barrier.clone());
    start_middleware(middleware_addr, barrier);

    upfront.join().expect("should join");
  }

  // Accept connection from an upfront proxy and expect to read a proxy protocol header in this stream.
  fn start_middleware(middleware_addr: SocketAddr, barrier: Arc<Barrier>) {
    let upfront_middleware_conn_listener = TcpListener::bind(&middleware_addr).expect("could not accept upfront middleware connection");
    let mut client_stream: Option<TcpStream> = None;
    barrier.wait();

    // mio::TcpListener use a nonblocking mode so we have to loop on accept
    loop {
      if let Ok((stream, _addr)) = upfront_middleware_conn_listener.accept() {
        client_stream = Some(stream);
        break;
      }
    }

    let mut session_metrics = SessionMetrics::new();
    let mut pool = Pool::with_capacity(1, 0, || BufferQueue::with_capacity(16384));
    let mut front_buf = pool.checkout().unwrap();
    let mut expect_pp = ExpectProxyProtocol::new(client_stream.unwrap(), Token(0), front_buf);

    if (ProtocolResult::Upgrade, ClientResult::Continue) != expect_pp.readable(&mut session_metrics) {
      panic!("Should receive a complete proxy protocol header");
    };
  }

  // Connect to the next middleware and send a proxy protocol header
  fn start_upfront_middleware(next_middleware_addr: SocketAddr, barrier: Arc<Barrier>) -> JoinHandle<()> {
    thread::spawn(move|| {
      let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
      let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
      let proxy_protocol = HeaderV2::new(Command::Local, src_addr, dst_addr).into_bytes();

      barrier.wait();
      match StdTcpStream::connect(&next_middleware_addr) {
        Ok(mut stream) => {
          stream.write(&proxy_protocol).unwrap();
        },
        Err(e) => panic!("could not connect to the next middleware: {}", e),
      };
    })
  }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate arg_parser;
extern crate coreutils;
extern crate extra;

use std::env;
use std::fs::File;
use std::io::{stderr, stdin, stdout, Read, Write};
use std::time::Instant;
use std::process::exit;
use arg_parser::ArgParser;
use coreutils::to_human_readable_string;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{dd} */ r#"
NAME
    dd - copy a file

SYNOPSIS
    dd [ -h | --help ] if=[FILE] of=[FILE]

DESCRIPTION
    The dd tool copies from a file to another file in 512-byte block sizes

OPTIONS
    -h
    --help
        display this help and exit

    bs=n
        set input and output block size to n bytes
    count=n
        copy only n blocks
    if=file
        read from file instead of standard input
    of=file
        write output to file instead of standard out

"#; /* @MANEND */

fn main() {
    let stdin = stdin();
    let stdin = stdin.lock();
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    let mut parser = ArgParser::new(5)
        .add_flag(&["h", "help"])
        .add_setting_default("bs", "512")
        .add_setting_default("count", "-1")
        .add_setting("if")
        .add_setting("of");
    parser.parse(env::args());

    if parser.found("help") {
        stdout.write(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        exit(0);
    }

    let bs: usize = parser.get_setting("bs").unwrap().parse::<usize>().unwrap();
    let count = parser.get_setting("count").unwrap().parse::<i32>().unwrap();

    let mut input: Box<Read> = match parser.found("if") {
        true => {
            let path = parser.get_setting("if").unwrap();
            Box::new(File::open(path).expect("dd: failed to open if"))
        },
        false => Box::new(stdin),
    };

    let mut output: Box<Write> = match parser.found("of") {
        true => {
            let path = parser.get_setting("of").unwrap();
            Box::new(File::create(path).expect("dd: failed to open of"))
        },
        false => Box::new(stdout),
    };

    let status = 1;
    let mut in_recs = 0;
    let mut in_extra = 0;
    let mut out_recs = 0;
    let mut out_extra = 0;
    let mut out_total = 0;
    let mut running = true;
    let mut buffer = vec![0; bs];
    let mut last_print = 0;
    let mut last_print_out = 0;
    let start = Instant::now();
    while running {
        let in_count = input.read(&mut buffer).expect("dd: failed to read if");
        if in_count < bs {
            if in_count > 0 {
                in_extra += 1;
            }
            running = false;
        } else {
            in_recs += 1;
            if count != -1 {
                if in_recs >= count {
                    running = false;
                }
            }
        }

        let out_count = output.write(&buffer[.. in_count]).expect("dd: failed to write of");
        if out_count < bs {
            if out_count > 0 {
                out_extra += 1;
            }
            running = false;
        } else {
            out_recs += 1;
        }

        out_total += out_count as u64;

        if status >= 2 {
            let elapsed = start.elapsed().as_secs();
            if elapsed > last_print {
                let _ = write!(stderr, "\r\x1B[K{} bytes ({}B) copied, {} s, {}B/s", out_total, to_human_readable_string(out_total), elapsed, to_human_readable_string(out_total - last_print_out));
                let _ = stderr.flush();
                last_print = elapsed;
                last_print_out = out_total;
            }
        }
    }

    if status >= 1 {
        let elapsed_duration = start.elapsed();
        let elapsed = elapsed_duration.as_secs() as f64 + (elapsed_duration.subsec_nanos() as f64)/1000000000.0;

        if status >= 2 && last_print > 0 {
            let _ = writeln!(stderr, "");
        }

        let _ = writeln!(stderr, "{}+{} records in", in_recs, in_extra);
        let _ = writeln!(stderr, "{}+{} records out", out_recs, out_extra);
        let _ = writeln!(stderr, "{} bytes ({}B) copied, {} s, {}B/s", out_total, to_human_readable_string(out_total), elapsed, to_human_readable_string((out_total as f64/elapsed) as u64));
    }
}
<commit_msg>dd: Fix status option<commit_after>#![deny(warnings)]

extern crate arg_parser;
extern crate coreutils;
extern crate extra;

use std::env;
use std::fs::File;
use std::io::{stderr, stdin, stdout, Read, Write};
use std::time::Instant;
use std::process::exit;
use arg_parser::ArgParser;
use coreutils::to_human_readable_string;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{dd} */ r#"
NAME
    dd - copy a file

SYNOPSIS
    dd [ -h | --help ] if=[FILE] of=[FILE]

DESCRIPTION
    The dd tool copies from a file to another file in 512-byte block sizes

OPTIONS
    -h
    --help
        display this help and exit

    bs=n
        set input and output block size to n bytes
    count=n
        copy only n blocks
    if=file
        read from file instead of standard input
    of=file
        write output to file instead of standard out

"#; /* @MANEND */

fn main() {
    let stdin = stdin();
    let stdin = stdin.lock();
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    let mut parser = ArgParser::new(6)
        .add_flag(&["h", "help"])
        .add_setting_default("bs", "512")
        .add_setting_default("count", "-1")
        .add_setting_default("status", "1")
        .add_setting("if")
        .add_setting("of");
    parser.parse(env::args());

    if parser.found("help") {
        stdout.write(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        exit(0);
    }

    let bs: usize = parser.get_setting("bs").unwrap().parse::<usize>().unwrap();
    let count = parser.get_setting("count").unwrap().parse::<i32>().unwrap();
    let status = parser.get_setting("status").unwrap().parse::<usize>().unwrap();

    let mut input: Box<Read> = match parser.found("if") {
        true => {
            let path = parser.get_setting("if").unwrap();
            Box::new(File::open(path).expect("dd: failed to open if"))
        },
        false => Box::new(stdin),
    };

    let mut output: Box<Write> = match parser.found("of") {
        true => {
            let path = parser.get_setting("of").unwrap();
            Box::new(File::create(path).expect("dd: failed to open of"))
        },
        false => Box::new(stdout),
    };

    let mut in_recs = 0;
    let mut in_extra = 0;
    let mut out_recs = 0;
    let mut out_extra = 0;
    let mut out_total = 0;
    let mut running = true;
    let mut buffer = vec![0; bs];
    let mut last_print = 0;
    let mut last_print_out = 0;
    let start = Instant::now();
    while running {
        let in_count = input.read(&mut buffer).expect("dd: failed to read if");
        if in_count < bs {
            if in_count > 0 {
                in_extra += 1;
            }
            running = false;
        } else {
            in_recs += 1;
            if count != -1 {
                if in_recs >= count {
                    running = false;
                }
            }
        }

        let out_count = output.write(&buffer[.. in_count]).expect("dd: failed to write of");
        if out_count < bs {
            if out_count > 0 {
                out_extra += 1;
            }
            running = false;
        } else {
            out_recs += 1;
        }

        out_total += out_count as u64;

        if status >= 2 {
            let elapsed = start.elapsed().as_secs();
            if elapsed > last_print {
                let _ = write!(stderr, "\r\x1B[K{} bytes ({}B) copied, {} s, {}B/s", out_total, to_human_readable_string(out_total), elapsed, to_human_readable_string(out_total - last_print_out));
                let _ = stderr.flush();
                last_print = elapsed;
                last_print_out = out_total;
            }
        }
    }

    if status >= 1 {
        let elapsed_duration = start.elapsed();
        let elapsed = elapsed_duration.as_secs() as f64 + (elapsed_duration.subsec_nanos() as f64)/1000000000.0;

        if status >= 2 && last_print > 0 {
            let _ = writeln!(stderr, "");
        }

        let _ = writeln!(stderr, "{}+{} records in", in_recs, in_extra);
        let _ = writeln!(stderr, "{}+{} records out", out_recs, out_extra);
        let _ = writeln!(stderr, "{} bytes ({}B) copied, {} s, {}B/s", out_total, to_human_readable_string(out_total), elapsed, to_human_readable_string((out_total as f64/elapsed) as u64));
    }
}
<|endoftext|>


<commit_before>// Copyright 2016-2019 James Bostock. See the LICENSE file at the top-level
// directory of this distribution.

// An implementation of the od(1) command in Rust.
// See http://man.cat-v.org/unix-6th/1/od
use std::env;
use std::io;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Stdout;
use std::io::Write;
use std::num::ParseIntError;

mod util;

/// Writes a chunk of output data as octal byte values.
fn write_oct_bytes(out: &mut BufWriter<Stdout>, data: &[u8])
                   -> io::Result<usize> {
    for byte in data {
        write!(out, " {:03o}", byte)?;
    }
    writeln!(out)?;
    Ok(data.len())
}

/// Writes a chunk of output data as octal (16 bit) word values. Words are
/// assumed to be little endian.
fn write_oct_words(out: &mut BufWriter<Stdout>, data: &[u8])
                   -> io::Result<usize> {
    for word in data.chunks(2) {
        write!(out, "  {:06o}", u16::from(word[1]) << 8 | u16::from(word[0]))?;
    }
    writeln!(out)?;
    Ok(data.len())
}

/// Writes a chunk of output data as decimal (16 bit) word values. Words are
/// assumed to be little endian.
fn write_dec_words(out: &mut BufWriter<Stdout>, data: &[u8])
                   -> io::Result<usize> {
    for word in data.chunks(2) {
        write!(out, "  {:06}", u16::from(word[1]) << 8 | u16::from(word[0]))?;
    }
    writeln!(out)?;
    Ok(data.len())
}

/// Writes a chunk of output data as hexadecimal (16 bit) word values. Words
/// are assumed to be little endian.
fn write_hex_words(out: &mut BufWriter<Stdout>, data: &[u8])
                   -> io::Result<usize> {
    for word in data.chunks(2) {
        write!(out, "  {:06x}", u16::from(word[1]) << 8 | u16::from(word[0]))?;
    }
    writeln!(out)?;
    Ok(data.len())
}

/// Writes a chunk of data as ASCII, reverting to octal byte values for
/// non-printable characters. Standard escape sequences are supported.
fn write_ascii_chars(out: &mut BufWriter<Stdout>, data: &[u8])
                     -> io::Result<usize> {
    for byte in data {
        match *byte {
            7u8 => write!(out, " \\g")?,
            8u8 => write!(out, " \\b")?,
            9u8 => write!(out, " \\t")?,
            10u8 => write!(out, " \\n")?,
            11u8 => write!(out, " \\v")?,
            12u8 => write!(out, " \\f")?,
            13u8 => write!(out, " \\r")?,
            _ => if *byte < 32u8 || *byte > 126u8 {
                write!(out, " {:03o}", *byte)?
            } else {
                write!(out, "   {}", *byte as char)?
            }
        }
    }
    writeln!(out)?;
    Ok(data.len())
}

const CHUNK_SIZE: usize = 16;

// The offset string is of the form [+]offset[.][b]
// +100 => 0o100
// +100. => 100
// +100b => 0o100 * 512
// +100.b => 100 * 512
fn parse_offset(offstr: &str) -> Result<u64, ParseIntError> {
    let mut char_indices = offstr.char_indices().rev();
    let mut mult = 1;
    let (s, r) = match char_indices.next() {
        Some((x, 'b')) => {
            mult = 512;
            match char_indices.next() {
                Some((y, '.')) => (&offstr[0..y], 10),
                Some((_, _)) => (&offstr[0..x], 8),
                None => (&offstr[0..0], 8)
            }
        },
        Some((x, '.')) => (&offstr[0..x], 10),
        Some((_, _)) => (&offstr[..], 8),
        None => (&offstr[0..0], 8)
    };

    match u64::from_str_radix(s, r) {
        Ok(n) => Ok(n * mult),
        Err(e) => Err(e)
    }
}

#[test]
fn test_parse_offset() {
    match parse_offset("100") {
        Ok(off) => assert!(off == 0o100),
        Err(_) => assert!(false)
    }

    match parse_offset("100.") {
        Ok(off) => assert!(off == 100),
        Err(_) => assert!(false)
    }

    match parse_offset("100b") {
        Ok(off) => assert!(off == 0o100 * 512),
        Err(_) => assert!(false)
    }

    match parse_offset("100.b") {
        Ok(off) => assert!(off == 100 * 512),
        Err(_) => assert!(false)
    }
}

/// Dumps the data read from the named input source to the standard output.
fn od(filename: &str, offset: u64,
      fmts: &[fn(&mut BufWriter<Stdout>,&[u8]) -> io::Result<usize>])
      -> io::Result<u64> {
    let mut reader = BufReader::new(util::Input::open(filename)?);
    let mut writer = BufWriter::new(io::stdout());
    let mut offset = offset;

    if offset > 0 {
        reader.seek(SeekFrom::Start(offset))?;
    }

    let mut chunk = [0; CHUNK_SIZE];
    loop {
        let n = reader.read(&mut chunk)?;
        if n > 0 {
            let mut first = true;
            for fmt in fmts.iter() {
                if first {
                    write!(writer, "{:07o}", offset)?;
                    first = false;
                } else {
                    write!(writer, "       ")?;
                }
                fmt(&mut writer, &chunk)?;
                offset += chunk.len() as u64;
            }
        }

        if n < CHUNK_SIZE {
            break
        }
    }
    writeln!(writer, "{:07o}", offset)?;
    Ok(offset)
}

fn main() {
    let args: Vec<_> = env::args().skip(1).collect();
    let mut idx = 0;
    let mut offset : u64 = 0;
    let mut offstr = "0";
    let mut fmts: Vec<fn(&mut BufWriter<Stdout>, &[u8]) -> io::Result<usize>> = Vec::new();

    if args.len() > idx && args[idx].starts_with('-') {
        for opt in args[idx].chars().skip(1) {
            match opt {
                'b' => fmts.push(write_oct_bytes),
                'c' => fmts.push(write_ascii_chars),
                'd' => fmts.push(write_dec_words),
                'h' => fmts.push(write_hex_words),
                'o' => fmts.push(write_oct_words),
                _ => println!("-{}: unrecognised option", opt),
            }
        }
        idx += 1;
    }

    // If no output formats have been specified, default to octal words.
    if fmts.is_empty() {
        fmts.push(write_oct_words);
    }

    let mut filename = String::from("-");
    if args.len() > idx {
        if args[idx].starts_with('+') {
            offstr = &args[idx][..];
        } else {
            filename = String::from(&args[idx][..]);
            idx += 1;
        }
    }

    if args.len() > idx {
        offstr = &args[idx][..];
    }

    match parse_offset(&offstr) {
        Ok(off) => offset = off,
        Err(e) => println!("{}: {}", offstr, e)
    }

    match od(&filename, offset, &fmts) {
        Ok(_) => std::process::exit(0),
        Err(e) => {
            eprintln!("Error: {}", e);
            std::process::exit(1)
        }
    }
}
<commit_msg>Add a type alias for format functions.<commit_after>// Copyright 2016-2019 James Bostock. See the LICENSE file at the top-level
// directory of this distribution.

// An implementation of the od(1) command in Rust.
// See http://man.cat-v.org/unix-6th/1/od
use std::env;
use std::io;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Stdout;
use std::io::Write;
use std::num::ParseIntError;

mod util;

type FmtFn = fn(&mut BufWriter<Stdout>, &[u8]) -> io::Result<usize>;

/// Writes a chunk of output data as octal byte values.
fn write_oct_bytes(out: &mut BufWriter<Stdout>, data: &[u8])
                   -> io::Result<usize> {
    for byte in data {
        write!(out, " {:03o}", byte)?;
    }
    writeln!(out)?;
    Ok(data.len())
}

/// Writes a chunk of output data as octal (16 bit) word values. Words are
/// assumed to be little endian.
fn write_oct_words(out: &mut BufWriter<Stdout>, data: &[u8])
                   -> io::Result<usize> {
    for word in data.chunks(2) {
        write!(out, "  {:06o}", u16::from(word[1]) << 8 | u16::from(word[0]))?;
    }
    writeln!(out)?;
    Ok(data.len())
}

/// Writes a chunk of output data as decimal (16 bit) word values. Words are
/// assumed to be little endian.
fn write_dec_words(out: &mut BufWriter<Stdout>, data: &[u8])
                   -> io::Result<usize> {
    for word in data.chunks(2) {
        write!(out, "  {:06}", u16::from(word[1]) << 8 | u16::from(word[0]))?;
    }
    writeln!(out)?;
    Ok(data.len())
}

/// Writes a chunk of output data as hexadecimal (16 bit) word values. Words
/// are assumed to be little endian.
fn write_hex_words(out: &mut BufWriter<Stdout>, data: &[u8])
                   -> io::Result<usize> {
    for word in data.chunks(2) {
        write!(out, "  {:06x}", u16::from(word[1]) << 8 | u16::from(word[0]))?;
    }
    writeln!(out)?;
    Ok(data.len())
}

/// Writes a chunk of data as ASCII, reverting to octal byte values for
/// non-printable characters. Standard escape sequences are supported.
fn write_ascii_chars(out: &mut BufWriter<Stdout>, data: &[u8])
                     -> io::Result<usize> {
    for byte in data {
        match *byte {
            7u8 => write!(out, " \\g")?,
            8u8 => write!(out, " \\b")?,
            9u8 => write!(out, " \\t")?,
            10u8 => write!(out, " \\n")?,
            11u8 => write!(out, " \\v")?,
            12u8 => write!(out, " \\f")?,
            13u8 => write!(out, " \\r")?,
            _ => if *byte < 32u8 || *byte > 126u8 {
                write!(out, " {:03o}", *byte)?
            } else {
                write!(out, "   {}", *byte as char)?
            }
        }
    }
    writeln!(out)?;
    Ok(data.len())
}

const CHUNK_SIZE: usize = 16;

// The offset string is of the form [+]offset[.][b]
// +100 => 0o100
// +100. => 100
// +100b => 0o100 * 512
// +100.b => 100 * 512
fn parse_offset(offstr: &str) -> Result<u64, ParseIntError> {
    let mut char_indices = offstr.char_indices().rev();
    let mut mult = 1;
    let (s, r) = match char_indices.next() {
        Some((x, 'b')) => {
            mult = 512;
            match char_indices.next() {
                Some((y, '.')) => (&offstr[0..y], 10),
                Some((_, _)) => (&offstr[0..x], 8),
                None => (&offstr[0..0], 8)
            }
        },
        Some((x, '.')) => (&offstr[0..x], 10),
        Some((_, _)) => (&offstr[..], 8),
        None => (&offstr[0..0], 8)
    };

    match u64::from_str_radix(s, r) {
        Ok(n) => Ok(n * mult),
        Err(e) => Err(e)
    }
}

#[test]
fn test_parse_offset() {
    match parse_offset("100") {
        Ok(off) => assert!(off == 0o100),
        Err(_) => assert!(false)
    }

    match parse_offset("100.") {
        Ok(off) => assert!(off == 100),
        Err(_) => assert!(false)
    }

    match parse_offset("100b") {
        Ok(off) => assert!(off == 0o100 * 512),
        Err(_) => assert!(false)
    }

    match parse_offset("100.b") {
        Ok(off) => assert!(off == 100 * 512),
        Err(_) => assert!(false)
    }
}

/// Dumps the data read from the named input source to the standard output.
fn od(filename: &str, offset: u64,
      fmt_fns: &[FmtFn])
      -> io::Result<u64> {
    let mut reader = BufReader::new(util::Input::open(filename)?);
    let mut writer = BufWriter::new(io::stdout());
    let mut offset = offset;

    if offset > 0 {
        reader.seek(SeekFrom::Start(offset))?;
    }

    let mut chunk = [0; CHUNK_SIZE];
    loop {
        let n = reader.read(&mut chunk)?;
        if n > 0 {
            let mut first = true;
            for fmt_fn in fmt_fns.iter() {
                if first {
                    write!(writer, "{:07o}", offset)?;
                    first = false;
                } else {
                    write!(writer, "       ")?;
                }
                fmt_fn(&mut writer, &chunk)?;
                offset += chunk.len() as u64;
            }
        }

        if n < CHUNK_SIZE {
            break
        }
    }
    writeln!(writer, "{:07o}", offset)?;
    Ok(offset)
}

fn main() {
    let args: Vec<_> = env::args().skip(1).collect();
    let mut idx = 0;
    let mut offset : u64 = 0;
    let mut offstr = "0";
    let mut fmt_fns: Vec<FmtFn> = Vec::new();

    if args.len() > idx && args[idx].starts_with('-') {
        for opt in args[idx].chars().skip(1) {
            match opt {
                'b' => fmt_fns.push(write_oct_bytes),
                'c' => fmt_fns.push(write_ascii_chars),
                'd' => fmt_fns.push(write_dec_words),
                'h' => fmt_fns.push(write_hex_words),
                'o' => fmt_fns.push(write_oct_words),
                _ => println!("-{}: unrecognised option", opt),
            }
        }
        idx += 1;
    }

    // If no output formats have been specified, default to octal words.
    if fmt_fns.is_empty() {
        fmt_fns.push(write_oct_words);
    }

    let mut filename = String::from("-");
    if args.len() > idx {
        if args[idx].starts_with('+') {
            offstr = &args[idx][..];
        } else {
            filename = String::from(&args[idx][..]);
            idx += 1;
        }
    }

    if args.len() > idx {
        offstr = &args[idx][..];
    }

    match parse_offset(&offstr) {
        Ok(off) => offset = off,
        Err(e) => println!("{}: {}", offstr, e)
    }

    match od(&filename, offset, &fmt_fns) {
        Ok(_) => std::process::exit(0),
        Err(e) => {
            eprintln!("Error: {}", e);
            std::process::exit(1)
        }
    }
}
<|endoftext|>


<commit_before>pub const FINGERPRINT_SIZE: usize = 2;
pub const BUCKET_SIZE: usize = 8;
const EMPTY_FINGERPRINT_DATA: [u8; FINGERPRINT_SIZE] = [100; FINGERPRINT_SIZE];

// Fingerprint Size is 1 byte so lets remove the Vec
#[derive(PartialEq, Copy, Clone, Hash)]
pub struct Fingerprint {
  data: [u8; FINGERPRINT_SIZE]
}

impl Fingerprint {
  /// Attempts to create a new Fingerprint based on the given
  /// number. If the created Fingerprint would be equal to the
  /// empty Fingerprint, None is returned.
  pub fn from_usize(mut n: usize) -> Option<Fingerprint> {
    let mut data = [0; FINGERPRINT_SIZE];
    for i in 0..FINGERPRINT_SIZE {
      data[i] = (n & 0xff) as u8;
      n >>= 8;
    }
    let result = Fingerprint{ data: data };
    if result.is_empty() { None }
    else { Some(result) }
  }

  /// Returns the empty Fingerprint.
  pub fn empty() -> Fingerprint {
    Fingerprint { data: EMPTY_FINGERPRINT_DATA }
  }

  /// Checks if this is the empty Fingerprint.
  pub fn is_empty(&self) -> bool {
    self.data == EMPTY_FINGERPRINT_DATA
  }
}

/// Manages BUCKET_SIZE fingerprints at most.
#[derive(Clone)]
pub struct Bucket {
    pub buffer: [Fingerprint; BUCKET_SIZE]
}

impl Bucket {
    /// Creates a new bucket with a pre-allocated buffer.
    pub fn new() -> Bucket {
        Bucket {
            buffer: [Fingerprint::empty(); BUCKET_SIZE]
        }
    }

    /// Inserts the fingerprint into the buffer if the buffer is not full. This
    /// operation is O(1).
    pub fn insert(&mut self, fp: Fingerprint) -> bool {
      for entry in self.buffer.iter_mut() {
        if entry.is_empty() {
          *entry = fp;
          return true;
        }
      }
      false
    }

    /// Deletes the given fingerprint from the bucket. This operation is O(1).
    pub fn delete(&mut self, fp: Fingerprint) -> bool {
        match self.get_fingerprint_index(fp) {
            Some(index) => { self.buffer[index] = Fingerprint::empty(); true }
            None => false
        }
    }

    /// Returns the index of the given fingerprint, if its found. O(1)
    pub fn get_fingerprint_index(&mut self, fp: Fingerprint) -> Option<usize> {
        self.buffer.iter().position(|e| *e == fp)
    }
}
<commit_msg>Restore original FINGERPRINT_SIZE and BUCKET_SIZE.<commit_after>pub const FINGERPRINT_SIZE: usize = 1;
pub const BUCKET_SIZE: usize = 4;
const EMPTY_FINGERPRINT_DATA: [u8; FINGERPRINT_SIZE] = [100; FINGERPRINT_SIZE];

// Fingerprint Size is 1 byte so lets remove the Vec
#[derive(PartialEq, Copy, Clone, Hash)]
pub struct Fingerprint {
  data: [u8; FINGERPRINT_SIZE]
}

impl Fingerprint {
  /// Attempts to create a new Fingerprint based on the given
  /// number. If the created Fingerprint would be equal to the
  /// empty Fingerprint, None is returned.
  pub fn from_usize(mut n: usize) -> Option<Fingerprint> {
    let mut data = [0; FINGERPRINT_SIZE];
    for i in 0..FINGERPRINT_SIZE {
      data[i] = (n & 0xff) as u8;
      n >>= 8;
    }
    let result = Fingerprint{ data: data };
    if result.is_empty() { None }
    else { Some(result) }
  }

  /// Returns the empty Fingerprint.
  pub fn empty() -> Fingerprint {
    Fingerprint { data: EMPTY_FINGERPRINT_DATA }
  }

  /// Checks if this is the empty Fingerprint.
  pub fn is_empty(&self) -> bool {
    self.data == EMPTY_FINGERPRINT_DATA
  }
}

/// Manages BUCKET_SIZE fingerprints at most.
#[derive(Clone)]
pub struct Bucket {
    pub buffer: [Fingerprint; BUCKET_SIZE]
}

impl Bucket {
    /// Creates a new bucket with a pre-allocated buffer.
    pub fn new() -> Bucket {
        Bucket {
            buffer: [Fingerprint::empty(); BUCKET_SIZE]
        }
    }

    /// Inserts the fingerprint into the buffer if the buffer is not full. This
    /// operation is O(1).
    pub fn insert(&mut self, fp: Fingerprint) -> bool {
      for entry in self.buffer.iter_mut() {
        if entry.is_empty() {
          *entry = fp;
          return true;
        }
      }
      false
    }

    /// Deletes the given fingerprint from the bucket. This operation is O(1).
    pub fn delete(&mut self, fp: Fingerprint) -> bool {
        match self.get_fingerprint_index(fp) {
            Some(index) => { self.buffer[index] = Fingerprint::empty(); true }
            None => false
        }
    }

    /// Returns the index of the given fingerprint, if its found. O(1)
    pub fn get_fingerprint_index(&mut self, fp: Fingerprint) -> Option<usize> {
        self.buffer.iter().position(|e| *e == fp)
    }
}
<|endoftext|>


<commit_before>use std::{cmp, io};

#[derive(Debug,PartialEq,Clone)]
pub(crate) struct Buffer {
  memory:   Vec<u8>,
  capacity: usize,
  position: usize,
  end:      usize
}

impl Buffer {
  pub(crate) fn with_capacity(capacity: usize) -> Buffer {
    Buffer {
      memory:   vec![0; capacity],
      capacity,
      position: 0,
      end:      0
    }
  }

  pub(crate) fn grow(&mut self, new_size: usize) -> bool {
    if self.capacity >= new_size {
      return false;
    }

    self.memory.resize(new_size, 0);
    self.capacity = new_size;
    true
  }

  pub(crate) fn available_data(&self) -> usize {
    self.end - self.position
  }

  pub(crate) fn available_space(&self) -> usize {
    self.capacity - self.end
  }

  pub(crate) fn consume(&mut self, count: usize) -> usize {
    let cnt        = cmp::min(count, self.available_data());
    self.position += cnt;
    cnt
  }

  pub(crate) fn fill(&mut self, count: usize) -> usize {
    let cnt   = cmp::min(count, self.available_space());
    self.end += cnt;
    cnt
  }

  pub(crate) fn data(&self) -> &[u8] {
    &self.memory[self.position..self.end]
  }

  pub(crate) fn space(&mut self) -> &mut [u8] {
    &mut self.memory[self.end..self.capacity]
  }

  pub(crate) fn shift(&mut self) {
    let length = self.end - self.position;
    if length > self.position {
      return
    } else {
      let (start, end) = self.memory.split_at_mut(length);
      start.copy_from_slice(&end[(self.position-length)..self.position]);
    }
    self.position = 0;
    self.end      = length;
  }

  pub(crate) fn shift_unless_available(&mut self, size: usize) {
    if self.available_space() < size {
      self.shift();
    }
  }
}

impl io::Write for &mut Buffer {
  fn write(&mut self, data: &[u8]) -> io::Result<usize> {
    let amt = self.space().write(data)?;
    self.fill(amt);
    Ok(amt)
  }

  fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

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

  #[test]
  fn fill_and_consume() {
    let mut b = Buffer::with_capacity(10);
    assert_eq!(b.available_data(), 0);
    assert_eq!(b.available_space(), 10);
    let res = b.space().write(&b"abcd"[..]).map(|size| { b.fill(size); size });
    assert_eq!(res.ok(), Some(4));
    assert_eq!(b.available_data(), 4);
    assert_eq!(b.available_space(), 6);

    assert_eq!(b.data(), &b"abcd"[..]);

    b.consume(2);
    assert_eq!(b.available_data(), 2);
    assert_eq!(b.available_space(), 6);
    assert_eq!(b.data(), &b"cd"[..]);

    b.shift();
    assert_eq!(b.available_data(), 2);
    assert_eq!(b.available_space(), 8);
    assert_eq!(b.data(), &b"cd"[..]);

    assert_eq!(b.space().write(&b"efghijklmnop"[..]).map(|size| { b.fill(size); size }).ok(), Some(8));
    assert_eq!(b.available_data(), 10);
    assert_eq!(b.available_space(), 0);
    assert_eq!(b.data(), &b"cdefghijkl"[..]);
    b.shift();
    assert_eq!(b.available_data(), 10);
    assert_eq!(b.available_space(), 0);
    assert_eq!(b.data(), &b"cdefghijkl"[..]);
  }
}
<commit_msg>simplify previous commit<commit_after>use std::{cmp, io};

#[derive(Debug,PartialEq,Clone)]
pub(crate) struct Buffer {
  memory:   Vec<u8>,
  capacity: usize,
  position: usize,
  end:      usize
}

impl Buffer {
  pub(crate) fn with_capacity(capacity: usize) -> Buffer {
    Buffer {
      memory:   vec![0; capacity],
      capacity,
      position: 0,
      end:      0
    }
  }

  pub(crate) fn grow(&mut self, new_size: usize) -> bool {
    if self.capacity >= new_size {
      return false;
    }

    self.memory.resize(new_size, 0);
    self.capacity = new_size;
    true
  }

  pub(crate) fn available_data(&self) -> usize {
    self.end - self.position
  }

  pub(crate) fn available_space(&self) -> usize {
    self.capacity - self.end
  }

  pub(crate) fn consume(&mut self, count: usize) -> usize {
    let cnt        = cmp::min(count, self.available_data());
    self.position += cnt;
    cnt
  }

  pub(crate) fn fill(&mut self, count: usize) -> usize {
    let cnt   = cmp::min(count, self.available_space());
    self.end += cnt;
    cnt
  }

  pub(crate) fn data(&self) -> &[u8] {
    &self.memory[self.position..self.end]
  }

  pub(crate) fn space(&mut self) -> &mut [u8] {
    &mut self.memory[self.end..self.capacity]
  }

  pub(crate) fn shift(&mut self) {
    let length = self.end - self.position;
    if length > self.position {
      return
    } else {
      let (start, end) = self.memory.split_at_mut(self.position);
      start[..length].copy_from_slice(&end[..length]);
    }
    self.position = 0;
    self.end      = length;
  }

  pub(crate) fn shift_unless_available(&mut self, size: usize) {
    if self.available_space() < size {
      self.shift();
    }
  }
}

impl io::Write for &mut Buffer {
  fn write(&mut self, data: &[u8]) -> io::Result<usize> {
    let amt = self.space().write(data)?;
    self.fill(amt);
    Ok(amt)
  }

  fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

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

  #[test]
  fn fill_and_consume() {
    let mut b = Buffer::with_capacity(10);
    assert_eq!(b.available_data(), 0);
    assert_eq!(b.available_space(), 10);
    let res = b.space().write(&b"abcd"[..]).map(|size| { b.fill(size); size });
    assert_eq!(res.ok(), Some(4));
    assert_eq!(b.available_data(), 4);
    assert_eq!(b.available_space(), 6);

    assert_eq!(b.data(), &b"abcd"[..]);

    b.consume(2);
    assert_eq!(b.available_data(), 2);
    assert_eq!(b.available_space(), 6);
    assert_eq!(b.data(), &b"cd"[..]);

    b.shift();
    assert_eq!(b.available_data(), 2);
    assert_eq!(b.available_space(), 8);
    assert_eq!(b.data(), &b"cd"[..]);

    assert_eq!(b.space().write(&b"efghijklmnop"[..]).map(|size| { b.fill(size); size }).ok(), Some(8));
    assert_eq!(b.available_data(), 10);
    assert_eq!(b.available_space(), 0);
    assert_eq!(b.data(), &b"cdefghijkl"[..]);
    b.shift();
    assert_eq!(b.available_data(), 10);
    assert_eq!(b.available_space(), 0);
    assert_eq!(b.data(), &b"cdefghijkl"[..]);
  }
}
<|endoftext|>