<commit_before>#[macro_use] extern crate clap;
extern crate tempdir;
extern crate rand;
extern crate base32;

use std::fs;

use clap::{Arg, SubCommand};

use tempdir::TempDir;

struct CmdErr {
    message: String,
    secondary: Option<String>,
}

trait ToCmdErr<T> {
    fn check(self, message: &str) -> Result<T, CmdErr>;
}

impl<T, E: ::std::fmt::Display> ToCmdErr<T> for Result<T, E> {
    fn check(self, message: &str) -> Result<T, CmdErr> {
        self.map_err(|e| CmdErr {
            message: message.to_owned(),
            secondary: Some(format!("{}", e)),
        })
    }
}

impl<T> ToCmdErr<T> for Option<T> {
    fn check(self, message: &str) -> Result<T, CmdErr> {
        match self {
            Some(t) => Ok(t),
            None => Err(CmdErr {
                message: message.to_owned(),
                secondary: None,
            }),
        }
    }
}

impl ToCmdErr<()> for bool {
    fn check(self, message: &str) -> Result<(), CmdErr> {
        if self {
            Ok(())
        } else {
            Err(CmdErr {
                message: message.to_owned(),
                secondary: None,
            })
        }
    }
}

fn cmd_run(image: &str, rootless: bool, volumes: &[(&str, &str)]) -> Result<Option<i32>, CmdErr> {
    if volumes.len() > 0 {
        return Err(CmdErr { message: "Volume bind-mounts are not supported yet".to_owned(), secondary: None });
    }

    let tmp_dir = TempDir::new("shipc").check("Cannot create temporary directory")?;
    eprintln!("info: Temporary directory: {:?}", tmp_dir.path());
    eprint!("info: Running image {}", image);
    if rootless {
        eprint!("info: in rootless mode");
    }
    eprintln!();
    for &(ref orig, ref dest) in volumes {
        eprintln!("info:   with volume {}:{}", orig, dest);
    }
    let image_path = ::std::path::Path::new(image);
    let image_metadata = fs::metadata(image).check("Invalid image path")?;
    let mut image_dir_path = image_path.to_path_buf();

    // Support for .tar.gz images: un-tar them in the temporary directory
    if image_metadata.file_type().is_file() {
        image_path.file_name().expect("Filename is expected for a file")
            .to_string_lossy().ends_with(".tar.gz").check("File is not a tarball")?;
        image_dir_path = tmp_dir.path().join("image");
        fs::create_dir(&image_dir_path).check("Internal error")?;
        eprintln!("info: Uncompressing image into {}", image_dir_path.to_string_lossy());
        let tar_output = ::std::process::Command::new("tar")
            .arg("--strip-components")
            .arg("1")
            .arg("-C")
            .arg(&image_dir_path)
            .arg("-xf")
            .arg(image_path)
            .output()
            .check("Cannot execute tar")?;
        if !tar_output.status.success() {
            return Err(CmdErr {
                message: "Failed to un-tar image".to_owned(),
                secondary: Some(String::from_utf8_lossy(&tar_output.stderr).into_owned()),
            });
        }
    }

    let bundle = "bundle";

    let bundle_path = tmp_dir.path().join(bundle);
    eprintln!("info: Unpacking image to {}", bundle_path.to_string_lossy());

    let mut umoci_command = ::std::process::Command::new("umoci");
    umoci_command
        .current_dir(tmp_dir.path())
        .arg("unpack")
        .arg("--image")
        .arg(&image_dir_path.file_name().expect("The image directory must have a last component"));
    if rootless {
        umoci_command.arg("--rootless");
    }
    umoci_command.arg(bundle);

    let umoci_output = umoci_command.output()
        .check("Cannot run umoci")?;

    if !umoci_output.status.success() {
        return Err(CmdErr {
            message: "Failed to unpack image with umoci".to_owned(),
            secondary: Some(String::from_utf8_lossy(&umoci_output.stderr).into_owned()),
        });
    }

    let container_name = {
        use rand::Rng;
        let bytes: [u8; 4] = unsafe { ::std::mem::transmute(rand::thread_rng().gen::<u32>().to_be()) };
        ::base32::encode(::base32::Alphabet::RFC4648 { padding: false }, &bytes[..])
    };

    let mut runc_command = ::std::process::Command::new("runc");
    runc_command
        .current_dir(bundle_path);
    if rootless {
        let root_dir_path = tmp_dir.path().join("root");
        fs::create_dir(&root_dir_path).check("Internal error")?;
        runc_command
            .arg("--root")
            .arg(&root_dir_path);
    }
    runc_command
        .arg("run")
        .arg(&container_name);

    eprintln!("info: Starting container with name {}", &container_name);
    eprintln!("info: If runc is successful, the next line will be inside the container");

    let runc_status = runc_command.status().check("Failed to start the container")?;

    eprintln!("info: Container exited with {}", runc_status);

    Ok(runc_status.code())
}

fn fail(message: &str) {
    eprintln!("error: {}", message);
    eprintln!("error: Run with --help for usage");
    ::std::process::exit(-1);
}

fn check(condition: bool, message: &str) {
    if !condition {
        fail(message);
    }
}

fn main() {
    let app = app_from_crate!()
        .about("Unpack and run oci images")
        .subcommand(SubCommand::with_name("run")
                    .about("Run an oci image")
                    .arg(Arg::with_name("image")
                         .required(true))
                    .arg(Arg::with_name("rootless")
                         .help("Run in rootless mode")
                         .long("rootless"))
                    .arg(Arg::with_name("volume")
                         .help("Rebind volumes")
                         .long("volume")
                         .short("v")
                         .takes_value(true)
                         .value_name("VOLUME")
                         .multiple(true)));
    let matches = app.clone().get_matches();

    let result = match matches.subcommand() {
        ("run", Some(sub_matches)) => {
            let rootless: bool = sub_matches.is_present("rootless");
            let image = sub_matches.value_of("image").unwrap();
            let volumes = sub_matches.values_of("volume").map(|vol_strs| {
                vol_strs.map(|s| {
                    let vol_arr: Vec<&str> = s.split(":").collect();
                    check(vol_arr.len() == 2, "Volume should be origin:destination");
                    let orig = vol_arr[0];
                    let dest = vol_arr[1];
                    (orig, dest)
                }).collect::<Vec<_>>()
            }).unwrap_or(Vec::new());
            cmd_run(image, rootless, &volumes[..])
        },
        _ => {
            let mut out = ::std::io::stdout();
            app.write_help(&mut out).ok().expect("error: Failed to print usage to stdout");
            println!();
            ::std::process::exit(-1);
        },
    };
    match result {
        Ok(exit_code) => ::std::process::exit(exit_code.unwrap_or(-1)),
        Err(err) => {
            eprintln!("error: {}", err.message);
            if let Some(secondary) = err.secondary {
                eprintln!("error: {}", secondary);
            }
            eprintln!("error: Run with --help for usage");
        },
    }
}
<commit_msg>Minor log fix<commit_after>#[macro_use] extern crate clap;
extern crate tempdir;
extern crate rand;
extern crate base32;

use std::fs;

use clap::{Arg, SubCommand};

use tempdir::TempDir;

struct CmdErr {
    message: String,
    secondary: Option<String>,
}

trait ToCmdErr<T> {
    fn check(self, message: &str) -> Result<T, CmdErr>;
}

impl<T, E: ::std::fmt::Display> ToCmdErr<T> for Result<T, E> {
    fn check(self, message: &str) -> Result<T, CmdErr> {
        self.map_err(|e| CmdErr {
            message: message.to_owned(),
            secondary: Some(format!("{}", e)),
        })
    }
}

impl<T> ToCmdErr<T> for Option<T> {
    fn check(self, message: &str) -> Result<T, CmdErr> {
        match self {
            Some(t) => Ok(t),
            None => Err(CmdErr {
                message: message.to_owned(),
                secondary: None,
            }),
        }
    }
}

impl ToCmdErr<()> for bool {
    fn check(self, message: &str) -> Result<(), CmdErr> {
        if self {
            Ok(())
        } else {
            Err(CmdErr {
                message: message.to_owned(),
                secondary: None,
            })
        }
    }
}

fn cmd_run(image: &str, rootless: bool, volumes: &[(&str, &str)]) -> Result<Option<i32>, CmdErr> {
    if volumes.len() > 0 {
        return Err(CmdErr { message: "Volume bind-mounts are not supported yet".to_owned(), secondary: None });
    }

    let tmp_dir = TempDir::new("shipc").check("Cannot create temporary directory")?;
    eprintln!("info: Temporary directory: {:?}", tmp_dir.path());
    eprint!("info: Running image {}", image);
    if rootless {
        eprint!(" in rootless mode");
    }
    eprintln!();
    for &(ref orig, ref dest) in volumes {
        eprintln!("info:   with volume {}:{}", orig, dest);
    }
    let image_path = ::std::path::Path::new(image);
    let image_metadata = fs::metadata(image).check("Invalid image path")?;
    let mut image_dir_path = image_path.to_path_buf();

    // Support for .tar.gz images: un-tar them in the temporary directory
    if image_metadata.file_type().is_file() {
        image_path.file_name().expect("Filename is expected for a file")
            .to_string_lossy().ends_with(".tar.gz").check("File is not a tarball")?;
        image_dir_path = tmp_dir.path().join("image");
        fs::create_dir(&image_dir_path).check("Internal error")?;
        eprintln!("info: Uncompressing image into {}", image_dir_path.to_string_lossy());
        let tar_output = ::std::process::Command::new("tar")
            .arg("--strip-components")
            .arg("1")
            .arg("-C")
            .arg(&image_dir_path)
            .arg("-xf")
            .arg(image_path)
            .output()
            .check("Cannot execute tar")?;
        if !tar_output.status.success() {
            return Err(CmdErr {
                message: "Failed to un-tar image".to_owned(),
                secondary: Some(String::from_utf8_lossy(&tar_output.stderr).into_owned()),
            });
        }
    }

    let bundle = "bundle";

    let bundle_path = tmp_dir.path().join(bundle);
    eprintln!("info: Unpacking image to {}", bundle_path.to_string_lossy());

    let mut umoci_command = ::std::process::Command::new("umoci");
    umoci_command
        .current_dir(tmp_dir.path())
        .arg("unpack")
        .arg("--image")
        .arg(&image_dir_path.file_name().expect("The image directory must have a last component"));
    if rootless {
        umoci_command.arg("--rootless");
    }
    umoci_command.arg(bundle);

    let umoci_output = umoci_command.output()
        .check("Cannot run umoci")?;

    if !umoci_output.status.success() {
        return Err(CmdErr {
            message: "Failed to unpack image with umoci".to_owned(),
            secondary: Some(String::from_utf8_lossy(&umoci_output.stderr).into_owned()),
        });
    }

    let container_name = {
        use rand::Rng;
        let bytes: [u8; 4] = unsafe { ::std::mem::transmute(rand::thread_rng().gen::<u32>().to_be()) };
        ::base32::encode(::base32::Alphabet::RFC4648 { padding: false }, &bytes[..])
    };

    let mut runc_command = ::std::process::Command::new("runc");
    runc_command
        .current_dir(bundle_path);
    if rootless {
        let root_dir_path = tmp_dir.path().join("root");
        fs::create_dir(&root_dir_path).check("Internal error")?;
        runc_command
            .arg("--root")
            .arg(&root_dir_path);
    }
    runc_command
        .arg("run")
        .arg(&container_name);

    eprintln!("info: Starting container with name {}", &container_name);
    eprintln!("info: If runc is successful, the next line will be inside the container");

    let runc_status = runc_command.status().check("Failed to start the container")?;

    eprintln!("info: Container exited with {}", runc_status);

    Ok(runc_status.code())
}

fn fail(message: &str) {
    eprintln!("error: {}", message);
    eprintln!("error: Run with --help for usage");
    ::std::process::exit(-1);
}

fn check(condition: bool, message: &str) {
    if !condition {
        fail(message);
    }
}

fn main() {
    let app = app_from_crate!()
        .about("Unpack and run oci images")
        .subcommand(SubCommand::with_name("run")
                    .about("Run an oci image")
                    .arg(Arg::with_name("image")
                         .required(true))
                    .arg(Arg::with_name("rootless")
                         .help("Run in rootless mode")
                         .long("rootless"))
                    .arg(Arg::with_name("volume")
                         .help("Rebind volumes")
                         .long("volume")
                         .short("v")
                         .takes_value(true)
                         .value_name("VOLUME")
                         .multiple(true)));
    let matches = app.clone().get_matches();

    let result = match matches.subcommand() {
        ("run", Some(sub_matches)) => {
            let rootless: bool = sub_matches.is_present("rootless");
            let image = sub_matches.value_of("image").unwrap();
            let volumes = sub_matches.values_of("volume").map(|vol_strs| {
                vol_strs.map(|s| {
                    let vol_arr: Vec<&str> = s.split(":").collect();
                    check(vol_arr.len() == 2, "Volume should be origin:destination");
                    let orig = vol_arr[0];
                    let dest = vol_arr[1];
                    (orig, dest)
                }).collect::<Vec<_>>()
            }).unwrap_or(Vec::new());
            cmd_run(image, rootless, &volumes[..])
        },
        _ => {
            let mut out = ::std::io::stdout();
            app.write_help(&mut out).ok().expect("error: Failed to print usage to stdout");
            println!();
            ::std::process::exit(-1);
        },
    };
    match result {
        Ok(exit_code) => ::std::process::exit(exit_code.unwrap_or(-1)),
        Err(err) => {
            eprintln!("error: {}", err.message);
            if let Some(secondary) = err.secondary {
                eprintln!("error: {}", secondary);
            }
            eprintln!("error: Run with --help for usage");
        },
    }
}
<|endoftext|>


<commit_before>//! Our main CLI tool.

#[macro_use]
extern crate conductor;
extern crate docopt;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate rustc_serialize;

use docopt::Docopt;
use std::env;
use std::fs;
use std::io::{self, Write};
use std::process;

use conductor::command_runner::OsCommandRunner;
use conductor::cmd::*;
use conductor::Error;

/// Our version number, set by Cargo at compile time.
const VERSION: &'static str = env!("CARGO_PKG_VERSION");

/// Our help string.
const USAGE: &'static str = "
conductor: Manage large, multi-pod docker-compose apps

Usage:
  conductor [options]
  conductor [options] new <name>
  conductor [options] pull
  conductor [options] up
  conductor [options] stop
  conductor [options] exec [exec options] <pod> <service> <command> [--] [<args>..]
  conductor [options] shell [exec options] <pod> <service>
  conductor [options] test <pod> <service>
  conductor [options] repo list
  conductor [options] repo clone <repo>
  conductor (--help | --version)

Commands:
  pull              Pull Docker images used by project
  up                Run project
  stop              Stop all containers associated with project
  exec              Run a command inside a container
  shell             Run an interactive shell inside a running container
  test              Run the tests associated with a service, if any
  repo list         List all git repository aliases and URLs
  repo clone        Clone a git repository using its short alias and mount it
                    into the containers that use it

Arguments:
  <repo>            Short alias for a repo (see `repo list`)
  <pod>             The name of a pod specified in `pods/`
  <service>         The name of a service in a pod

Exec options:
  -d                Run command detached in background
  --privileged      Run a command with elevated privileges
  --user <user>     User as which to run a command
  -T                Do not allocate a TTY when running a command

General options:
  -h, --help        Show this message
  --version         Show the version of conductor
  --override=<override>
                    Use overrides from the specified subdirectory of
                    `pods/overrides` [default: development]
  --default-tags=<tag_file>
                    A list of tagged image names, one per line, to
                    be used as defaults for images

Run conductor in a directory containing a `pods` subdirectory.  For more
information, see https://github.com/faradayio/conductor.
";

/// Our parsed command-line arguments.  See [docopt.rs][] for an
/// explanation of how this works.
///
/// [docopt.rs]: https://github.com/docopt/docopt.rs
#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)] // Allow uppercase options without warnings.
struct Args {
    cmd_pull: bool,
    cmd_up: bool,
    cmd_stop: bool,
    cmd_exec: bool,
    cmd_shell: bool,
    cmd_test: bool,
    cmd_repo: bool,
    cmd_list: bool,
    cmd_clone: bool,
    cmd_new: bool,

    arg_args: Option<Vec<String>>,
    arg_command: Option<String>,
    arg_name: Option<String>,
    arg_pod: Option<String>,
    arg_repo: Option<String>,
    arg_service: Option<String>,

    // Exec options.
    flag_d: bool,
    flag_privileged: bool,
    flag_user: Option<String>,
    flag_T: bool,

    // General options.
    flag_version: bool,
    flag_override: String,
    flag_default_tags: Option<String>,
}

impl Args {
    /// Extract `exec::Options` from our command-line arguments.
    fn to_exec_options(&self) -> conductor::exec::Options {
        conductor::exec::Options {
            detached: self.flag_d,
            privileged: self.flag_privileged,
            user: self.flag_user.clone(),
            allocate_tty: !self.flag_T,
            ..Default::default()
        }
    }

    /// Extract `exec::Target` from our command-line arguments.
    fn to_exec_target<'a>(&'a self, project: &'a conductor::Project,
                          ovr: &'a conductor::Override) ->
        Result<Option<conductor::exec::Target<'a>>, Error>
    {
        match (&self.arg_pod, &self.arg_service) {
            (&Some(ref pod), &Some(ref service)) =>
                Ok(Some(try!(conductor::exec::Target::new(project, ovr, pod,
                                                          service)))),
            _ => Ok(None),
        }
    }

    /// Extract `exec::Command` from our command-line arguments.
    fn to_exec_command(&self) -> Option<conductor::exec::Command> {
        // We have an `Option<Vec<String>>` and we want a `&[String]`,
        // so do a little munging.
        let args = self.arg_args.as_ref()
            .map(|v| (v as &[_])).unwrap_or(&[]);
        if let &Some(ref command) = &self.arg_command {
            Some(conductor::exec::Command::new(command).with_args(&args))
        } else {
            None
        }
    }
}

/// The function which does the real work.  Unlike `main`, we have a return
/// type of `Result` and may therefore use `try!` to handle errors.
fn run(args: &Args) -> Result<(), Error> {

    if args.cmd_new {
        try!(conductor::Project::generate(&try!(env::current_dir()),
                                          args.arg_name.as_ref().unwrap()));
        return Ok(());
    }

    let mut proj = try!(conductor::Project::from_current_dir());
    if let Some(ref default_tags_path) = args.flag_default_tags {
        let file = try!(fs::File::open(default_tags_path));
        proj.set_default_tags(try!(conductor::DefaultTags::read(file)));
    }
    let ovr = try!(proj.ovr(&args.flag_override).ok_or_else(|| {
        err!("override {} is not defined", &args.flag_override)
    }));
    try!(proj.output());
    let runner = OsCommandRunner;

    if args.cmd_pull {
        try!(proj.pull(&runner, &ovr));
    } else if args.cmd_up {
        try!(proj.up(&runner, &ovr));
    } else if args.cmd_stop {
        try!(proj.stop(&runner, &ovr));
    } else if args.cmd_exec {
        let target = try!(args.to_exec_target(&proj, &ovr)).unwrap();
        let opts = args.to_exec_options();
        let cmd = args.to_exec_command().unwrap();
        try!(proj.exec(&runner, &target, &cmd, &opts));
    } else if args.cmd_shell {
        let target = try!(args.to_exec_target(&proj, &ovr)).unwrap();
        let opts = args.to_exec_options();
        try!(proj.shell(&runner, &target, &opts));
    } else if args.cmd_test {
        let test_ovr = try!(proj.ovr("test").ok_or_else(|| {
            err!("override test is required to run tests")
        }));
        let target = try!(args.to_exec_target(&proj, &test_ovr)).unwrap();
        try!(proj.test(&runner, &target));
    } else if args.cmd_repo && args.cmd_list {
        try!(proj.repo_list(&runner));
    } else if args.cmd_repo && args.cmd_clone {
        try!(proj.repo_clone(&runner, args.arg_repo.as_ref().unwrap()));
        // Regenerate our output now that we've cloned.
        try!(proj.output());
    }

    Ok(())
}

/// Our main entry point.
fn main() {
    // Initialize logging with some custom options, mostly so we can see
    // our own warnings.
    let mut builder = env_logger::LogBuilder::new();
    builder.filter(Some("docker_compose"), log::LogLevelFilter::Warn);
    builder.filter(Some("conductor"), log::LogLevelFilter::Warn);
    if let Ok(config) = env::var("RUST_LOG") {
        builder.parse(&config);
    }
    builder.init().unwrap();

    // Parse our args using docopt.rs.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    debug!("Arguments: {:?}", &args);

    // Display our version if we were asked to do so.
    if args.flag_version {
        println!("conductor {}", VERSION);
        process::exit(0);
    }

    // Defer all our real work to `run`, and handle any errors.  This is a
    // standard Rust pattern to make error-handling in `main` nicer.
    if let Err(ref err) = run(&args) {
        // We use `unwrap` here to turn I/O errors into application panics.
        // If we can't print a message to stderr without an I/O error,
        // the situation is hopeless.
        write!(io::stderr(), "Error: {}\n", err).unwrap();
        process::exit(1);
    }
}
<commit_msg>Add `new` to main help message<commit_after>//! Our main CLI tool.

#[macro_use]
extern crate conductor;
extern crate docopt;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate rustc_serialize;

use docopt::Docopt;
use std::env;
use std::fs;
use std::io::{self, Write};
use std::process;

use conductor::command_runner::OsCommandRunner;
use conductor::cmd::*;
use conductor::Error;

/// Our version number, set by Cargo at compile time.
const VERSION: &'static str = env!("CARGO_PKG_VERSION");

/// Our help string.
const USAGE: &'static str = "
conductor: Manage large, multi-pod docker-compose apps

Usage:
  conductor [options]
  conductor [options] new <name>
  conductor [options] pull
  conductor [options] up
  conductor [options] stop
  conductor [options] exec [exec options] <pod> <service> <command> [--] [<args>..]
  conductor [options] shell [exec options] <pod> <service>
  conductor [options] test <pod> <service>
  conductor [options] repo list
  conductor [options] repo clone <repo>
  conductor (--help | --version)

Commands:
  new               Create a directory containing a new sample project
  pull              Pull Docker images used by project
  up                Run project
  stop              Stop all containers associated with project
  exec              Run a command inside a container
  shell             Run an interactive shell inside a running container
  test              Run the tests associated with a service, if any
  repo list         List all git repository aliases and URLs
  repo clone        Clone a git repository using its short alias and mount it
                    into the containers that use it

Arguments:
  <repo>            Short alias for a repo (see `repo list`)
  <pod>             The name of a pod specified in `pods/`
  <service>         The name of a service in a pod

Exec options:
  -d                Run command detached in background
  --privileged      Run a command with elevated privileges
  --user <user>     User as which to run a command
  -T                Do not allocate a TTY when running a command

General options:
  -h, --help        Show this message
  --version         Show the version of conductor
  --override=<override>
                    Use overrides from the specified subdirectory of
                    `pods/overrides` [default: development]
  --default-tags=<tag_file>
                    A list of tagged image names, one per line, to
                    be used as defaults for images

Run conductor in a directory containing a `pods` subdirectory.  For more
information, see https://github.com/faradayio/conductor.
";

/// Our parsed command-line arguments.  See [docopt.rs][] for an
/// explanation of how this works.
///
/// [docopt.rs]: https://github.com/docopt/docopt.rs
#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)] // Allow uppercase options without warnings.
struct Args {
    cmd_pull: bool,
    cmd_up: bool,
    cmd_stop: bool,
    cmd_exec: bool,
    cmd_shell: bool,
    cmd_test: bool,
    cmd_repo: bool,
    cmd_list: bool,
    cmd_clone: bool,
    cmd_new: bool,

    arg_args: Option<Vec<String>>,
    arg_command: Option<String>,
    arg_name: Option<String>,
    arg_pod: Option<String>,
    arg_repo: Option<String>,
    arg_service: Option<String>,

    // Exec options.
    flag_d: bool,
    flag_privileged: bool,
    flag_user: Option<String>,
    flag_T: bool,

    // General options.
    flag_version: bool,
    flag_override: String,
    flag_default_tags: Option<String>,
}

impl Args {
    /// Extract `exec::Options` from our command-line arguments.
    fn to_exec_options(&self) -> conductor::exec::Options {
        conductor::exec::Options {
            detached: self.flag_d,
            privileged: self.flag_privileged,
            user: self.flag_user.clone(),
            allocate_tty: !self.flag_T,
            ..Default::default()
        }
    }

    /// Extract `exec::Target` from our command-line arguments.
    fn to_exec_target<'a>(&'a self, project: &'a conductor::Project,
                          ovr: &'a conductor::Override) ->
        Result<Option<conductor::exec::Target<'a>>, Error>
    {
        match (&self.arg_pod, &self.arg_service) {
            (&Some(ref pod), &Some(ref service)) =>
                Ok(Some(try!(conductor::exec::Target::new(project, ovr, pod,
                                                          service)))),
            _ => Ok(None),
        }
    }

    /// Extract `exec::Command` from our command-line arguments.
    fn to_exec_command(&self) -> Option<conductor::exec::Command> {
        // We have an `Option<Vec<String>>` and we want a `&[String]`,
        // so do a little munging.
        let args = self.arg_args.as_ref()
            .map(|v| (v as &[_])).unwrap_or(&[]);
        if let &Some(ref command) = &self.arg_command {
            Some(conductor::exec::Command::new(command).with_args(&args))
        } else {
            None
        }
    }
}

/// The function which does the real work.  Unlike `main`, we have a return
/// type of `Result` and may therefore use `try!` to handle errors.
fn run(args: &Args) -> Result<(), Error> {

    if args.cmd_new {
        try!(conductor::Project::generate(&try!(env::current_dir()),
                                          args.arg_name.as_ref().unwrap()));
        return Ok(());
    }

    let mut proj = try!(conductor::Project::from_current_dir());
    if let Some(ref default_tags_path) = args.flag_default_tags {
        let file = try!(fs::File::open(default_tags_path));
        proj.set_default_tags(try!(conductor::DefaultTags::read(file)));
    }
    let ovr = try!(proj.ovr(&args.flag_override).ok_or_else(|| {
        err!("override {} is not defined", &args.flag_override)
    }));
    try!(proj.output());
    let runner = OsCommandRunner;

    if args.cmd_pull {
        try!(proj.pull(&runner, &ovr));
    } else if args.cmd_up {
        try!(proj.up(&runner, &ovr));
    } else if args.cmd_stop {
        try!(proj.stop(&runner, &ovr));
    } else if args.cmd_exec {
        let target = try!(args.to_exec_target(&proj, &ovr)).unwrap();
        let opts = args.to_exec_options();
        let cmd = args.to_exec_command().unwrap();
        try!(proj.exec(&runner, &target, &cmd, &opts));
    } else if args.cmd_shell {
        let target = try!(args.to_exec_target(&proj, &ovr)).unwrap();
        let opts = args.to_exec_options();
        try!(proj.shell(&runner, &target, &opts));
    } else if args.cmd_test {
        let test_ovr = try!(proj.ovr("test").ok_or_else(|| {
            err!("override test is required to run tests")
        }));
        let target = try!(args.to_exec_target(&proj, &test_ovr)).unwrap();
        try!(proj.test(&runner, &target));
    } else if args.cmd_repo && args.cmd_list {
        try!(proj.repo_list(&runner));
    } else if args.cmd_repo && args.cmd_clone {
        try!(proj.repo_clone(&runner, args.arg_repo.as_ref().unwrap()));
        // Regenerate our output now that we've cloned.
        try!(proj.output());
    }

    Ok(())
}

/// Our main entry point.
fn main() {
    // Initialize logging with some custom options, mostly so we can see
    // our own warnings.
    let mut builder = env_logger::LogBuilder::new();
    builder.filter(Some("docker_compose"), log::LogLevelFilter::Warn);
    builder.filter(Some("conductor"), log::LogLevelFilter::Warn);
    if let Ok(config) = env::var("RUST_LOG") {
        builder.parse(&config);
    }
    builder.init().unwrap();

    // Parse our args using docopt.rs.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    debug!("Arguments: {:?}", &args);

    // Display our version if we were asked to do so.
    if args.flag_version {
        println!("conductor {}", VERSION);
        process::exit(0);
    }

    // Defer all our real work to `run`, and handle any errors.  This is a
    // standard Rust pattern to make error-handling in `main` nicer.
    if let Err(ref err) = run(&args) {
        // We use `unwrap` here to turn I/O errors into application panics.
        // If we can't print a message to stderr without an I/O error,
        // the situation is hopeless.
        write!(io::stderr(), "Error: {}\n", err).unwrap();
        process::exit(1);
    }
}
<|endoftext|>


<commit_before>/// An example of a client/server agnostic WebSocket that takes input from stdin and sends that
/// input to all other peers.
///
/// For example, to create a network like this:
///
/// 3013 ---- 3012 ---- 3014
///   \        |
///    \       |
///     \      |
///      \     |
///       \    |
///        \   |
///         \  |
///          3015
///
/// Run these commands in separate processes
/// ./peer2peer
/// ./peer2peer --server localhost:3013 ws://localhost:3012
/// ./peer2peer --server localhost:3014 ws://localhost:3012
/// ./peer2peer --server localhost:3015 ws://localhost:3012 ws://localhost:3013
///
/// Stdin on 3012 will be sent to all other peers
/// Stdin on 3013 will be sent to 3012 and 3015
/// Stdin on 3014 will be sent to 3012 only
/// Stdin on 3015 will be sent to 3012 and 2013

extern crate ws;
extern crate url;
extern crate clap;
extern crate env_logger;
extern crate rustc_serialize;
extern crate bincode;
#[macro_use]
extern crate log;

pub mod handler;
pub mod message;

use message::PeerMessage;
use handler::MessageFactory;

use std::collections::hash_map::HashMap;
use std::clone::Clone;
use std::io;
use std::io::prelude::*;
use std::thread;
use std::sync::{Arc, Mutex};

use bincode::rustc_serialize::encode;

use clap::{App, Arg};

fn main() {
    // Setup logging
    env_logger::init().unwrap();

    // Parse command line arguments
    let matches = App::new("Simple Peer 2 Peer")
                      .version("1.0")
                      .author("Jonathan Almeida <hello@jonalmeida.com>")
                      .about("Connect to other peers and listen for incoming connections.")
                      .arg(Arg::with_name("server")
                               .short("s")
                               .long("server")
                               .value_name("SERVER")
                               .help("Set the address to listen for new connections. (default: \
                                      localhost:3012"))
                      .arg(Arg::with_name("PEER")
                               .help("A WebSocket URL to attempt to connect to at start.")
                               .multiple(true))
                      .arg(Arg::with_name("demo")
                               .short("d")
                               .long("demo")
                               .value_name("DEMO_SERVER")
                               .help("Sleeps for 4 seconds before receiving messages from this \
                                      peer."))
                      .get_matches();

    // Get address of this peer
    let my_addr = String::from(matches.value_of("server").unwrap_or("localhost:3012"));
    let demo_addr = matches.value_of("demo");
    let my_addr_clone = my_addr.clone();

    let mut clock = HashMap::new();
    clock.insert(my_addr.clone(), 0u32);
    let clocks = Arc::new(Mutex::new(clock));
    let connecting_clocks = clocks.clone();
    let mut factory = MessageFactory::build(connecting_clocks.clone()).me(my_addr.clone().as_str());

    if let Some(demo) = demo_addr {
        factory.demo(demo);
    }

    // Create simple websocket that just prints out messages
    let mut me = ws::WebSocket::new(factory).unwrap();

    // Get a sender for ALL connections to the websocket
    let broacaster = me.broadcaster();

    // Setup thread for listening to stdin and sending messages to connections
    let input = thread::spawn(move || {
        let stdin = io::stdin();
        for line in stdin.lock().lines() {

            // Incrementing clock before sending
            let clock_clone = clocks.clone();
            let mut clock_lock = clock_clone.lock().unwrap();
            *clock_lock.get_mut(&my_addr_clone.clone()).unwrap() += 1;

            // Constructing new message
            let message = PeerMessage {
                sender: my_addr_clone.clone(),
                clocks: clock_lock.clone(),
                message: line.unwrap(),
            };

            // Encoding into bytes
            let encoded: Vec<u8> = encode(&message, bincode::SizeLimit::Infinite).unwrap();

            // Sending bytes to all connected clients
            broacaster.send(&*encoded.into_boxed_slice()).unwrap();
        }
    });

    // Connect to any existing peers specified on the cli
    if let Some(peers) = matches.values_of("PEER") {
        for peer in peers {
            me.connect(url::Url::parse(peer).unwrap()).unwrap();
        }
    }

    // Run the websocket
    me.listen(my_addr.clone().as_str()).unwrap();
    input.join().unwrap();

}
<commit_msg>Better checking, reduced unwrap usage<commit_after>/// An example of a client/server agnostic WebSocket that takes input from stdin and sends that
/// input to all other peers.
///
/// For example, to create a network like this:
///
/// 3013 ---- 3012 ---- 3014
///   \        |
///    \       |
///     \      |
///      \     |
///       \    |
///        \   |
///         \  |
///          3015
///
/// Run these commands in separate processes
/// ./peer2peer
/// ./peer2peer --server localhost:3013 ws://localhost:3012
/// ./peer2peer --server localhost:3014 ws://localhost:3012
/// ./peer2peer --server localhost:3015 ws://localhost:3012 ws://localhost:3013
///
/// Stdin on 3012 will be sent to all other peers
/// Stdin on 3013 will be sent to 3012 and 3015
/// Stdin on 3014 will be sent to 3012 only
/// Stdin on 3015 will be sent to 3012 and 2013

extern crate ws;
extern crate url;
extern crate clap;
extern crate env_logger;
extern crate rustc_serialize;
extern crate bincode;
#[macro_use]
extern crate log;

pub mod handler;
pub mod message;

use message::PeerMessage;
use handler::MessageFactory;

use std::collections::hash_map::HashMap;
use std::clone::Clone;
use std::io;
use std::io::prelude::*;
use std::thread;
use std::sync::{Arc, Mutex};

use bincode::rustc_serialize::encode;

use clap::{App, Arg};

fn main() {
    // Setup logging
    env_logger::init().unwrap();

    // Parse command line arguments
    let matches = App::new("Simple Peer 2 Peer")
                      .version("1.0")
                      .author("Jonathan Almeida <hello@jonalmeida.com>")
                      .about("Connect to other peers and listen for incoming connections.")
                      .arg(Arg::with_name("server")
                               .short("s")
                               .long("server")
                               .value_name("SERVER")
                               .help("Set the address to listen for new connections. (default: \
                                      localhost:3012"))
                      .arg(Arg::with_name("PEER")
                               .help("A WebSocket URL to attempt to connect to at start.")
                               .multiple(true))
                      .arg(Arg::with_name("demo")
                               .short("d")
                               .long("demo")
                               .value_name("DEMO_SERVER")
                               .help("Sleeps for 4 seconds before receiving messages from this \
                                      peer."))
                      .get_matches();

    // Get address of this peer
    let my_addr = String::from(matches.value_of("server").unwrap_or("localhost:3012"));
    let demo_addr = matches.value_of("demo");
    let my_addr_clone = my_addr.clone();

    let mut init_clock = HashMap::new();
    init_clock.insert(my_addr.clone(), 0u32);
    let clock = Arc::new(Mutex::new(init_clock));
    let connecting_clocks = clock.clone();
    let mut factory = MessageFactory::build(connecting_clocks.clone()).me(my_addr.clone().as_str());

    if let Some(demo) = demo_addr {
        factory.demo(demo);
    }

    // Create simple websocket that just prints out messages
    let mut me = ws::WebSocket::new(factory).unwrap();

    // Get a sender for ALL connections to the websocket
    let broacaster = me.broadcaster();

    // Setup thread for listening to stdin and sending messages to connections
    let input = thread::spawn(move || {
        let stdin = io::stdin();
        for line in stdin.lock().lines() {

            // Incrementing clock before sending
            let mut clock = clock.lock().unwrap();
            if let Some(clock) = clock.get_mut(&my_addr_clone) {
                *clock += 1;
            }

            // Constructing new message
            let message = PeerMessage {
                sender: my_addr_clone.clone(),
                clocks: clock.clone(),
                message: line.unwrap_or(String::from("n/a")),
            };

            // Encoding into bytes
            if let Ok(encoded_msg) = encode(&message, bincode::SizeLimit::Infinite) {
                // Sending bytes to all connected clients
                broacaster.send(&*encoded_msg.into_boxed_slice()).unwrap();
            }
        }
    });

    // Connect to any existing peers specified on the cli
    if let Some(peers) = matches.values_of("PEER") {
        for peer in peers {
            if let Ok(peer) = url::Url::parse(peer) {
                me.connect(peer).unwrap();
            }
        }
    }

    // Run the websocket
    me.listen(my_addr.clone().as_str()).unwrap();
    input.join().unwrap();

}
<|endoftext|>


<commit_before>use std::io::{Write, BufWriter};
use std::fs::File;

extern crate rand;
use rand::Rng;

mod vector;
mod ray;
mod sphere;
mod hitable;
mod hitable_list;
mod camera;

use vector::{Vec3, dot, cross, unit_vector};
use ray::Ray;
use sphere::{Sphere};
use hitable::{Hitable, hit_record};
use hitable_list::Hitable_list;
use camera::Camera;

fn color<T: Hitable>(ray : Ray, world : &T) -> Vec3 {
  let mut rec = hit_record::new(0.0, Vec3::origin(), Vec3::origin());
  if world.hit(ray, 0.0, std::f64::MAX, &mut rec) {
    let target = rec.p + rec.normal + random_in_unit_sphere();
    return color(Ray::new(rec.p, target - rec.p), world) * 0.5
  }
  else {
    let unit_direction = unit_vector(ray.direction());
    let t = (unit_direction.y() + 1.0) * 0.5;
    return Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t;
  }
}

fn random_in_unit_sphere() -> Vec3 {
  let mut p : Vec3;
  let mut rng = rand::thread_rng();
  loop {
    p = Vec3::new(rng.gen::<f64>(),rng.gen::<f64>(),rng.gen::<f64>()) * 2.0 - Vec3::all(1.0);
    if dot(p, p) < 1.0 { break; }
  }
  p
}

fn random<T: Hitable>(i : i32, j : i32, nx : i32, ny : i32, cam : Camera, world : &T, rng : &mut rand::ThreadRng) -> Vec3 {
  let sample_size = 100;
  let mut samples = 0;
  let mut col = Vec3::origin();
  loop {
    let u = (i as f64 + rng.gen::<f64>()) / nx as f64;
    let v = (j as f64 + rng.gen::<f64>()) / ny as f64;
    let r = cam.get_ray(u, v);

    col += color(r, world);
    samples += 1;
    if samples >= sample_size { break; }
  }

  col /= sample_size as f64;
  return col;
}

fn stratified<T: Hitable>(i : i32, j : i32, nx : i32, ny : i32, cam : Camera, world : &T, rng : &mut rand::ThreadRng) -> Vec3 {
  let mut col = Vec3::origin();
  let grid_size = 5;
  let step = 1.0 / grid_size as f64;
  let low_i = i as f64 - 0.5;
  let low_j = j as f64 - 0.5;

  for ii in 0..grid_size {
    for jj in 0..grid_size {
      let u = ((low_i + ii as f64 * step) + rng.gen_range(0.0, step)) / nx as f64;
      let v = ((low_j + jj as f64 * step) + rng.gen_range(0.0, step)) / ny as f64;
      let r = cam.get_ray(u, v);

      col += color(r, world);
    }
  }

  col /= (grid_size * grid_size) as f64;
  return col;
}

fn main() {
  let nx = 200;
  let ny = 100;
  let data = format!("{}\n{} {}\n{}\n", "P3", nx, ny, 255);
  let f = File::create("target/image.ppm").expect("Unable to create file");
  let mut f = BufWriter::new(f);
  f.write_all(data.as_bytes()).expect("Unable to write data");

  let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
  let horizontal = Vec3::new(4.0, 0.0, 0.0);
  let vertical = Vec3::new(0.0, 2.0, 0.0);
  let origin = Vec3::new(0.0, 0.0, 0.0);

  let cam = Camera::new(lower_left_corner, vertical, horizontal, origin);

  let mut list : Vec<Box<Hitable>> = Vec::new();
  list.push(Box::new(Sphere::new(Vec3::new(0.0,0.0,-1.0), 0.5)));
  list.push(Box::new(Sphere::new(Vec3::new(0.0,-100.5,-1.0), 100.0)));
  let world = Hitable_list::new(list);

  let mut rng = rand::thread_rng();

  for j in (0..ny).rev() {
    for i in 0..nx {
      let col = stratified(i, j, nx, ny, cam, &world, &mut rng);

      let ir = (255.99*col.r()) as i32;
      let ig = (255.99*col.g()) as i32;
      let ib = (255.99*col.b()) as i32;
      f.write_all(format!("{} {} {}\n", ir, ig, ib).as_bytes()).expect("Unable to write data");
    }
  }
}<commit_msg>Add calculation to adjust gamma<commit_after>use std::io::{Write, BufWriter};
use std::fs::File;

extern crate rand;
use rand::Rng;

mod vector;
mod ray;
mod sphere;
mod hitable;
mod hitable_list;
mod camera;

use vector::{Vec3, dot, cross, unit_vector};
use ray::Ray;
use sphere::{Sphere};
use hitable::{Hitable, hit_record};
use hitable_list::Hitable_list;
use camera::Camera;

fn color<T: Hitable>(ray : Ray, world : &T) -> Vec3 {
  let mut rec = hit_record::new(0.0, Vec3::origin(), Vec3::origin());
  if world.hit(ray, 0.0, std::f64::MAX, &mut rec) {
    let target = rec.p + rec.normal + random_in_unit_sphere();
    return color(Ray::new(rec.p, target - rec.p), world) * 0.5
  }
  else {
    let unit_direction = unit_vector(ray.direction());
    let t = (unit_direction.y() + 1.0) * 0.5;
    return Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t;
  }
}

fn random_in_unit_sphere() -> Vec3 {
  let mut p : Vec3;
  let mut rng = rand::thread_rng();
  loop {
    p = Vec3::new(rng.gen::<f64>(),rng.gen::<f64>(),rng.gen::<f64>()) * 2.0 - Vec3::all(1.0);
    if dot(p, p) < 1.0 { break; }
  }
  p
}

fn random<T: Hitable>(i : i32, j : i32, nx : i32, ny : i32, cam : Camera, world : &T, rng : &mut rand::ThreadRng) -> Vec3 {
  let sample_size = 100;
  let mut samples = 0;
  let mut col = Vec3::origin();
  loop {
    let u = (i as f64 + rng.gen::<f64>()) / nx as f64;
    let v = (j as f64 + rng.gen::<f64>()) / ny as f64;
    let r = cam.get_ray(u, v);

    col += color(r, world);
    samples += 1;
    if samples >= sample_size { break; }
  }

  col /= sample_size as f64;
  return col;
}

fn stratified<T: Hitable>(i : i32, j : i32, nx : i32, ny : i32, cam : Camera, world : &T, rng : &mut rand::ThreadRng) -> Vec3 {
  let mut col = Vec3::origin();
  let grid_size = 5;
  let step = 1.0 / grid_size as f64;
  let low_i = i as f64 - 0.5;
  let low_j = j as f64 - 0.5;

  for ii in 0..grid_size {
    for jj in 0..grid_size {
      let u = ((low_i + ii as f64 * step) + rng.gen_range(0.0, step)) / nx as f64;
      let v = ((low_j + jj as f64 * step) + rng.gen_range(0.0, step)) / ny as f64;
      let r = cam.get_ray(u, v);

      col += color(r, world);
    }
  }

  col /= (grid_size * grid_size) as f64;
  return col;
}

fn main() {
  let nx = 200;
  let ny = 100;
  let data = format!("{}\n{} {}\n{}\n", "P3", nx, ny, 255);
  let f = File::create("target/image.ppm").expect("Unable to create file");
  let mut f = BufWriter::new(f);
  f.write_all(data.as_bytes()).expect("Unable to write data");

  let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
  let horizontal = Vec3::new(4.0, 0.0, 0.0);
  let vertical = Vec3::new(0.0, 2.0, 0.0);
  let origin = Vec3::new(0.0, 0.0, 0.0);

  let cam = Camera::new(lower_left_corner, vertical, horizontal, origin);

  let mut list : Vec<Box<Hitable>> = Vec::new();
  list.push(Box::new(Sphere::new(Vec3::new(0.0,0.0,-1.0), 0.5)));
  list.push(Box::new(Sphere::new(Vec3::new(0.0,-100.5,-1.0), 100.0)));
  let world = Hitable_list::new(list);

  let mut rng = rand::thread_rng();

  for j in (0..ny).rev() {
    for i in 0..nx {
      let mut col = stratified(i, j, nx, ny, cam, &world, &mut rng);
      col = Vec3::new(col[0].sqrt(), col[1].sqrt(), col[2].sqrt());

      let ir = (255.99*col.r()) as i32;
      let ig = (255.99*col.g()) as i32;
      let ib = (255.99*col.b()) as i32;
      f.write_all(format!("{} {} {}\n", ir, ig, ib).as_bytes()).expect("Unable to write data");
    }
  }
}<|endoftext|>


<commit_before>#![feature(deque_extras)]
#![feature(box_syntax)]
#![feature(plugin)]
#![plugin(peg_syntax_ext)]

extern crate glob;

use std::collections::HashMap;
use std::fs::File;
use std::io::{stdout, Read, Write};
use std::env;
use std::process;

use self::directory_stack::DirectoryStack;
use self::input_editor::readln;
use self::peg::{parse, Job};
use self::variables::Variables;
use self::history::History;
use self::flow_control::{FlowControl, is_flow_control_command, Statement};
use self::status::{SUCCESS, NO_SUCH_COMMAND, TERMINATED};

pub mod directory_stack;
pub mod to_num;
pub mod input_editor;
pub mod peg;
pub mod variables;
pub mod history;
pub mod flow_control;
pub mod status;

/// This struct will contain all of the data structures related to this
/// instance of the shell.
pub struct Shell {
    variables: Variables,
    flow_control: FlowControl,
    directory_stack: DirectoryStack,
    history: History,
}

impl Shell {
    /// Panics if DirectoryStack construction fails
    pub fn new() -> Self {
        Shell {
            variables: Variables::new(),
            flow_control: FlowControl::new(),
            directory_stack: DirectoryStack::new().expect(""),
            history: History::new(),
        }
    }

    pub fn print_prompt(&self) {
        self.print_prompt_prefix();
        match self.flow_control.current_statement {
            Statement::For(_, _) => self.print_for_prompt(),
            Statement::Default => self.print_default_prompt(),
        }
        if let Err(message) = stdout().flush() {
            println!("{}: failed to flush prompt to stdout", message);
        }

    }

    // TODO eventually this thing should be gone
    fn print_prompt_prefix(&self) {
        let prompt_prefix = self.flow_control.modes.iter().rev().fold(String::new(), |acc, mode| {
            acc +
            if mode.value {
                "+ "
            } else {
                "- "
            }
        });
        print!("{}", prompt_prefix);
    }

    fn print_for_prompt(&self) {
        print!("for> ");
    }

    fn print_default_prompt(&self) {
        let cwd = env::current_dir().ok().map_or("?".to_string(),
                                                 |ref p| p.to_str().unwrap_or("?").to_string());
        print!("ion:{}# ", cwd);
    }

    fn on_command(&mut self, command_string: &str, commands: &HashMap<&str, Command>) {
        self.history.add(command_string.to_string());

        let mut jobs = parse(command_string);

        // Execute commands
        for job in jobs.drain(..) {
            if self.flow_control.collecting_block {
                // TODO move this logic into "end" command
                if job.command == "end" {
                    self.flow_control.collecting_block = false;
                    let block_jobs: Vec<Job> = self.flow_control
                                                   .current_block
                                                   .jobs
                                                   .drain(..)
                                                   .collect();
                    let mut variable = String::new();
                    let mut values: Vec<String> = vec![];
                    if let Statement::For(ref var, ref vals) = self.flow_control.current_statement {
                        variable = var.clone();
                        values = vals.clone();
                    }
                    for value in values {
                        self.variables.set_var(&variable, &value);
                        for job in block_jobs.iter() {
                            self.run_job(job, commands);
                        }
                    }
                    self.flow_control.current_statement = Statement::Default;
                } else {
                    self.flow_control.current_block.jobs.push(job);
                }
            } else {
                if self.flow_control.skipping() && !is_flow_control_command(&job.command) {
                    continue;
                }
                self.run_job(&job, commands);
            }
        }
    }

    fn run_job(&mut self, job: &Job, commands: &HashMap<&str, Command>) {
        let mut job = self.variables.expand_job(job);
        job.expand_globs();
        let exit_status = if let Some(command) = commands.get(job.command.as_str()) {
            Some((*command.main)(job.args.as_slice(), self))
        } else {
            self.run_external_commmand(&job)
        };
        if let Some(code) = exit_status {
            self.variables.set_var("?", &code.to_string());
        }
    }

    /// Returns an exit code if a command was run
    fn run_external_commmand(&mut self, job: &Job) -> Option<i32> {
        let mut command = process::Command::new(&job.command);
        for i in 1..job.args.len() {
            if let Some(arg) = job.args.get(i) {
                command.arg(arg);
            }
        }
        if job.background {
            command.stdin(process::Stdio::null());
        }
        match command.spawn() {
            Ok(mut child) => {
                if job.background {
                    None
                } else {
                    Some(Shell::wait_and_get_status(&mut child, &job.command))
                }

            }
            Err(err) => {
                println!("{}: Failed to execute: {}", job.command, err);
                Some(NO_SUCH_COMMAND)
            }
        }
    }

    // TODO don't pass in command and do printing outside this function
    fn wait_and_get_status(child: &mut process::Child, command: &str) -> i32 {
        match child.wait() {
            Ok(status) => {
                if let Some(code) = status.code() {
                    code
                } else {
                    println!("{}: child ended by signal", command);
                    TERMINATED
                }
            }
            Err(err) => {
                println!("{}: Failed to wait: {}", command, err);
                100 // TODO what should we return here?
            }
        }
    }

}

/// Structure which represents a Terminal's command.
/// This command structure contains a name, and the code which run the
/// functionnality associated to this one, with zero, one or several argument(s).
/// # Example
/// ```
/// let my_command = Command {
///     name: "my_command",
///     help: "Describe what my_command does followed by a newline showing usage",
///     main: box|args: &[String], &mut Shell| -> i32 {
///         println!("Say 'hello' to my command! :-D");
///     }
/// }
/// ```
pub struct Command {
    pub name: &'static str,
    pub help: &'static str,
    pub main: Box<Fn(&[String], &mut Shell) -> i32>,
}

impl Command {
    /// Return the map from command names to commands
    pub fn map() -> HashMap<&'static str, Self> {
        let mut commands: HashMap<&str, Self> = HashMap::new();

        commands.insert("cd",
                        Command {
                            name: "cd",
                            help: "Change the current directory\n    cd <path>",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.directory_stack.cd(args)
                            },
                        });

        commands.insert("dirs",
                        Command {
                            name: "dirs",
                            help: "Display the current directory stack",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.directory_stack.dirs(args)
                            },
                        });

        commands.insert("exit",
                        Command {
                            name: "exit",
                            help: "To exit the curent session",
                            main: box |args: &[String], _: &mut Shell| -> i32 {
                                if let Some(status) = args.get(1) {
                                    if let Ok(status) = status.parse::<i32>() {
                                        process::exit(status);
                                    }
                                }
                                // TODO should use exit status of previously run command, not 0
                                process::exit(0);
                            },
                        });

        commands.insert("let",
                        Command {
                            name: "let",
                            help: "View, set or unset variables",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.variables.let_(args)
                            },
                        });

        commands.insert("read",
                        Command {
                            name: "read",
                            help: "Read some variables\n    read <variable>",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.variables.read(args)
                            },
                        });

        commands.insert("pushd",
                        Command {
                            name: "pushd",
                            help: "Push a directory to the stack",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.directory_stack.pushd(args)
                            },
                        });

        commands.insert("popd",
                        Command {
                            name: "popd",
                            help: "Pop a directory from the stack",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.directory_stack.popd(args)
                            },
                        });

        commands.insert("history",
                        Command {
                            name: "history",
                            help: "Display a log of all commands previously executed",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.history.history(args)
                            },
                        });

        commands.insert("if",
                        Command {
                            name: "if",
                            help: "Conditionally execute code",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.flow_control.if_(args)
                            },
                        });

        commands.insert("else",
                        Command {
                            name: "else",
                            help: "Execute code if a previous condition was false",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.flow_control.else_(args)
                            },
                        });

        commands.insert("end",
                        Command {
                            name: "end",
                            help: "End a code block",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.flow_control.end(args)
                            },
                        });

        commands.insert("for",
                        Command {
                            name: "for",
                            help: "Iterate through a list",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.flow_control.for_(args)
                            },
                        });

        let command_helper: HashMap<&'static str, &'static str> = commands.iter()
                                                                          .map(|(k, v)| {
                                                                              (*k, v.help)
                                                                          })
                                                                          .collect();

        commands.insert("help",
                        Command {
                            name: "help",
                            help: "Display helpful information about a given command, or list \
                                   commands if none specified\n    help <command>",
                            main: box move |args: &[String], _: &mut Shell| -> i32 {
                                if let Some(command) = args.get(1) {
                                    if command_helper.contains_key(command.as_str()) {
                                        match command_helper.get(command.as_str()) {
                                            Some(help) => println!("{}", help),
                                            None => {
                                                println!("Command helper not found [run 'help']...")
                                            }
                                        }
                                    } else {
                                        println!("Command helper not found [run 'help']...");
                                    }
                                } else {
                                    for (command, _help) in command_helper.iter() {
                                        println!("{}", command);
                                    }
                                }
                                SUCCESS
                            },
                        });

        commands
    }
}

fn main() {
    let commands = Command::map();
    let mut shell = Shell::new();

    for arg in env::args().skip(1) {
        let mut command_list = String::new();
        if let Ok(mut file) = File::open(&arg) {
            if let Err(message) = file.read_to_string(&mut command_list) {
                println!("{}: Failed to read {}", message, arg);
            }
        }
        shell.on_command(&command_list, &commands);
        return;
    }

    loop {
        shell.print_prompt();

        if let Some(command) = readln() {
            let command = command.trim();
            if !command.is_empty() {
                shell.on_command(command, &commands);
            }
        } else {
            break;
        }
    }
}
<commit_msg>WIP job control with threads<commit_after>#![feature(deque_extras)]
#![feature(box_syntax)]
#![feature(plugin)]
#![plugin(peg_syntax_ext)]

extern crate glob;

use std::collections::HashMap;
use std::fs::File;
use std::io::{stdout, Read, Write};
use std::env;
use std::process;
use std::thread;

use self::directory_stack::DirectoryStack;
use self::input_editor::readln;
use self::peg::{parse, Job};
use self::variables::Variables;
use self::history::History;
use self::flow_control::{FlowControl, is_flow_control_command, Statement};
use self::status::{SUCCESS, NO_SUCH_COMMAND, TERMINATED};

pub mod directory_stack;
pub mod to_num;
pub mod input_editor;
pub mod peg;
pub mod variables;
pub mod history;
pub mod flow_control;
pub mod status;

/// This struct will contain all of the data structures related to this
/// instance of the shell.
pub struct Shell {
    variables: Variables,
    flow_control: FlowControl,
    directory_stack: DirectoryStack,
    history: History,
}

impl Shell {
    /// Panics if DirectoryStack construction fails
    pub fn new() -> Self {
        Shell {
            variables: Variables::new(),
            flow_control: FlowControl::new(),
            directory_stack: DirectoryStack::new().expect(""),
            history: History::new(),
        }
    }

    pub fn print_prompt(&self) {
        self.print_prompt_prefix();
        match self.flow_control.current_statement {
            Statement::For(_, _) => self.print_for_prompt(),
            Statement::Default => self.print_default_prompt(),
        }
        if let Err(message) = stdout().flush() {
            println!("{}: failed to flush prompt to stdout", message);
        }

    }

    // TODO eventually this thing should be gone
    fn print_prompt_prefix(&self) {
        let prompt_prefix = self.flow_control.modes.iter().rev().fold(String::new(), |acc, mode| {
            acc +
            if mode.value {
                "+ "
            } else {
                "- "
            }
        });
        print!("{}", prompt_prefix);
    }

    fn print_for_prompt(&self) {
        print!("for> ");
    }

    fn print_default_prompt(&self) {
        let cwd = env::current_dir().ok().map_or("?".to_string(),
                                                 |ref p| p.to_str().unwrap_or("?").to_string());
        print!("ion:{}# ", cwd);
    }

    fn on_command(&mut self, command_string: &str, commands: &HashMap<&str, Command>) {
        self.history.add(command_string.to_string());

        let mut jobs = parse(command_string);

        // Execute commands
        for job in jobs.drain(..) {
            if self.flow_control.collecting_block {
                // TODO move this logic into "end" command
                if job.command == "end" {
                    self.flow_control.collecting_block = false;
                    let block_jobs: Vec<Job> = self.flow_control
                                                   .current_block
                                                   .jobs
                                                   .drain(..)
                                                   .collect();
                    let mut variable = String::new();
                    let mut values: Vec<String> = vec![];
                    if let Statement::For(ref var, ref vals) = self.flow_control.current_statement {
                        variable = var.clone();
                        values = vals.clone();
                    }
                    for value in values {
                        self.variables.set_var(&variable, &value);
                        for job in block_jobs.iter() {
                            self.run_job(job, commands);
                        }
                    }
                    self.flow_control.current_statement = Statement::Default;
                } else {
                    self.flow_control.current_block.jobs.push(job);
                }
            } else {
                if self.flow_control.skipping() && !is_flow_control_command(&job.command) {
                    continue;
                }
                self.run_job(&job, commands);
            }
        }
    }

    fn run_job(&mut self, job: &Job, commands: &HashMap<&str, Command>) {
        let mut job = self.variables.expand_job(job);
        job.expand_globs();
        let exit_status = if let Some(command) = commands.get(job.command.as_str()) {
            Some((*command.main)(job.args.as_slice(), self))
        } else {
            self.run_external_commmand(&job)
        };
        if let Some(code) = exit_status {
            self.variables.set_var("?", &code.to_string());
        }
    }

    /// Returns an exit code if a command was run
    fn run_external_commmand(&mut self, job: &Job) -> Option<i32> {
        let mut command = process::Command::new(&job.command);
        for i in 1..job.args.len() {
            if let Some(arg) = job.args.get(i) {
                command.arg(arg);
            }
        }
        if job.background {
            command.stdin(process::Stdio::null());
            thread::spawn(move || {
                if let Ok(mut child) = command.spawn() {
                    Shell::wait_and_get_status(&mut child, &job.command);
                }
            });
            None
        } else {
            match command.spawn() {
                Ok(mut child) => Some(Shell::wait_and_get_status(&mut child, &job.command)),
                Err(err) => {
                    println!("{}: Failed to execute: {}", job.command, err);
                    Some(NO_SUCH_COMMAND)
                }
            }
        }
    }

    // TODO don't pass in command and do printing outside this function
    fn wait_and_get_status(child: &mut process::Child, command: &str) -> i32 {
        match child.wait() {
            Ok(status) => {
                if let Some(code) = status.code() {
                    code
                } else {
                    println!("{}: child ended by signal", command);
                    TERMINATED
                }
            }
            Err(err) => {
                println!("{}: Failed to wait: {}", command, err);
                100 // TODO what should we return here?
            }
        }
    }
}

/// Structure which represents a Terminal's command.
/// This command structure contains a name, and the code which run the
/// functionnality associated to this one, with zero, one or several argument(s).
/// # Example
/// ```
/// let my_command = Command {
///     name: "my_command",
///     help: "Describe what my_command does followed by a newline showing usage",
///     main: box|args: &[String], &mut Shell| -> i32 {
///         println!("Say 'hello' to my command! :-D");
///     }
/// }
/// ```
pub struct Command {
    pub name: &'static str,
    pub help: &'static str,
    pub main: Box<Fn(&[String], &mut Shell) -> i32>,
}

impl Command {
    /// Return the map from command names to commands
    pub fn map() -> HashMap<&'static str, Self> {
        let mut commands: HashMap<&str, Self> = HashMap::new();

        commands.insert("cd",
                        Command {
                            name: "cd",
                            help: "Change the current directory\n    cd <path>",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.directory_stack.cd(args)
                            },
                        });

        commands.insert("dirs",
                        Command {
                            name: "dirs",
                            help: "Display the current directory stack",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.directory_stack.dirs(args)
                            },
                        });

        commands.insert("exit",
                        Command {
                            name: "exit",
                            help: "To exit the curent session",
                            main: box |args: &[String], _: &mut Shell| -> i32 {
                                if let Some(status) = args.get(1) {
                                    if let Ok(status) = status.parse::<i32>() {
                                        process::exit(status);
                                    }
                                }
                                // TODO should use exit status of previously run command, not 0
                                process::exit(0);
                            },
                        });

        commands.insert("let",
                        Command {
                            name: "let",
                            help: "View, set or unset variables",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.variables.let_(args)
                            },
                        });

        commands.insert("read",
                        Command {
                            name: "read",
                            help: "Read some variables\n    read <variable>",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.variables.read(args)
                            },
                        });

        commands.insert("pushd",
                        Command {
                            name: "pushd",
                            help: "Push a directory to the stack",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.directory_stack.pushd(args)
                            },
                        });

        commands.insert("popd",
                        Command {
                            name: "popd",
                            help: "Pop a directory from the stack",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.directory_stack.popd(args)
                            },
                        });

        commands.insert("history",
                        Command {
                            name: "history",
                            help: "Display a log of all commands previously executed",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.history.history(args)
                            },
                        });

        commands.insert("if",
                        Command {
                            name: "if",
                            help: "Conditionally execute code",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.flow_control.if_(args)
                            },
                        });

        commands.insert("else",
                        Command {
                            name: "else",
                            help: "Execute code if a previous condition was false",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.flow_control.else_(args)
                            },
                        });

        commands.insert("end",
                        Command {
                            name: "end",
                            help: "End a code block",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.flow_control.end(args)
                            },
                        });

        commands.insert("for",
                        Command {
                            name: "for",
                            help: "Iterate through a list",
                            main: box |args: &[String], shell: &mut Shell| -> i32 {
                                shell.flow_control.for_(args)
                            },
                        });

        let command_helper: HashMap<&'static str, &'static str> = commands.iter()
                                                                          .map(|(k, v)| {
                                                                              (*k, v.help)
                                                                          })
                                                                          .collect();

        commands.insert("help",
                        Command {
                            name: "help",
                            help: "Display helpful information about a given command, or list \
                                   commands if none specified\n    help <command>",
                            main: box move |args: &[String], _: &mut Shell| -> i32 {
                                if let Some(command) = args.get(1) {
                                    if command_helper.contains_key(command.as_str()) {
                                        match command_helper.get(command.as_str()) {
                                            Some(help) => println!("{}", help),
                                            None => {
                                                println!("Command helper not found [run 'help']...")
                                            }
                                        }
                                    } else {
                                        println!("Command helper not found [run 'help']...");
                                    }
                                } else {
                                    for (command, _help) in command_helper.iter() {
                                        println!("{}", command);
                                    }
                                }
                                SUCCESS
                            },
                        });

        commands
    }
}

fn main() {
    let commands = Command::map();
    let mut shell = Shell::new();

    for arg in env::args().skip(1) {
        let mut command_list = String::new();
        if let Ok(mut file) = File::open(&arg) {
            if let Err(message) = file.read_to_string(&mut command_list) {
                println!("{}: Failed to read {}", message, arg);
            }
        }
        shell.on_command(&command_list, &commands);
        return;
    }

    loop {
        shell.print_prompt();

        if let Some(command) = readln() {
            let command = command.trim();
            if !command.is_empty() {
                shell.on_command(command, &commands);
            }
        } else {
            break;
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::{thread, time};
use rand::Rng;

static NTHREADS: i32 = 64;

fn main() {
    // Channels have two endpoints: the `Sender<T>` and the `Receiver<T>`,
    // where `T` is the type of the message to be transferred
    // (type annotation is superfluous)
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();

    for id in 0..NTHREADS {
        // The sender endpoint can be copied
        let thread_tx = tx.clone();

        // Each thread will send its id via the channel
        thread::spawn(move || {

            let sleep = rand::thread_rng().gen_range(1, 1001);
            thread::sleep(time::Duration::from_millis(sleep));

            // The thread takes ownership over `thread_tx`
            // Each thread queues a message in the channel
            thread_tx.send(id).unwrap();

            // Sending is a non-blocking operation, the thread will continue
            // immediately after sending its message
            println!("thread {} finished", id);
        });
    }

    // Here, all the messages are collected
    let mut ids = Vec::with_capacity(NTHREADS as usize);
    for _ in 0..NTHREADS {
        // The `recv` method picks a message from the channel
        // `recv` will block the current thread if there no messages available
        ids.push(rx.recv());
    }

    // Show the order in which the messages were sent
    println!("{:?}", ids);
}
<commit_msg>Add actor + actor ref<commit_after>extern crate rand;

use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::{thread, time};
use rand::Rng;

#[derive(Debug)]
struct Actor {
    inbox: Receiver<i32>
}

#[derive(Debug)]
struct ActorRef {
    outbox: Sender<i32>
}

impl Actor {
    fn new() -> (ActorRef, Actor) {
        let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();
        return (ActorRef{outbox: tx}, Actor{inbox: rx})
    }
}

fn main() {
    let a = Actor::new();
    println!("{:?}", a);
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

#[macro_use]
extern crate clap;

extern crate env_logger;
#[macro_use]
extern crate log;

extern crate stats;

extern crate prometheus;
extern crate hyper;

mod timing_analyzer;
mod error_analyzer;
mod args;
mod filter;
mod log_parser;
use log_parser::log_events::*;
mod render;
mod request_response_matcher;
use request_response_matcher::*;
mod http_handler;
mod result;

fn main() {
    env_logger::init().expect("Failed to initialize logging.");

    let args = args::parse_args(env::args()).expect("Failed to parse arguments.");

    let result = run(&args);

    let mut stream;
    let mut renderer: Box<render::Renderer>;

    renderer = match args.graphite_server {
        Some(ref graphite_server) => {
            stream = TcpStream::connect((graphite_server.as_ref(), args.graphite_port.unwrap()))
                .expect("Could not connect to the Graphite server");

            Box::new(render::graphite::GraphiteRenderer::new(UTC::now(),
                                                             args.graphite_prefix.clone(),
                                                             &mut stream))
        }
        None => Box::new(render::TerminalRenderer::new()),
    };

    renderer.render(result);

    if args.prometheus_listen.is_some() {
        let binding_address = args.prometheus_listen.clone().unwrap();
        http_handler::listen_http(args, &binding_address);
    }

}

fn run(args: &args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult {
    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).expect("Failed to open file.")),
    };

    let pairs = extract_pairs(input, &args.conditions);
    let timing_result = timing_analyzer::analyze(&pairs);
    let error_result = error_analyzer::analyze(&pairs);

    result::RequestLogAnalyzerResult {
        count: pairs.len(),
        timing: timing_result,
        error: error_result,
    }
}

fn extract_pairs(input: Box<io::Read>,
                 conditions: &filter::FilterConditions)
                 -> Vec<Box<RequestResponsePair>> {
    let reader = io::BufReader::new(input);

    let mut events_iterator = reader.lines()
        .map(parse_event)
        .filter(|event| event.is_ok())
        .map(|event| event.unwrap());

    let pairs_iterator =
        request_response_matcher::RequestResponsePairIterator::new(&mut events_iterator);

    let pairs: Vec<Box<RequestResponsePair>> =
        pairs_iterator.filter(|pair| filter::matches_filter(pair, conditions))
            .map(|pair| Box::new(pair))
            .collect();

    pairs
}

fn parse_event(line: Result<String, std::io::Error>) -> Result<LogEvent, &'static str> {
    match line {
        Ok(line) => log_parser::parse_line(&line),
        Err(_) => Err("Failed to read line."),
    }
}

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

    #[test]
    fn test_run() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/simple-1.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
        };

        let result = run(&args);
        assert_eq!(result.count, 2);

        let timing = result.timing.unwrap();
        assert_eq!(timing.min, 7);
        assert_eq!(timing.max, 10);
    }

    #[test]
    fn test_run_ignore_broken_lines() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/broken.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
        };

        let result = run(&args);
        assert_eq!(result.count, 1);
    }

    #[test]
    fn test_parse_event() {
        let lines = vec![Ok(String::from("08/Apr/2016:09:57:47 +0200 [001] -> GET \
                                          /content/some/page.html HTTP/1.1")),
                         Ok(String::from("08/Apr/2016:09:57:47 +0200 [001] <- 200 text/html 7ms"))];

        let result: Vec<Result<LogEvent, &str>> = lines.into_iter().map(parse_event).collect();
        assert_eq!(result.len(), 2);
    }
}
<commit_msg>Integrate the iterator handling again<commit_after>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

#[macro_use]
extern crate clap;

extern crate env_logger;
#[macro_use]
extern crate log;

extern crate stats;

extern crate prometheus;
extern crate hyper;

mod timing_analyzer;
mod error_analyzer;
mod args;
mod filter;
mod log_parser;
use log_parser::log_events::*;
mod render;
mod request_response_matcher;
use request_response_matcher::*;
mod http_handler;
mod result;

fn main() {
    env_logger::init().expect("Failed to initialize logging.");

    let args = args::parse_args(env::args()).expect("Failed to parse arguments.");

    let result = run(&args);

    let mut stream;
    let mut renderer: Box<render::Renderer>;

    renderer = match args.graphite_server {
        Some(ref graphite_server) => {
            stream = TcpStream::connect((graphite_server.as_ref(), args.graphite_port.unwrap()))
                .expect("Could not connect to the Graphite server");

            Box::new(render::graphite::GraphiteRenderer::new(UTC::now(),
                                                             args.graphite_prefix.clone(),
                                                             &mut stream))
        }
        None => Box::new(render::TerminalRenderer::new()),
    };

    renderer.render(result);

    if args.prometheus_listen.is_some() {
        let binding_address = args.prometheus_listen.clone().unwrap();
        http_handler::listen_http(args, &binding_address);
    }

}

fn run(args: &args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult {
    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).expect("Failed to open file.")),
    };

    let reader = io::BufReader::new(input);

    let mut events_iterator = reader.lines()
        .map(parse_event)
        .filter(|event| event.is_ok())
        .map(|event| event.unwrap());

    let pairs_iterator =
        request_response_matcher::RequestResponsePairIterator::new(&mut events_iterator);

    let pairs: Vec<Box<RequestResponsePair>> =
        pairs_iterator.filter(|pair| filter::matches_filter(pair, &args.conditions))
            .map(|pair| Box::new(pair))
            .collect();

    let timing_result = timing_analyzer::analyze(&pairs);
    let error_result = error_analyzer::analyze(&pairs);

    result::RequestLogAnalyzerResult {
        count: pairs.len(),
        timing: timing_result,
        error: error_result,
    }
}

fn parse_event(line: Result<String, std::io::Error>) -> Result<LogEvent, &'static str> {
    match line {
        Ok(line) => log_parser::parse_line(&line),
        Err(_) => Err("Failed to read line."),
    }
}

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

    #[test]
    fn test_run() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/simple-1.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
        };

        let result = run(&args);
        assert_eq!(result.count, 2);

        let timing = result.timing.unwrap();
        assert_eq!(timing.min, 7);
        assert_eq!(timing.max, 10);
    }

    #[test]
    fn test_run_ignore_broken_lines() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/broken.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
        };

        let result = run(&args);
        assert_eq!(result.count, 1);
    }

    #[test]
    fn test_parse_event() {
        let lines = vec![Ok(String::from("08/Apr/2016:09:57:47 +0200 [001] -> GET \
                                          /content/some/page.html HTTP/1.1")),
                         Ok(String::from("08/Apr/2016:09:57:47 +0200 [001] <- 200 text/html 7ms"))];

        let result: Vec<Result<LogEvent, &str>> = lines.into_iter().map(parse_event).collect();
        assert_eq!(result.len(), 2);
    }
}
<|endoftext|>