<commit_before>extern crate rand;
mod game;
use game::{Game, State};
mod user_interface;
use user_interface::UserInterface;
mod dictionary;
use dictionary::Dict;
mod image;
use image::RewardingScheme;

use std::env;
use std::error::Error;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;

const COMMANDLINE_HELP: &str = "\
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
HANGMAN GAME
         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (\")
    `-j                |
      `-._(           /
         |_\\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lives:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: hangman [FILE]...
        hangman (-c|--help)
        hangman

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to 'hangman-words.txt'. In
case no `[FILE]` is found, a template configuration file 'hangman-words.txt' is
written into the current working directory. Multiple `[FILE]`s are concatted.

`[FILE]` is an Ascii file containing 4 different line-types:

- lines starting with `#` is ignored.

- lines starting with `|` are part of an optional Ascii-Art image shown
  progressively in the course of the game. If not defined here, built in
  Ascii-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:
   `:success-rewarding`       Every guessed character shows a bit more of the
                              image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                  image. Choose this mode together with a
                              traditional gallows image (not built in).

- lines starting with `-` are _guessing strings_. At the beginning of the game
  one line is randomly chosen and all characters are hidden.  In
  order to give additional hints it is possible to enclose some characters with
  `+*+`.  These words are then displayed in clear. For example a config line:
  `+- Guess *me*+` is shown in the game as: `_ _ _ _ _ _ m e`.

";

const LIVES: u8 = 7;
const PATHSTR: &str = "hangman-words.txt";
const OFFSET: (usize, usize) = (1, 1);

const CONF_TEMPLATE: &str = "\
### This is a sample word-list for the hangman game

### Sample word-list
#   ----------------
#
# Before every game one line is randomly chosen.
# Empty lines and lines starting with # are ignored.
# Lines with guessing strings must start with '-'.
# Words enclosed with * are not hidden when the game starts:
#   - *guess *me
# shows in the game:
#   g u e s s   _ _
#

- *guess* me
- hang*man*
- good luck

# Lines starting with ':' are game modifier. They change
# the logic how the image is progressively disclosed:
#   ':success-rewarding'       Every guessed character shows a bit more of the
#                              image. This mode is default.
#   ':traditional-rewarding'   Every lost live discloses a bit more of the
#                  image. Choose this mode together with a
#                              traditional gallows image (not built in).

### Sample custom image
#   -------------------
#
# Instead of built in images a word list can use a
# custom image. Lines starting with '|' are interpreted
# as image-lines. Delete '#' in the following lines to
# try out this feature.

#:traditional-rewarding
#|  ______
#|  |    |
#|  |    O
#|  |   /|\\
#|  |    |
#|  |   / \\
#|__|_____
#||      |___
#||_________|
";

const CONF_DEMO: &str = "- *Demo: add own words to config file and *start* again!*";

// ------------------ MAIN ---------------------------------------------

pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
    let mut f = File::open(pathstr)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            println!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths: Vec<PathBuf> = env::args().skip(1).map(|s| PathBuf::from(s)).collect();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.is_empty() {
        conf_file_paths.push(PathBuf::from(PATHSTR))
    };

    // read and concat all config files given on command line
    let cwd = env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {
        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s) => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        println!(
                            "As no config-file :\n\
                             \t{:?}\n\
                             was found a template file is written in the \
                             current working directory.\n\
                             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                            path, cwd
                        );
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        println!(
                            "Couldn't write hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            Error::description(&why),
                            cwd
                        );
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE GAME

    let mut ui = UserInterface::new(&config, OFFSET);

    let mut dict = Dict::new(&config);
    if dict.len() == 0 {
        println!(
            "No guessing words in config-file(s):\n\
             \t{:?}\n\
             were found. Current working directory is:\n\
             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
            conf_file_paths, cwd
        );
        // wait for [Enter] key
        let input = &mut String::new();
        io::stdin().read_line(input).unwrap();
        dict = Dict::new(CONF_DEMO);
    };

    // PLAY

    'playing: loop {
        let mut game = Game::new(&(dict.get_random_word()), LIVES);
        let chars_to_guess = game.visible_chars();

        // The game loop
        let mut line_buffer = String::new();
        let reader = io::stdin();
        print!("\x1b[2J"); // clear screen
        'running_game: loop {
            if game.lives > 0 || ui.image.rewarding_scheme == RewardingScheme::UnhideWhenLostLife {
                ui.image.disclose(
                    (game.lives as usize, LIVES as usize),
                    (game.visible_chars(), chars_to_guess),
                );
            }
            ui.message = format!("{}", game);
            println!("{}", ui);

            match game.get_state() {
                State::Victory => {
                    println!("Congratulations! You won!");
                    break 'running_game;
                }
                State::Defeat => {
                    println!("Sorry, you lost! Better luck next time!");
                    break 'running_game;
                }
                _ => {}
            }

            print!("Type a letter then type [Enter]: ");
            io::stdout().flush().unwrap();

            // Read next char and send it
            line_buffer.clear();
            reader.read_line(&mut line_buffer).unwrap();

            game.guess(match line_buffer.chars().next() {
                Some(char_) => char_,
                None => continue 'running_game,
            });
        }

        print!("New game? Type [Y]es or [n]o: ");
        io::stdout().flush().unwrap();

        // Read next char
        line_buffer.clear();
        reader.read_line(&mut line_buffer).unwrap();
        println! {"(c) Jens Getreu, 2016."};

        match line_buffer.trim_end().chars().next() {
            Some(char_) if char_ == 'N' || char_ == 'n' => break 'playing,
            Some(_) | None => {}
        }
    }
}
<commit_msg>prefer turbo fish in collect()<commit_after>extern crate rand;
mod game;
use game::{Game, State};
mod user_interface;
use user_interface::UserInterface;
mod dictionary;
use dictionary::Dict;
mod image;
use image::RewardingScheme;

use std::env;
use std::error::Error;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;

const COMMANDLINE_HELP: &str = "\
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
HANGMAN GAME
         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (\")
    `-j                |
      `-._(           /
         |_\\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lives:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: hangman [FILE]...
        hangman (-c|--help)
        hangman

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to 'hangman-words.txt'. In
case no `[FILE]` is found, a template configuration file 'hangman-words.txt' is
written into the current working directory. Multiple `[FILE]`s are concatted.

`[FILE]` is an Ascii file containing 4 different line-types:

- lines starting with `#` is ignored.

- lines starting with `|` are part of an optional Ascii-Art image shown
  progressively in the course of the game. If not defined here, built in
  Ascii-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:
   `:success-rewarding`       Every guessed character shows a bit more of the
                              image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                  image. Choose this mode together with a
                              traditional gallows image (not built in).

- lines starting with `-` are _guessing strings_. At the beginning of the game
  one line is randomly chosen and all characters are hidden.  In
  order to give additional hints it is possible to enclose some characters with
  `+*+`.  These words are then displayed in clear. For example a config line:
  `+- Guess *me*+` is shown in the game as: `_ _ _ _ _ _ m e`.

";

const LIVES: u8 = 7;
const PATHSTR: &str = "hangman-words.txt";
const OFFSET: (usize, usize) = (1, 1);

const CONF_TEMPLATE: &str = "\
### This is a sample word-list for the hangman game

### Sample word-list
#   ----------------
#
# Before every game one line is randomly chosen.
# Empty lines and lines starting with # are ignored.
# Lines with guessing strings must start with '-'.
# Words enclosed with * are not hidden when the game starts:
#   - *guess *me
# shows in the game:
#   g u e s s   _ _
#

- *guess* me
- hang*man*
- good luck

# Lines starting with ':' are game modifier. They change
# the logic how the image is progressively disclosed:
#   ':success-rewarding'       Every guessed character shows a bit more of the
#                              image. This mode is default.
#   ':traditional-rewarding'   Every lost live discloses a bit more of the
#                  image. Choose this mode together with a
#                              traditional gallows image (not built in).

### Sample custom image
#   -------------------
#
# Instead of built in images a word list can use a
# custom image. Lines starting with '|' are interpreted
# as image-lines. Delete '#' in the following lines to
# try out this feature.

#:traditional-rewarding
#|  ______
#|  |    |
#|  |    O
#|  |   /|\\
#|  |    |
#|  |   / \\
#|__|_____
#||      |___
#||_________|
";

const CONF_DEMO: &str = "- *Demo: add own words to config file and *start* again!*";

// ------------------ MAIN ---------------------------------------------

pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
    let mut f = File::open(pathstr)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            println!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths = env::args()
                .skip(1)
                .map(|s| PathBuf::from(s))
                .collect::<Vec<PathBuf>>();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.is_empty() {
        conf_file_paths.push(PathBuf::from(PATHSTR))
    };

    // read and concat all config files given on command line
    let cwd = env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {
        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s) => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        println!(
                            "As no config-file :\n\
                             \t{:?}\n\
                             was found a template file is written in the \
                             current working directory.\n\
                             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                            path, cwd
                        );
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        println!(
                            "Couldn't write hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            Error::description(&why),
                            cwd
                        );
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE GAME

    let mut ui = UserInterface::new(&config, OFFSET);

    let mut dict = Dict::new(&config);
    if dict.len() == 0 {
        println!(
            "No guessing words in config-file(s):\n\
             \t{:?}\n\
             were found. Current working directory is:\n\
             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
            conf_file_paths, cwd
        );
        // wait for [Enter] key
        let input = &mut String::new();
        io::stdin().read_line(input).unwrap();
        dict = Dict::new(CONF_DEMO);
    };

    // PLAY

    'playing: loop {
        let mut game = Game::new(&(dict.get_random_word()), LIVES);
        let chars_to_guess = game.visible_chars();

        // The game loop
        let mut line_buffer = String::new();
        let reader = io::stdin();
        print!("\x1b[2J"); // clear screen
        'running_game: loop {
            if game.lives > 0 || ui.image.rewarding_scheme == RewardingScheme::UnhideWhenLostLife {
                ui.image.disclose(
                    (game.lives as usize, LIVES as usize),
                    (game.visible_chars(), chars_to_guess),
                );
            }
            ui.message = format!("{}", game);
            println!("{}", ui);

            match game.get_state() {
                State::Victory => {
                    println!("Congratulations! You won!");
                    break 'running_game;
                }
                State::Defeat => {
                    println!("Sorry, you lost! Better luck next time!");
                    break 'running_game;
                }
                _ => {}
            }

            print!("Type a letter then type [Enter]: ");
            io::stdout().flush().unwrap();

            // Read next char and send it
            line_buffer.clear();
            reader.read_line(&mut line_buffer).unwrap();

            game.guess(match line_buffer.chars().next() {
                Some(char_) => char_,
                None => continue 'running_game,
            });
        }

        print!("New game? Type [Y]es or [n]o: ");
        io::stdout().flush().unwrap();

        // Read next char
        line_buffer.clear();
        reader.read_line(&mut line_buffer).unwrap();
        println! {"(c) Jens Getreu, 2016."};

        match line_buffer.trim_end().chars().next() {
            Some(char_) if char_ == 'N' || char_ == 'n' => break 'playing,
            Some(_) | None => {}
        }
    }
}
<|endoftext|>


<commit_before>extern crate nom_lua53;

use std::io::{self, Read};
use nom_lua53::{parse_all, ParseResult, Statement};

fn main() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).expect("Couldn't read from stdin");

    match parse_all(&*input) {
        ParseResult::Done(blk) => {
            for stmt in blk.stmts {
                println!("{:?}", stmt);
                match stmt {
                    Statement::LVarAssign(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    Statement::Assignment(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    _ => {}
                }
            }
        }

        ParseResult::Error(rest, ss) => {
            println!("Error. statements == {:#?}", ss);
            println!("rest == '{}'", String::from_utf8_lossy(rest));
        }
    }
}
<commit_msg>New enum to represent a runtime value<commit_after>extern crate nom_lua53;

use std::io::{self, Read};
use nom_lua53::{parse_all, ParseResult, Statement};

#[derive(Debug)]
enum LuaValue {
    Integer(isize),
    Float(f64),
    Boolean(bool),
    Str(String),
}

fn main() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).expect("Couldn't read from stdin");

    match parse_all(&*input) {
        ParseResult::Done(blk) => {
            for stmt in blk.stmts {
                println!("{:?}", stmt);
                match stmt {
                    Statement::LVarAssign(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    Statement::Assignment(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    _ => {}
                }
            }
        }

        ParseResult::Error(rest, ss) => {
            println!("Error. statements == {:#?}", ss);
            println!("rest == '{}'", String::from_utf8_lossy(rest));
        }
    }
}
<|endoftext|>


<commit_before>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![feature(static_in_const)]

#![allow(unknown_lints, missing_docs_in_private_items)]

extern crate clippy_lints;
extern crate getopts;
extern crate rustc;
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_plugin;
extern crate syntax;

use rustc_driver::{driver, CompilerCalls, RustcDefaultCalls, Compilation};
use rustc::session::{config, Session};
use rustc::session::config::{Input, ErrorOutputType};
use std::path::PathBuf;
use std::process::{self, Command};
use syntax::ast;
use std::io::{self, Write};

use clippy_lints::utils::cargo;

struct ClippyCompilerCalls {
    default: RustcDefaultCalls,
    run_lints: bool,
}

impl ClippyCompilerCalls {
    fn new(run_lints: bool) -> Self {
        ClippyCompilerCalls {
            default: RustcDefaultCalls,
            run_lints: run_lints,
        }
    }
}

impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
    fn early_callback(
        &mut self,
        matches: &getopts::Matches,
        sopts: &config::Options,
        cfg: &ast::CrateConfig,
        descriptions: &rustc_errors::registry::Registry,
        output: ErrorOutputType
    ) -> Compilation {
        self.default.early_callback(matches, sopts, cfg, descriptions, output)
    }
    fn no_input(
        &mut self,
        matches: &getopts::Matches,
        sopts: &config::Options,
        cfg: &ast::CrateConfig,
        odir: &Option<PathBuf>,
        ofile: &Option<PathBuf>,
        descriptions: &rustc_errors::registry::Registry
    ) -> Option<(Input, Option<PathBuf>)> {
        self.default.no_input(matches, sopts, cfg, odir, ofile, descriptions)
    }
    fn late_callback(
        &mut self,
        matches: &getopts::Matches,
        sess: &Session,
        input: &Input,
        odir: &Option<PathBuf>,
        ofile: &Option<PathBuf>
    ) -> Compilation {
        self.default.late_callback(matches, sess, input, odir, ofile)
    }
    fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
        let mut control = self.default.build_controller(sess, matches);

        if self.run_lints {
            let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
            control.after_parse.callback = Box::new(move |state| {
                {
                    let mut registry = rustc_plugin::registry::Registry::new(state.session,
                                                                             state.krate
                                                                                .as_ref()
                                                                                .expect("at this compilation stage \
                                                                                the krate must be parsed")
                                                                                .span);
                    registry.args_hidden = Some(Vec::new());
                    clippy_lints::register_plugins(&mut registry);

                    let rustc_plugin::registry::Registry { early_lint_passes,
                                                           late_lint_passes,
                                                           lint_groups,
                                                           llvm_passes,
                                                           attributes,
                                                           mir_passes,
                                                           .. } = registry;
                    let sess = &state.session;
                    let mut ls = sess.lint_store.borrow_mut();
                    for pass in early_lint_passes {
                        ls.register_early_pass(Some(sess), true, pass);
                    }
                    for pass in late_lint_passes {
                        ls.register_late_pass(Some(sess), true, pass);
                    }

                    for (name, to) in lint_groups {
                        ls.register_group(Some(sess), true, name, to);
                    }

                    sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                    sess.mir_passes.borrow_mut().extend(mir_passes);
                    sess.plugin_attributes.borrow_mut().extend(attributes);
                }
                old(state);
            });
        }

        control
    }
}

use std::path::Path;

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

The feature `cargo-clippy` is automatically defined for convenience. You can use
it to allow or deny lints from the code, eg.:

    #[cfg_attr(feature = "cargo-clippy", allow(needless_lifetimes))]
"#;

#[allow(print_stdout)]
fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

#[allow(print_stdout)]
fn show_version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}

pub fn main() {
    use std::env;

    if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
        panic!("yummy");
    }

    // Check for version and help flags even when invoked as 'cargo-clippy'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    let dep_path = env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps");

    if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is executed on the initial call to `cargo clippy`

        let manifest_path_arg = std::env::args().skip(2).find(|val| val.starts_with("--manifest-path="));

        let mut metadata = if let Ok(metadata) = cargo::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) {
            metadata
        } else {
            let _ = io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata."));
            process::exit(101);
        };

        assert_eq!(metadata.version, 1);

        let manifest_path = manifest_path_arg.map(|arg| PathBuf::from(Path::new(&arg["--manifest-path=".len()..])));

        let current_dir = std::env::current_dir();

        let package_index = metadata.packages
            .iter()
            .position(|package| {
                let package_manifest_path = Path::new(&package.manifest_path);
                if let Some(ref manifest_path) = manifest_path {
                    package_manifest_path == manifest_path
                } else {
                    let current_dir = current_dir.as_ref().expect("could not read current directory");
                    let package_manifest_directory = package_manifest_path.parent()
                        .expect("could not find parent directory of package manifest");
                    package_manifest_directory == current_dir
                }
            })
            .expect("could not find matching package");
        let package = metadata.packages.remove(package_index);
        for target in package.targets {
            let args = std::env::args().skip(2);
            if let Some(first) = target.kind.get(0) {
                if target.kind.len() > 1 || first.ends_with("lib") {
                    if let Err(code) = process(std::iter::once("--lib".to_owned()).chain(args), &dep_path) {
                        std::process::exit(code);
                    }
                } else if ["bin", "example", "test", "bench"].contains(&&**first) {
                    if let Err(code) = process(vec![format!("--{}", first), target.name].into_iter().chain(args),
                                               &dep_path) {
                        std::process::exit(code);
                    }
                }
            } else {
                panic!("badly formatted cargo metadata: target::kind is an empty array");
            }
        }
    } else {
        // this arm is executed when cargo-clippy runs `cargo rustc` with the `RUSTC` env var set to itself

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("SYSROOT")
                .map(|s| s.to_owned())
                .or(Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| s.trim().to_owned()))
                .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
            env::args().collect()
        } else {
            env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
        };

        // this check ensures that dependencies are built but not linted and the final crate is
        // linted but not built
        let clippy_enabled = env::args().any(|s| s == "-Zno-trans");

        if clippy_enabled {
            args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
        }

        let mut ccc = ClippyCompilerCalls::new(clippy_enabled);
        let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);

        if let Err(err_count) = result {
            if err_count > 0 {
                std::process::exit(1);
            }
        }
    }
}

fn process<P, I>(old_args: I, dep_path: P) -> Result<(), i32>
    where P: AsRef<Path>,
          I: Iterator<Item = String>
{

    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("-L".to_owned());
    args.push(dep_path.as_ref().to_string_lossy().into_owned());
    args.push("-Zno-trans".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-clippy""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<commit_msg>dogfood fallout<commit_after>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![feature(static_in_const)]

#![allow(unknown_lints, missing_docs_in_private_items)]

extern crate clippy_lints;
extern crate getopts;
extern crate rustc;
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_plugin;
extern crate syntax;

use rustc_driver::{driver, CompilerCalls, RustcDefaultCalls, Compilation};
use rustc::session::{config, Session};
use rustc::session::config::{Input, ErrorOutputType};
use std::path::PathBuf;
use std::process::{self, Command};
use syntax::ast;
use std::io::{self, Write};

use clippy_lints::utils::cargo;

struct ClippyCompilerCalls {
    default: RustcDefaultCalls,
    run_lints: bool,
}

impl ClippyCompilerCalls {
    fn new(run_lints: bool) -> Self {
        ClippyCompilerCalls {
            default: RustcDefaultCalls,
            run_lints: run_lints,
        }
    }
}

impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
    fn early_callback(
        &mut self,
        matches: &getopts::Matches,
        sopts: &config::Options,
        cfg: &ast::CrateConfig,
        descriptions: &rustc_errors::registry::Registry,
        output: ErrorOutputType
    ) -> Compilation {
        self.default.early_callback(matches, sopts, cfg, descriptions, output)
    }
    fn no_input(
        &mut self,
        matches: &getopts::Matches,
        sopts: &config::Options,
        cfg: &ast::CrateConfig,
        odir: &Option<PathBuf>,
        ofile: &Option<PathBuf>,
        descriptions: &rustc_errors::registry::Registry
    ) -> Option<(Input, Option<PathBuf>)> {
        self.default.no_input(matches, sopts, cfg, odir, ofile, descriptions)
    }
    fn late_callback(
        &mut self,
        matches: &getopts::Matches,
        sess: &Session,
        input: &Input,
        odir: &Option<PathBuf>,
        ofile: &Option<PathBuf>
    ) -> Compilation {
        self.default.late_callback(matches, sess, input, odir, ofile)
    }
    fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
        let mut control = self.default.build_controller(sess, matches);

        if self.run_lints {
            let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
            control.after_parse.callback = Box::new(move |state| {
                {
                    let mut registry = rustc_plugin::registry::Registry::new(state.session,
                                                                             state.krate
                                                                                .as_ref()
                                                                                .expect("at this compilation stage \
                                                                                the krate must be parsed")
                                                                                .span);
                    registry.args_hidden = Some(Vec::new());
                    clippy_lints::register_plugins(&mut registry);

                    let rustc_plugin::registry::Registry { early_lint_passes,
                                                           late_lint_passes,
                                                           lint_groups,
                                                           llvm_passes,
                                                           attributes,
                                                           mir_passes,
                                                           .. } = registry;
                    let sess = &state.session;
                    let mut ls = sess.lint_store.borrow_mut();
                    for pass in early_lint_passes {
                        ls.register_early_pass(Some(sess), true, pass);
                    }
                    for pass in late_lint_passes {
                        ls.register_late_pass(Some(sess), true, pass);
                    }

                    for (name, to) in lint_groups {
                        ls.register_group(Some(sess), true, name, to);
                    }

                    sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                    sess.mir_passes.borrow_mut().extend(mir_passes);
                    sess.plugin_attributes.borrow_mut().extend(attributes);
                }
                old(state);
            });
        }

        control
    }
}

use std::path::Path;

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

The feature `cargo-clippy` is automatically defined for convenience. You can use
it to allow or deny lints from the code, eg.:

    #[cfg_attr(feature = "cargo-clippy", allow(needless_lifetimes))]
"#;

#[allow(print_stdout)]
fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

#[allow(print_stdout)]
fn show_version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}

pub fn main() {
    use std::env;

    if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
        panic!("yummy");
    }

    // Check for version and help flags even when invoked as 'cargo-clippy'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    let dep_path = env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps");

    if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is executed on the initial call to `cargo clippy`

        let manifest_path_arg = std::env::args().skip(2).find(|val| val.starts_with("--manifest-path="));

        let mut metadata = if let Ok(metadata) = cargo::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) {
            metadata
        } else {
            let _ = io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata."));
            process::exit(101);
        };

        assert_eq!(metadata.version, 1);

        let manifest_path = manifest_path_arg.map(|arg| PathBuf::from(Path::new(&arg["--manifest-path=".len()..])));

        let current_dir = std::env::current_dir();

        let package_index = metadata.packages
            .iter()
            .position(|package| {
                let package_manifest_path = Path::new(&package.manifest_path);
                if let Some(ref manifest_path) = manifest_path {
                    package_manifest_path == manifest_path
                } else {
                    let current_dir = current_dir.as_ref().expect("could not read current directory");
                    let package_manifest_directory = package_manifest_path.parent()
                        .expect("could not find parent directory of package manifest");
                    package_manifest_directory == current_dir
                }
            })
            .expect("could not find matching package");
        let package = metadata.packages.remove(package_index);
        for target in package.targets {
            let args = std::env::args().skip(2);
            if let Some(first) = target.kind.get(0) {
                if target.kind.len() > 1 || first.ends_with("lib") {
                    if let Err(code) = process(std::iter::once("--lib".to_owned()).chain(args), &dep_path) {
                        std::process::exit(code);
                    }
                } else if ["bin", "example", "test", "bench"].contains(&&**first) {
                    if let Err(code) = process(vec![format!("--{}", first), target.name].into_iter().chain(args),
                                               &dep_path) {
                        std::process::exit(code);
                    }
                }
            } else {
                panic!("badly formatted cargo metadata: target::kind is an empty array");
            }
        }
    } else {
        // this arm is executed when cargo-clippy runs `cargo rustc` with the `RUSTC` env var set to itself

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("SYSROOT")
                .map(|s| s.to_owned())
                .or_else(|| Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| s.trim().to_owned()))
                .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
            env::args().collect()
        } else {
            env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
        };

        // this check ensures that dependencies are built but not linted and the final crate is
        // linted but not built
        let clippy_enabled = env::args().any(|s| s == "-Zno-trans");

        if clippy_enabled {
            args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
        }

        let mut ccc = ClippyCompilerCalls::new(clippy_enabled);
        let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);

        if let Err(err_count) = result {
            if err_count > 0 {
                std::process::exit(1);
            }
        }
    }
}

fn process<P, I>(old_args: I, dep_path: P) -> Result<(), i32>
    where P: AsRef<Path>,
          I: Iterator<Item = String>
{

    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("-L".to_owned());
    args.push(dep_path.as_ref().to_string_lossy().into_owned());
    args.push("-Zno-trans".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-clippy""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<|endoftext|>


<commit_before>/*
    Copyright © 2013 Free Software Foundation, Inc
    See licensing in LICENSE file

    File: examples/ex_1.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      Simple "Hello, world" example.
*/

extern crate ncurses;

use ncurses::*;

fn main()
{
  /* Start ncurses. */
  initscr();

  /* Print to the back buffer. */
  printw("Hello, world!");

  /* Update the screen. */
  refresh();

  /* Wait for a key press. */
  getch();

  /* Terminate ncurses. */
  endwin();
}
<commit_msg>Now that is one sick box<commit_after>/*
    Copyright © 2013 Free Software Foundation, Inc
    See licensing in LICENSE file

    File: examples/ex_4.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      Window creation and input example.
      Use the cursor keys to move the window
      around the screen.
*/

extern crate ncurses;

use ncurses::*;

static WINDOW_HEIGHT: i32 = 2;
static WINDOW_WIDTH: i32 = 3;

fn main()
{
  /* Setup ncurses. */
  initscr();
  raw();

  /* Allow for extended keyboard (like F1). */
  keypad(stdscr, true);
  noecho();

  /* Invisible cursor. */
  curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);

  /* Status/help info. */
  printw("Use the arrow keys to move");
  mvprintw(LINES - 1, 0, "Press F1 to exit");
  refresh();

  /* Get the screen bounds. */
  let mut max_x = 0;
  let mut max_y = 0;
  getmaxyx(stdscr, &mut max_y, &mut max_x);

  /* Start in the center. */
  let mut start_y = (max_y - WINDOW_HEIGHT) / 2;
  let mut start_x = (max_x - WINDOW_WIDTH) / 2;
  let mut win = create_win(start_y, start_x);

  let mut ch = getch();
  while ch != KEY_F(1)
  {
    match ch
    {
      KEY_LEFT =>
      {
        start_x -= 1;
        destroy_win(win);
        win = create_win(start_y, start_x);
      },
      KEY_RIGHT =>
      {
        start_x += 1;
        destroy_win(win);
        win = create_win(start_y, start_x);
      },
      KEY_UP =>
      {
        start_y -= 1;
        destroy_win(win);
        win = create_win(start_y, start_x);
      },
      KEY_DOWN =>
      {
        start_y += 1;
        destroy_win(win);
        win = create_win(start_y, start_x);
      },
      _ => { }
    }
    ch = getch();
  }

  endwin();
}

fn create_win(start_y: i32, start_x: i32) -> WINDOW
{
  let win = newwin(WINDOW_HEIGHT, WINDOW_WIDTH, start_y, start_x);
  box_(win, 0, 0);
  wrefresh(win);
  win
}

fn destroy_win(win: WINDOW)
{
  let ch = ' ' as chtype;
        wborder(win, ch, ch, ch, ch, ch, ch, ch, ch);
        wrefresh(win);
        delwin(win);
}
<|endoftext|>


<commit_before>//  rpc-perf - RPC Performance Testing
//  Copyright 2015 Twitter, Inc
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

#[macro_use]
extern crate log;

extern crate bytes;
extern crate getopts;
extern crate heatmap;
extern crate histogram;
extern crate time;
extern crate mio;
extern crate mpmc;
extern crate regex;
extern crate rpcperf_parser as parser;
extern crate rpcperf_request as request;
extern crate shuteye;
extern crate toml;
extern crate waterfall;

pub mod client;
pub mod config;
pub mod connection;
pub mod logger;
pub mod net;
pub mod state;
pub mod stats;

use getopts::Options;
use log::LogLevelFilter;
use mpmc::Queue as BoundedQueue;
use std::env;
use std::net::ToSocketAddrs;
use std::thread;
use std::sync::mpsc;
use std::process;

use client::Client;
use config::BenchmarkConfig;
use connection::Connection;
use logger::SimpleLogger;
use net::InternetProtocol;
use stats::Stat;
use request::workload::{Protocol, Workload};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

const BUCKET_SIZE: usize = 10_000;

struct ClientConfig {
    servers: Vec<String>,
    connections: usize,
    stats_tx: mpsc::Sender<Stat>,
    client_protocol: Protocol,
    internet_protocol: InternetProtocol,
    work_rx: BoundedQueue<Vec<u8>>,
    tcp_nodelay: bool,
    mio_config: mio::EventLoopConfig,
}

fn start(config: ClientConfig) {
    let mut event_loop = mio::EventLoop::configured(config.mio_config.clone()).unwrap();
    let mut client = Client::new(config.work_rx.clone());

    let mut failures = 0;
    let mut connects = 0;

    for server in &config.servers {
        let address = &server.to_socket_addrs().unwrap().next().unwrap();
        for _ in 0..config.connections {
            match net::to_mio_tcp_stream(address, config.internet_protocol) {
                Ok(stream) => {
                    match client.connections.insert_with(|token| {
                        Connection::new(stream,
                                        token,
                                        config.stats_tx.clone(),
                                        config.client_protocol,
                                        config.tcp_nodelay)
                    }) {
                        Some(token) => {
                            event_loop.register(&client.connections[token].socket,
                                                token,
                                                mio::EventSet::writable(),
                                                mio::PollOpt::edge() | mio::PollOpt::oneshot())
                                      .unwrap();
                            connects += 1;
                        }
                        _ => debug!("too many established connections"),
                    }
                }
                Err(e) => {
                    failures += 1;
                    debug!("connect error: {}", e);
                }
            }
        }
    }
    info!("Connections: {} Failures: {}", connects, failures);
    if failures == config.connections {
        error!("All connections have failed");
        process::exit(1);
    } else {
        event_loop.run(&mut client).unwrap();
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn opts() -> Options {
    let mut opts = Options::new();

    opts.optmulti("s", "server", "server address", "HOST:PORT");
    opts.optopt("t", "threads", "number of threads", "INTEGER");
    opts.optopt("c", "connections", "connections per thread", "INTEGER");
    opts.optopt("d", "duration", "number of seconds per window", "INTEGER");
    opts.optopt("w", "windows", "number of windows in test", "INTEGER");
    opts.optopt("p", "protocol", "client protocol", "STRING");
    opts.optopt("", "config", "TOML config file", "FILE");
    opts.optopt("", "trace", "write histogram data to file", "FILE");
    opts.optopt("", "waterfall", "output waterfall PNG", "FILE");
    opts.optflag("", "tcp-nodelay", "enable tcp nodelay");
    opts.optflag("", "flush", "flush cache prior to test");
    opts.optflag("", "ipv4", "force IPv4 only");
    opts.optflag("", "ipv6", "force IPv6 only");
    opts.optflag("", "version", "show version and exit");
    opts.optflagmulti("v", "verbose", "verbosity (stacking)");
    opts.optflag("h", "help", "print this help menu");

    opts
}

fn set_log_level(level: usize) {
    let log_filter;
    match level {
        0 => {
            log_filter = LogLevelFilter::Info;
        }
        1 => {
            log_filter = LogLevelFilter::Debug;
        }
        _ => {
            log_filter = LogLevelFilter::Trace;
        }
    }
    let _ = log::set_logger(|max_log_level| {
        max_log_level.set(log_filter);
        Box::new(SimpleLogger)
    });
}

fn choose_layer_3(ipv4: bool, ipv6: bool) -> Result<InternetProtocol, String> {
    if ipv4 && ipv6 {
        return Err("Use only --ipv4 or --ipv6".to_owned());
    }

    if !ipv4 && !ipv6 {
        return Ok(InternetProtocol::Any);
    } else if ipv4 {
        return Ok(InternetProtocol::IpV4);
    } else if ipv6 {
        return Ok(InternetProtocol::IpV6);
    }

    Err("No InternetProtocols remaining! Bad config/options".to_owned())
}

pub fn main() {
    let args: Vec<String> = env::args().collect();

    let program = &args[0];

    let opts = opts();

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };

    if matches.opt_present("help") {
        print_usage(&program, opts);
        return;
    }

    if matches.opt_present("version") {
        println!("rpc-perf {}", VERSION);
        return;
    }

    // defaults
    let mut config: BenchmarkConfig = Default::default();
    let client_protocol: Protocol;

    set_log_level(matches.opt_count("verbose"));

    info!("rpc-perf {} initializing...", VERSION);

    if matches.opt_count("server") < 1 {
        error!("require server parameter");
        print_usage(&program, opts);
        return;
    };

    let waterfall = matches.opt_str("waterfall");
    let trace = matches.opt_str("trace");

    // load config from file if specified
    if let Some(toml) = matches.opt_str("config") {
        match config::load_config(&toml) {
            Ok(cfg) => {
                config = cfg;
            }
            Err(msg) => {
                error!("{}", msg);
                return;
            }
        }
    }

    // override config with commandline options
    config.override_protocol(matches.opt_str("protocol"));
    config.override_threads(matches.opt_str("threads"));
    config.override_connections(matches.opt_str("connections"));
    config.override_windows(matches.opt_str("windows"));
    config.override_duration(matches.opt_str("duration"));

    let internet_protocol = match choose_layer_3(matches.opt_present("ipv4"),
                                           matches.opt_present("ipv6")) {
        Ok(i) => i,
        Err(e) => {
            error!("{}", e);
            return;
        }
    };

    if matches.opt_present("tcp-nodelay") {
        config.tcp_nodelay = true;
    }

    let workq = BoundedQueue::<Vec<u8>>::with_capacity(BUCKET_SIZE);

    // these map to workload and conflict with config for simplicity
    if config.workloads.is_empty() {
        error!("configuration contains no workload sections");
        return;
    }

    match Protocol::new(&*config.protocol) {
        Ok(p) => {
            client_protocol = p;
        }
        Err(_) => {
            error!("Bad protocol: {}", &*config.protocol);
            return;
        }
    }

    if matches.opt_present("flush") {
        match client_protocol {
            Protocol::Memcache => {
                let _ = workq.push(request::memcache::flush_all().into_bytes());
            }
            Protocol::Redis => {
                let _ = workq.push(request::redis::flushall().into_bytes());
            }
            _ => {}
        }
    }

    let evconfig = mio::EventLoopConfig::default();

    info!("-----");
    info!("Config:");
    for server in matches.opt_strs("server") {
        info!("Config: Server: {} Protocol: {}", server, config.protocol);
    }
    info!("Config: IP: {:?} TCP_NODELAY: {}",
          internet_protocol,
          config.tcp_nodelay);
    info!("Config: Threads: {} Connections: {}",
          config.threads,
          config.connections);
    info!("Config: Windows: {} Duration: {}",
          config.windows,
          config.duration);
    info!("-----");
    info!("Workload:");

    for (i, w) in config.workloads.iter().enumerate() {
        info!("Workload {}: Method: {} Rate: {}", i, w.method, w.rate);

        let protocol = Protocol::new(&config.protocol.clone()).unwrap();

        let mut workload = Workload::new(protocol,
                                         w.method.clone(),
                                         Some(w.rate as u64),
                                         workq.clone())
                               .unwrap();

        for p in &w.parameters {
            info!("Parameter: {:?}", p);
            workload.add_param(p.clone());
        }

        thread::spawn(move || {
            loop {
                workload.run();
            }
        });
    }

    let (stats_sender, stats_receiver) = mpsc::channel();

    let receiver = stats::Receiver::new(stats_receiver);

    info!("-----");
    info!("Connecting...");
    // spawn client threads
    for i in 0..config.threads {
        info!("Client: {}", i);
        let stats_tx = stats_sender.clone();
        let servers = matches.opt_strs("server");
        let connections = config.connections;
        let work_rx = workq.clone();
        let tcp_nodelay = config.tcp_nodelay;
        let internet_protocol = internet_protocol;
        let evconfig = evconfig.clone();

        let client_config = ClientConfig {
            servers: servers,
            connections: connections,
            stats_tx: stats_tx,
            client_protocol: client_protocol,
            internet_protocol: internet_protocol,
            work_rx: work_rx,
            tcp_nodelay: tcp_nodelay,
            mio_config: evconfig,
        };

        thread::spawn(move || {
            start(client_config);
        });
    }

    receiver.run(config.duration, config.windows, trace, waterfall);
}
<commit_msg>simplify main<commit_after>//  rpc-perf - RPC Performance Testing
//  Copyright 2015 Twitter, Inc
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

#[macro_use]
extern crate log;

extern crate bytes;
extern crate getopts;
extern crate heatmap;
extern crate histogram;
extern crate time;
extern crate mio;
extern crate mpmc;
extern crate regex;
extern crate rpcperf_parser as parser;
extern crate rpcperf_request as request;
extern crate shuteye;
extern crate toml;
extern crate waterfall;

pub mod client;
pub mod config;
pub mod connection;
pub mod logger;
pub mod net;
pub mod state;
pub mod stats;

use getopts::Options;
use log::LogLevelFilter;
use mpmc::Queue as BoundedQueue;
use std::env;
use std::net::ToSocketAddrs;
use std::thread;
use std::sync::mpsc;
use std::process;

use client::Client;
use config::BenchmarkConfig;
use connection::Connection;
use logger::SimpleLogger;
use net::InternetProtocol;
use stats::Stat;
use request::workload::{Protocol, Workload};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

const BUCKET_SIZE: usize = 10_000;

struct ClientConfig {
    servers: Vec<String>,
    connections: usize,
    stats_tx: mpsc::Sender<Stat>,
    client_protocol: Protocol,
    internet_protocol: InternetProtocol,
    work_rx: BoundedQueue<Vec<u8>>,
    tcp_nodelay: bool,
    mio_config: mio::EventLoopConfig,
}

fn start(config: ClientConfig) {
    let mut event_loop = mio::EventLoop::configured(config.mio_config.clone()).unwrap();
    let mut client = Client::new(config.work_rx.clone());

    let mut failures = 0;
    let mut connects = 0;

    for server in &config.servers {
        let address = &server.to_socket_addrs().unwrap().next().unwrap();
        for _ in 0..config.connections {
            match net::to_mio_tcp_stream(address, config.internet_protocol) {
                Ok(stream) => {
                    match client.connections.insert_with(|token| {
                        Connection::new(stream,
                                        token,
                                        config.stats_tx.clone(),
                                        config.client_protocol,
                                        config.tcp_nodelay)
                    }) {
                        Some(token) => {
                            event_loop.register(&client.connections[token].socket,
                                                token,
                                                mio::EventSet::writable(),
                                                mio::PollOpt::edge() | mio::PollOpt::oneshot())
                                      .unwrap();
                            connects += 1;
                        }
                        _ => debug!("too many established connections"),
                    }
                }
                Err(e) => {
                    failures += 1;
                    debug!("connect error: {}", e);
                }
            }
        }
    }
    info!("Connections: {} Failures: {}", connects, failures);
    if failures == config.connections {
        error!("All connections have failed");
        process::exit(1);
    } else {
        event_loop.run(&mut client).unwrap();
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn opts() -> Options {
    let mut opts = Options::new();

    opts.optmulti("s", "server", "server address", "HOST:PORT");
    opts.optopt("t", "threads", "number of threads", "INTEGER");
    opts.optopt("c", "connections", "connections per thread", "INTEGER");
    opts.optopt("d", "duration", "number of seconds per window", "INTEGER");
    opts.optopt("w", "windows", "number of windows in test", "INTEGER");
    opts.optopt("p", "protocol", "client protocol", "STRING");
    opts.optopt("", "config", "TOML config file", "FILE");
    opts.optopt("", "trace", "write histogram data to file", "FILE");
    opts.optopt("", "waterfall", "output waterfall PNG", "FILE");
    opts.optflag("", "tcp-nodelay", "enable tcp nodelay");
    opts.optflag("", "flush", "flush cache prior to test");
    opts.optflag("", "ipv4", "force IPv4 only");
    opts.optflag("", "ipv6", "force IPv6 only");
    opts.optflag("", "version", "show version and exit");
    opts.optflagmulti("v", "verbose", "verbosity (stacking)");
    opts.optflag("h", "help", "print this help menu");

    opts
}

fn set_log_level(level: usize) {
    let log_filter;
    match level {
        0 => {
            log_filter = LogLevelFilter::Info;
        }
        1 => {
            log_filter = LogLevelFilter::Debug;
        }
        _ => {
            log_filter = LogLevelFilter::Trace;
        }
    }
    let _ = log::set_logger(|max_log_level| {
        max_log_level.set(log_filter);
        Box::new(SimpleLogger)
    });
}

fn choose_layer_3(ipv4: bool, ipv6: bool) -> Result<InternetProtocol, String> {
    if ipv4 && ipv6 {
        return Err("Use only --ipv4 or --ipv6".to_owned());
    }

    if !ipv4 && !ipv6 {
        return Ok(InternetProtocol::Any);
    } else if ipv4 {
        return Ok(InternetProtocol::IpV4);
    } else if ipv6 {
        return Ok(InternetProtocol::IpV6);
    }

    Err("No InternetProtocols remaining! Bad config/options".to_owned())
}

fn launch_workloads(protocol: String,
                    workloads: Vec<config::BenchmarkWorkload>,
                    work_queue: mpmc::Queue<Vec<u8>>) {

    for (i, w) in workloads.iter().enumerate() {
        info!("Workload {}: Method: {} Rate: {}", i, w.method, w.rate);

        let protocol = Protocol::new(&protocol.clone()).unwrap();

        let mut workload = Workload::new(protocol,
                                         w.method.clone(),
                                         Some(w.rate as u64),
                                         work_queue.clone())
                               .unwrap();

        for p in &w.parameters {
            info!("Parameter: {:?}", p);
            workload.add_param(p.clone());
        }

        thread::spawn(move || {
            loop {
                workload.run();
            }
        });
    }
}

fn load_config(file: Option<String>) -> BenchmarkConfig {
    let mut config: BenchmarkConfig = Default::default();

     // load config from file if specified
    if let Some(toml) = file {
        match config::load_config(&toml) {
            Ok(cfg) => {
                config = cfg;
            }
            Err(msg) => {
                error!("{}", msg);
                panic!();
            }
        }
    }

    config
}

pub fn main() {
    let args: Vec<String> = env::args().collect();

    let program = &args[0];

    let opts = opts();

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };

    if matches.opt_present("help") {
        print_usage(&program, opts);
        return;
    }

    if matches.opt_present("version") {
        println!("rpc-perf {}", VERSION);
        return;
    }

    // defaults
    let client_protocol: Protocol;

    set_log_level(matches.opt_count("verbose"));

    info!("rpc-perf {} initializing...", VERSION);

    if matches.opt_count("server") < 1 {
        error!("require server parameter");
        print_usage(&program, opts);
        return;
    };

    let waterfall = matches.opt_str("waterfall");
    let trace = matches.opt_str("trace");

    let mut config = load_config(matches.opt_str("config"));

    // override config with commandline options
    config.override_protocol(matches.opt_str("protocol"));
    config.override_threads(matches.opt_str("threads"));
    config.override_connections(matches.opt_str("connections"));
    config.override_windows(matches.opt_str("windows"));
    config.override_duration(matches.opt_str("duration"));

    let internet_protocol = match choose_layer_3(matches.opt_present("ipv4"),
                                                 matches.opt_present("ipv6")) {
        Ok(i) => i,
        Err(e) => {
            error!("{}", e);
            return;
        }
    };

    if matches.opt_present("tcp-nodelay") {
        config.tcp_nodelay = true;
    }

    let work_queue = BoundedQueue::<Vec<u8>>::with_capacity(BUCKET_SIZE);

    // these map to workload and conflict with config for simplicity
    if config.workloads.is_empty() {
        error!("configuration contains no workload sections");
        return;
    }

    match Protocol::new(&*config.protocol) {
        Ok(p) => {
            client_protocol = p;
        }
        Err(_) => {
            error!("Bad protocol: {}", &*config.protocol);
            return;
        }
    }

    if matches.opt_present("flush") {
        match client_protocol {
            Protocol::Memcache => {
                let _ = work_queue.push(request::memcache::flush_all().into_bytes());
            }
            Protocol::Redis => {
                let _ = work_queue.push(request::redis::flushall().into_bytes());
            }
            _ => {}
        }
    }

    let evconfig = mio::EventLoopConfig::default();

    info!("-----");
    info!("Config:");
    for server in matches.opt_strs("server") {
        info!("Config: Server: {} Protocol: {}", server, config.protocol);
    }
    info!("Config: IP: {:?} TCP_NODELAY: {}",
          internet_protocol,
          config.tcp_nodelay);
    info!("Config: Threads: {} Connections: {}",
          config.threads,
          config.connections);
    info!("Config: Windows: {} Duration: {}",
          config.windows,
          config.duration);
    info!("-----");
    info!("Workload:");

    launch_workloads(config.protocol, config.workloads, work_queue.clone());

    let (stats_sender, stats_receiver) = mpsc::channel();

    let receiver = stats::Receiver::new(stats_receiver);

    info!("-----");
    info!("Connecting...");
    // spawn client threads
    for i in 0..config.threads {
        info!("Client: {}", i);

        let client_config = ClientConfig {
            servers: matches.opt_strs("server"),
            connections: config.connections,
            stats_tx: stats_sender.clone(),
            client_protocol: client_protocol,
            internet_protocol: internet_protocol,
            work_rx: work_queue.clone(),
            tcp_nodelay: config.tcp_nodelay,
            mio_config: evconfig.clone(),
        };

        thread::spawn(move || {
            start(client_config);
        });
    }

    receiver.run(config.duration, config.windows, trace, waterfall);
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

fn rot13(c: char) -> char {
    let base = match c {
        'a'...'z' => 'a' as u8,
        'A'...'Z' => 'A' as u8,
        _ => return c,
    };

    let rotated = ((c as u8) - base + 13) % 26;
    (rotated + base) as char
}

fn rotate_stdin() -> Result<String, io::Error> {
    let mut buffer = String::new();
    try!(io::stdin().read_to_string(&mut buffer));
    Ok(buffer.chars().map(rot13).collect::<String>())
}

fn rotate_file(filename: String) -> Result<String, io::Error> {
    let mut file = try!(File::open(filename));
    let mut contents: Vec<u8> = Vec::new();
    try!(file.read_to_end(&mut contents));

    let encoded_str = String::from_utf8(contents).unwrap()
                                                 .chars()
                                                 .map(rot13)
                                                 .collect::<String>();
    Ok(encoded_str)
}

fn main() {
    let rotated = env::args().nth(1)
        .map_or_else(|| rotate_stdin(),
                     |filename| rotate_file(filename));
    println!("{}", rotated.unwrap());
}
<commit_msg>Improved error handling.<commit_after>use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

type IOResult<T> = Result<T, io::Error>;

fn rot13(c: char) -> char {
    let base = match c {
        'a'...'z' => 'a' as u8,
        'A'...'Z' => 'A' as u8,
        _ => return c,
    };

    let rotated = ((c as u8) - base + 13) % 26;
    (rotated + base) as char
}

fn rotate_stdin() -> IOResult<String> {
    let mut buffer = String::new();
    try!(io::stdin().read_to_string(&mut buffer));
    Ok(buffer.chars().map(rot13).collect::<String>())
}

fn rotate_file(filename: String) -> IOResult<String> {
    let mut file = try!(File::open(filename));
    let mut contents: Vec<u8> = Vec::new();
    try!(file.read_to_end(&mut contents));

    let encoded_str = String::from_utf8(contents).unwrap()
                                                 .chars()
                                                 .map(rot13)
                                                 .collect::<String>();
    Ok(encoded_str)
}

fn main() {
    let rotated = env::args().nth(1)
        .map_or_else(|| rotate_stdin(),
                     |filename| rotate_file(filename));

    match rotated {
        Ok(text) => println!("{}", text),
        Err(err) => println!("Error: {}", err)
    }
}
<|endoftext|>


<commit_before>#![feature(async_closure)]

#[macro_use]
extern crate prettytable;

use ct_tools::common::{sha256_hex, Log};
use ct_tools::ct::submit_cert_to_logs;
use ct_tools::google::{fetch_all_ct_logs, fetch_trusted_ct_logs};
use ct_tools::{crtsh, letsencrypt};
use dirs;
use futures::stream::{StreamExt, TryStreamExt};
use hyper;
use hyper_rustls;
use net2;
use net2::unix::UnixTcpBuilderExt;
use pem;
use rustls;
use rustls::Session;
use std::fs::{self, File};
use std::io::Read;
use std::net::SocketAddr;
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
use structopt;
use structopt::StructOpt;
use tokio_io::AsyncWriteExt;
use tokio_net::driver::Handle;
use tokio_net::process::Command;
use tokio_rustls;

fn pems_to_chain(data: &[u8]) -> Vec<Vec<u8>> {
    pem::parse_many(data)
        .into_iter()
        .filter(|p| p.tag == "CERTIFICATE")
        .map(|p| p.contents)
        .collect()
}

fn new_http_client(
) -> hyper::Client<hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>> {
    hyper::Client::builder().build(hyper_rustls::HttpsConnector::new())
}

fn compute_paths(paths: &[String]) -> Vec<String> {
    paths
        .iter()
        .flat_map(|p| {
            if fs::metadata(p).unwrap().is_dir() {
                fs::read_dir(p)
                    .unwrap()
                    .map(|d| d.unwrap().path().to_str().unwrap().to_string())
                    .collect()
            } else {
                vec![p.clone()]
            }
        })
        .collect()
}

async fn submit(paths: &[String], all_logs: bool) {
    let http_client = new_http_client();

    let logs = if all_logs {
        fetch_all_ct_logs(&http_client).await
    } else {
        fetch_trusted_ct_logs(&http_client).await
    };

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let logs = &logs;
            let mut chain = pems_to_chain(&contents);
            async move {
                if chain.len() == 1 {
                    // TODO: There's got to be some way to do this ourselves, instead of using crt.sh
                    // as a glorified AIA chaser.
                    println!(
                    "[{}] Only one certificate in chain, using crt.sh to build a full chain ...",
                    &path
                );
                    let new_chain = crtsh::build_chain_for_cert(&http_client, &chain[0]).await;
                    chain = match new_chain {
                        Ok(c) => c,
                        Err(()) => {
                            println!("[{}] Unable to build a chain", path);
                            return futures::future::ready(());
                        }
                    }
                }
                println!("[{}] Submitting ...", &path);
                let timeout = Duration::from_secs(30);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;

                if !scts.is_empty() {
                    println!(
                        "[{}] Find the cert on crt.sh: {}",
                        path,
                        crtsh::url_for_cert(&chain[0])
                    );
                    let mut table = prettytable::Table::new();
                    table.add_row(row!["Log"]);
                    for (log_idx, _) in scts {
                        let log = &logs[log_idx];
                        table.add_row(row![log.description]);
                    }
                    table.printstd();
                    println!();
                    println!();
                } else {
                    println!("[{}] No SCTs obtained", &path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(4)
        .for_each(async move |()| ());
    work.await;
}

async fn check(paths: &[String]) {
    let http_client = new_http_client();

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let chain = pems_to_chain(&contents);
            async move {
                if !chain.is_empty() && crtsh::is_cert_logged(&http_client, &chain[0]).await {
                    println!("{} was already logged", path);
                } else {
                    println!("{} has not been logged", path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(16)
        .for_each(async move |()| ());
    work.await;
}

async fn handle_request<C: hyper::client::connect::Connect + 'static>(
    request: hyper::Request<hyper::Body>,
    templates: Arc<tera::Tera>,
    http_client: Arc<hyper::Client<C>>,
    logs: Arc<Vec<Log>>,
    client_cert: Option<rustls::Certificate>,
) -> Result<hyper::Response<hyper::Body>, hyper::Error> {
    let mut crtsh_url = None;
    let mut rendered_cert = None;
    if let Some(cert) = client_cert {
        if request.method() == hyper::Method::POST {
            if let Ok(chain) = crtsh::build_chain_for_cert(&http_client, &cert.0).await {
                let timeout = Duration::from_secs(5);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;
                if !scts.is_empty() {
                    crtsh_url = Some(crtsh::url_for_cert(&chain[0]));
                    println!("Successfully submitted: {}", sha256_hex(&chain[0]));
                }
            }
        }

        let mut process = Command::new("openssl")
            .arg("x509")
            .arg("-text")
            .arg("-noout")
            .arg("-inform")
            .arg("der")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();
        let cert_bytes = cert.0.clone();
        let mut stdin = process.stdin().take().unwrap();
        AsyncWriteExt::write_all(&mut stdin, &cert_bytes)
            .await
            .unwrap();
        let out = process.wait_with_output().await.unwrap();
        rendered_cert = Some(String::from_utf8_lossy(&out.stdout).into_owned());
    }

    let mut context = tera::Context::new();
    context.insert("cert", &rendered_cert);
    context.insert("crtsh_url", &crtsh_url);
    let body = templates.render("home.html", &context).unwrap();
    Ok(hyper::Response::builder()
        .status(hyper::StatusCode::OK)
        .body(body.into())
        .unwrap())
}

struct NoVerificationCertificateVerifier;
impl rustls::ClientCertVerifier for NoVerificationCertificateVerifier {
    fn client_auth_mandatory(&self) -> bool {
        false
    }

    fn verify_client_cert(
        &self,
        _: &[rustls::Certificate],
    ) -> Result<rustls::ClientCertVerified, rustls::TLSError> {
        Ok(rustls::ClientCertVerified::assertion())
    }

    fn client_auth_root_subjects(&self) -> rustls::DistinguishedNames {
        rustls::DistinguishedNames::new()
    }
}

async fn server(local_dev: bool, domain: Option<&str>, letsencrypt_env: Option<&str>) {
    match (local_dev, domain, letsencrypt_env) {
        (true, Some(_), _) | (true, _, Some(_)) => {
            panic!("Can't use both --local-dev and --letsencrypt-env or --domain")
        }
        (_, Some(_), None) | (_, None, Some(_)) => {
            panic!("When using Let's Encrypt, must set both --letsencrypt-env and --domain")
        }
        (false, _, None) => panic!("Must set at least one of --local-dev or --letsencrypt-env"),
        _ => {}
    };

    // Disable certificate verification. In any normal context, this would be horribly insecure!
    // However, all we're doing is taking the certs and then turning around and submitting them to
    // CT logs, so it doesn't matter if they're verified.
    let mut tls_config = rustls::ServerConfig::new(Arc::new(NoVerificationCertificateVerifier));
    if local_dev {
        // TODO: not all the details on the cert are perfect, but it's fine.
        let (cert, pkey) = letsencrypt::generate_temporary_cert("localhost");
        tls_config
            .set_single_cert(
                vec![letsencrypt::openssl_cert_to_rustls(&cert)],
                letsencrypt::openssl_pkey_to_rustls(&pkey),
            )
            .unwrap();
    } else {
        let letsencrypt_url = match letsencrypt_env.unwrap() {
            "prod" => "https://acme-v01.api.letsencrypt.org/directory",
            "dev" => "https://acme-staging.api.letsencrypt.org/directory",
            _ => unreachable!(),
        };
        let cert_cache = letsencrypt::DiskCache::new(
            dirs::home_dir()
                .unwrap()
                .join(".ct-tools")
                .join("certificates"),
        );
        tls_config.cert_resolver = Arc::new(letsencrypt::AutomaticCertResolver::new(
            letsencrypt_url,
            vec![domain.unwrap().to_string()],
            cert_cache,
        ));
    }
    tls_config.set_persistence(rustls::ServerSessionMemoryCache::new(1024));
    tls_config.ticketer = rustls::Ticketer::new();

    let http_client = Arc::new(new_http_client());
    let logs = Arc::new(fetch_trusted_ct_logs(&http_client).await);
    let templates = Arc::new(tera::compile_templates!("templates/*"));

    let addr = if local_dev {
        "0.0.0.0:8000"
    } else {
        "0.0.0.0:443"
    }
    .parse()
    .unwrap();

    // If there aren't at least two threads, the Let's Encrypt integration will deadlock.
    println!("Listening on https://{} ...", addr);
    let listener = match addr {
        SocketAddr::V4(_) => net2::TcpBuilder::new_v4().unwrap(),
        SocketAddr::V6(_) => net2::TcpBuilder::new_v6().unwrap(),
    };
    listener.reuse_port(true).unwrap();
    listener.reuse_address(true).unwrap();
    listener.bind(addr).unwrap();
    let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(tls_config));
    let connections =
        tokio::net::TcpListener::from_std(listener.listen(1024).unwrap(), &Handle::default())
            .unwrap()
            .incoming()
            .and_then(move |sock| tls_acceptor.accept(sock))
            .filter(move |s| futures::future::ready(s.is_ok()))
            .boxed();
    let server = hyper::Server::builder(connections).serve(hyper::service::make_service_fn(
        move |conn: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
            let http_client = Arc::clone(&http_client);
            let templates = Arc::clone(&templates);
            let logs = Arc::clone(&logs);
            let client_cert = conn
                .get_ref()
                .1
                .get_peer_certificates()
                .map(|mut chain| chain.remove(0));

            async {
                Ok::<_, hyper::Error>(hyper::service::service_fn(
                    move |r: hyper::Request<hyper::Body>| {
                        handle_request(
                            r,
                            Arc::clone(&templates),
                            Arc::clone(&http_client),
                            Arc::clone(&logs),
                            client_cert.clone(),
                        )
                    },
                ))
            }
        },
    ));

    server.await.unwrap()
}

#[derive(StructOpt)]
#[structopt(name = "ct-tools")]
enum Opt {
    #[structopt(name = "submit", about = "Directly submits certificates to CT logs")]
    Submit {
        #[structopt(
            long = "all-logs",
            help = "Submit to all logs, instead of just ones trusted by Chrome"
        )]
        all_logs: bool,
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "check",
        about = "Checks whether a certificate exists in CT logs"
    )]
    Check {
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "server",
        about = "Run an HTTPS server that submits client certificates to CT logs"
    )]
    Server {
        #[structopt(
            long = "--local-dev",
            help = "Local development, do not obtain a certificate"
        )]
        local_dev: bool,
        #[structopt(long = "domain", help = "Domain this is running as")]
        domain: Option<String>,
        #[structopt(
            long = "letsencrypt-env",
            raw(possible_values = "&[\"dev\", \"prod\"]"),
            help = "Let's Encrypt environment to use"
        )]
        letsencrypt_env: Option<String>,
    },
}

#[tokio::main]
async fn main() {
    match Opt::from_args() {
        Opt::Submit { paths, all_logs } => {
            submit(&paths, all_logs).await;
        }
        Opt::Check { paths } => {
            check(&paths).await;
        }
        Opt::Server {
            local_dev,
            domain,
            letsencrypt_env,
        } => {
            server(
                local_dev,
                domain.as_ref().map(|s| s.as_ref()),
                letsencrypt_env.as_ref().map(|s| s.as_ref()),
            )
            .await;
        }
    }
}
<commit_msg>Get rid of the last extern crate<commit_after>#![feature(async_closure)]

use ct_tools::common::{sha256_hex, Log};
use ct_tools::ct::submit_cert_to_logs;
use ct_tools::google::{fetch_all_ct_logs, fetch_trusted_ct_logs};
use ct_tools::{crtsh, letsencrypt};
use dirs;
use futures::stream::{StreamExt, TryStreamExt};
use hyper;
use hyper_rustls;
use net2;
use net2::unix::UnixTcpBuilderExt;
use pem;
use rustls;
use rustls::Session;
use std::fs::{self, File};
use std::io::Read;
use std::net::SocketAddr;
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
use structopt;
use structopt::StructOpt;
use tokio_io::AsyncWriteExt;
use tokio_net::driver::Handle;
use tokio_net::process::Command;
use tokio_rustls;

fn pems_to_chain(data: &[u8]) -> Vec<Vec<u8>> {
    pem::parse_many(data)
        .into_iter()
        .filter(|p| p.tag == "CERTIFICATE")
        .map(|p| p.contents)
        .collect()
}

fn new_http_client(
) -> hyper::Client<hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>> {
    hyper::Client::builder().build(hyper_rustls::HttpsConnector::new())
}

fn compute_paths(paths: &[String]) -> Vec<String> {
    paths
        .iter()
        .flat_map(|p| {
            if fs::metadata(p).unwrap().is_dir() {
                fs::read_dir(p)
                    .unwrap()
                    .map(|d| d.unwrap().path().to_str().unwrap().to_string())
                    .collect()
            } else {
                vec![p.clone()]
            }
        })
        .collect()
}

async fn submit(paths: &[String], all_logs: bool) {
    let http_client = new_http_client();

    let logs = if all_logs {
        fetch_all_ct_logs(&http_client).await
    } else {
        fetch_trusted_ct_logs(&http_client).await
    };

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let logs = &logs;
            let mut chain = pems_to_chain(&contents);
            async move {
                if chain.len() == 1 {
                    // TODO: There's got to be some way to do this ourselves, instead of using crt.sh
                    // as a glorified AIA chaser.
                    println!(
                    "[{}] Only one certificate in chain, using crt.sh to build a full chain ...",
                    &path
                );
                    let new_chain = crtsh::build_chain_for_cert(&http_client, &chain[0]).await;
                    chain = match new_chain {
                        Ok(c) => c,
                        Err(()) => {
                            println!("[{}] Unable to build a chain", path);
                            return futures::future::ready(());
                        }
                    }
                }
                println!("[{}] Submitting ...", &path);
                let timeout = Duration::from_secs(30);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;

                if !scts.is_empty() {
                    println!(
                        "[{}] Find the cert on crt.sh: {}",
                        path,
                        crtsh::url_for_cert(&chain[0])
                    );
                    let mut table = prettytable::Table::new();
                    table.add_row(prettytable::Row::new(vec![prettytable::Cell::new("Log")]));
                    for (log_idx, _) in scts {
                        let log = &logs[log_idx];
                        table.add_row(prettytable::Row::new(vec![prettytable::Cell::new(
                            &log.description,
                        )]));
                    }
                    table.printstd();
                    println!();
                    println!();
                } else {
                    println!("[{}] No SCTs obtained", &path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(4)
        .for_each(async move |()| ());
    work.await;
}

async fn check(paths: &[String]) {
    let http_client = new_http_client();

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let chain = pems_to_chain(&contents);
            async move {
                if !chain.is_empty() && crtsh::is_cert_logged(&http_client, &chain[0]).await {
                    println!("{} was already logged", path);
                } else {
                    println!("{} has not been logged", path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(16)
        .for_each(async move |()| ());
    work.await;
}

async fn handle_request<C: hyper::client::connect::Connect + 'static>(
    request: hyper::Request<hyper::Body>,
    templates: Arc<tera::Tera>,
    http_client: Arc<hyper::Client<C>>,
    logs: Arc<Vec<Log>>,
    client_cert: Option<rustls::Certificate>,
) -> Result<hyper::Response<hyper::Body>, hyper::Error> {
    let mut crtsh_url = None;
    let mut rendered_cert = None;
    if let Some(cert) = client_cert {
        if request.method() == hyper::Method::POST {
            if let Ok(chain) = crtsh::build_chain_for_cert(&http_client, &cert.0).await {
                let timeout = Duration::from_secs(5);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;
                if !scts.is_empty() {
                    crtsh_url = Some(crtsh::url_for_cert(&chain[0]));
                    println!("Successfully submitted: {}", sha256_hex(&chain[0]));
                }
            }
        }

        let mut process = Command::new("openssl")
            .arg("x509")
            .arg("-text")
            .arg("-noout")
            .arg("-inform")
            .arg("der")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();
        let cert_bytes = cert.0.clone();
        let mut stdin = process.stdin().take().unwrap();
        AsyncWriteExt::write_all(&mut stdin, &cert_bytes)
            .await
            .unwrap();
        let out = process.wait_with_output().await.unwrap();
        rendered_cert = Some(String::from_utf8_lossy(&out.stdout).into_owned());
    }

    let mut context = tera::Context::new();
    context.insert("cert", &rendered_cert);
    context.insert("crtsh_url", &crtsh_url);
    let body = templates.render("home.html", &context).unwrap();
    Ok(hyper::Response::builder()
        .status(hyper::StatusCode::OK)
        .body(body.into())
        .unwrap())
}

struct NoVerificationCertificateVerifier;
impl rustls::ClientCertVerifier for NoVerificationCertificateVerifier {
    fn client_auth_mandatory(&self) -> bool {
        false
    }

    fn verify_client_cert(
        &self,
        _: &[rustls::Certificate],
    ) -> Result<rustls::ClientCertVerified, rustls::TLSError> {
        Ok(rustls::ClientCertVerified::assertion())
    }

    fn client_auth_root_subjects(&self) -> rustls::DistinguishedNames {
        rustls::DistinguishedNames::new()
    }
}

async fn server(local_dev: bool, domain: Option<&str>, letsencrypt_env: Option<&str>) {
    match (local_dev, domain, letsencrypt_env) {
        (true, Some(_), _) | (true, _, Some(_)) => {
            panic!("Can't use both --local-dev and --letsencrypt-env or --domain")
        }
        (_, Some(_), None) | (_, None, Some(_)) => {
            panic!("When using Let's Encrypt, must set both --letsencrypt-env and --domain")
        }
        (false, _, None) => panic!("Must set at least one of --local-dev or --letsencrypt-env"),
        _ => {}
    };

    // Disable certificate verification. In any normal context, this would be horribly insecure!
    // However, all we're doing is taking the certs and then turning around and submitting them to
    // CT logs, so it doesn't matter if they're verified.
    let mut tls_config = rustls::ServerConfig::new(Arc::new(NoVerificationCertificateVerifier));
    if local_dev {
        // TODO: not all the details on the cert are perfect, but it's fine.
        let (cert, pkey) = letsencrypt::generate_temporary_cert("localhost");
        tls_config
            .set_single_cert(
                vec![letsencrypt::openssl_cert_to_rustls(&cert)],
                letsencrypt::openssl_pkey_to_rustls(&pkey),
            )
            .unwrap();
    } else {
        let letsencrypt_url = match letsencrypt_env.unwrap() {
            "prod" => "https://acme-v01.api.letsencrypt.org/directory",
            "dev" => "https://acme-staging.api.letsencrypt.org/directory",
            _ => unreachable!(),
        };
        let cert_cache = letsencrypt::DiskCache::new(
            dirs::home_dir()
                .unwrap()
                .join(".ct-tools")
                .join("certificates"),
        );
        tls_config.cert_resolver = Arc::new(letsencrypt::AutomaticCertResolver::new(
            letsencrypt_url,
            vec![domain.unwrap().to_string()],
            cert_cache,
        ));
    }
    tls_config.set_persistence(rustls::ServerSessionMemoryCache::new(1024));
    tls_config.ticketer = rustls::Ticketer::new();

    let http_client = Arc::new(new_http_client());
    let logs = Arc::new(fetch_trusted_ct_logs(&http_client).await);
    let templates = Arc::new(tera::compile_templates!("templates/*"));

    let addr = if local_dev {
        "0.0.0.0:8000"
    } else {
        "0.0.0.0:443"
    }
    .parse()
    .unwrap();

    // If there aren't at least two threads, the Let's Encrypt integration will deadlock.
    println!("Listening on https://{} ...", addr);
    let listener = match addr {
        SocketAddr::V4(_) => net2::TcpBuilder::new_v4().unwrap(),
        SocketAddr::V6(_) => net2::TcpBuilder::new_v6().unwrap(),
    };
    listener.reuse_port(true).unwrap();
    listener.reuse_address(true).unwrap();
    listener.bind(addr).unwrap();
    let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(tls_config));
    let connections =
        tokio::net::TcpListener::from_std(listener.listen(1024).unwrap(), &Handle::default())
            .unwrap()
            .incoming()
            .and_then(move |sock| tls_acceptor.accept(sock))
            .filter(move |s| futures::future::ready(s.is_ok()))
            .boxed();
    let server = hyper::Server::builder(connections).serve(hyper::service::make_service_fn(
        move |conn: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
            let http_client = Arc::clone(&http_client);
            let templates = Arc::clone(&templates);
            let logs = Arc::clone(&logs);
            let client_cert = conn
                .get_ref()
                .1
                .get_peer_certificates()
                .map(|mut chain| chain.remove(0));

            async {
                Ok::<_, hyper::Error>(hyper::service::service_fn(
                    move |r: hyper::Request<hyper::Body>| {
                        handle_request(
                            r,
                            Arc::clone(&templates),
                            Arc::clone(&http_client),
                            Arc::clone(&logs),
                            client_cert.clone(),
                        )
                    },
                ))
            }
        },
    ));

    server.await.unwrap()
}

#[derive(StructOpt)]
#[structopt(name = "ct-tools")]
enum Opt {
    #[structopt(name = "submit", about = "Directly submits certificates to CT logs")]
    Submit {
        #[structopt(
            long = "all-logs",
            help = "Submit to all logs, instead of just ones trusted by Chrome"
        )]
        all_logs: bool,
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "check",
        about = "Checks whether a certificate exists in CT logs"
    )]
    Check {
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "server",
        about = "Run an HTTPS server that submits client certificates to CT logs"
    )]
    Server {
        #[structopt(
            long = "--local-dev",
            help = "Local development, do not obtain a certificate"
        )]
        local_dev: bool,
        #[structopt(long = "domain", help = "Domain this is running as")]
        domain: Option<String>,
        #[structopt(
            long = "letsencrypt-env",
            raw(possible_values = "&[\"dev\", \"prod\"]"),
            help = "Let's Encrypt environment to use"
        )]
        letsencrypt_env: Option<String>,
    },
}

#[tokio::main]
async fn main() {
    match Opt::from_args() {
        Opt::Submit { paths, all_logs } => {
            submit(&paths, all_logs).await;
        }
        Opt::Check { paths } => {
            check(&paths).await;
        }
        Opt::Server {
            local_dev,
            domain,
            letsencrypt_env,
        } => {
            server(
                local_dev,
                domain.as_ref().map(|s| s.as_ref()),
                letsencrypt_env.as_ref().map(|s| s.as_ref()),
            )
            .await;
        }
    }
}
<|endoftext|>