<commit_before>extern crate git2;

use git2::{Repository,Branch,BranchType,Oid,Reference};

struct Program {
    repo: Repository,
}

fn get_branch_remote(reference: Reference) -> Oid {
    let b = Branch::wrap(reference);
    let upstream = match b.upstream() {
        Ok(u) => u,
        Err(e) => panic!("Failed to get upstream: {}.", e)
    };
    match upstream.get().target() {
        Some(o) => o,
        None => panic!("Failed to get oid of branch.")
    }
}

impl Program {
    fn get_head(&self) -> Reference {
        match self.repo.head() {
            Ok(head) => head,
            Err(e) => panic!("Failed to get head: {}.", e)
        }
    }

    fn get_current_branch_oid(&self) -> Oid {
        let head = self.get_head();
        match head.target() {
            Some(v) => v,
            None => panic!("Failed to get reference to head."),
        }
    }

    fn get_current_branch_name(&self) -> String {
        match self.get_head().name() {
            Some(v) => v.to_string(),
            None => panic!("Failed to get name of head."),
        }
    }

    // fn get_current_branch_remote(&self) -> String {
    //     let oid = self.get_current_branch_oid();
    //     let b = Branch::wrap(oid);
    //     let upstream = b.upstream().ok().unwrap();
    //     String::from(upstream.name().ok().unwrap().unwrap())
    // }

    fn get_current_branch_remote_oid(&self) -> Oid {
        get_branch_remote(self.get_head())
    }

    fn get_current_branch_ahead_behind(&self) -> (usize, usize)  {
        let res = self.repo.graph_ahead_behind(
            self.get_current_branch_oid(),
            self.get_current_branch_remote_oid(),
        );
        match res {
            Ok(r) => r,
            Err(e) => panic!("failed to compute ahead/behind: {}", e)
        }
    }
}

fn main() {
    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => panic!("failed to open: {}", e),
    };
    let program = Program{ repo: repo };
    println!("{}", program.get_current_branch_name());
    let (ahead, behind) = program.get_current_branch_ahead_behind();
    println!("A: {}, B: {}", ahead, behind);
    // let current_branch_name = get_current_branch_name(repo);
    // println!("{:?}", current_branch_name);
    // let us = match repo.find_branch("origin/master", BranchType::Remote) {
    //     Ok(us) => us,
    //     Err(e) => panic!("failed to find origin/master: {}", e)
    // };
}
<commit_msg>use shorthand branch name<commit_after>extern crate git2;

use git2::{Repository,Branch,BranchType,Oid,Reference};

struct Program {
    repo: Repository,
}

fn get_branch_remote(reference: Reference) -> Oid {
    let b = Branch::wrap(reference);
    let upstream = match b.upstream() {
        Ok(u) => u,
        Err(e) => panic!("Failed to get upstream: {}.", e)
    };
    match upstream.get().target() {
        Some(o) => o,
        None => panic!("Failed to get oid of branch.")
    }
}

impl Program {
    fn get_head(&self) -> Reference {
        match self.repo.head() {
            Ok(head) => head,
            Err(e) => panic!("Failed to get head: {}.", e)
        }
    }

    fn get_current_branch_oid(&self) -> Oid {
        let head = self.get_head();
        match head.target() {
            Some(v) => v,
            None => panic!("Failed to get reference to head."),
        }
    }

    fn get_current_branch_name(&self) -> String {
        match self.get_head().shorthand() {
            Some(v) => v.to_string(),
            None => panic!("Failed to get name of head."),
        }
    }

    // fn get_current_branch_remote(&self) -> String {
    //     let oid = self.get_current_branch_oid();
    //     let b = Branch::wrap(oid);
    //     let upstream = b.upstream().ok().unwrap();
    //     String::from(upstream.name().ok().unwrap().unwrap())
    // }

    fn get_current_branch_remote_oid(&self) -> Oid {
        get_branch_remote(self.get_head())
    }

    fn get_current_branch_ahead_behind(&self) -> (usize, usize)  {
        let res = self.repo.graph_ahead_behind(
            self.get_current_branch_oid(),
            self.get_current_branch_remote_oid(),
        );
        match res {
            Ok(r) => r,
            Err(e) => panic!("failed to compute ahead/behind: {}", e)
        }
    }
}

fn main() {
    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => panic!("failed to open: {}", e),
    };
    let program = Program{ repo: repo };
    println!("{}", program.get_current_branch_name());
    let (ahead, behind) = program.get_current_branch_ahead_behind();
    println!("A: {}, B: {}", ahead, behind);
    // let current_branch_name = get_current_branch_name(repo);
    // println!("{:?}", current_branch_name);
    // let us = match repo.find_branch("origin/master", BranchType::Remote) {
    //     Ok(us) => us,
    //     Err(e) => panic!("failed to find origin/master: {}", e)
    // };
}
<|endoftext|>


<commit_before>//Copyright (c) 2016, Ruslan Baratov, Alex Frappier Lachapelle
//All rights reserved.

use std::error::Error;
use std::fs;
use std::fs::OpenOptions;
use std::path::{Path, PathBuf};
use std::str::FromStr;

extern crate clap;
use clap::{Arg, App};

#[macro_use]
extern crate log;
use log::LogLevelFilter;

extern crate hyper;

mod commands;
mod config;
mod logger;
mod webhooks;

include!("logger_macros.rs");

////////////////////////////////////////////////////////////
//                          Funcs                         //
////////////////////////////////////////////////////////////

//Utils
//NOTE: No logging here, these functions fire before logger is initialized
fn file_path_validator(file_path: String) -> Result<(), String> {
    match OpenOptions::new().read(true).write(true).create(true).open(Path::new(&file_path)) {
        Ok(_)    => Ok(()),
        Err(err) => {Err(format!("Cannot open file \"{}\" due to an error: \"{}\"", file_path.as_str(), err.description()))}
    }
}

fn dir_path_validator(dir_path: String) -> Result<(), String> {

    //Check if it's even a directory.
    match fs::metadata(&dir_path) {
        Ok(metadata) => {
            if !metadata.is_dir() {
                let mut err_string = "\"".to_string();
                err_string.push_str(dir_path.as_str());
                err_string.push_str("\" is not a directory.");
                return Err(err_string);
            }
        },
        Err(err)     => {
            let mut err_string = "Failed to acquire metadata for \"".to_string();
            err_string.push_str(dir_path.as_str());
            err_string.push_str("\": ");
            err_string.push_str(err.description());
            return Err(err_string);
        }
    }

    //Test if we can write a file to this directory
    let mut counter = 0;
    loop {
        //Generate a file name.
        let mut tmp_file_path:     String = dir_path.clone();
        let     tmp_file_name:     String = format!("ab{}ba.tmp", counter);
        let     tmp_file_path_buf: PathBuf;
        tmp_file_path.push_str(tmp_file_name.as_str());
        tmp_file_path_buf = PathBuf::from(&tmp_file_path);

        //If file does not exist, check if we can create it with r/w permissions.
        if !tmp_file_path_buf.exists() {
            match OpenOptions::new().read(true).write(true).create(true).open(&tmp_file_path_buf.as_path()) {
                Ok(_)    => {
                    match fs::remove_file(&tmp_file_path_buf) {
                        Ok(_)    => return Ok(()),
                        Err(err) => {
                            let mut err_string = "Failed to delete temporary file: \"".to_string();
                            err_string.push_str(&tmp_file_path);
                            err_string.push_str("\": ");
                            err_string.push_str(err.description());
                            return Err(err_string);
                        }
                    }
                }
                Err(err) => {
                    let mut err_string = "Invalid directory: \"".to_string();
                    err_string.push_str(err.description());
                    err_string.push_str("\"");
                    return Err(err_string);
                }
            }
        }
        counter += 1;
    }
}

fn max_log_level_validator(max_log_level: String) -> Result<(), String> {
    match &max_log_level.to_lowercase()[..] {
        "error" => return Ok(()),
        "warn"  => return Ok(()),
        "info"  => return Ok(()),
        "debug" => return Ok(()),
        "trace" => return Ok(()),
        _       => return Err(String::from("Invalid log level"))
    }
}

fn log_size_validator(log_size: String) -> Result<(), String> {
    match u64::from_str(&log_size[..]) {
        Ok(_)    => return Ok(()),
        Err(err) => return Err(format!("Failed to parse the log size: {}", err.description()))
    }
}

////////////////////////////////////////////////////////////
//                          Main                          //
////////////////////////////////////////////////////////////

fn main() {

    let hunterbot_version = "0.1.0";
    let matches           = App::new("HunterBot")
    .version(hunterbot_version)
    .arg(Arg::with_name("CONFIG")
        .short("c")
        .long("config")
        .help("Sets a custom file path for the config.")
        .validator(file_path_validator)
        .takes_value(true))
    .arg(Arg::with_name("LOG")
        .short("l")
        .long("log-dir")
        .help("Sets a custom directory path for the log.")
        .validator(dir_path_validator)
        .takes_value(true))
    .arg(Arg::with_name("MAXLOGLEVEL")
        .short("m")
        .long("max-log-level")
        .help("Sets the maximum logging level, \"Info\" is the default, valid values are (in increasing order): error, warn, info, debug, trace.")
        .validator(max_log_level_validator)
        .takes_value(true))
    .arg(Arg::with_name("LOGSIZE")
        .short("s")
        .long("log-size")
        .help("Sets the maximum log file (in MB) before being rotated.")
        .validator(log_size_validator)
        .takes_value(true))
    .get_matches();

    let hunterbot_config_path = matches.value_of("CONFIG").unwrap_or("./HunterBotConfig.toml");
    let hunterbot_log_dir     = matches.value_of("LOG").unwrap_or("./");
    let log_size              = u64::from_str(matches.value_of("LOGSIZE").unwrap_or("5")).unwrap();
    let max_log_level         = match matches.value_of("MAXLOGLEVEL").unwrap_or("info") {
        "error" => LogLevelFilter::Error,
        "warn"  => LogLevelFilter::Warn,
        "info"  => LogLevelFilter::Info,
        "debug" => LogLevelFilter::Debug,
        "trace" => LogLevelFilter::Trace,
        _       => LogLevelFilter::Error
    };

    //Start logger
    let (logger, rx) = logger::Logger::new(&max_log_level);
    match logger::Logger::init(logger, max_log_level) {
        Ok(())   => (),
        Err(err) => {panic!("Failed to initialize the logger: {}", err);}
    }
    logger::Logger::process_logs(rx, PathBuf::from(hunterbot_log_dir), log_size);

    thread_info!("Logger booted.");
    thread_debug!("matches: {:?}", matches);
    thread_debug!("hunterbot_config_path: {}", hunterbot_config_path);
    thread_debug!("hunterbot_log_dir: {}", hunterbot_log_dir);
    thread_debug!("log_size: {}", log_size);
    thread_debug!("max_log_level: {}", max_log_level);

    //Load config
    let mut config = config::ConfigHandler::new();

    thread_info!("Opening config...");
    match config.load(&hunterbot_config_path.to_string()) {
        Ok(())   => {thread_info!("Success!");}
        Err(err) => {thread_crash!("Error loading the config: {}", err);}
    }

    config.validate();

    //Setup webhooks
    webhooks::register(&mut config);

    //Listen for/process webhooks
    webhooks::listen(&mut config);
}
<commit_msg>Changed formatting on some log messages.<commit_after>//Copyright (c) 2016, Ruslan Baratov, Alex Frappier Lachapelle
//All rights reserved.

use std::error::Error;
use std::fs;
use std::fs::OpenOptions;
use std::path::{Path, PathBuf};
use std::str::FromStr;

extern crate clap;
use clap::{Arg, App};

#[macro_use]
extern crate log;
use log::LogLevelFilter;

extern crate hyper;

mod commands;
mod config;
mod logger;
mod webhooks;

include!("logger_macros.rs");

////////////////////////////////////////////////////////////
//                          Funcs                         //
////////////////////////////////////////////////////////////

//Utils
//NOTE: No logging here, these functions fire before logger is initialized
fn file_path_validator(file_path: String) -> Result<(), String> {
    match OpenOptions::new().read(true).write(true).create(true).open(Path::new(&file_path)) {
        Ok(_)    => Ok(()),
        Err(err) => {Err(format!("Cannot open file \"{}\" due to an error: \"{}\"", file_path.as_str(), err.description()))}
    }
}

fn dir_path_validator(dir_path: String) -> Result<(), String> {

    //Check if it's even a directory.
    match fs::metadata(&dir_path) {
        Ok(metadata) => {
            if !metadata.is_dir() {
                let mut err_string = "\"".to_string();
                err_string.push_str(dir_path.as_str());
                err_string.push_str("\" is not a directory.");
                return Err(err_string);
            }
        },
        Err(err)     => {
            let mut err_string = "Failed to acquire metadata for \"".to_string();
            err_string.push_str(dir_path.as_str());
            err_string.push_str("\": ");
            err_string.push_str(err.description());
            return Err(err_string);
        }
    }

    //Test if we can write a file to this directory
    let mut counter = 0;
    loop {
        //Generate a file name.
        let mut tmp_file_path:     String = dir_path.clone();
        let     tmp_file_name:     String = format!("ab{}ba.tmp", counter);
        let     tmp_file_path_buf: PathBuf;
        tmp_file_path.push_str(tmp_file_name.as_str());
        tmp_file_path_buf = PathBuf::from(&tmp_file_path);

        //If file does not exist, check if we can create it with r/w permissions.
        if !tmp_file_path_buf.exists() {
            match OpenOptions::new().read(true).write(true).create(true).open(&tmp_file_path_buf.as_path()) {
                Ok(_)    => {
                    match fs::remove_file(&tmp_file_path_buf) {
                        Ok(_)    => return Ok(()),
                        Err(err) => {
                            let mut err_string = "Failed to delete temporary file: \"".to_string();
                            err_string.push_str(&tmp_file_path);
                            err_string.push_str("\": ");
                            err_string.push_str(err.description());
                            return Err(err_string);
                        }
                    }
                }
                Err(err) => {
                    let mut err_string = "Invalid directory: \"".to_string();
                    err_string.push_str(err.description());
                    err_string.push_str("\"");
                    return Err(err_string);
                }
            }
        }
        counter += 1;
    }
}

fn max_log_level_validator(max_log_level: String) -> Result<(), String> {
    match &max_log_level.to_lowercase()[..] {
        "error" => return Ok(()),
        "warn"  => return Ok(()),
        "info"  => return Ok(()),
        "debug" => return Ok(()),
        "trace" => return Ok(()),
        _       => return Err(String::from("Invalid log level"))
    }
}

fn log_size_validator(log_size: String) -> Result<(), String> {
    match u64::from_str(&log_size[..]) {
        Ok(_)    => return Ok(()),
        Err(err) => return Err(format!("Failed to parse the log size: {}", err.description()))
    }
}

////////////////////////////////////////////////////////////
//                          Main                          //
////////////////////////////////////////////////////////////

fn main() {

    let hunterbot_version = "0.1.0";
    let matches           = App::new("HunterBot")
    .version(hunterbot_version)
    .arg(Arg::with_name("CONFIG")
        .short("c")
        .long("config")
        .help("Sets a custom file path for the config.")
        .validator(file_path_validator)
        .takes_value(true))
    .arg(Arg::with_name("LOG")
        .short("l")
        .long("log-dir")
        .help("Sets a custom directory path for the log.")
        .validator(dir_path_validator)
        .takes_value(true))
    .arg(Arg::with_name("MAXLOGLEVEL")
        .short("m")
        .long("max-log-level")
        .help("Sets the maximum logging level, \"Info\" is the default, valid values are (in increasing order): error, warn, info, debug, trace.")
        .validator(max_log_level_validator)
        .takes_value(true))
    .arg(Arg::with_name("LOGSIZE")
        .short("s")
        .long("log-size")
        .help("Sets the maximum log file (in MB) before being rotated.")
        .validator(log_size_validator)
        .takes_value(true))
    .get_matches();

    let hunterbot_config_path = matches.value_of("CONFIG").unwrap_or("./HunterBotConfig.toml");
    let hunterbot_log_dir     = matches.value_of("LOG").unwrap_or("./");
    let log_size              = u64::from_str(matches.value_of("LOGSIZE").unwrap_or("5")).unwrap();
    let max_log_level         = match matches.value_of("MAXLOGLEVEL").unwrap_or("info") {
        "error" => LogLevelFilter::Error,
        "warn"  => LogLevelFilter::Warn,
        "info"  => LogLevelFilter::Info,
        "debug" => LogLevelFilter::Debug,
        "trace" => LogLevelFilter::Trace,
        _       => LogLevelFilter::Error
    };

    //Start logger
    let (logger, rx) = logger::Logger::new(&max_log_level);
    match logger::Logger::init(logger, max_log_level) {
        Ok(())   => (),
        Err(err) => {panic!("Failed to initialize the logger: {}", err);}
    }
    logger::Logger::process_logs(rx, PathBuf::from(hunterbot_log_dir), log_size);

    thread_info!("Logger booted.");
    thread_debug!("matches: {:?}", matches);
    thread_debug!("Option: hunterbot_config_path: {}", hunterbot_config_path);
    thread_debug!("Option: hunterbot_log_dir:     {}", hunterbot_log_dir);
    thread_debug!("Option: log_size:              {}", log_size);
    thread_debug!("Option: max_log_level:         {}", max_log_level);

    //Load config
    let mut config = config::ConfigHandler::new();

    thread_info!("Opening config...");
    match config.load(&hunterbot_config_path.to_string()) {
        Ok(())   => {thread_info!("Success!");}
        Err(err) => {thread_crash!("Error loading the config: {}", err);}
    }

    config.validate();

    //Setup webhooks
    webhooks::register(&mut config);

    //Listen for/process webhooks
    webhooks::listen(&mut config);
}
<|endoftext|>


<commit_before>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *   [
  *     [0,11723767],
  *     [1, 65802]
  *    ]
  *  ],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
  * the function names for the corresponding addresses (in the "stacks" array) and returns JSON
  * such as:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

extern crate breakpad_symbols;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use hyper::Client;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/**
  * Incoming JSON request format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // index, offset
    stacks: Vec<Vec<(i8,u64)>>,
    version: u8,
}

/**
  * Outgoing JSON response format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    symbolicatedStacks: Vec<Vec<String>>,
    knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let address = "0.0.0.0:5000";

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();

            debug!("decoded.stacks: {:?}", decoded.stacks);

            let stacks = decoded.stacks[0].clone();
            for stack in &stacks {
                let (index, offset) = *stack;

                let mut offsets = vec!();
                if stack_map.contains_key(&index) {
                    offsets = stack_map.get(&index).unwrap().clone();
                }
                offsets.push(offset);
                stack_map.insert(index, offsets);
            }

            debug!("stack_map: {:?}", stack_map);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map.clone());
            res.write_all(symbol_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let mut body = String::new();
                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                res.read_to_string(&mut body).unwrap();

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);
            // FIXME this is having problems with CRLF

            let mut symbols = vec!();
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    if counter == -1 {
                        symbols.push(format!("0x{:x}", address));
                    } else {
                        debug!("attempt to symbolicate: {} for: {:?}", *address, &symbol_path);
                        match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                            Some(x) => symbols.push(x),
                            // return the address rather than function name if symbol not found
                            None => symbols.push(format!("0x{:x}", address)),
                        }
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    for handle in handles {
        let (debug_file_name, symbols) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(true);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<commit_msg>remove dead code<commit_after>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *   [
  *     [0,11723767],
  *     [1, 65802]
  *    ]
  *  ],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
  * the function names for the corresponding addresses (in the "stacks" array) and returns JSON
  * such as:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

extern crate breakpad_symbols;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use hyper::Client;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/**
  * Incoming JSON request format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // index, offset
    stacks: Vec<Vec<(i8,u64)>>,
    version: u8,
}

/**
  * Outgoing JSON response format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    symbolicatedStacks: Vec<Vec<String>>,
    knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let address = "0.0.0.0:5000";

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();

            debug!("decoded.stacks: {:?}", decoded.stacks);

            let stacks = decoded.stacks[0].clone();
            for stack in &stacks {
                let (index, offset) = *stack;

                let mut offsets = vec!();
                if stack_map.contains_key(&index) {
                    offsets = stack_map.get(&index).unwrap().clone();
                }
                offsets.push(offset);
                stack_map.insert(index, offsets);
            }

            debug!("stack_map: {:?}", stack_map);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map.clone());
            res.write_all(symbol_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let mut body = String::new();
                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                res.read_to_string(&mut body).unwrap();

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);
            // FIXME this is having problems with CRLF

            let mut symbols = vec!();
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    debug!("attempt to symbolicate: {} for: {:?}", *address, &symbol_path);
                    match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                        Some(x) => symbols.push(x),
                        // return the address rather than function name if symbol not found
                        None => symbols.push(format!("0x{:x}", address)),
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    for handle in handles {
        let (debug_file_name, symbols) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(true);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate elf;
extern crate rbpf;

use std::process;
use clap::{Arg, App};

fn main() {
    let matches = App::new("ebpf-disasm")
        .version("1.0")
        .author("Jan-Erik Rediger")
        .about("Output disassembled eBPF read from an ELF file")
        .arg(Arg::with_name("section")
             .short("s")
             .long("section")
             .value_name("SECTION")
             .help("Specify a section name")
             .takes_value(true))
        .arg(Arg::with_name("INPUT")
             .help("Sets the input ELF file to use")
             .required(true)
             .index(1))
        .get_matches();

    let section = matches.value_of("section").unwrap_or(".classifier");
    let input_file = matches.value_of("INPUT").unwrap();

    let file = match elf::File::open_path(&input_file) {
        Ok(f) => f,
        Err(e) => {
            println!("Could not read elf file.");
            println!("Error: {:?}", e);
            process::exit(1);
        }
    };

    let text_scn = match file.get_section(&section) {
        Some(s) => s,
        None => {
            println!("Failed to lookup '{}' section.", section);
            process::exit(1);
        }
    };

    let prog = &text_scn.data;

    for insn in rbpf::disassembler::to_insn_vec(prog) {
        println!("{}", insn.desc.replace(" ", "\t"));
    }
}
<commit_msg>Option to show raw bytecode<commit_after>extern crate clap;
extern crate elf;
extern crate rbpf;

use std::process;
use clap::{Arg, App};

fn main() {
    let matches = App::new("ebpf-disasm")
        .version("1.0")
        .author("Jan-Erik Rediger")
        .about("Output disassembled eBPF read from an ELF file")
        .arg(Arg::with_name("section")
             .short("s")
             .long("section")
             .value_name("SECTION")
             .help("Specify a section name")
             .takes_value(true))
        .arg(Arg::with_name("INPUT")
             .help("Sets the input ELF file to use")
             .required(true)
             .index(1))
        .arg(Arg::with_name("bytecode")
            .short("b")
            .long("bytecode")
            .help("Only show raw bytecode"))
        .get_matches();

    let section = matches.value_of("section").unwrap_or(".classifier");
    let input_file = matches.value_of("INPUT").unwrap();
    let show_bytecode = matches.is_present("bytecode");

    let file = match elf::File::open_path(&input_file) {
        Ok(f) => f,
        Err(e) => {
            println!("Could not read elf file.");
            println!("Error: {:?}", e);
            process::exit(1);
        }
    };

    let text_scn = match file.get_section(&section) {
        Some(s) => s,
        None => {
            println!("Failed to lookup '{}' section.", section);
            process::exit(1);
        }
    };

    let prog = &text_scn.data;

    if show_bytecode {
        for insn in prog.chunks(8) {
            for i in insn {
                print!("0x{:>02x}, ", i);
            }
            println!("");
        }
    } else {
        for insn in rbpf::disassembler::to_insn_vec(prog) {
            println!("{}", insn.desc.replace(" ", "\t"));
        }
    }
}
<|endoftext|>


<commit_before>//! Loads the configuration and runs the game workflow.
//! This file not sourced for the wasm32 (wasm) target.
#![cfg(not(target_arch = "wasm32"))]

extern crate crossterm;
extern crate rand;
extern crate thiserror;
mod application;
mod dictionary;
mod game;
mod image;
mod secret;
use application::Application;
use application::HangmanBackend;
use application::{AUTHOR, CONF_TEMPLATE, TITLE, VERSION};
use game::State;
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Write;
use std::path::PathBuf;
use std::process;

use crossterm::cursor::MoveTo;
use crossterm::cursor::MoveToNextLine;
use crossterm::queue;
use crossterm::style::Color;
use crossterm::style::Print;
use crossterm::style::SetForegroundColor;
use crossterm::terminal::Clear;
use crossterm::terminal::ClearType;
use std::io::stdout;

/// Text to show as command-line help --help
const COMMANDLINE_HELP: &str = r#"
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.

===================================
-----------------------------------
ASCII-ART HANGMAN FOR KIDS

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

Lifes:  1       Last guess: 3

 g o o _ _ l _ _ k

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

 Usage: ascii-hangman
        ascii-hangman [FILE]...
        ascii-hangman -h|--help
        asciiart-ascii-hangman-for-kids -V|--version

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

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

`[FILE]` is a UTF-8 file containing 4 different line-types:

- lines starting with a letter, a digit or '-' are secret strings. At the
  beginning of the game one line is randomly chosen and all characters are
  hidden. If you want to give an additional hint, enclose some characters
  with `_`.  The enclosed is then displayed in clear when the game starts.
  For example the configuration line:

      Guess _me_

  is shown in the game as:

      _ _ _ _ _ _ m e

- lines starting with `#` are ignored. This can be used for comments.

- lines starting with `|` are part of an optional custom 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).

The following shows an example of a custom image (copy it left-aligned
into the config-file):

        |  ,~~--~~-.
        | +      | |\
        | || |~ |`,/-\
        | *\_) \_) `-'

If you prefer a traditional gallows image, add the following:

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

"#;

/// Default configuration filename when no filename is given at the command-line.
const PATHSTR: &str = "ascii-hangman-words.txt";
/// Fallback sample configuration when no configuration file can be found.

/// Fallback secret when no configuration file can be found.
const CONF_DEMO: &str = "- _Demo: add own words to config file and start a_gain_!";

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

/// Reads the configuration file.
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)
}

/// Writes a sample configuration file on disk. Called when no configuration file can be found.
pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

/// Starts the game.
/// This is the terminal application
#[allow(unused_labels)]
fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            eprintln!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(ref a) if a == "-V" || a == "--version" => {
            eprintln!("{}", VERSION.unwrap());
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths = env::args()
        .skip(1)
        .map(PathBuf::from)
        .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 concatenate 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(_) => {
                        eprintln!(
                            "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 s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        eprintln!(
                            "Couldn't write ascii-hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            why.to_string(),
                            cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE THE GAME

    let mut app = match Application::new(&config) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("ERROR IN CONFIGURATION FILE\n{}", e);

            // wait for [Enter] key
            let s = &mut String::new();
            io::stdin().read_line(s).unwrap();
            process::exit(1);
        }
    };

    app.render();

    // PLAY

    'playing: loop {
        // Read user input
        io::stdout().flush().unwrap();
        // Read next char and send it
        let key = &mut String::new();
        io::stdin().read_line(key).unwrap();

        app.process_user_input(key);
        app.render();
        io::stdout().flush().unwrap();
        let state = app.get_state();
        if state != State::Ongoing {
            if state != State::VictoryGameOver {
                queue!(
                    stdout(),
                    MoveToNextLine(1),
                    Print("Press any key to continue or [Ctrl+C] to quit.")
                )
                .unwrap();
                stdout().flush().unwrap();
            } else {
                break;
            };
        };
    }

    queue!(stdout(), MoveToNextLine(1), Print(AUTHOR)).unwrap();
    stdout().flush().unwrap();
    let key = &mut String::new();
    io::stdin().read_line(key).unwrap();
}

/// Trait to render the text user interface (TUI)
trait Render {
    /// Renders and prints the TUI on the terminal.
    fn render(&self) {}
}

impl Render for Application {
    fn render(&self) {
        // Disclose parts of the image.

        // Clear all lines in terminal;
        queue!(stdout(), Clear(ClearType::All), MoveTo(0, 0)).unwrap();

        #[cfg(not(windows))]
        queue!(stdout(), SetForegroundColor(Color::White),).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();

        queue!(
            stdout(),
            Print(&TITLE),
            MoveToNextLine(1),
            SetForegroundColor(Color::DarkYellow),
        )
        .unwrap();

        // Print image.
        queue!(stdout(), Print(self.render_image()), MoveToNextLine(1)).unwrap();

        // Print game status.
        #[cfg(not(windows))]
        queue!(stdout(), SetForegroundColor(Color::White),).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();
        queue!(stdout(), Print(self.render_game_lifes()), Print("\t")).unwrap();
        queue!(
            stdout(),
            Print(self.render_game_last_guess()),
            MoveToNextLine(2)
        )
        .unwrap();

        // Print secret.
        #[cfg(not(windows))]
        queue!(stdout(), SetForegroundColor(Color::DarkGreen),).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::White),).unwrap();
        queue!(stdout(), Print(self.render_secret()), MoveToNextLine(1)).unwrap();

        // Print instructions.
        #[cfg(not(windows))]
        queue!(stdout(), SetForegroundColor(Color::White),).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();

        queue!(stdout(), Print(self.render_instructions()), Print(" ")).unwrap();
        // Print queued.
        stdout().flush().unwrap();
    }
}
<commit_msg>linux: be terminal background agnostic<commit_after>//! Loads the configuration and runs the game workflow.
//! This file not sourced for the wasm32 (wasm) target.
#![cfg(not(target_arch = "wasm32"))]

extern crate crossterm;
extern crate rand;
extern crate thiserror;
mod application;
mod dictionary;
mod game;
mod image;
mod secret;
use application::Application;
use application::HangmanBackend;
use application::{AUTHOR, CONF_TEMPLATE, TITLE, VERSION};
use game::State;
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Write;
use std::path::PathBuf;
use std::process;

use crossterm::cursor::MoveTo;
use crossterm::cursor::MoveToNextLine;
use crossterm::queue;
use crossterm::style::Color;
use crossterm::style::Print;
#[cfg(not(target_os = "windows"))]
use crossterm::style::ResetColor;
use crossterm::style::SetForegroundColor;
use crossterm::terminal::Clear;
use crossterm::terminal::ClearType;
use std::io::stdout;

/// Text to show as command-line help --help
const COMMANDLINE_HELP: &str = r#"
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.

===================================
-----------------------------------
ASCII-ART HANGMAN FOR KIDS

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

Lifes:  1       Last guess: 3

 g o o _ _ l _ _ k

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

 Usage: ascii-hangman
        ascii-hangman [FILE]...
        ascii-hangman -h|--help
        asciiart-ascii-hangman-for-kids -V|--version

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

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

`[FILE]` is a UTF-8 file containing 4 different line-types:

- lines starting with a letter, a digit or '-' are secret strings. At the
  beginning of the game one line is randomly chosen and all characters are
  hidden. If you want to give an additional hint, enclose some characters
  with `_`.  The enclosed is then displayed in clear when the game starts.
  For example the configuration line:

      Guess _me_

  is shown in the game as:

      _ _ _ _ _ _ m e

- lines starting with `#` are ignored. This can be used for comments.

- lines starting with `|` are part of an optional custom 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).

The following shows an example of a custom image (copy it left-aligned
into the config-file):

        |  ,~~--~~-.
        | +      | |\
        | || |~ |`,/-\
        | *\_) \_) `-'

If you prefer a traditional gallows image, add the following:

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

"#;

/// Default configuration filename when no filename is given at the command-line.
const PATHSTR: &str = "ascii-hangman-words.txt";
/// Fallback sample configuration when no configuration file can be found.

/// Fallback secret when no configuration file can be found.
const CONF_DEMO: &str = "- _Demo: add own words to config file and start a_gain_!";

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

/// Reads the configuration file.
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)
}

/// Writes a sample configuration file on disk. Called when no configuration file can be found.
pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

/// Starts the game.
/// This is the terminal application
#[allow(unused_labels)]
fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            eprintln!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(ref a) if a == "-V" || a == "--version" => {
            eprintln!("{}", VERSION.unwrap());
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths = env::args()
        .skip(1)
        .map(PathBuf::from)
        .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 concatenate 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(_) => {
                        eprintln!(
                            "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 s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        eprintln!(
                            "Couldn't write ascii-hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            why.to_string(),
                            cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE THE GAME

    let mut app = match Application::new(&config) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("ERROR IN CONFIGURATION FILE\n{}", e);

            // wait for [Enter] key
            let s = &mut String::new();
            io::stdin().read_line(s).unwrap();
            process::exit(1);
        }
    };

    app.render();

    // PLAY

    'playing: loop {
        // Read user input
        io::stdout().flush().unwrap();
        // Read next char and send it
        let key = &mut String::new();
        io::stdin().read_line(key).unwrap();

        app.process_user_input(key);
        app.render();
        io::stdout().flush().unwrap();
        let state = app.get_state();
        if state != State::Ongoing {
            if state != State::VictoryGameOver {
                queue!(
                    stdout(),
                    MoveToNextLine(1),
                    Print("Press any key to continue or [Ctrl+C] to quit.")
                )
                .unwrap();
                stdout().flush().unwrap();
            } else {
                break;
            };
        };
    }

    queue!(stdout(), MoveToNextLine(1), Print(AUTHOR)).unwrap();
    stdout().flush().unwrap();
    let key = &mut String::new();
    io::stdin().read_line(key).unwrap();
}

/// Trait to render the text user interface (TUI)
trait Render {
    /// Renders and prints the TUI on the terminal.
    fn render(&self) {}
}

impl Render for Application {
    fn render(&self) {
        // Disclose parts of the image.

        // Clear all lines in terminal;
        queue!(stdout(), Clear(ClearType::All), MoveTo(0, 0)).unwrap();

        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();

        queue!(
            stdout(),
            Print(&TITLE),
            MoveToNextLine(1),
            SetForegroundColor(Color::DarkYellow),
        )
        .unwrap();

        // Print image.
        queue!(stdout(), Print(self.render_image()), MoveToNextLine(1)).unwrap();

        // Print game status.
        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();
        queue!(stdout(), Print(self.render_game_lifes()), Print("\t")).unwrap();
        queue!(
            stdout(),
            Print(self.render_game_last_guess()),
            MoveToNextLine(2)
        )
        .unwrap();

        // Print secret.
        #[cfg(not(windows))]
        queue!(stdout(), SetForegroundColor(Color::DarkGreen),).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::White),).unwrap();
        queue!(stdout(), Print(self.render_secret()), MoveToNextLine(1)).unwrap();

        // Print instructions.
        #[cfg(not(windows))]
        queue!(stdout(), ResetColor).unwrap();
        #[cfg(windows)]
        queue!(stdout(), SetForegroundColor(Color::Grey),).unwrap();

        queue!(stdout(), Print(self.render_instructions()), Print(" ")).unwrap();
        // Print queued.
        stdout().flush().unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate regex;

use regex::Regex;

use std::cell::Ref;
use std::cell::RefCell;

use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::io::BufReader;

use std::{thread, time};

struct Attack
{
    damage: u64,
    victim: String,
    timestamp: String,
    crit: String // "" for did not crit?
}

struct Attacker
{
    attacks: RefCell<Vec<Attack>>,
    final_damage: u64,
    final_healed: u64,
    name: String
}

struct Encounter
{
    attackers: RefCell<Vec<Attacker>>,
    encounter_start: String, //timestamp of when the fight started, get this from whatever starts the encounter
    encounter_duration: u64 //duration of the encounter in nanoseconds, divide by 1000 to get seconds
}

impl Encounter
{
    fn exists(&self, name:&str) -> bool
    {
        for i in 0..((self.attackers.borrow()).len())
        {
            if ((self.attackers).borrow())[i].name == name
            {
                return  true;
            }
        }
        return false;
    }

    fn add_attacker(&self, attacker: &str)
    {
        println!("Totes adding it, just don't check!");
        (self.attackers).borrow_mut().push(Attacker{attacks: RefCell::new(Vec::new()), final_damage: 0, final_healed: 0, name: String::from(attacker)});
    }
}

fn main()
{
    let f = File::open("/media/bergman/Games/SteamLibrary/SteamApps/common/EverQuest 2/logs/Maj'Dul/eq2log_Shepherd.txt").unwrap();
    /*{
        Ok(file) => file,
        Err(e) =>
        {
            println!("{}", e);
        }
    };*/

    //Start a encounter, this code will be moved into the main loop when it works
    let mut encounters: Vec<Encounter> = Vec::new();
    encounters.push(Encounter{ attackers: RefCell::new(Vec::new()), encounter_start: String::from("START"), encounter_duration : 0});

    let re = Regex::new(r"\((?P<time>\d+)\)\[(?P<datetime>(\D|\d)+)\] (?P<attacker>\D+)(YOUR|'s) (?P<attack>\D+) ((multi attacks)|multi) (?P<target>\D+) for a (?P<crittype>\D+) of (?P<damage>\d+) (cold|heat|mental|arcane|poison|noxious) damage").unwrap();

    let mut file = BufReader::new(&f);

    let mut buffer = String::new();
    let mut battle_timer = time::Instant::now();
    'main: loop
    {
        if file.read_line(&mut buffer).unwrap() > 0
        {
            /*CHECK IF ATTACK
                CHECK ATTACKER, IF NO ATTACKER EXISTS CREATE A NEW ONE <-- this is done by String-matching the ATTACKERS name
                    PARSE ATTACK INTO ENCOUNTERS.ATTACKERS.ATTACK <-- this is done by String-matching the ATTACKERS name
            SAME FOR HEAL*/
            let temp = re.captures(buffer.as_str()) ;
            match temp {None => {}, Some(cap) =>
            {
                //look to see if the attacker already has a post, if so place the attack there, if not push a new attacker
                if encounters[0].exists(cap.name("attacker").unwrap())
                {
                    println!("He's there alrighty!");
                }
                else
                {
                    encounters[0].add_attacker(cap.name("attacker").unwrap());
                }
                //encounters[0].attackers.cap.name("datetime").unwrap()
            }};
            //if buffer.find("attacks") != ;
            encounters[0].encounter_duration += battle_timer.elapsed().subsec_nanos() as u64;
            battle_timer = time::Instant::now();
        }
        else
        {
            thread::sleep(time::Duration::from_millis(100));
            if battle_timer.elapsed() >= time::Duration::from_millis(3000)
            {
                println!("New battle incoming, {:?} time has elapsed since the last one.", battle_timer.elapsed().as_secs());
            }
        }
        buffer.clear();
    }
}

/* \[P<DATE>\]
\((?P<time>\d+)\)
(1477272172)[Mon Oct 24 03:22:52 2016] YOUR Raging Whirlwind multi attacks Bonesnapper for a critical of 38996 cold damage.\r\n
(1477272172)[Mon Oct 24 03:22:52 2016] Kabernet\'s Asylum multi attacks Bonesnapper for a critical of 36622 mental damage.\r\n

DATASTRUCTURE:
I should probably parse attacks into vectors as well, as there are a limited number of attacks viable and I am likely to want to be able to output all of a specific attack or the total damage of one particular attack
    This is advanced though so I won't do it for the first iteration of the program.

what I should be able to do:

* printout a full encounter as player dps, sorted by dps.
** This should be calculated by final damage divided by encounter_time in seconds

* printout a full encounter as player hps, sorted by hps
** This should be calculated by final healed divided by encounter_time in seconds

*/
<commit_msg>Changed the encounter fn attack to take a regex::Captures instead of a &str<commit_after>extern crate regex;

use regex::Regex;

use std::cell::Ref;
use std::cell::RefCell;

use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::io::BufReader;

use std::{thread, time};

struct Attack
{
    damage: u64,
    victim: String,
    timestamp: String,
    crit: String // "" for did not crit?
}

struct Attacker
{
    attacks: RefCell<Vec<Attack>>,
    final_damage: u64,
    final_healed: u64,
    name: String
}

struct Encounter
{
    attackers: RefCell<Vec<Attacker>>,
    encounter_start: String, //timestamp of when the fight started, get this from whatever starts the encounter
    encounter_duration: u64 //duration of the encounter in nanoseconds, divide by 1000 to get seconds
}

impl Encounter
{
    fn exists(&self, name:&str) -> bool
    {
        for i in 0..((self.attackers.borrow()).len())
        {
            if ((self.attackers).borrow())[i].name == name
            {
                return  true;
            }
        }
        return false;
    }

    fn add_attacker(&self, attacker: &str)
    {
        (self.attackers).borrow_mut().push(Attacker{attacks: RefCell::new(Vec::new()), final_damage: 0, final_healed: 0, name: String::from(attacker)});
    }

    fn attack(&self, attack_data: regex::Captures)
    {
        println!("{}",attack_data.name("attack").unwrap());
    }
}

fn main()
{
    let f = File::open("/home/bergman/Documents/rust/EXP/eq2log_Shepherd.txt").unwrap();
    /*{
        Ok(file) => file,
        Err(e) =>
        {
            println!("{}", e);
        }
    };*/

    //Start a encounter, this code will be moved into the main loop when it works
    let mut encounters: Vec<Encounter> = Vec::new();
    encounters.push(Encounter{ attackers: RefCell::new(Vec::new()), encounter_start: String::from("START"), encounter_duration : 0});

    let re = Regex::new(r"\((?P<time>\d+)\)\[(?P<datetime>(\D|\d)+)\] (?P<attacker>\D+)(YOUR|'s) (?P<attack>\D+) ((multi attacks)|multi) (?P<target>\D+) for a (?P<crittype>\D+) of (?P<damage>\d+) (cold|heat|mental|arcane|poison|noxious) damage").unwrap();

    let mut file = BufReader::new(&f);

    let mut buffer = String::new();
    let mut battle_timer = time::Instant::now();
    'main: loop
    {
        if file.read_line(&mut buffer).unwrap() > 0
        {
            /*CHECK IF ATTACK
                CHECK ATTACKER, IF NO ATTACKER EXISTS CREATE A NEW ONE <-- this is done by String-matching the ATTACKERS name
                    PARSE ATTACK INTO ENCOUNTERS.ATTACKERS.ATTACK <-- this is done by String-matching the ATTACKERS name
            SAME FOR HEAL*/
            let temp = re.captures(buffer.as_str()) ;
            match temp {None => {}, Some(cap) =>
            {
                //look to see if the attacker already has a post, if so place the attack there, if not push a new attacker
                if !encounters[0].exists(cap.name("attacker").unwrap())
                {
                    encounters[0].add_attacker(cap.name("attacker").unwrap());
                }
                encounters[0].attack(cap);
                //encounters[0].attackers.cap.name("datetime").unwrap()
            }};
            //if buffer.find("attacks") != ;
            encounters[0].encounter_duration += battle_timer.elapsed().subsec_nanos() as u64;
            battle_timer = time::Instant::now();
        }
        else
        {
            thread::sleep(time::Duration::from_millis(100));
            if battle_timer.elapsed() >= time::Duration::from_millis(3000)
            {
                println!("New battle incoming, {:?} time has elapsed since the last one.", battle_timer.elapsed().as_secs());
            }
        }
        buffer.clear();
    }
}

/*
(1477272172)[Mon Oct 24 03:22:52 2016] YOUR Raging Whirlwind multi attacks Bonesnapper for a critical of 38996 cold damage.\r\n
(1477272172)[Mon Oct 24 03:22:52 2016] Kabernet\'s Asylum multi attacks Bonesnapper for a critical of 36622 mental damage.\r\n

DATASTRUCTURE:
I should probably parse attacks into vectors as well, as there are a limited number of attacks viable and I am likely to want to be able to output all of a specific attack or the total damage of one particular attack
    This is advanced though so I won't do it for the first iteration of the program.

what I should be able to do:

* printout a full encounter as player dps, sorted by dps.
** This should be calculated by final damage divided by encounter_time in seconds

* printout a full encounter as player hps, sorted by hps
** This should be calculated by final healed divided by encounter_time in seconds

*/
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_signal;

use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_io::IoStream;
use std::mem;

use librespot::core::authentication::{get_credentials, Credentials};
use librespot::core::cache::Cache;
use librespot::core::config::{Bitrate, DeviceType, PlayerConfig, SessionConfig, ConnectConfig};
use librespot::core::session::Session;
use librespot::core::version;

use librespot::playback::audio_backend::{self, Sink, BACKENDS};
use librespot::discovery::discovery::{discovery, DiscoveryStream};
use librespot::playback::mixer::{self, Mixer};
use librespot::playback::player::Player;
use librespot::spirc::{Spirc, SpircTask};

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

fn setup_logging(verbose: bool) {
    let mut builder = LogBuilder::new();
    match env::var("RUST_LOG") {
        Ok(config) => {
            builder.parse(&config);
            builder.init().unwrap();

            if verbose {
                warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
            }
        }
        Err(_) => {
            if verbose {
                builder.parse("mdns=info,librespot=trace");
            } else {
                builder.parse("mdns=info,librespot=info");
            }
            builder.init().unwrap();
        }
    }
}

fn list_backends() {
    println!("Available Backends : ");
    for (&(name, _), idx) in BACKENDS.iter().zip(0..) {
        if idx == 0 {
            println!("- {} (default)", name);
        } else {
            println!("- {}", name);
        }
    }
}

#[derive(Clone)]
struct Setup {
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,

    mixer: fn() -> Box<Mixer>,

    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    credentials: Option<Credentials>,
    enable_discovery: bool,
    zeroconf_port: u16,
}

fn setup(args: &[String]) -> Setup {
    let mut opts = getopts::Options::new();
    opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
        .optflag("", "disable-audio-cache", "Disable caching of the audio data.")
        .reqopt("n", "name", "Device name", "NAME")
        .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
        .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
        .optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
        .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
        .optflag("v", "verbose", "Enable verbose output")
        .optopt("u", "username", "Username to sign in with", "USERNAME")
        .optopt("p", "password", "Password", "PASSWORD")
        .optflag("", "disable-discovery", "Disable discovery mode")
        .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND")
        .optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE")
        .optopt("", "mixer", "Mixer to use", "MIXER")
        .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME")
        .optopt("z", "zeroconf-port", "The port the internal server advertised over zeroconf uses.", "ZEROCONF_PORT");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
            exit(1);
        }
    };

    let verbose = matches.opt_present("verbose");
    setup_logging(verbose);

    info!("librespot {} ({}). Built on {}. Build ID: {}",
             version::short_sha(),
             version::commit_date(),
             version::short_now(),
             version::build_id());

    let backend_name = matches.opt_str("backend");
    if backend_name == Some("?".into()) {
        list_backends();
        exit(0);
    }

    let backend = audio_backend::find(backend_name)
        .expect("Invalid backend");

    let device = matches.opt_str("device");

    let mixer_name = matches.opt_str("mixer");
    let mixer = mixer::find(mixer_name.as_ref())
        .expect("Invalid mixer");

    let initial_volume = matches
        .opt_str("initial-volume")
        .map(|volume| {
            let volume = volume.parse::<i32>().unwrap();
            if volume < 0 || volume > 100 {
                panic!("Initial volume must be in the range 0-100");
            }
            volume * 0xFFFF / 100
        })
        .unwrap_or(0x8000);

    let zeroconf_port =
    matches.opt_str("zeroconf-port")
           .map(|port| port.parse::<u16>().unwrap())
           .unwrap_or(0);

    let name = matches.opt_str("name").unwrap();
    let use_audio_cache = !matches.opt_present("disable-audio-cache");

    let cache = matches.opt_str("c").map(|cache_location| {
        Cache::new(PathBuf::from(cache_location), use_audio_cache)
    });

    let credentials = {
        let cached_credentials = cache.as_ref().and_then(Cache::credentials);

        get_credentials(
            matches.opt_str("username"),
            matches.opt_str("password"),
            cached_credentials
        )
    };

    let session_config = {
        let device_id = librespot::core::session::device_id(&name);

        SessionConfig {
            user_agent: version::version_string(),
            device_id: device_id,
        }
    };

    let player_config = {
        let bitrate = matches.opt_str("b").as_ref()
            .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
            .unwrap_or(Bitrate::default());

        PlayerConfig {
            bitrate: bitrate,
            onstart: matches.opt_str("onstart"),
            onstop: matches.opt_str("onstop"),
        }
    };

    let connect_config = {
        let device_type = matches.opt_str("device-type").as_ref()
            .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type"))
            .unwrap_or(DeviceType::default());

        ConnectConfig {
            name: name,
            device_type: device_type,
            volume: initial_volume,
        }
    };

    let enable_discovery = !matches.opt_present("disable-discovery");

    Setup {
        backend: backend,
        cache: cache,
        session_config: session_config,
        player_config: player_config,
        connect_config: connect_config,
        credentials: credentials,
        device: device,
        enable_discovery: enable_discovery,
        zeroconf_port: zeroconf_port,
        mixer: mixer,
    }
}

struct Main {
    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,
    mixer: fn() -> Box<Mixer>,
    handle: Handle,

    discovery: Option<DiscoveryStream>,
    signal: IoStream<()>,

    spirc: Option<Spirc>,
    spirc_task: Option<SpircTask>,
    connect: Box<Future<Item=Session, Error=io::Error>>,

    shutdown: bool,
}

impl Main {
    fn new(handle: Handle, setup: Setup) -> Main {
        let mut task = Main {
            handle: handle.clone(),
            cache: setup.cache,
            session_config: setup.session_config,
            player_config: setup.player_config,
            connect_config: setup.connect_config,
            backend: setup.backend,
            device: setup.device,
            mixer: setup.mixer,

            connect: Box::new(futures::future::empty()),
            discovery: None,
            spirc: None,
            spirc_task: None,
            shutdown: false,
            signal: Box::new(tokio_signal::ctrl_c(&handle).flatten_stream()),
        };

        if setup.enable_discovery {
            let config = task.connect_config.clone();
            let device_id = task.session_config.device_id.clone();

            task.discovery = Some(discovery(&handle, config, device_id, setup.zeroconf_port).unwrap());
        }

        if let Some(credentials) = setup.credentials {
            task.credentials(credentials);
        }

        task
    }

    fn credentials(&mut self, credentials: Credentials) {
        let config = self.session_config.clone();
        let handle = self.handle.clone();

        let connection = Session::connect(config, credentials, self.cache.clone(), handle);

        self.connect = connection;
        self.spirc = None;
        let task = mem::replace(&mut self.spirc_task, None);
        if let Some(task) = task {
            self.handle.spawn(task);
        }
    }
}

impl Future for Main {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        loop {
            let mut progress = false;

            if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
                if let Some(ref spirc) = self.spirc {
                    spirc.shutdown();
                }
                self.credentials(creds);

                progress = true;
            }

            if let Async::Ready(session) = self.connect.poll().unwrap() {
                self.connect = Box::new(futures::future::empty());
                let device = self.device.clone();
                let mixer = (self.mixer)();
                let player_config = self.player_config.clone();
                let connect_config = self.connect_config.clone();

                let audio_filter = mixer.get_audio_filter();
                let backend = self.backend;
                let player = Player::new(player_config, session.clone(), audio_filter, move || {
                    (backend)(device)
                });

                let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer);
                self.spirc = Some(spirc);
                self.spirc_task = Some(spirc_task);

                progress = true;
            }

            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
                if !self.shutdown {
                    if let Some(ref spirc) = self.spirc {
                        spirc.shutdown();
                    }
                    self.shutdown = true;
                } else {
                    return Ok(Async::Ready(()));
                }

                progress = true;
            }

            if let Some(ref mut spirc_task) = self.spirc_task {
                if let Async::Ready(()) = spirc_task.poll().unwrap() {
                    if self.shutdown {
                        return Ok(Async::Ready(()));
                    } else {
                        panic!("Spirc shut down unexpectedly");
                    }
                }
            }

            if !progress {
                return Ok(Async::NotReady);
            }
        }
    }
}

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let args: Vec<String> = std::env::args().collect();

    core.run(Main::new(handle, setup(&args))).unwrap()
}
<commit_msg>Remove the -z as an alias for the --zeroconf-port parameter<commit_after>#[macro_use] extern crate log;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_signal;

use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_io::IoStream;
use std::mem;

use librespot::core::authentication::{get_credentials, Credentials};
use librespot::core::cache::Cache;
use librespot::core::config::{Bitrate, DeviceType, PlayerConfig, SessionConfig, ConnectConfig};
use librespot::core::session::Session;
use librespot::core::version;

use librespot::playback::audio_backend::{self, Sink, BACKENDS};
use librespot::discovery::discovery::{discovery, DiscoveryStream};
use librespot::playback::mixer::{self, Mixer};
use librespot::playback::player::Player;
use librespot::spirc::{Spirc, SpircTask};

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

fn setup_logging(verbose: bool) {
    let mut builder = LogBuilder::new();
    match env::var("RUST_LOG") {
        Ok(config) => {
            builder.parse(&config);
            builder.init().unwrap();

            if verbose {
                warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
            }
        }
        Err(_) => {
            if verbose {
                builder.parse("mdns=info,librespot=trace");
            } else {
                builder.parse("mdns=info,librespot=info");
            }
            builder.init().unwrap();
        }
    }
}

fn list_backends() {
    println!("Available Backends : ");
    for (&(name, _), idx) in BACKENDS.iter().zip(0..) {
        if idx == 0 {
            println!("- {} (default)", name);
        } else {
            println!("- {}", name);
        }
    }
}

#[derive(Clone)]
struct Setup {
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,

    mixer: fn() -> Box<Mixer>,

    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    credentials: Option<Credentials>,
    enable_discovery: bool,
    zeroconf_port: u16,
}

fn setup(args: &[String]) -> Setup {
    let mut opts = getopts::Options::new();
    opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
        .optflag("", "disable-audio-cache", "Disable caching of the audio data.")
        .reqopt("n", "name", "Device name", "NAME")
        .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
        .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
        .optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
        .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
        .optflag("v", "verbose", "Enable verbose output")
        .optopt("u", "username", "Username to sign in with", "USERNAME")
        .optopt("p", "password", "Password", "PASSWORD")
        .optflag("", "disable-discovery", "Disable discovery mode")
        .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND")
        .optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE")
        .optopt("", "mixer", "Mixer to use", "MIXER")
        .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME")
        .optopt("", "zeroconf-port", "The port the internal server advertised over zeroconf uses.", "ZEROCONF_PORT");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
            exit(1);
        }
    };

    let verbose = matches.opt_present("verbose");
    setup_logging(verbose);

    info!("librespot {} ({}). Built on {}. Build ID: {}",
             version::short_sha(),
             version::commit_date(),
             version::short_now(),
             version::build_id());

    let backend_name = matches.opt_str("backend");
    if backend_name == Some("?".into()) {
        list_backends();
        exit(0);
    }

    let backend = audio_backend::find(backend_name)
        .expect("Invalid backend");

    let device = matches.opt_str("device");

    let mixer_name = matches.opt_str("mixer");
    let mixer = mixer::find(mixer_name.as_ref())
        .expect("Invalid mixer");

    let initial_volume = matches
        .opt_str("initial-volume")
        .map(|volume| {
            let volume = volume.parse::<i32>().unwrap();
            if volume < 0 || volume > 100 {
                panic!("Initial volume must be in the range 0-100");
            }
            volume * 0xFFFF / 100
        })
        .unwrap_or(0x8000);

    let zeroconf_port =
    matches.opt_str("zeroconf-port")
           .map(|port| port.parse::<u16>().unwrap())
           .unwrap_or(0);

    let name = matches.opt_str("name").unwrap();
    let use_audio_cache = !matches.opt_present("disable-audio-cache");

    let cache = matches.opt_str("c").map(|cache_location| {
        Cache::new(PathBuf::from(cache_location), use_audio_cache)
    });

    let credentials = {
        let cached_credentials = cache.as_ref().and_then(Cache::credentials);

        get_credentials(
            matches.opt_str("username"),
            matches.opt_str("password"),
            cached_credentials
        )
    };

    let session_config = {
        let device_id = librespot::core::session::device_id(&name);

        SessionConfig {
            user_agent: version::version_string(),
            device_id: device_id,
        }
    };

    let player_config = {
        let bitrate = matches.opt_str("b").as_ref()
            .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
            .unwrap_or(Bitrate::default());

        PlayerConfig {
            bitrate: bitrate,
            onstart: matches.opt_str("onstart"),
            onstop: matches.opt_str("onstop"),
        }
    };

    let connect_config = {
        let device_type = matches.opt_str("device-type").as_ref()
            .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type"))
            .unwrap_or(DeviceType::default());

        ConnectConfig {
            name: name,
            device_type: device_type,
            volume: initial_volume,
        }
    };

    let enable_discovery = !matches.opt_present("disable-discovery");

    Setup {
        backend: backend,
        cache: cache,
        session_config: session_config,
        player_config: player_config,
        connect_config: connect_config,
        credentials: credentials,
        device: device,
        enable_discovery: enable_discovery,
        zeroconf_port: zeroconf_port,
        mixer: mixer,
    }
}

struct Main {
    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,
    mixer: fn() -> Box<Mixer>,
    handle: Handle,

    discovery: Option<DiscoveryStream>,
    signal: IoStream<()>,

    spirc: Option<Spirc>,
    spirc_task: Option<SpircTask>,
    connect: Box<Future<Item=Session, Error=io::Error>>,

    shutdown: bool,
}

impl Main {
    fn new(handle: Handle, setup: Setup) -> Main {
        let mut task = Main {
            handle: handle.clone(),
            cache: setup.cache,
            session_config: setup.session_config,
            player_config: setup.player_config,
            connect_config: setup.connect_config,
            backend: setup.backend,
            device: setup.device,
            mixer: setup.mixer,

            connect: Box::new(futures::future::empty()),
            discovery: None,
            spirc: None,
            spirc_task: None,
            shutdown: false,
            signal: Box::new(tokio_signal::ctrl_c(&handle).flatten_stream()),
        };

        if setup.enable_discovery {
            let config = task.connect_config.clone();
            let device_id = task.session_config.device_id.clone();

            task.discovery = Some(discovery(&handle, config, device_id, setup.zeroconf_port).unwrap());
        }

        if let Some(credentials) = setup.credentials {
            task.credentials(credentials);
        }

        task
    }

    fn credentials(&mut self, credentials: Credentials) {
        let config = self.session_config.clone();
        let handle = self.handle.clone();

        let connection = Session::connect(config, credentials, self.cache.clone(), handle);

        self.connect = connection;
        self.spirc = None;
        let task = mem::replace(&mut self.spirc_task, None);
        if let Some(task) = task {
            self.handle.spawn(task);
        }
    }
}

impl Future for Main {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        loop {
            let mut progress = false;

            if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
                if let Some(ref spirc) = self.spirc {
                    spirc.shutdown();
                }
                self.credentials(creds);

                progress = true;
            }

            if let Async::Ready(session) = self.connect.poll().unwrap() {
                self.connect = Box::new(futures::future::empty());
                let device = self.device.clone();
                let mixer = (self.mixer)();
                let player_config = self.player_config.clone();
                let connect_config = self.connect_config.clone();

                let audio_filter = mixer.get_audio_filter();
                let backend = self.backend;
                let player = Player::new(player_config, session.clone(), audio_filter, move || {
                    (backend)(device)
                });

                let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer);
                self.spirc = Some(spirc);
                self.spirc_task = Some(spirc_task);

                progress = true;
            }

            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
                if !self.shutdown {
                    if let Some(ref spirc) = self.spirc {
                        spirc.shutdown();
                    }
                    self.shutdown = true;
                } else {
                    return Ok(Async::Ready(()));
                }

                progress = true;
            }

            if let Some(ref mut spirc_task) = self.spirc_task {
                if let Async::Ready(()) = spirc_task.poll().unwrap() {
                    if self.shutdown {
                        return Ok(Async::Ready(()));
                    } else {
                        panic!("Spirc shut down unexpectedly");
                    }
                }
            }

            if !progress {
                return Ok(Async::NotReady);
            }
        }
    }
}

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let args: Vec<String> = std::env::args().collect();

    core.run(Main::new(handle, setup(&args))).unwrap()
}
<|endoftext|>


<commit_before>extern crate getopts;

use getopts::Options;
use std::os;
use std::net::{SocketAddr};
use std::str::FromStr;

fn main() {
    let args: Vec<String> = os::args();
    let mut opts = Options::new();
    let matches = match opts.parse(args.tail()) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };
    let addr_arg = matches.free[0].as_slice();
    let addr:SocketAddr = FromStr::from_str(addr_arg.as_slice()).unwrap();
}
<commit_msg>create ICMP transport channel<commit_after>extern crate getopts;
extern crate pnet;

use getopts::Options;
use std::os;
use std::net::{SocketAddr};
use std::str::FromStr;

use pnet::transport::TransportProtocol::{Ipv4};
use pnet::old_packet::ip::IpNextHeaderProtocols;
use pnet::transport::TransportChannelType::{Layer3};
use pnet::transport::{transport_channel};

fn main() {
    let args: Vec<String> = os::args();
    let mut opts = Options::new();
    let matches = match opts.parse(args.tail()) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };
    let addr_arg = matches.free[0].as_slice();
    let addr:SocketAddr = FromStr::from_str(addr_arg.as_slice()).unwrap();

    let protocol = Layer3(IpNextHeaderProtocols::Test1);
    let (mut tx, mut rx) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("An error occurred when creating the transport channel:
                        {}", e)
    };
}
<|endoftext|>