<commit_before>extern crate cjdrs;
extern crate mio;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

#[cfg(not(test))] use std::{os, io};
#[cfg(not(test))] use docopt::Docopt;
#[cfg(not(test))] use cjdrs::CjdrsError;
#[cfg(not(test))] use cjdrs::CjdrsResult;
#[cfg(not(test))] use cjdrs::Config;
#[cfg(not(test))] use cjdrs::EventHandler;
#[cfg(not(test))] use cjdrs::interface::{self, NetInterface};
#[cfg(not(test))] use cjdrs::Router;
#[cfg(not(test))] use cjdrs::{PrivateKey, PrivateIdentity};

static USAGE: &'static str = "
Usage: cjdrs --help
       cjdrs init [--cfg=<file>]
       cjdrs run [--cfg=<file>]

Options:
  -h, --help      Show this message.
  --cfg=<file>    Configuration file [default: cjdrs.conf]

1. Run 'cjdrs init' to generate a configuration file.
2. Edit the configuration file as needed.
2. Run 'cjdrs run' to start cjdrs.

Configuration file defaults to 'cjdrs.conf' if not given.
";

#[derive(RustcDecodable, Show)]
struct Args {
	cmd_init: bool,
	cmd_run: bool,
	flag_cfg: String,
}

#[cfg(not(test))]
fn main() {
	if let Err(e) = choose_command() {
		os::set_exit_status(1);

		let mut stderr = io::stdio::stderr();
		writeln!(&mut stderr, "Error: {:?}", e).unwrap();
	}
}

#[cfg(not(test))]
fn choose_command() -> CjdrsResult<()> {
	let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
	let config_path = Path::new(args.flag_cfg);

	cjdrs::init();

	if args.cmd_init {
		init_config(&config_path)
	} else {
		assert!(args.cmd_run);
		let config = try!(Config::load(&config_path));
		run_cjdrs(&config)
	}
}

#[cfg(not(test))]
fn init_config(config_path: &Path) -> CjdrsResult<()> {
	let identity = PrivateIdentity::generate();

	let config = Config::get_default(&identity);
	try!(config.write(config_path));

	println!("Created a new configuration file '{}'", config_path.display());
	println!("Public key: {}", identity.public_key);
	println!("Address:    {}", identity.address);

	Ok(())
}

#[cfg(not(test))]
fn run_cjdrs(config: &Config) -> CjdrsResult<()> {
	// Create identity
	let my_identity = {
		let private_key = try!(PrivateKey::from_string(config.privateKey.as_slice()));
		try!(PrivateIdentity::from_private_key(&private_key).ok_or(
			CjdrsError::NoAddressForPrivateKey(private_key)))
	};

	println!("Public key: {}", my_identity.public_key);
	println!("Address:    {}", my_identity.address);

	// Turn on interfaces
	let tun_interface = interface::Tun::new(
		config.tunDevice.as_slice(),
		&my_identity.address);
	println!("Opened tun device '{}'", tun_interface.get_name());

	let udp_interface = try!(interface::Udp::create(config.udpBind.as_slice()));

	let interfaces: Vec<Box<NetInterface>> = vec![
		(box tun_interface) as Box<NetInterface>,
		(box udp_interface) as Box<NetInterface>
	];

	let router = Router::new(&my_identity.address);

	// Start up the event loop
	let mut mio_loop: mio::EventLoop<usize, ()> = try!(mio::EventLoop::new());

	let event_handler = EventHandler::new(
		my_identity,
		interfaces,
		router);

	try!(event_handler.register_handlers(&mut mio_loop));
	try!(mio_loop.run(event_handler));

	Ok(())
}
<commit_msg>box removal fallout<commit_after>extern crate cjdrs;
extern crate mio;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

#[cfg(not(test))] use std::{os, io};
#[cfg(not(test))] use docopt::Docopt;
#[cfg(not(test))] use cjdrs::CjdrsError;
#[cfg(not(test))] use cjdrs::CjdrsResult;
#[cfg(not(test))] use cjdrs::Config;
#[cfg(not(test))] use cjdrs::EventHandler;
#[cfg(not(test))] use cjdrs::interface::{self, NetInterface};
#[cfg(not(test))] use cjdrs::Router;
#[cfg(not(test))] use cjdrs::{PrivateKey, PrivateIdentity};

static USAGE: &'static str = "
Usage: cjdrs --help
       cjdrs init [--cfg=<file>]
       cjdrs run [--cfg=<file>]

Options:
  -h, --help      Show this message.
  --cfg=<file>    Configuration file [default: cjdrs.conf]

1. Run 'cjdrs init' to generate a configuration file.
2. Edit the configuration file as needed.
2. Run 'cjdrs run' to start cjdrs.

Configuration file defaults to 'cjdrs.conf' if not given.
";

#[derive(RustcDecodable, Show)]
struct Args {
	cmd_init: bool,
	cmd_run: bool,
	flag_cfg: String,
}

#[cfg(not(test))]
fn main() {
	if let Err(e) = choose_command() {
		os::set_exit_status(1);

		let mut stderr = io::stdio::stderr();
		writeln!(&mut stderr, "Error: {:?}", e).unwrap();
	}
}

#[cfg(not(test))]
fn choose_command() -> CjdrsResult<()> {
	let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
	let config_path = Path::new(args.flag_cfg);

	cjdrs::init();

	if args.cmd_init {
		init_config(&config_path)
	} else {
		assert!(args.cmd_run);
		let config = try!(Config::load(&config_path));
		run_cjdrs(&config)
	}
}

#[cfg(not(test))]
fn init_config(config_path: &Path) -> CjdrsResult<()> {
	let identity = PrivateIdentity::generate();

	let config = Config::get_default(&identity);
	try!(config.write(config_path));

	println!("Created a new configuration file '{}'", config_path.display());
	println!("Public key: {}", identity.public_key);
	println!("Address:    {}", identity.address);

	Ok(())
}

#[cfg(not(test))]
fn run_cjdrs(config: &Config) -> CjdrsResult<()> {
	// Create identity
	let my_identity = {
		let private_key = try!(PrivateKey::from_string(config.privateKey.as_slice()));
		try!(PrivateIdentity::from_private_key(&private_key).ok_or(
			CjdrsError::NoAddressForPrivateKey(private_key)))
	};

	println!("Public key: {}", my_identity.public_key);
	println!("Address:    {}", my_identity.address);

	// Turn on interfaces
	let tun_interface = interface::Tun::new(
		config.tunDevice.as_slice(),
		&my_identity.address);
	println!("Opened tun device '{}'", tun_interface.get_name());

	let udp_interface = try!(interface::Udp::create(config.udpBind.as_slice()));

	let interfaces: Vec<Box<NetInterface>> = vec![
		Box::new(tun_interface) as Box<NetInterface>,
		Box::new(udp_interface) as Box<NetInterface>,
	];

	let router = Router::new(&my_identity.address);

	// Start up the event loop
	let mut mio_loop: mio::EventLoop<usize, ()> = try!(mio::EventLoop::new());

	let event_handler = EventHandler::new(
		my_identity,
		interfaces,
		router);

	try!(event_handler.register_handlers(&mut mio_loop));
	try!(mio_loop.run(event_handler));

	Ok(())
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate rand;
extern crate urlencoded;

use iron::headers;
use iron::prelude::*;
use iron::method::Method;
use iron::modifiers::{Redirect, Header};
use iron::{Handler, Url, status};

use rand::Rng;

use std::collections::HashMap;
use std::sync::RwLock;

use urlencoded::UrlEncodedBody;

struct UrlShortenerHandler {
    shortened_urls: RwLock<HashMap<String, Url>>,
}

impl UrlShortenerHandler {
    fn new() -> Self {
        UrlShortenerHandler { shortened_urls: RwLock::new(HashMap::new()) }
    }
}

impl Handler for UrlShortenerHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        match &req.method {
            &Method::Post => {
                let long_url = Url::parse(req.get_ref::<UrlEncodedBody>()
                        .unwrap()
                        .get("url")
                        .unwrap()
                        .get(0)
                        .unwrap())
                    .unwrap();

                let random_key: String = rand::thread_rng().gen_ascii_chars().take(5).collect();
                let short_url = format!("http://localhost:3000/{}", random_key);

                self.shortened_urls.write().unwrap().insert(random_key, long_url);

                Ok(Response::with((status::Created, Header(headers::Location(short_url)))))
            }
            &Method::Get | &Method::Head => {
                let request_path = &req.url.path().join("/");
                match self.shortened_urls.read().unwrap().get(request_path) {
                    Some(url) => Ok(Response::with((status::Found, Redirect(url.clone())))),
                    None => Ok(Response::with(status::NotFound)),
                }
            }
            _ => Ok(Response::with(status::NotFound)),
        }
    }
}

fn main() {
    println!("Starting URL Shortener on port 3000...");

    let handler = UrlShortenerHandler::new();
    Iron::new(handler).http("localhost:3000").unwrap();
}
<commit_msg>Handle error when port 3000 already in use<commit_after>extern crate iron;
extern crate rand;
extern crate urlencoded;

use iron::headers;
use iron::prelude::*;
use iron::method::Method;
use iron::modifiers::{Redirect, Header};
use iron::{Handler, Url, status};

use rand::Rng;

use std::collections::HashMap;
use std::sync::RwLock;

use urlencoded::UrlEncodedBody;

struct UrlShortenerHandler {
    shortened_urls: RwLock<HashMap<String, Url>>,
}

impl UrlShortenerHandler {
    fn new() -> Self {
        UrlShortenerHandler { shortened_urls: RwLock::new(HashMap::new()) }
    }
}

impl Handler for UrlShortenerHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        match &req.method {
            &Method::Post => {
                let long_url = Url::parse(req.get_ref::<UrlEncodedBody>()
                        .unwrap()
                        .get("url")
                        .unwrap()
                        .get(0)
                        .unwrap())
                    .unwrap();

                let random_key: String = rand::thread_rng().gen_ascii_chars().take(5).collect();
                let short_url = format!("http://localhost:3000/{}", random_key);

                self.shortened_urls.write().unwrap().insert(random_key, long_url);

                Ok(Response::with((status::Created, Header(headers::Location(short_url)))))
            }
            &Method::Get | &Method::Head => {
                let request_path = &req.url.path().join("/");
                match self.shortened_urls.read().unwrap().get(request_path) {
                    Some(url) => Ok(Response::with((status::Found, Redirect(url.clone())))),
                    None => Ok(Response::with(status::NotFound)),
                }
            }
            _ => Ok(Response::with(status::NotFound)),
        }
    }
}

fn main() {
    println!("Starting URL Shortener on port 3000...");

    let handler = UrlShortenerHandler::new();
    match Iron::new(handler).http("localhost:3000") {
        Ok(_) => println!("Server started"),
        Err(e) => {
            println!("Error: {}", e.to_string());
            std::process::exit(-1);
        }
    }
}
<|endoftext|>


<commit_before>use std::process::{ Child, Command, Stdio, ExitStatus };
use std::io::{ BufRead, BufReader, BufWriter, Read,  Write };
use std::path::{ Path, PathBuf };
use std::net::{ TcpListener, TcpStream };
use std::thread;
use std::fs::File;

extern crate argparse;
extern crate rand;
extern crate hyper;
extern crate time;

use argparse::{ ArgumentParser };
use rand::distributions::{IndependentSample, Range};
use hyper::Client;

struct ListenerData {
    listener: TcpListener,
    port: u16,
}

fn create_listener() -> Result<ListenerData, &'static str>
{
    let possible_port_values = Range::new( 1024, 65535 );
    let mut rng = rand::thread_rng();
    for _ in 1..5 {
        let try_port = possible_port_values.ind_sample( &mut rng );

        let result = TcpListener::bind( ( "127.0.0.1", try_port ) );
        if result.is_ok() {
            return Ok( ListenerData{ listener: result.unwrap(), port: try_port } )
        }
    }

    Err( &"Failed to bind port" )
}

struct Credentials {
    username: String,
    user_token: String,
}

fn parse_credentials( credentials_file: &Path ) -> Result<Credentials, &str>
{
    let f = File::open( credentials_file ).unwrap();
    let mut reader = BufReader::new( f );

    let mut credentials_version = String::new();
    reader.read_line( &mut credentials_version ).unwrap();
    match credentials_version.trim().as_ref() {
        "0.1.0" => {
            let mut username = String::new();
            let mut token = String::new();
            reader.read_line( &mut username ).unwrap();
            reader.read_line( &mut token ).unwrap();
            Ok( Credentials{ username: username.trim().to_string(), user_token: token.trim().to_string() } )
        },
        _ => Err( "Unknown version" ),
    }
}

fn make_api_request( client: &Client, endpoint: &str, username: &str, user_token: &str, start_time_utc: &i64, current_time_utc: &i64 ) -> bool
{
    let mut url: String = "http://development.gamejolt.com/api/game/v1_1/client-sessions/".to_string();
    url.push_str( endpoint );
    url.push_str( &format!( "?username={}&user_token={}&start_timestamp={}&current_timestamp={}&format=dump", username, user_token, start_time_utc, current_time_utc ) );

    let response = client.get( &url ).send();
    if response.is_err() {
        return false
    }

    let mut response = response.unwrap();
    let result = match response.status {
        hyper::Ok => {
            let mut result: String = String::new();
            match response.read_to_string( &mut result ) {
                Ok(_) => result.starts_with( "SUCCESS" ),
                Err(_) => false,
            }
        }
        _ => false
    };
    result
}

fn main()
{
    let mut wrapper_id = String::new();
    let mut pid_path = String::new();
    let mut package_path = String::new();
    let mut game_path = String::new();
    let mut game_args: Vec<String> = std::vec::Vec::new();

    {
        let mut ap: ArgumentParser = ArgumentParser::new();
        ap.refer( &mut wrapper_id ).add_argument( "wrapper-id", argparse::Store, "A unique identifier for the wrapper" ).required();
        ap.refer( &mut pid_path ).add_argument( "pid-path", argparse::Store, "Path to the pid directory" ).required();
        ap.refer( &mut package_path ).add_argument( "package-path", argparse::Store, "Path to the package installation directory" ).required();
        ap.refer( &mut game_path ).add_argument( "game-path", argparse::Store, "Path to the game file relative to the package installation directory" ).required();
        ap.refer( &mut game_args ).add_argument( "game-args", argparse::Collect, "Additional arguments to the game file" );
        ap.parse_args_or_exit();
    }

    let listener_data = create_listener().unwrap();
    let listener = listener_data.listener;

    let task_wrapper_id = wrapper_id.clone();
    let task_package_path = package_path.clone();
    let task_game_path = game_path.clone();
    let task_game_args = game_args.clone();
    thread::spawn( move || {

        for stream in listener.incoming() {
            let mut stream: TcpStream = stream.unwrap_or_else( |e| panic!( "Could not get incoming connection stream: {}", e ) );
            let out_str = String::from( "v0.1.0:Game Wrapper:" ) + &task_wrapper_id + ":" + &task_package_path + ":" + &task_game_path + ":\"" + &task_game_args.join( &"\",\"" ) + "\"";
            stream.write( out_str.as_bytes() ).unwrap_or_else( |e| panic!( "Could not send reply to the new connection: {}", e ) );
        }
    } );

    let mut pid_path = PathBuf::from( pid_path );
    pid_path.push( wrapper_id );

    match File::create( &pid_path ) {
        Ok( f ) => {
            let mut writer = BufWriter::new( f );
            writer.write( listener_data.port.to_string().as_bytes() )
                .expect( "Failed to write pid file" );
            writer.flush()
                .expect( "Failed to flush pid file" );
            drop( writer );
        },
        Err( e ) => println!( "Failed to write pid file: {}", e ),
    };

    // Get the full game file path.
    let mut game_file = PathBuf::from( package_path.clone() );
    game_file.push( game_path );

    // Current working directory is the directory of the game executable
    let cwd: &Path = game_file.parent()
        .expect( "Could not get parent dir from path" );

    let mut package_credentials: PathBuf = PathBuf::from( &package_path );
    package_credentials.push( ".gj-credentials" );

    let mut game_file_credentials: PathBuf = PathBuf::from( cwd );
    game_file_credentials.push( ".gj-credentials" );

    // Copy over the credential file from the package directory to the game executable's directory.
    let mut supports_game_api = std::fs::copy( package_credentials.as_path(), game_file_credentials.as_path() ).is_ok();

    let mut credentials: Credentials = Credentials{ username: String::new(), user_token: String::new() };
    if supports_game_api {
        let get_credentials = parse_credentials( game_file_credentials.as_path() );
        supports_game_api = get_credentials.is_ok();
        credentials = get_credentials.unwrap();
    }

    let timestamp = time::get_time().sec;

    if supports_game_api {
        game_args.push( credentials.username.to_string() );
        game_args.push( credentials.user_token.to_string() );

        let game_api_username = credentials.username.clone();
        let game_api_token = credentials.user_token.clone();
        let game_api_timestamp = timestamp.clone();

        thread::spawn( move || {
            let mut client = Client::new();
            client.set_write_timeout( Some( std::time::Duration::new( 5, 0 ) ) );
            client.set_read_timeout( Some( std::time::Duration::new( 5, 0 ) ) );
            let mut timestamp = time::get_time().sec;
            make_api_request( &client, "open", &game_api_username, &game_api_token, &game_api_timestamp, &timestamp );
            loop {
                std::thread::sleep( std::time::Duration::from_secs( 10 ) );
                if !make_api_request( &client, "ping", &game_api_username, &game_api_token, &0, &0, ) {
                    timestamp = time::get_time().sec;
                    make_api_request( &client, "open", &game_api_username, &game_api_token, &game_api_timestamp, &timestamp );
                }
            }
        } );
    }

    let mut game_handle: Child = Command::new( &game_file )
        .current_dir( cwd )
        .args( game_args.as_slice() )
        .stderr( Stdio::piped() )
        .spawn()
        .unwrap_or_else( |e| panic!( "Couldn't launch game: {}", e ) );

    let result: ExitStatus = game_handle.wait().unwrap_or_else( |e| panic!( "Couldn't wait for game to launch/finish {}", e ) );

    if supports_game_api {
        let mut client = Client::new();
        client.set_write_timeout( Some( std::time::Duration::from_secs( 5 ) ) );
        client.set_read_timeout( Some( std::time::Duration::from_secs( 5 ) ) );
        make_api_request( &client, "close", &credentials.username, &credentials.user_token, &0, &0 );
    }

    if std::fs::remove_file( &pid_path ).is_err() {
        println!( "Failed to remove pid file: {}", pid_path.display() );
    }

    println!( "Game exited with exit code {}", result.code().expect( "Could not discern wrapped game exit code" ) );

    if !result.success() {
        let mut err_str = String::new();
        match game_handle.stderr.expect( "Could not get err stream" ).read_to_string( &mut err_str )  {
            Err(e) => panic!( "Could not read err stream {}", e ),
            Ok(_) => println!( "Stderr: {}", err_str ),
        }
    }
}
<commit_msg>Get rid of api auto sessions<commit_after>use std::process::{ Child, Command, Stdio, ExitStatus };
use std::io::{ BufRead, BufReader, BufWriter, Read,  Write };
use std::path::{ Path, PathBuf };
use std::net::{ TcpListener, TcpStream };
use std::thread;
use std::fs::File;

extern crate argparse;
extern crate rand;

use argparse::{ ArgumentParser };
use rand::distributions::{IndependentSample, Range};

struct ListenerData {
    listener: TcpListener,
    port: u16,
}

fn create_listener() -> Result<ListenerData, &'static str>
{
    let possible_port_values = Range::new( 1024, 65535 );
    let mut rng = rand::thread_rng();
    for _ in 1..5 {
        let try_port = possible_port_values.ind_sample( &mut rng );

        let result = TcpListener::bind( ( "127.0.0.1", try_port ) );
        if result.is_ok() {
            return Ok( ListenerData{ listener: result.unwrap(), port: try_port } )
        }
    }

    Err( &"Failed to bind port" )
}

struct Credentials {
    username: String,
    user_token: String,
}

fn parse_credentials( credentials_file: &Path ) -> Result<Credentials, &str>
{
    let f = File::open( credentials_file ).unwrap();
    let mut reader = BufReader::new( f );

    let mut credentials_version = String::new();
    reader.read_line( &mut credentials_version ).unwrap();
    match credentials_version.trim().as_ref() {
        "0.1.0" => {
            let mut username = String::new();
            let mut token = String::new();
            reader.read_line( &mut username ).unwrap();
            reader.read_line( &mut token ).unwrap();
            Ok( Credentials{ username: username.trim().to_string(), user_token: token.trim().to_string() } )
        },
        _ => Err( "Unknown version" ),
    }
}

fn main()
{
    let mut wrapper_id = String::new();
    let mut pid_path = String::new();
    let mut package_path = String::new();
    let mut game_path = String::new();
    let mut game_args: Vec<String> = std::vec::Vec::new();

    {
        let mut ap: ArgumentParser = ArgumentParser::new();
        ap.refer( &mut wrapper_id ).add_argument( "wrapper-id", argparse::Store, "A unique identifier for the wrapper" ).required();
        ap.refer( &mut pid_path ).add_argument( "pid-path", argparse::Store, "Path to the pid directory" ).required();
        ap.refer( &mut package_path ).add_argument( "package-path", argparse::Store, "Path to the package installation directory" ).required();
        ap.refer( &mut game_path ).add_argument( "game-path", argparse::Store, "Path to the game file relative to the package installation directory" ).required();
        ap.refer( &mut game_args ).add_argument( "game-args", argparse::Collect, "Additional arguments to the game file" );
        ap.parse_args_or_exit();
    }

    let listener_data = create_listener().unwrap();
    let listener = listener_data.listener;

    let task_wrapper_id = wrapper_id.clone();
    let task_package_path = package_path.clone();
    let task_game_path = game_path.clone();
    let task_game_args = game_args.clone();
    thread::spawn( move || {

        for stream in listener.incoming() {
            let mut stream: TcpStream = stream.unwrap_or_else( |e| panic!( "Could not get incoming connection stream: {}", e ) );
            let out_str = String::from( "v0.1.0:Game Wrapper:" ) + &task_wrapper_id + ":" + &task_package_path + ":" + &task_game_path + ":\"" + &task_game_args.join( &"\",\"" ) + "\"";
            stream.write( out_str.as_bytes() ).unwrap_or_else( |e| panic!( "Could not send reply to the new connection: {}", e ) );
        }
    } );

    let mut pid_path = PathBuf::from( pid_path );
    pid_path.push( wrapper_id );

    match File::create( &pid_path ) {
        Ok( f ) => {
            let mut writer = BufWriter::new( f );
            writer.write( listener_data.port.to_string().as_bytes() )
                .expect( "Failed to write pid file" );
            writer.flush()
                .expect( "Failed to flush pid file" );
            drop( writer );
        },
        Err( e ) => println!( "Failed to write pid file: {}", e ),
    };

    // Get the full game file path.
    let mut game_file = PathBuf::from( package_path.clone() );
    game_file.push( game_path );

    // Current working directory is the directory of the game executable
    let cwd: &Path = game_file.parent()
        .expect( "Could not get parent dir from path" );

    let mut package_credentials: PathBuf = PathBuf::from( &package_path );
    package_credentials.push( ".gj-credentials" );

    let mut game_file_credentials: PathBuf = PathBuf::from( cwd );
    game_file_credentials.push( ".gj-credentials" );

    // Copy over the credential file from the package directory to the game executable's directory.
    let mut supports_game_api = std::fs::copy( package_credentials.as_path(), game_file_credentials.as_path() ).is_ok();

    let mut credentials: Credentials = Credentials{ username: String::new(), user_token: String::new() };
    if supports_game_api {
        let get_credentials = parse_credentials( game_file_credentials.as_path() );
        supports_game_api = get_credentials.is_ok();
        credentials = get_credentials.unwrap();
    }

    if supports_game_api {
        game_args.push( String::from( "gj_username" ) );
        game_args.push( credentials.username.to_string() );
        game_args.push( String::from( "gj_token" ) );
        game_args.push( credentials.user_token.to_string() );
    }

    let mut game_handle: Child = Command::new( &game_file )
        .current_dir( cwd )
        .args( game_args.as_slice() )
        .stderr( Stdio::piped() )
        .spawn()
        .unwrap_or_else( |e| panic!( "Couldn't launch game: {}", e ) );

    let result: ExitStatus = game_handle.wait().unwrap_or_else( |e| panic!( "Couldn't wait for game to launch/finish {}", e ) );

    if std::fs::remove_file( &pid_path ).is_err() {
        println!( "Failed to remove pid file: {}", pid_path.display() );
    }

    println!( "Game exited with exit code {}", result.code().expect( "Could not discern wrapped game exit code" ) );

    if !result.success() {
        let mut err_str = String::new();
        match game_handle.stderr.expect( "Could not get err stream" ).read_to_string( &mut err_str )  {
            Err(e) => panic!( "Could not read err stream {}", e ),
            Ok(_) => println!( "Stderr: {}", err_str ),
        }
    }
}
<|endoftext|>


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

use std::path::Path;
use rand::Rng;
use clap::{Arg, App};
use image::{DynamicImage, GenericImage, RgbaImage};

arg_enum!{
    enum Mode {
        Brute
    }
}

fn main() {
    let matches = App::new("asami")
        .version("0.1.0")
        .author("Sebastian Schulz <mail@sesc.me>")
        .about("pixel sorter")
        .args_from_usage(
            "<INPUT> 'The input file to use'
             <OUTPUT> 'The output file to use'")
        .arg(Arg::from_usage("-m --mode <MODE> 'The mode to use'")
            .possible_values(&Mode::variants())
            .default_value("Brute"))
        .arg(Arg::from_usage("--min <MIN> 'The minimum chunk size to sort'")
            .default_value("32"))
        .arg(Arg::from_usage("--max <MAX> 'The maximum chunk size to sort'")
            .default_value("64"))
        .get_matches();

    let input = matches.value_of("INPUT").unwrap();
    let output = matches.value_of("OUTPUT").unwrap();

    let min = value_t!(matches, "min", u32).expect("the minimum chunk size has to be a number");
    let max = value_t!(matches, "max", u32).expect("the maximum chunk size has to be a number");

    let mode = matches.value_of("mode").unwrap();

    println!("mode: {}", mode);

    handle_request(input, output, min, max);
}

fn handle_request(input: &str, output: &str, chunk_min_length: u32, chunk_max_length: u32) {
    println!("input: {}, output: {}", input, output);
    println!("minimum chunk size: {}, maximum chunk size: {}", chunk_min_length, chunk_max_length);

    let ref in_img = image::open(&Path::new(input)).unwrap();

    println!("dimensions {:?}", in_img.dimensions());
    println!("{:?}", in_img.color());

    let out_img = brute_sort(in_img, chunk_min_length, chunk_max_length);

    let _ = out_img.save(output).unwrap();
}

fn brute_sort(input: &DynamicImage, chunk_min_length: u32, chunk_max_length: u32) -> RgbaImage {
    let mut output = RgbaImage::new(input.width(), input.height());

    let mut out_x = 0;
    let mut out_y = 0;

    let mut tmp = Vec::new();

    let mut threshold = calculate_chunk_threshold(chunk_min_length, chunk_max_length);

    for (_, _, pixel) in input.pixels() {
        tmp.push(pixel);
        if tmp.len() > threshold as usize {
            threshold = calculate_chunk_threshold(chunk_min_length, chunk_max_length);
            tmp.sort_by(|a, b| (a[3]).cmp(&b[1]));
            write_pixel_to_image(&mut output, &mut out_x, &mut out_y, &mut tmp);
        }
    }
    output
}

fn calculate_chunk_threshold(chunk_min_length: u32, chunk_max_length: u32) -> u32 {
    rand::thread_rng().gen_range(chunk_min_length, chunk_max_length + 1)
}

fn write_pixel_to_image(image: &mut RgbaImage, x: &mut u32, y: &mut u32, pixels: &mut Vec<image::Rgba<u8>>){
    while !pixels.is_empty() {
        image.put_pixel(*x, *y, pixels.pop().unwrap());
        *y = *y + (*x + 1) / image.width();
        *x = (*x + 1) % image.width();
    }
}
<commit_msg>coord++ in extra function<commit_after>extern crate rand;
#[macro_use]
extern crate clap;
extern crate image;

use std::path::Path;
use rand::Rng;
use clap::{Arg, App};
use image::{DynamicImage, GenericImage, RgbaImage};

arg_enum!{
    enum Mode {
        Brute
    }
}

fn main() {
    let matches = App::new("asami")
        .version("0.1.0")
        .author("Sebastian Schulz <mail@sesc.me>")
        .about("pixel sorter")
        .args_from_usage(
            "<INPUT> 'The input file to use'
             <OUTPUT> 'The output file to use'")
        .arg(Arg::from_usage("-m --mode <MODE> 'The mode to use'")
            .possible_values(&Mode::variants())
            .default_value("Brute"))
        .arg(Arg::from_usage("--min <MIN> 'The minimum chunk size to sort'")
            .default_value("32"))
        .arg(Arg::from_usage("--max <MAX> 'The maximum chunk size to sort'")
            .default_value("64"))
        .get_matches();

    let input = matches.value_of("INPUT").unwrap();
    let output = matches.value_of("OUTPUT").unwrap();

    let min = value_t!(matches, "min", u32).expect("the minimum chunk size has to be a number");
    let max = value_t!(matches, "max", u32).expect("the maximum chunk size has to be a number");

    let mode = matches.value_of("mode").unwrap();

    println!("mode: {}", mode);

    handle_request(input, output, min, max);
}

fn handle_request(input: &str, output: &str, chunk_min_length: u32, chunk_max_length: u32) {
    println!("input: {}, output: {}", input, output);
    println!("minimum chunk size: {}, maximum chunk size: {}", chunk_min_length, chunk_max_length);

    let ref in_img = image::open(&Path::new(input)).unwrap();

    println!("dimensions {:?}", in_img.dimensions());
    println!("{:?}", in_img.color());

    let out_img = brute_sort(in_img, chunk_min_length, chunk_max_length);

    let _ = out_img.save(output).unwrap();
}

fn brute_sort(input: &DynamicImage, chunk_min_length: u32, chunk_max_length: u32) -> RgbaImage {
    let mut output = RgbaImage::new(input.width(), input.height());

    let mut out_x = 0;
    let mut out_y = 0;

    let mut tmp = Vec::new();

    let mut threshold = calculate_chunk_threshold(chunk_min_length, chunk_max_length);

    for (_, _, pixel) in input.pixels() {
        tmp.push(pixel);
        if tmp.len() == threshold as usize {
            tmp.sort_by(|a, b| (a[3]).cmp(&b[1]));
            write_pixels_to_image(&mut output, out_x, out_y, &mut tmp);
            increase_coordinates(&mut out_x, &mut out_y, threshold, &output);
            threshold = calculate_chunk_threshold(chunk_min_length, chunk_max_length);
        }
    }
    write_pixels_to_image(&mut output, out_x, out_y, &mut tmp);
    output
}

fn calculate_chunk_threshold(chunk_min_length: u32, chunk_max_length: u32) -> u32 {
    rand::thread_rng().gen_range(chunk_min_length, chunk_max_length + 1)
}

fn increase_coordinates(x: &mut u32, y: &mut u32, steps: u32, image: &RgbaImage) {
    *y = *y + (*x + steps) / image.width();
    *x = (*x + steps) % image.width();
}

fn write_pixels_to_image(image: &mut RgbaImage, x: u32, y: u32, pixels: &mut Vec<image::Rgba<u8>>){
    let mut x = x;
    let mut y = y;

    while !pixels.is_empty() {
        image.put_pixel(x, y, pixels.pop().unwrap());
        increase_coordinates(&mut x, &mut y, 1, &image);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
extern crate rand;
use rand::Rng;

type Player = [i8; 7];

#[derive(Clone, Debug)]
struct Game {
    p: [Player; 2],
    t: i8,
}

fn new_player() -> Player {
    [4,4,4,4,4,4,0]
}

#[derive(Debug, PartialEq)]
enum State {
    InProgress,
    Draw,
    Win(i8),
}

impl Game {
    fn new() -> Game {
        Game{p: [new_player(), new_player()], t: 0}
    }

    fn print(&self) {
        print!("|{:2}|", self.p[1][6]);
        for i in 0..6 {
            print!(" [{:2}]", self.p[1][5 - i])
        }
        print!(" /--\\");
        if self.t == 1 {
            print!(" <-- turn");
        }
        println!();
        print!("\\--/ ");
        for i in 0..6 {
            print!("[{:2}] ", self.p[0][i])
        }
        print!("|{:2}|", self.p[0][6]);
        if self.t == 0 {
            print!(" <-- turn");
        }
        println!();
    }

    fn state(&self) -> State {
        let p0full: i8 = self.p[0].iter().sum();
        let p1full: i8 = self.p[1].iter().sum();
        let p0 = p0full - self.p[0][6];
        let p1 = p1full - self.p[1][6];
        if p0 != 0 && p1 != 0 {
            State::InProgress
        } else {
            if p0full < p1full {
                State::Win(1)
            } else if p0full > p1full {
                State::Win(0)
            } else {
                State::Draw
            }
        }
    }

    fn step(&self, s: usize) -> Option<Game> {
        if s >= 6 { panic!("wrong start index"); }
        let mut n: Game = self.clone();
        let mut leftover = n.p[n.t as usize][s];
        if leftover == 0 { return None; }

        let mut side: usize = n.t as usize;
        let mut pos: usize = s + 1;
        n.p[side][s] = 0;

        loop {
            n.p[side][pos] += 1;
            leftover -= 1;
            if leftover == 0 { break; }
            if (pos == 5 && side != self.t as usize) || (pos == 6) {
                // pos == 6 => we have reached the super bucket and have to switch sides
                // pos == 5 => we have reached opponents 0 bucket and have to return home
                side = 1 - side;
                pos = 0;
            } else {
                pos += 1;
            }
        }

        if pos < 6 { // if we have reached the super bucket, we don't give up turn
            // check for take-over
            if side == self.t as usize && n.p[side][pos] == 1 {
                let opp = 5 - pos;
                n.p[side][6] += n.p[1 - side][opp];
                n.p[1 - side][opp] = 0;
            }
            n.t = 1 - n.t; // switch the turn
        }
        Some(n)
    }

    fn possible(&self) -> Vec<usize> {
        let side = self.t as usize;
        let mut res: Vec<usize> = Vec::new();
        for i in 0..6 {
            if self.p[side][i] != 0 {
                res.push(i);
            }
        }
        res
    }
}

// next computes all the posible games for the next turn
fn next(g: &Game) -> Vec<Game> {
    let mut res: Vec<Game> = Vec::new();
    let turn = g.t;
    for i in 0..6 {
        if let Some(cand) = g.step(i) {
            if cand.t == turn && cand.state() == State::InProgress {
                for elem in next(&cand).into_iter() {
                    res.push(elem);
                }
            } else {
                res.push(cand);
            }
        }
    }
    res
}

fn show_first_row() {
    let g = Game::new();
    g.print();
    println!("{:?}", g.state());
    for t in next(&g).into_iter() {
        t.print();
    }
}

fn play_random_game() {
    let mut g = Game::new();
    let mut rng = rand::thread_rng();
    loop {
        g.print();
        let state = g.state();
        if state != State::InProgress {
            println!("{:?}", state);
            return;
        }

        let possibilities = next(&g);
        let n = possibilities.len();
        g = possibilities[rng.gen_range(0, n)].clone();
    }
}

fn main() {
    play_random_game();
}
<commit_msg>add Depth-First search for outcomes<commit_after>#![allow(dead_code)]
extern crate rand;
use rand::Rng;
use std::collections::*;

type Player = [i8; 7];

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct Game {
    p: [Player; 2],
    t: i8,
}

fn new_player() -> Player {
    [4,4,4,4,4,4,0]
}

#[derive(Debug, PartialEq, Clone)]
enum State {
    InProgress,
    Draw,
    Win(i8),
}

impl Game {
    fn new() -> Game {
        Game{p: [new_player(), new_player()], t: 0}
    }

    fn print(&self) {
        print!("|{:2}|", self.p[1][6]);
        for i in 0..6 {
            print!(" [{:2}]", self.p[1][5 - i])
        }
        print!(" /--\\");
        if self.t == 1 {
            print!(" <-- turn");
        }
        println!();
        print!("\\--/ ");
        for i in 0..6 {
            print!("[{:2}] ", self.p[0][i])
        }
        print!("|{:2}|", self.p[0][6]);
        if self.t == 0 {
            print!(" <-- turn");
        }
        println!();
    }

    fn state(&self) -> State {
        let p0full: i8 = self.p[0].iter().sum();
        let p1full: i8 = self.p[1].iter().sum();
        let p0 = p0full - self.p[0][6];
        let p1 = p1full - self.p[1][6];
        if p0 != 0 && p1 != 0 {
            State::InProgress
        } else {
            if p0full < p1full {
                State::Win(1)
            } else if p0full > p1full {
                State::Win(0)
            } else {
                State::Draw
            }
        }
    }

    fn step(&self, s: usize) -> Option<Game> {
        if s >= 6 { panic!("wrong start index"); }
        let mut n: Game = self.clone();
        let mut leftover = n.p[n.t as usize][s];
        if leftover == 0 { return None; }

        let mut side: usize = n.t as usize;
        let mut pos: usize = s + 1;
        n.p[side][s] = 0;

        loop {
            n.p[side][pos] += 1;
            leftover -= 1;
            if leftover == 0 { break; }
            if (pos == 5 && side != self.t as usize) || (pos == 6) {
                // pos == 6 => we have reached the super bucket and have to switch sides
                // pos == 5 => we have reached opponents 0 bucket and have to return home
                side = 1 - side;
                pos = 0;
            } else {
                pos += 1;
            }
        }

        if pos < 6 { // if we have reached the super bucket, we don't give up turn
            // check for take-over
            if side == self.t as usize && n.p[side][pos] == 1 {
                let opp = 5 - pos;
                n.p[side][6] += n.p[1 - side][opp];
                n.p[1 - side][opp] = 0;
            }
            n.t = 1 - n.t; // switch the turn
        }
        Some(n)
    }

    fn possible(&self) -> Vec<usize> {
        let side = self.t as usize;
        let mut res: Vec<usize> = Vec::new();
        for i in 0..6 {
            if self.p[side][i] != 0 {
                res.push(i);
            }
        }
        res
    }
}

// next computes all the posible games for the next turn
fn next(g: &Game) -> Vec<Game> {
    let mut res: Vec<Game> = Vec::new();
    let turn = g.t;
    for i in 0..6 {
        if let Some(cand) = g.step(i) {
            if cand.t == turn && cand.state() == State::InProgress {
                for elem in next(&cand).into_iter() {
                    res.push(elem);
                }
            } else {
                res.push(cand);
            }
        }
    }
    res
}

fn show_first_row() {
    let g = Game::new();
    g.print();
    println!("{:?}", g.state());
    for t in next(&g).into_iter() {
        t.print();
    }
}

fn play_random_game() {
    let mut g = Game::new();
    let mut rng = rand::thread_rng();
    loop {
        println!("-----------------------------");
        println!("{:?}", g);
        g.print();
        let state = g.state();
        if state != State::InProgress {
            println!("{:?}", state);
            return;
        }

        let possibilities = next(&g);
        let n = possibilities.len();
        g = possibilities[rng.gen_range(0, n)].clone();
    }
}

fn find_outcome(g: &Game, cache: &mut HashMap<Game, (State, i8)>) -> (State, i8) {
    let gstate = g.state();
    if gstate != State::InProgress {
        return (gstate, -1);
    }
    if let Some(ans) = cache.get(&g) {
        return ans.clone();
    }

    let player = g.t;
    let mut best = State::Win(1 - player); // initialize the best with worst-case scenario -- winning of the other player
    let mut step: i8 = -1;

    for i in 0..6 {
        if let Some(ng) = g.step(i) {
            let outcome = find_outcome(&ng, cache);
            match outcome.0 {
                State::InProgress => panic!(),
                State::Draw => {
                    if best != State::Draw {
                        best = State::Draw;
                        step = i as i8;
                    }
                },
                State::Win(p) => {
                    if p == player { // that's the best outcome
                        best = outcome.0;
                        step = i as i8;
                        break; // no need to search further
                    } else {
                        // that's the worst outcome
                        if best != State::Draw {
                            step = i as i8; // only update the step
                        }
                    }
                }
            }
        }
    }

    debug_assert!(step >= 0, "best case must always be found");

    let ans = (best, step);
    cache.insert(g.clone(), ans.clone());
    ans
}

fn experiment_with_outcomes() {
    let mut g = Game { p: [[1, 2, 0, 0, 1, 0, 30], [1, 0, 0, 0, 1, 1, 11]], t: 0 };
    let mut cache: HashMap<Game, (State, i8)> = HashMap::new();
    let mut outcome = find_outcome(&g, &mut cache);
    println!("Outcome: {:?}", outcome);
    println!("Positions in cache: {}", cache.len());

    loop {
        g.print();
        if g.state() == State::InProgress {
            g = g.step(outcome.1 as usize).unwrap();
            outcome = find_outcome(&g, &mut cache);
        } else {
            break;
        }
    }
}

fn main() {
    //play_random_game();
    experiment_with_outcomes();
}
<|endoftext|>


<commit_before>extern crate chifir;
extern crate termion;

use termion::raw::IntoRawMode;
use termion::async_stdin;

use std::io::{self, Write};

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock().into_raw_mode().unwrap();
    let stdin = async_stdin();

    write!(stdout,
           "{}{}",
           termion::clear::All,
           termion::cursor::Goto(1, 1))
        .unwrap();
    stdout.flush().unwrap();

    let mut vm = chifir::machine::Machine {
        memory: vec![
            0xf, 0x2, 0x0, 0x3, // 00 - Read key press and store it in $2
            0x8, 0x2, 0x2, 0x3, // 04 - $2 <- $2 - $3
            0x2, 0xb, 0x2, 0xf, // 08 - If $2 = 0, then PC <- 15
            0x1, 0xf, 0x0, 0x0, // 12 - Else PC <- 0
        ],
        counter: 0,
        output: stdout,
        input: stdin,
    };

    vm.step();
    while vm.next() != 0 {
        vm.step();
    }
}
<commit_msg>Allow an opcode of zero (break) as a starting point.<commit_after>extern crate chifir;
extern crate termion;

use termion::raw::IntoRawMode;
use termion::async_stdin;

use std::io::{self, Write};

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock().into_raw_mode().unwrap();
    let stdin = async_stdin();

    write!(stdout,
           "{}{}",
           termion::clear::All,
           termion::cursor::Goto(1, 1))
        .unwrap();
    stdout.flush().unwrap();

    let mut vm = chifir::machine::Machine {
        memory: vec![
            0xf, 0x2, 0x0, 0x3, // 00 - Read key press and store it in $2
            0x8, 0x2, 0x2, 0x3, // 04 - $2 <- $2 - $3
            0x2, 0xb, 0x2, 0xf, // 08 - If $2 = 0, then PC <- 15
            0x1, 0xf, 0x0, 0x0, // 12 - Else PC <- 0
        ],
        counter: 0,
        output: stdout,
        input: stdin,
    };

    while vm.next() != 0 {
        vm.step();
    }
}
<|endoftext|>


<commit_before>mod ringbuffer;

use crate::plot_data::PlotData;
use anyhow::{anyhow, Result};
use crossterm::event::{KeyEvent, KeyModifiers};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dns_lookup::lookup_host;
use pinger::{ping, PingResult};
use std::io;
use std::io::Write;
use std::iter;
use std::net::IpAddr;
use std::ops::Add;
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::sync::{mpsc, Arc};
use std::thread;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset};
use tui::Terminal;

mod plot_data;

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.")]
struct Args {
    #[structopt(
        long,
        help = "Graph the execution time for a list of commands rather than pinging hosts"
    )]
    cmd: bool,
    #[structopt(
        short = "n",
        long,
        help = "Watch interval seconds (provide partial seconds like '0.5')",
        default_value = "0.5"
    )]
    watch_interval: f32,
    #[structopt(help = "Hosts or IPs to ping, or commands to run if --cmd is provided.")]
    hosts_or_commands: Vec<String>,
    #[structopt(
        short,
        long,
        default_value = "100",
        help = "Determines the number pings to display."
    )]
    buffer: usize,
}

struct App {
    data: Vec<PlotData>,
}

impl App {
    fn new(data: Vec<PlotData>) -> Self {
        App { data }
    }

    fn update(&mut self, host_idx: usize, item: Option<Duration>) {
        let host = &mut self.data[host_idx];
        host.update(item);
    }

    fn y_axis_bounds(&self) -> [f64; 2] {
        // Find the Y axis bounds for our chart.
        // This is trickier than the x-axis. We iterate through all our PlotData structs
        // and find the min/max of all the values. Then we add a 10% buffer to them.
        let iter = self
            .data
            .iter()
            .map(|b| b.data.as_slice())
            .flatten()
            .map(|v| v.1);
        let min = iter.clone().fold(f64::INFINITY, |a, b| a.min(b));
        let max = iter.fold(0f64, |a, b| a.max(b));
        // Add a 10% buffer to the top and bottom
        let max_10_percent = (max * 10_f64) / 100_f64;
        let min_10_percent = (min * 10_f64) / 100_f64;
        [min - min_10_percent, max + max_10_percent]
    }

    fn x_axis_bounds(&self) -> [f64; 2] {
        let window_min = self.data.iter().map(|d| d.window_min);
        let window_max = self.data.iter().map(|d| d.window_max);
        [
            window_min.fold(f64::INFINITY, |a, b| a.min(b)),
            window_max.fold(0f64, |a, b| a.max(b)),
        ]
    }

    fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Create 7 labels for our y axis, based on the y-axis bounds we computed above.
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let increment = Duration::from_micros((difference / 3f64) as u64);
        let duration = Duration::from_micros(min as u64);

        (0..7)
            .map(|i| Span::raw(format!("{:?}", duration.add(increment * i))))
            .collect()
    }
}

#[derive(Debug)]
enum Update {
    Result(Duration),
    Timeout,
    Unknown,
}

impl From<PingResult> for Update {
    fn from(result: PingResult) -> Self {
        match result {
            PingResult::Pong(duration, _) => Update::Result(duration),
            PingResult::Timeout(_) => Update::Timeout,
            PingResult::Unknown(_) => Update::Unknown,
        }
    }
}

#[derive(Debug)]
enum Event {
    Update(usize, Update),
    Input(KeyEvent),
}

fn start_cmd_thread(
    watch_cmd: &str,
    host_id: usize,
    watch_interval: f32,
    cmd_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let mut words = watch_cmd.split_ascii_whitespace();
    let cmd = words
        .next()
        .expect("Must specify a command to watch")
        .to_string();
    let cmd_args = words
        .into_iter()
        .map(|w| w.to_string())
        .collect::<Vec<String>>();

    let interval = Duration::from_millis((watch_interval * 1000.0) as u64);

    // Pump cmd watches into the queue
    thread::spawn(move || -> Result<()> {
        while !kill_event.load(Ordering::Acquire) {
            let start = Instant::now();
            let mut child = Command::new(&cmd)
                .args(&cmd_args)
                .stderr(Stdio::null())
                .stdout(Stdio::null())
                .spawn()?;
            let status = child.wait()?;
            let duration = start.elapsed();
            let update = if status.success() {
                Update::Result(duration)
            } else {
                Update::Timeout
            };
            cmd_tx.send(Event::Update(host_id, update))?;
            thread::sleep(interval);
        }
        Ok(())
    })
}

fn start_ping_thread(
    host: String,
    host_id: usize,
    ping_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping(host)?;
        while !kill_event.load(Ordering::Acquire) {
            ping_tx.send(Event::Update(host_id, stream.recv()?.into()))?;
        }
        Ok(())
    })
}

fn get_host_ipaddr(host: &str) -> Result<String> {
    let ipaddr: Vec<IpAddr> = match lookup_host(host) {
        Ok(ip) => ip,
        Err(_) => return Err(anyhow!("Could not resolve hostname {}", host)),
    };
    let ipaddr = ipaddr.first();
    Ok(ipaddr.unwrap().to_string())
}

fn main() -> Result<()> {
    let args = Args::from_args();

    let mut data = vec![];

    for (idx, host_or_cmd) in args.hosts_or_commands.iter().enumerate() {
        let display = match args.cmd {
            true => host_or_cmd.to_string(),
            false => format!("{} ({})", host_or_cmd, get_host_ipaddr(host_or_cmd)?),
        };
        data.push(PlotData::new(
            display,
            args.buffer,
            Style::default().fg(Color::Indexed(idx as u8 + 1)),
        ));
    }

    let mut app = App::new(data);
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;

    let (key_tx, rx) = mpsc::channel();

    let mut threads = vec![];

    let killed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));

    for (host_id, host_or_cmd) in args.hosts_or_commands.iter().cloned().enumerate() {
        if args.cmd {
            let cmd_thread = start_cmd_thread(
                &host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            );
            threads.push(cmd_thread);
        } else {
            threads.push(start_ping_thread(
                host_or_cmd,
                host_id,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            ));
        }
    }

    // Pump keyboard messages into the queue
    let killed_thread = std::sync::Arc::clone(&killed);
    let key_thread = thread::spawn(move || -> Result<()> {
        while !killed_thread.load(Ordering::Acquire) {
            if event::poll(Duration::from_millis(100))? {
                if let CEvent::Key(key) = event::read()? {
                    key_tx.send(Event::Input(key))?;
                }
            }
        }
        Ok(())
    });
    threads.push(key_thread);

    loop {
        match rx.recv()? {
            Event::Update(host_id, update) => {
                match update {
                    Update::Result(duration) => app.update(host_id, Some(duration)),
                    Update::Timeout => app.update(host_id, None),
                    Update::Unknown => (),
                };
                terminal.draw(|f| {
                    // Split our
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(1)
                        .constraints(
                            iter::repeat(Constraint::Length(1))
                                .take(app.data.len())
                                .chain(iter::once(Constraint::Percentage(10)))
                                .collect::<Vec<_>>()
                                .as_ref(),
                        )
                        .split(f.size());

                    let total_chunks = chunks.len();

                    let header_chunks = chunks[0..total_chunks - 1].to_owned();
                    let chart_chunk = chunks[total_chunks - 1].to_owned();

                    for (plot_data, chunk) in app.data.iter().zip(header_chunks) {
                        let header_layout = Layout::default()
                            .direction(Direction::Horizontal)
                            .constraints(
                                [
                                    Constraint::Percentage(25),
                                    Constraint::Percentage(25),
                                    Constraint::Percentage(25),
                                    Constraint::Percentage(25),
                                ]
                                .as_ref(),
                            )
                            .split(chunk);

                        for (area, paragraph) in
                            header_layout.into_iter().zip(plot_data.header_stats())
                        {
                            f.render_widget(paragraph, area);
                        }
                    }

                    let datasets: Vec<Dataset> = app.data.iter().map(|d| d.into()).collect();

                    let y_axis_bounds = app.y_axis_bounds();

                    let chart = Chart::new(datasets)
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(app.x_axis_bounds()),
                        )
                        .y_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );

                    f.render_widget(chart, chart_chunk)
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                _ => {}
            },
        }
    }

    for thread in threads {
        thread.join().unwrap()?;
    }

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}
<commit_msg>Fixing chart labels to better align with min/max<commit_after>mod ringbuffer;

use crate::plot_data::PlotData;
use anyhow::{anyhow, Result};
use crossterm::event::{KeyEvent, KeyModifiers};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dns_lookup::lookup_host;
use pinger::{ping, PingResult};
use std::io;
use std::io::Write;
use std::iter;
use std::net::IpAddr;
use std::ops::Add;
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::sync::{mpsc, Arc};
use std::thread;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset};
use tui::Terminal;

mod plot_data;

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.")]
struct Args {
    #[structopt(
        long,
        help = "Graph the execution time for a list of commands rather than pinging hosts"
    )]
    cmd: bool,
    #[structopt(
        short = "n",
        long,
        help = "Watch interval seconds (provide partial seconds like '0.5')",
        default_value = "0.5"
    )]
    watch_interval: f32,
    #[structopt(help = "Hosts or IPs to ping, or commands to run if --cmd is provided.")]
    hosts_or_commands: Vec<String>,
    #[structopt(
        short,
        long,
        default_value = "100",
        help = "Determines the number pings to display."
    )]
    buffer: usize,
}

struct App {
    data: Vec<PlotData>,
}

impl App {
    fn new(data: Vec<PlotData>) -> Self {
        App { data }
    }

    fn update(&mut self, host_idx: usize, item: Option<Duration>) {
        let host = &mut self.data[host_idx];
        host.update(item);
    }

    fn y_axis_bounds(&self) -> [f64; 2] {
        // Find the Y axis bounds for our chart.
        // This is trickier than the x-axis. We iterate through all our PlotData structs
        // and find the min/max of all the values. Then we add a 10% buffer to them.
        let iter = self
            .data
            .iter()
            .map(|b| b.data.as_slice())
            .flatten()
            .map(|v| v.1);
        let min = iter.clone().fold(f64::INFINITY, |a, b| a.min(b));
        let max = iter.fold(0f64, |a, b| a.max(b));
        // Add a 10% buffer to the top and bottom
        let max_10_percent = (max * 10_f64) / 100_f64;
        let min_10_percent = (min * 10_f64) / 100_f64;
        [min - min_10_percent, max + max_10_percent]
    }

    fn x_axis_bounds(&self) -> [f64; 2] {
        let window_min = self.data.iter().map(|d| d.window_min);
        let window_max = self.data.iter().map(|d| d.window_max);
        [
            window_min.fold(f64::INFINITY, |a, b| a.min(b)),
            window_max.fold(0f64, |a, b| a.max(b)),
        ]
    }

    fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Create 7 labels for our y axis, based on the y-axis bounds we computed above.
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let num_labels = 7;
        // Split difference into one chunk for each of the 7 labels
        let increment = Duration::from_micros((difference / num_labels as f64) as u64);
        let duration = Duration::from_micros(min as u64);

        (0..num_labels)
            .map(|i| Span::raw(format!("{:?}", duration.add(increment * i))))
            .collect()
    }
}

#[derive(Debug)]
enum Update {
    Result(Duration),
    Timeout,
    Unknown,
}

impl From<PingResult> for Update {
    fn from(result: PingResult) -> Self {
        match result {
            PingResult::Pong(duration, _) => Update::Result(duration),
            PingResult::Timeout(_) => Update::Timeout,
            PingResult::Unknown(_) => Update::Unknown,
        }
    }
}

#[derive(Debug)]
enum Event {
    Update(usize, Update),
    Input(KeyEvent),
}

fn start_cmd_thread(
    watch_cmd: &str,
    host_id: usize,
    watch_interval: f32,
    cmd_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let mut words = watch_cmd.split_ascii_whitespace();
    let cmd = words
        .next()
        .expect("Must specify a command to watch")
        .to_string();
    let cmd_args = words
        .into_iter()
        .map(|w| w.to_string())
        .collect::<Vec<String>>();

    let interval = Duration::from_millis((watch_interval * 1000.0) as u64);

    // Pump cmd watches into the queue
    thread::spawn(move || -> Result<()> {
        while !kill_event.load(Ordering::Acquire) {
            let start = Instant::now();
            let mut child = Command::new(&cmd)
                .args(&cmd_args)
                .stderr(Stdio::null())
                .stdout(Stdio::null())
                .spawn()?;
            let status = child.wait()?;
            let duration = start.elapsed();
            let update = if status.success() {
                Update::Result(duration)
            } else {
                Update::Timeout
            };
            cmd_tx.send(Event::Update(host_id, update))?;
            thread::sleep(interval);
        }
        Ok(())
    })
}

fn start_ping_thread(
    host: String,
    host_id: usize,
    ping_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping(host)?;
        while !kill_event.load(Ordering::Acquire) {
            ping_tx.send(Event::Update(host_id, stream.recv()?.into()))?;
        }
        Ok(())
    })
}

fn get_host_ipaddr(host: &str) -> Result<String> {
    let ipaddr: Vec<IpAddr> = match lookup_host(host) {
        Ok(ip) => ip,
        Err(_) => return Err(anyhow!("Could not resolve hostname {}", host)),
    };
    let ipaddr = ipaddr.first();
    Ok(ipaddr.unwrap().to_string())
}

fn main() -> Result<()> {
    let args = Args::from_args();

    let mut data = vec![];

    for (idx, host_or_cmd) in args.hosts_or_commands.iter().enumerate() {
        let display = match args.cmd {
            true => host_or_cmd.to_string(),
            false => format!("{} ({})", host_or_cmd, get_host_ipaddr(host_or_cmd)?),
        };
        data.push(PlotData::new(
            display,
            args.buffer,
            Style::default().fg(Color::Indexed(idx as u8 + 1)),
        ));
    }

    let mut app = App::new(data);
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;

    let (key_tx, rx) = mpsc::channel();

    let mut threads = vec![];

    let killed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));

    for (host_id, host_or_cmd) in args.hosts_or_commands.iter().cloned().enumerate() {
        if args.cmd {
            let cmd_thread = start_cmd_thread(
                &host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            );
            threads.push(cmd_thread);
        } else {
            threads.push(start_ping_thread(
                host_or_cmd,
                host_id,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            ));
        }
    }

    // Pump keyboard messages into the queue
    let killed_thread = std::sync::Arc::clone(&killed);
    let key_thread = thread::spawn(move || -> Result<()> {
        while !killed_thread.load(Ordering::Acquire) {
            if event::poll(Duration::from_millis(100))? {
                if let CEvent::Key(key) = event::read()? {
                    key_tx.send(Event::Input(key))?;
                }
            }
        }
        Ok(())
    });
    threads.push(key_thread);

    loop {
        match rx.recv()? {
            Event::Update(host_id, update) => {
                match update {
                    Update::Result(duration) => app.update(host_id, Some(duration)),
                    Update::Timeout => app.update(host_id, None),
                    Update::Unknown => (),
                };
                terminal.draw(|f| {
                    // Split our
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(1)
                        .constraints(
                            iter::repeat(Constraint::Length(1))
                                .take(app.data.len())
                                .chain(iter::once(Constraint::Percentage(10)))
                                .collect::<Vec<_>>()
                                .as_ref(),
                        )
                        .split(f.size());

                    let total_chunks = chunks.len();

                    let header_chunks = chunks[0..total_chunks - 1].to_owned();
                    let chart_chunk = chunks[total_chunks - 1].to_owned();

                    for (plot_data, chunk) in app.data.iter().zip(header_chunks) {
                        let header_layout = Layout::default()
                            .direction(Direction::Horizontal)
                            .constraints(
                                [
                                    Constraint::Percentage(25),
                                    Constraint::Percentage(25),
                                    Constraint::Percentage(25),
                                    Constraint::Percentage(25),
                                ]
                                .as_ref(),
                            )
                            .split(chunk);

                        for (area, paragraph) in
                            header_layout.into_iter().zip(plot_data.header_stats())
                        {
                            f.render_widget(paragraph, area);
                        }
                    }

                    let datasets: Vec<Dataset> = app.data.iter().map(|d| d.into()).collect();

                    let y_axis_bounds = app.y_axis_bounds();

                    let chart = Chart::new(datasets)
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(app.x_axis_bounds()),
                        )
                        .y_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );

                    f.render_widget(chart, chart_chunk)
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                _ => {}
            },
        }
    }

    for thread in threads {
        thread.join().unwrap()?;
    }

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}
<|endoftext|>


<commit_before>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) {
  let project_path = Path::new(project);
  let subdir = project_path.components().nth(0).unwrap();
  let module_name = project_path.components().last().unwrap();

  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();
  in_tmp_repo("fetch --all");

  transfer_to_tmp(newrev);
  let parent_commit_obj = tmp_repo.revparse_single(CENTRAL_NAME).unwrap();
  let mut parent_commit_oid = parent_commit_obj.as_commit().unwrap().id();

  let s = module_name.as_ref().to_str().unwrap();
  let oldrev = format!(
    "{}",tmp_repo.revparse_single(&format!("remotes/modules/{}/master",s)).unwrap().id()
  );

  {
    let old = tmp_repo.revparse_single(&oldrev).unwrap().id();
    let new = tmp_repo.revparse_single(&newrev).unwrap().id();

    let module_commit_obj = tmp_repo.revparse_single(&newrev).unwrap();
    if !tmp_repo.graph_descendant_of(new,old).unwrap() {
      println!(".");
      println!("===========================================================");
      println!("======== Commit not based on master, rebase first! ========");
      println!("===========================================================");
      return;
    }
  }

  let walk = {
    let mut walk = tmp_repo.revwalk().unwrap();
    walk.set_sorting( Sort::from_bits(5).unwrap());
    walk.push_range(&format!("{}..{}", oldrev, newrev));
    walk
  };

    println!("===== project path: {}", project);
    println!("===== Apply commits from {} to {}", oldrev, newrev);

  for rev in walk {
    let newrev = format!("{}",rev.unwrap());
    if oldrev == newrev { continue; }
    println!("===== Apply commit {}", newrev);

    let module_commit_obj = tmp_repo.revparse_single(&newrev).unwrap();
    let module_commit = module_commit_obj.as_commit().unwrap();
    let module_tree = module_commit.tree().unwrap();

    let parent_commit = tmp_repo.find_commit(parent_commit_oid).unwrap();

    let new_tree = {
      let master_tree = parent_commit.tree().unwrap();
      let new_tree_oid = module_to_subfolder(Path::new(module_name.as_ref()), &module_tree, &master_tree);
      tmp_repo.find_tree(new_tree_oid).unwrap()
    };

    parent_commit_oid = make_commit(&tmp_repo, &new_tree, module_commit, &vec!(&parent_commit)).unwrap();

  }

  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let x = push_from_tmp(
    &tmp_repo,
    &tmp_repo.find_commit(parent_commit_oid).unwrap(),
    CENTRAL_NAME,
    "refs/for/master"
  );
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
}

fn central_submit(newrev: &str) {
  println!("central_submit");
  let mpath = Path::new("modules");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let modules = tmp_repo.find_tree(central_tree.get_path(mpath).unwrap().id());
  tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
      );
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
  .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
  .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
  .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> String {
  let args: Vec<&str> = cmd.split(" ").collect();
  let output = Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().unwrap();
  return format!("{}", String::from_utf8_lossy(&output.stderr));
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    repo.remote("central_repo",
      &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
    );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
          .arg(format!("bsw/modules/{}",module))
          .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
      AUTOMATION_USER,
      module
    );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
        repo.remote(&remote_name, &remote_url);
    }
  }

  in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = builder.write().unwrap();
  println!("module_to_subfolder {}", r);
  return r;
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
                      .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository, commit: &Commit,repo: &str ,to: &str) -> String {
  tmp_repo.set_head_detached(commit.id());
  return in_tmp_repo(
    &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
      AUTOMATION_USER,
      repo,
      to
    )
  )
}

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    repo.set_head_detached(parents[0].id());
  }
  return repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
  ).ok();
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
      .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
      .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
      .arg(clap::Arg::with_name("project").long("project").takes_value(true))
      .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
      .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
      .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
      .arg(clap::Arg::with_name("change").long("change").takes_value(true))
      .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
      .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
      .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
      .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
      .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
      .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let uploader = args.value_of("uploader").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_automation = uploader.contains("Automation");
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // if !is_review && !is_automation {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<commit_msg>warnig free<commit_after>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) {
  let project_path = Path::new(project);
  let module_name = project_path.components().last().unwrap();

  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();
  in_tmp_repo("fetch --all");

  transfer_to_tmp(newrev);
  let parent_commit_obj = tmp_repo.revparse_single(CENTRAL_NAME).unwrap();
  let mut parent_commit_oid = parent_commit_obj.as_commit().unwrap().id();

  let s = module_name.as_ref().to_str().unwrap();
  let oldrev = format!(
    "{}",tmp_repo.revparse_single(&format!("remotes/modules/{}/master",s)).unwrap().id()
    );

  {
    let old = tmp_repo.revparse_single(&oldrev).unwrap().id();
    let new = tmp_repo.revparse_single(&newrev).unwrap().id();

    if !tmp_repo.graph_descendant_of(new,old).unwrap() {
      println!(".");
      println!("===========================================================");
      println!("======== Commit not based on master, rebase first! ========");
      println!("===========================================================");
      return;
    }
  }

  let walk = {
    let mut walk = tmp_repo.revwalk().unwrap();
    walk.set_sorting( Sort::from_bits(5).unwrap());
    let _ = walk.push_range(&format!("{}..{}", oldrev, newrev));
    walk
  };

  println!("===== project path: {}", project);
  println!("===== Apply commits from {} to {}", oldrev, newrev);

  for rev in walk {
    let newrev = format!("{}",rev.unwrap());
    if oldrev == newrev { continue; }
    println!("===== Apply commit {}", newrev);

    let module_commit_obj = tmp_repo.revparse_single(&newrev).unwrap();
    let module_commit = module_commit_obj.as_commit().unwrap();
    let module_tree = module_commit.tree().unwrap();

    let parent_commit = tmp_repo.find_commit(parent_commit_oid).unwrap();

    let new_tree = {
      let master_tree = parent_commit.tree().unwrap();
      let new_tree_oid = module_to_subfolder(Path::new(module_name.as_ref()), &module_tree, &master_tree);
      tmp_repo.find_tree(new_tree_oid).unwrap()
    };

    parent_commit_oid = make_commit(&tmp_repo, &new_tree, module_commit, &vec!(&parent_commit)).unwrap();

  }

  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let x = push_from_tmp(
    &tmp_repo,
    &tmp_repo.find_commit(parent_commit_oid).unwrap(),
    CENTRAL_NAME,
    "refs/for/master"
    );
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
}

fn central_submit(newrev: &str) {
  println!("central_submit");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let _ = tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    let _ = tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
        );
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
    .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
    .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
    .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> String {
  let args: Vec<&str> = cmd.split(" ").collect();
  let output = Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().unwrap();
  return format!("{}", String::from_utf8_lossy(&output.stderr));
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    let _ = repo.remote("central_repo",
                        &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
                       );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
      .arg("gerrit")
      .arg("create-project")
      .arg(format!("bsw/modules/{}",module))
      .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
                             AUTOMATION_USER,
                             module
                            );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
      let _ = repo.remote(&remote_name, &remote_url);
    }
  }

  in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = builder.write().unwrap();
  println!("module_to_subfolder {}", r);
  return r;
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
    .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository, commit: &Commit,repo: &str ,to: &str) -> String {
  let _ = tmp_repo.set_head_detached(commit.id());
  in_tmp_repo(
    &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
             AUTOMATION_USER,
             repo,
             to
            )
    )
}

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    let _ = repo.set_head_detached(parents[0].id());
  }
  return repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
    ).ok();
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
    .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
    .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
    .arg(clap::Arg::with_name("project").long("project").takes_value(true))
    .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
    .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
    .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
    .arg(clap::Arg::with_name("change").long("change").takes_value(true))
    .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
    .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
    .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
    .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
    .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
    .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // let uploader = args.value_of("uploader").unwrap_or("");
    // if !is_review && !uploader.contains("Automation") {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<|endoftext|>


<commit_before>use crossbeam::bounded;
use crossterm::{InputEvent, KeyEvent, TerminalInput};
use rand::distributions::Uniform;
use rand::prelude::Distribution;
use rusty_audio::Audio;
use rusty_sword::coord::{key_to_direction, Coord};
use rusty_sword::monster::Monster;
use rusty_sword::render::render_loop;
use rusty_sword::timer::Timer;
use rusty_sword::world::World;
use std::thread::{sleep, spawn};
use std::time::{Duration, Instant};

fn main() {
    let mut audio = Audio::new();
    audio.add("monster_dies", "clips/monster_dies.wav");
    audio.add("monster_spawns", "clips/monster_spawns.wav");
    audio.add("player_dies", "clips/player_dies.wav");

    let mut world = World::new(30, 60);

    // Make a channel to send the world to the render thread, and a channel to receive it back
    // This is a silly design, we can demonstrate using channels for thread communication. :-P
    let (render_tx, render_rx) = bounded::<World>(0);
    let (main_tx, main_rx) = bounded::<World>(0);

    // Render Thread
    let render_thread = { spawn(move || render_loop(render_rx, main_tx)) };

    // Game Loop
    sleep(Duration::from_millis(100));
    let input = TerminalInput::new();
    let mut reader = input.read_async();
    let mut rng = rand::thread_rng();
    let mut spawn_timer = Timer::from_millis(1000);
    let mut last_instant = Instant::now();
    'gameloop: loop {
        let delta = last_instant.elapsed();
        last_instant = Instant::now();
        let mut player = &mut world.player;

        // Player moves?
        let mut player_moved = false;
        while let Some(event) = reader.next() {
            match event {
                InputEvent::Keyboard(KeyEvent::Char('q')) | InputEvent::Keyboard(KeyEvent::Esc) => {
                    break 'gameloop
                }
                InputEvent::Keyboard(k) => {
                    if let Some(direction) = key_to_direction(k) {
                        player_moved =
                            player.travel(direction, &world.floor, &mut world.dirty_coords);
                    }
                }
                _ => {}
            }
        }

        // Update monster timers
        for monster in world.monsters.iter_mut() {
            monster.move_timer.update(delta);
        }

        // Monsters move?
        if !player_moved {
            for monster in world.monsters.iter_mut() {
                monster.try_travel(player.coord, &mut world.dirty_coords);
            }
        }

        // Did a monster die?
        let num_monsters = world.monsters.len();
        world
            .monsters
            .retain(|monster| monster.coord != player.sword_coord);
        let num_killed = num_monsters - world.monsters.len();
        if num_killed > 0 {
            player.score += num_killed as u64;
            audio.play("monster_dies");
        }

        // Spawn a new monster!
        spawn_timer.update(delta);
        if spawn_timer.ready {
            spawn_timer = Timer::from_millis(Uniform::new(1000, 5000).sample(&mut rng));
            let to_coord = Coord::new(
                Uniform::new(1, world.floor.rows).sample(&mut rng),
                Uniform::new(1, world.floor.cols).sample(&mut rng),
            );
            if to_coord != player.coord {
                world.monsters.push(Monster::new(to_coord, &mut rng));
                audio.play("monster_spawns");
            }
        }

        // Did the player die?
        if world
            .monsters
            .iter()
            .any(|monster| monster.coord == player.coord)
        {
            audio.play("player_dies");
            audio.wait(); // Wait until the sound finishes, so we can hear it before quitting.
            break 'gameloop;
        }

        // Give the whole world to the renderer
        render_tx.send(world).unwrap();
        // Get the whole world back
        world = main_rx.recv().unwrap();
        // Don't exceed ~60/fps
        if let Some(t) = Duration::from_secs_f64(1. / 60.).checked_sub(last_instant.elapsed()) {
            sleep(t);
        }
    }

    // Close the render_tx channel, which will trigger the render thread to exit
    drop(render_tx);
    // Wait for the render thread to actually exit
    render_thread.join().unwrap();

    println!("Thanks for playing!");
}
<commit_msg>Remove unnecessary sleep.<commit_after>use crossbeam::bounded;
use crossterm::{InputEvent, KeyEvent, TerminalInput};
use rand::distributions::Uniform;
use rand::prelude::Distribution;
use rusty_audio::Audio;
use rusty_sword::coord::{key_to_direction, Coord};
use rusty_sword::monster::Monster;
use rusty_sword::render::render_loop;
use rusty_sword::timer::Timer;
use rusty_sword::world::World;
use std::thread::{sleep, spawn};
use std::time::{Duration, Instant};

fn main() {
    let mut audio = Audio::new();
    audio.add("monster_dies", "clips/monster_dies.wav");
    audio.add("monster_spawns", "clips/monster_spawns.wav");
    audio.add("player_dies", "clips/player_dies.wav");

    let mut world = World::new(30, 60);

    // Make a channel to send the world to the render thread, and a channel to receive it back
    // This is a silly design, we can demonstrate using channels for thread communication. :-P
    let (render_tx, render_rx) = bounded::<World>(0);
    let (main_tx, main_rx) = bounded::<World>(0);

    // Render Thread
    let render_thread = { spawn(move || render_loop(render_rx, main_tx)) };

    // Game Loop
    let input = TerminalInput::new();
    let mut reader = input.read_async();
    let mut rng = rand::thread_rng();
    let mut spawn_timer = Timer::from_millis(1000);
    let mut last_instant = Instant::now();
    'gameloop: loop {
        let delta = last_instant.elapsed();
        last_instant = Instant::now();
        let mut player = &mut world.player;

        // Player moves?
        let mut player_moved = false;
        while let Some(event) = reader.next() {
            match event {
                InputEvent::Keyboard(KeyEvent::Char('q')) | InputEvent::Keyboard(KeyEvent::Esc) => {
                    break 'gameloop
                }
                InputEvent::Keyboard(k) => {
                    if let Some(direction) = key_to_direction(k) {
                        player_moved =
                            player.travel(direction, &world.floor, &mut world.dirty_coords);
                    }
                }
                _ => {}
            }
        }

        // Update monster timers
        for monster in world.monsters.iter_mut() {
            monster.move_timer.update(delta);
        }

        // Monsters move?
        if !player_moved {
            for monster in world.monsters.iter_mut() {
                monster.try_travel(player.coord, &mut world.dirty_coords);
            }
        }

        // Did a monster die?
        let num_monsters = world.monsters.len();
        world
            .monsters
            .retain(|monster| monster.coord != player.sword_coord);
        let num_killed = num_monsters - world.monsters.len();
        if num_killed > 0 {
            player.score += num_killed as u64;
            audio.play("monster_dies");
        }

        // Spawn a new monster!
        spawn_timer.update(delta);
        if spawn_timer.ready {
            spawn_timer = Timer::from_millis(Uniform::new(1000, 5000).sample(&mut rng));
            let to_coord = Coord::new(
                Uniform::new(1, world.floor.rows).sample(&mut rng),
                Uniform::new(1, world.floor.cols).sample(&mut rng),
            );
            if to_coord != player.coord {
                world.monsters.push(Monster::new(to_coord, &mut rng));
                audio.play("monster_spawns");
            }
        }

        // Did the player die?
        if world
            .monsters
            .iter()
            .any(|monster| monster.coord == player.coord)
        {
            audio.play("player_dies");
            audio.wait(); // Wait until the sound finishes, so we can hear it before quitting.
            break 'gameloop;
        }

        // Give the whole world to the renderer
        render_tx.send(world).unwrap();
        // Get the whole world back
        world = main_rx.recv().unwrap();
        // Don't exceed ~60/fps
        if let Some(t) = Duration::from_secs_f64(1. / 60.).checked_sub(last_instant.elapsed()) {
            sleep(t);
        }
    }

    // Close the render_tx channel, which will trigger the render thread to exit
    drop(render_tx);
    // Wait for the render thread to actually exit
    render_thread.join().unwrap();

    println!("Thanks for playing!");
}
<|endoftext|>


<commit_before>extern crate crypto;
extern crate mio;
extern crate num;
extern crate protobuf;
extern crate rand;
extern crate rustc_serialize;
extern crate time;

use address::Address;
use node::UdpNode;
use std::env;

mod address;
mod client;
mod messages;
mod network;
mod node;
mod node_bucket;
mod multi;
mod routing_table;
mod transaction;
#[cfg(test)]
mod tests;

fn main() {
    let args: Vec<String> = env::args().collect();
    let secret = args[1].clone();

    if secret == "multi" {
        let port_start = args[2].clone().parse::<u16>().unwrap();
        let port_end = args[3].clone().parse::<u16>().unwrap();
        let router = args.get(4).map(|p| p.clone().parse::<u16>().unwrap());
        multi::start_multiple(port_start, port_end, router);
    } else {
        let address = Address::for_content(secret.as_str());
        let port = args[2].clone().parse::<u16>().unwrap();
        let self_node = node::UdpNode::new(address, ("127.0.0.1", port));

        let routers: Vec<Box<node::Node>> = match args.get(3) {
            Some(router_port) => {
                let router_address = ("127.0.0.1", router_port.clone().parse::<u16>().unwrap());
                let router_node = Box::new(UdpNode::new(Address::null(), router_address));
                vec![router_node]
            }
            None => vec![]
        };

        let network = network::Network::new(self_node, port, routers);
        let client = client::Client::new(address);
        client.run(network)
    }
}
<commit_msg>Accept full socket addr instead of just port for router<commit_after>extern crate crypto;
extern crate mio;
extern crate num;
extern crate protobuf;
extern crate rand;
extern crate rustc_serialize;
extern crate time;

use address::Address;
use node::UdpNode;
use std::env;

mod address;
mod client;
mod messages;
mod network;
mod node;
mod node_bucket;
mod multi;
mod routing_table;
mod transaction;
#[cfg(test)]
mod tests;

fn main() {
    let args: Vec<String> = env::args().collect();
    let secret = args[1].clone();

    if secret == "multi" {
        let port_start = args[2].clone().parse::<u16>().unwrap();
        let port_end = args[3].clone().parse::<u16>().unwrap();
        let router = args.get(4).map(|p| p.clone().parse::<u16>().unwrap());
        multi::start_multiple(port_start, port_end, router);
    } else {
        let address = Address::for_content(secret.as_str());
        let port = args[2].clone().parse::<u16>().unwrap();
        let self_node = node::UdpNode::new(address, ("127.0.0.1", port));

        let routers: Vec<Box<node::Node>> = match args.get(3) {
            Some(router_address) => {
                let router_node = Box::new(UdpNode::new(Address::null(), router_address.as_str()));
                vec![router_node]
            }
            None => vec![]
        };

        let network = network::Network::new(self_node, port, routers);
        let client = client::Client::new(address);
        client.run(network)
    }
}
<|endoftext|>