<commit_before>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::thread;
use std::net;
use std::env;
use std::convert::AsRef;
extern crate getopts;
use getopts::Options;

fn socket(listen_on: net::SocketAddr) -> net::UdpSocket {
    let attempt = net::UdpSocket::bind(listen_on);
    let mut socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        },
    Err(err) => panic!("Could not bind: {}", err)
    }
    socket
}

fn read_message(socket: net::UdpSocket) -> Vec<u8> {
    let mut buf: [u8; 1] = [0; 1];
    let result = socket.recv_from(&mut buf);
    drop(socket);
    let mut data;
    match result {
        Ok((amt, _)) => {
        data = Vec::from(&buf[0..amt]);
        },
    Err(err) => panic!("Read error: {}", err)
    }
    data
}

pub fn send_message(send_addr: net::SocketAddr, target: net::SocketAddr, data: Vec<u8>) {
    let socket = socket(send_addr);
    let _ = socket.send_to(&data, target);
    drop(socket);
}

pub fn listen(listen_on: net::SocketAddr) -> thread::JoinHandle<Vec<u8>> {
    let socket = socket(listen_on);
    let handle = thread::spawn(move || {
        read_message(socket)
    });
    handle
}

#[cfg(test)]
mod test {
    use std::net;
    use std::thread;
    use super::*;

    #[test]
    fn test_udp() {
        println!("UDP");
        let ip = net::Ipv4Addr::new(127, 0, 0, 1);
        let listen_addr = net::SocketAddrV4::new(ip, 8888);
        let send_addr = net::SocketAddrV4::new(ip, 8889);
        let future = listen(net::SocketAddr::V4(listen_addr));
        let message: Vec<u8> = vec![10];
        // give the thread 3s to open the socket
        thread::sleep_ms(3000);
        send_message(
            net::SocketAddr::V4(send_addr),
            net::SocketAddr::V4(listen_addr),
            message);
        println!("Waiting");
        let received = future.join().unwrap();
        println!("Got {} bytes", received.len());
        assert_eq!(1, received.len());
        assert_eq!(10, received[0]);
    }
}

fn convert(inp: &str) -> String {
    // to_lowercase is still unstable.
    // This madness from IRC is the workaround
    // works though
    let outp = inp
        .chars()
        .flat_map(char::to_lowercase)
        .collect::<String>();
    outp
}

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

fn main() {
    let mut opts = Options::new();
    // get command-line input
    // Strings do not live for the entire life of your program
    // http://stackoverflow.com/a/23977218/416626
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    opts.optopt("p", "", "Set destination UDP port. Must be an integer.", "PORT");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => {m}
        Err(f) => {panic!(f.to_string())}
    };
    // Get port from the option, or specify the default
    let port = matches.opt_str("p").unwrap_or("1738".to_string());
    // cast to int or bail out horribly
    let numeric_port = port.parse::<u16>();
    // ensure we got an int, or set an error flag
    let proceed = match numeric_port {
        Ok(ref v) => true,
        Err(ref e) => false
    };
    if !proceed {
        print_usage(&program, opts);
        return;
    }
    // gather non-option arguments
    let arg = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    let intermediate: &str = &*arg;

    // match command-line input or bail out horribly
    let to_send = match convert(intermediate).as_ref() {
        "white" => "white".to_string(),
        "red" => "red".to_string(),
        "orange" => "orange".to_string(),
        "yellow" => "yellow".to_string(),
        "green" => "green".to_string(),
        "cyan" => "cyan".to_string(),
        "blue" => "blue".to_string(),
        "purple" => "purple".to_string(),
        "black" => "black".to_string(),
        "?" => "question".to_string(),
        "!" => "exclamation".to_string(),
        "quit" => "quit".to_string(),
        _ => panic!("Unknown option!")
    };
    // blam our control message into a vector
    let mut message: Vec<u8> = Vec::new();
    message.extend(to_send
        .as_bytes()
        .iter()
        .cloned()
    );
    // bind to the correct UDP port
    let ip = net::Ipv4Addr::new(127, 0, 0, 1);
    // parse() gives us an int Result which we need to unwrap
    let listen_addr = net::SocketAddrV4::new(ip, numeric_port.unwrap());
    let send_addr = net::SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(
        net::SocketAddr::V4(send_addr),
        net::SocketAddr::V4(listen_addr),
        message);
}
<commit_msg>Better comments about success and error handling<commit_after>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::thread;
use std::net;
use std::env;
use std::convert::AsRef;
extern crate getopts;
use getopts::Options;

fn socket(listen_on: net::SocketAddr) -> net::UdpSocket {
    let attempt = net::UdpSocket::bind(listen_on);
    let mut socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        },
    Err(err) => panic!("Could not bind: {}", err)
    }
    socket
}

fn read_message(socket: net::UdpSocket) -> Vec<u8> {
    let mut buf: [u8; 1] = [0; 1];
    let result = socket.recv_from(&mut buf);
    drop(socket);
    let mut data;
    match result {
        Ok((amt, _)) => {
        data = Vec::from(&buf[0..amt]);
        },
    Err(err) => panic!("Read error: {}", err)
    }
    data
}

pub fn send_message(send_addr: net::SocketAddr, target: net::SocketAddr, data: Vec<u8>) {
    let socket = socket(send_addr);
    let _ = socket.send_to(&data, target);
    drop(socket);
}

pub fn listen(listen_on: net::SocketAddr) -> thread::JoinHandle<Vec<u8>> {
    let socket = socket(listen_on);
    let handle = thread::spawn(move || {
        read_message(socket)
    });
    handle
}

#[cfg(test)]
mod test {
    use std::net;
    use std::thread;
    use super::*;

    #[test]
    fn test_udp() {
        println!("UDP");
        let ip = net::Ipv4Addr::new(127, 0, 0, 1);
        let listen_addr = net::SocketAddrV4::new(ip, 8888);
        let send_addr = net::SocketAddrV4::new(ip, 8889);
        let future = listen(net::SocketAddr::V4(listen_addr));
        let message: Vec<u8> = vec![10];
        // give the thread 3s to open the socket
        thread::sleep_ms(3000);
        send_message(
            net::SocketAddr::V4(send_addr),
            net::SocketAddr::V4(listen_addr),
            message);
        println!("Waiting");
        let received = future.join().unwrap();
        println!("Got {} bytes", received.len());
        assert_eq!(1, received.len());
        assert_eq!(10, received[0]);
    }
}

fn convert(inp: &str) -> String {
    // to_lowercase is still unstable.
    // This madness from IRC is the workaround
    // works though
    let outp = inp
        .chars()
        .flat_map(char::to_lowercase)
        .collect::<String>();
    outp
}

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

fn main() {
    let mut opts = Options::new();
    // get command-line input
    // Strings do not live for the entire life of your program
    // http://stackoverflow.com/a/23977218/416626
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    opts.optopt("p", "", "Set destination UDP port. Must be an integer.", "PORT");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => {m}
        Err(f) => {panic!(f.to_string())}
    };
    // Get port from the option, or specify the default
    let port = matches.opt_str("p").unwrap_or("1738".to_string());
    // cast to int and ensure it worked, or set an error flag and bail out
    let numeric_port = port.parse::<u16>();
    let proceed = match numeric_port {
        Ok(ref v) => true,
        Err(ref e) => false
    };
    if !proceed {
        print_usage(&program, opts);
        return;
    }
    // gather non-option arguments
    let arg = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    let intermediate: &str = &*arg;

    // match command-line input or bail out horribly
    let to_send = match convert(intermediate).as_ref() {
        "white" => "white".to_string(),
        "red" => "red".to_string(),
        "orange" => "orange".to_string(),
        "yellow" => "yellow".to_string(),
        "green" => "green".to_string(),
        "cyan" => "cyan".to_string(),
        "blue" => "blue".to_string(),
        "purple" => "purple".to_string(),
        "black" => "black".to_string(),
        "?" => "question".to_string(),
        "!" => "exclamation".to_string(),
        "quit" => "quit".to_string(),
        _ => panic!("Unknown option!")
    };
    // blam our control message into a vector
    let mut message: Vec<u8> = Vec::new();
    message.extend(to_send
        .as_bytes()
        .iter()
        .cloned()
    );
    // bind to the correct UDP port
    let ip = net::Ipv4Addr::new(127, 0, 0, 1);
    // parse() gives us Result which we need to unwrap
    // it's safe to unwrap here, cos we already checked success with
    let listen_addr = net::SocketAddrV4::new(ip, numeric_port.unwrap());
    let send_addr = net::SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(
        net::SocketAddr::V4(send_addr),
        net::SocketAddr::V4(listen_addr),
        message);
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate ppbert;

use std::env;
use std::fs;
use std::io::{self, ErrorKind, Read, Write, BufWriter};
use std::process::exit;
use std::time::{Duration, Instant};

use getopts::Options;

use ppbert::bertterm::BertTerm;
use ppbert::consts::VERSION;
use ppbert::error::{BertError, Result};
use ppbert::parser;

const PROG_NAME: &str = "ppbert";

fn opt_usize(m: &getopts::Matches, opt: &str, default: usize) -> usize {
    match m.opt_get_default(opt, default) {
        Ok(n) => n,
        Err(_) => {
            eprintln!("'{}' must be a number", opt);
            exit(1);
        }
    }
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("V", "version", "display version");
    opts.optflag("h", "help", "display this help");
    opts.optopt("i", "indent", "indent with NUM spaces", "NUM");
    opts.optopt("m", "per-line", "print at most NUM basic terms per line", "NUM");
    opts.optflag("p", "parse", "parse only, not pretty print");
    opts.optflag("2", "bert2", "parse .bert2 files");
    opts.optflag("d", "disk-log", "parse disk_log files");
    opts.optflag("v", "verbose", "show diagnostics on stderr");
    opts.optflag("j", "json", "print as JSON");
    opts.optflag("t", "transform-proplists", "convert proplists to JSON objects");

    let mut matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}: {}", PROG_NAME, e);
            eprintln!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
            exit(1);
        }
    };

    if matches.opt_present("help") {
        println!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, VERSION);
        exit(0);
    }

    // If no files to process, use stdin.
    if matches.free.is_empty() {
        matches.free.push("-".to_owned());
    }

    let indent_width = opt_usize(&matches, "indent", 2);
    let max_per_line = opt_usize(&matches, "per-line", 6);
    let parse_only = matches.opt_present("parse");
    let json = matches.opt_present("json");
    let transform_proplists = matches.opt_present("transform-proplists");
    let verbose = matches.opt_present("verbose");

    let parse_fn =
        if matches.opt_present("bert2") {
            parser::Parser::bert2_next
        } else if matches.opt_present("disk-log") {
            parser::Parser::disk_log_next
        } else {
            parser::Parser::bert_next
        };
    let output_fn = match (json, transform_proplists) {
        (true, false)  => pp_json,
        (true, true)   => pp_json_proplist,
        (false, false) => pp_bert,
        (false, true)  => {
            eprintln!("{}: --transform-proplists is only valid with the --json flag", PROG_NAME);
            pp_bert
        }
    };

    let mut return_code = 0;
    for file in &matches.free {
        let res = handle_file(file, parse_only, verbose,
                              indent_width, max_per_line,
                              parse_fn, output_fn);
        match res {
            Ok(()) => (),
            Err(ref e) => {
                if broken_pipe(e) {
                    break;
                }
                return_code = 1;
                eprintln!("{}: {}: {}", PROG_NAME, file, e);
            }
        }
    }
    exit(return_code);

}

fn broken_pipe(err: &BertError) -> bool {
    match *err {
        BertError::IoError(ref ioerr) =>
            ioerr.kind() == ErrorKind::BrokenPipe,
        _ => false
    }
}

fn handle_file(
    file: &str,
    parse_only: bool,
    verbose: bool,
    indent: usize,
    terms_per_line: usize,
    parse_fn: fn(&mut parser::Parser) -> Option<Result<BertTerm>>,
    pp_fn: fn(BertTerm, usize, usize) -> Result<()>
) -> Result<()> {

    // Read file or stdin into buffer
    let now = Instant::now();
    let buf = if file == "-" {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let mut buf: Vec<u8> = Vec::with_capacity(4096);
        stdin.read_to_end(&mut buf)?;
        buf
    } else {
        fs::read(file)?
    };
    let read_dur = now.elapsed();

    let mut parser = parser::Parser::new(buf);
    let mut parse_dur = Duration::new(0, 0);
    let mut pp_dur = Duration::new(0, 0);

    loop {
        let now = Instant::now();
        let next_item = parse_fn(&mut parser);
        parse_dur += now.elapsed();

        match next_item {
            None => { break; }
            Some(Err(e)) => { return Err(e); }
            Some(Ok(t)) => {
                if !parse_only {
                    let now = Instant::now();
                    pp_fn(t, indent, terms_per_line)?;
                    pp_dur += now.elapsed();
                }
            }
        }
    }

    if verbose {
        eprintln!("{}: {} read time: {}.{:06} seconds", PROG_NAME, file, read_dur.as_secs(), read_dur.subsec_micros());
        eprintln!("{}: {} parse time: {}.{:06} seconds", PROG_NAME, file, parse_dur.as_secs(), parse_dur.subsec_micros());
        if !parse_only {
            eprintln!("{}: {} print time: {}.{:06} seconds", PROG_NAME, file, pp_dur.as_secs(), pp_dur.subsec_micros());
        }
    }

    return Ok(());
}

/// Outputs a BertTerm to stdout.
fn pp_bert(term: BertTerm, indent_width: usize, terms_per_line: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_erlang(&mut stdout, indent_width, terms_per_line)?;
    stdout.flush()?;
    writeln!(&mut stdout, "")?;
    return Ok(());
}

/// Outputs a BertTerm as JSON to stdout.
fn pp_json(term: BertTerm, _: usize, _: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_json(&mut stdout, false)?;
    writeln!(&mut stdout, "")?;
    stdout.flush()?;
    return Ok(());
}

/// Outputs a BertTerm as JSON to stdout;
/// Erlang proplists are converted to JSON objects.
fn pp_json_proplist(term: BertTerm, _: usize, _: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_json(&mut stdout, true)?;
    writeln!(&mut stdout, "")?;
    stdout.flush()?;
    return Ok(());
}
<commit_msg>Refactor: add read_bytes() function<commit_after>extern crate getopts;
extern crate ppbert;

use std::env;
use std::fs;
use std::io::{self, ErrorKind, Read, Write, BufWriter};
use std::process::exit;
use std::time::{Duration, Instant};

use getopts::Options;

use ppbert::bertterm::BertTerm;
use ppbert::consts::VERSION;
use ppbert::error::{BertError, Result};
use ppbert::parser;

const PROG_NAME: &str = "ppbert";

fn opt_usize(m: &getopts::Matches, opt: &str, default: usize) -> usize {
    match m.opt_get_default(opt, default) {
        Ok(n) => n,
        Err(_) => {
            eprintln!("'{}' must be a number", opt);
            exit(1);
        }
    }
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("V", "version", "display version");
    opts.optflag("h", "help", "display this help");
    opts.optopt("i", "indent", "indent with NUM spaces", "NUM");
    opts.optopt("m", "per-line", "print at most NUM basic terms per line", "NUM");
    opts.optflag("p", "parse", "parse only, not pretty print");
    opts.optflag("2", "bert2", "parse .bert2 files");
    opts.optflag("d", "disk-log", "parse disk_log files");
    opts.optflag("v", "verbose", "show diagnostics on stderr");
    opts.optflag("j", "json", "print as JSON");
    opts.optflag("t", "transform-proplists", "convert proplists to JSON objects");

    let mut matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}: {}", PROG_NAME, e);
            eprintln!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
            exit(1);
        }
    };

    if matches.opt_present("help") {
        println!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, VERSION);
        exit(0);
    }

    // If no files to process, use stdin.
    if matches.free.is_empty() {
        matches.free.push("-".to_owned());
    }

    let indent_width = opt_usize(&matches, "indent", 2);
    let max_per_line = opt_usize(&matches, "per-line", 6);
    let parse_only = matches.opt_present("parse");
    let json = matches.opt_present("json");
    let transform_proplists = matches.opt_present("transform-proplists");
    let verbose = matches.opt_present("verbose");

    let parse_fn =
        if matches.opt_present("bert2") {
            parser::Parser::bert2_next
        } else if matches.opt_present("disk-log") {
            parser::Parser::disk_log_next
        } else {
            parser::Parser::bert_next
        };
    let output_fn = match (json, transform_proplists) {
        (true, false)  => pp_json,
        (true, true)   => pp_json_proplist,
        (false, false) => pp_bert,
        (false, true)  => {
            eprintln!("{}: --transform-proplists is only valid with the --json flag", PROG_NAME);
            pp_bert
        }
    };

    let mut return_code = 0;
    for file in &matches.free {
        let res = handle_file(file, parse_only, verbose,
                              indent_width, max_per_line,
                              parse_fn, output_fn);
        match res {
            Ok(()) => (),
            Err(ref e) => {
                if broken_pipe(e) {
                    break;
                }
                return_code = 1;
                eprintln!("{}: {}: {}", PROG_NAME, file, e);
            }
        }
    }
    exit(return_code);

}

fn broken_pipe(err: &BertError) -> bool {
    match *err {
        BertError::IoError(ref ioerr) =>
            ioerr.kind() == ErrorKind::BrokenPipe,
        _ => false
    }
}

fn read_bytes(filename: &str) -> Result<Vec<u8>> {
    if filename == "-" {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let mut buf: Vec<u8> = Vec::with_capacity(4096);
        stdin.read_to_end(&mut buf)?;
        return Ok(buf);
    } else {
        let buf = fs::read(filename)?;
        return Ok(buf);
    }
}

fn handle_file(
    file: &str,
    parse_only: bool,
    verbose: bool,
    indent: usize,
    terms_per_line: usize,
    parse_fn: fn(&mut parser::Parser) -> Option<Result<BertTerm>>,
    pp_fn: fn(BertTerm, usize, usize) -> Result<()>
) -> Result<()> {

    // Read file or stdin into buffer
    let now = Instant::now();
    let buf = read_bytes(file)?;
    let read_dur = now.elapsed();

    let mut parser = parser::Parser::new(buf);
    let mut parse_dur = Duration::new(0, 0);
    let mut pp_dur = Duration::new(0, 0);

    loop {
        let now = Instant::now();
        let next_item = parse_fn(&mut parser);
        parse_dur += now.elapsed();

        match next_item {
            None => { break; }
            Some(Err(e)) => { return Err(e); }
            Some(Ok(t)) => {
                if !parse_only {
                    let now = Instant::now();
                    pp_fn(t, indent, terms_per_line)?;
                    pp_dur += now.elapsed();
                }
            }
        }
    }

    if verbose {
        eprintln!("{}: {} read time: {}.{:06} seconds", PROG_NAME, file, read_dur.as_secs(), read_dur.subsec_micros());
        eprintln!("{}: {} parse time: {}.{:06} seconds", PROG_NAME, file, parse_dur.as_secs(), parse_dur.subsec_micros());
        if !parse_only {
            eprintln!("{}: {} print time: {}.{:06} seconds", PROG_NAME, file, pp_dur.as_secs(), pp_dur.subsec_micros());
        }
    }

    return Ok(());
}

/// Outputs a BertTerm to stdout.
fn pp_bert(term: BertTerm, indent_width: usize, terms_per_line: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_erlang(&mut stdout, indent_width, terms_per_line)?;
    stdout.flush()?;
    writeln!(&mut stdout, "")?;
    return Ok(());
}

/// Outputs a BertTerm as JSON to stdout.
fn pp_json(term: BertTerm, _: usize, _: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_json(&mut stdout, false)?;
    writeln!(&mut stdout, "")?;
    stdout.flush()?;
    return Ok(());
}

/// Outputs a BertTerm as JSON to stdout;
/// Erlang proplists are converted to JSON objects.
fn pp_json_proplist(term: BertTerm, _: usize, _: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_json(&mut stdout, true)?;
    writeln!(&mut stdout, "")?;
    stdout.flush()?;
    return Ok(());
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nickel;
extern crate calibration_web_app;

use nickel::Nickel;

fn main() {
    let mut server = Nickel::new();

    server.utilize(router! {
        get "**" => |_req, _res| {
            "Calibration start!"
        }
    });

    server.listen("127.0.0.1:6767");
}
<commit_msg>first successful test!<commit_after>#[macro_use]
extern crate nickel;
extern crate calibration_web_app;

use nickel::{Nickel, HttpRouter};

fn main() {
    let mut server = Nickel::new();

    server.get("/", middleware!("<html><title>Calibration!!!</title></html>"));

    server.listen("127.0.0.1:6767");
}

#[test]
fn server_test() {
    assert!(true);
}
<|endoftext|>


<commit_before>extern crate rsnl;

//use rsnl::{socket};

fn main() {
	let nls = rsnl::socket::new();
	nls.connect(rsnl::NetlinkProtocol::zu);
	let buf = 0;

	nls.send_simple(0xfa, 0, &buf, 0);
}
<commit_msg>Add test for msg::append<commit_after>extern crate rsnl;

//use rsnl::{socket};

fn main() {
	let nls = rsnl::socket::new();
	nls.connect(rsnl::NetlinkProtocol::zu);
	let buf = 0;

	nls.send_simple(0xfa, 0, &buf, 0);

    let msg = rsnl::msg::new();
    let code = b"Foobar\0";
    let r = msg.append(&code, 7, 0);
    println!("Value: {}", r);
}
<|endoftext|>


<commit_before>extern crate docopt;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate error_chain;

use docopt::Docopt;
use std::fs::File;
use std::io::{Read, Write, stdin, stdout, stderr};

mod errors {
    error_chain!{
        foreign_links {
            Io(::std::io::Error);
        }
    }
}

use errors::*;

const USAGE: &str = "
hexlify 0.0.1

Perform bytes-to-hexstring conversion and vice-versa as implemented
in Python's binascii.{un,}hexlify. Read from stdin if <file> is \"-\"
or not specified. Whitespace is ignored during decoding.

Usage: hexlify [options] [<file>]

Options:
  -d --decode          Decode stream.
  -i --ignore-garbage  Ignore non-hex values.
  -h --help            Show this help screen.
";

const ERR_NOT_IN_HEX: &str = "\
Character does not match hex-alphabet. Only a-z, A-Z and 0-9 are allowed.
Make sure not to confuse decoding with encoding or use -i to ignore non-hex characters.\
";

const ERR_ODD_CHARS: &str = "\
Input had odd number of characters, please be cautious.\
";

#[derive(Deserialize)]
struct Args {
    arg_file: Option<String>,
    flag_decode: bool,
    flag_ignore_garbage: bool,
}

fn to_hex_value(c: u8) -> Result<u8> {
    match c as char {
        '0'...'9' => Ok(c - '0' as u8),
        'a'...'f' => Ok(c - 'a' as u8 + 10),
        'A'...'F' => Ok(c - 'A' as u8 + 10),
        _ => Err(ERR_NOT_IN_HEX.into()),
    }
}

fn pair_to_hex(pair: &[u8; 2]) -> Result<u8> {
    let l = to_hex_value(pair[0])?;
    let r = to_hex_value(pair[1])?;
    Ok(l * 16 + r)
}

fn run(file: Option<String>, decode: bool, ignore_garbage: bool) -> Result<()> {
    let src: Box<Read> = match file {
        Some(path) => {
            if path != "-" {
                Box::new(File::open(&path).chain_err(
                    || format!("can't open \"{}\"", path),
                )?)
            } else {
                Box::new(stdin())
            }
        }
        None => Box::new(stdin()),
    };

    if decode {
        let mut side = 0;
        let mut pair = [0 as u8; 2];

        for byte in src.bytes() {
            let byte = byte?;

            if (byte as char).is_whitespace() {
                continue;
            }

            if to_hex_value(byte).is_err() && ignore_garbage {
                continue;
            }

            pair[side] = byte;

            if side == 0 {
                side = 1;
            } else {
                stdout().write(&[pair_to_hex(&pair)?])?;
                side = 0;
            }
        }

        if side == 1 {
            stdout().flush()?;
            return Err(ERR_ODD_CHARS.into());
        }
    } else {
        for byte in src.bytes() {
            print!("{:02X}", byte?);
        }
    }

    Ok(())
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    if let Err(ref e) = run(args.arg_file, args.flag_decode, args.flag_ignore_garbage) {
        writeln!(stderr(), "error: {}", e).unwrap();

        for e in e.iter().skip(1) {
            writeln!(stderr(), "caused by: {}", e).unwrap();
        }

        if let Some(backtrace) = e.backtrace() {
            writeln!(stderr(), "backtrace: {:?}", backtrace).unwrap();
        }

        ::std::process::exit(1);
    }
}

#[cfg(test)]
mod test {
    use super::{to_hex_value, pair_to_hex};

    #[test]
    fn test_to_hex_value() {
        assert_eq!(to_hex_value('0' as u8).unwrap(), 0);
        assert_eq!(to_hex_value('9' as u8).unwrap(), 9);
        assert_eq!(to_hex_value('a' as u8).unwrap(), 10);
        assert_eq!(to_hex_value('f' as u8).unwrap(), 15);
        assert_eq!(to_hex_value('A' as u8).unwrap(), 10);
        assert_eq!(to_hex_value('F' as u8).unwrap(), 15);
    }

    #[test]
    fn test_pair_to_hex() {
        assert_eq!(pair_to_hex(&['0' as u8, '0' as u8]).unwrap(), 0x00);
        assert_eq!(pair_to_hex(&['a' as u8, 'a' as u8]).unwrap(), 0xAA);
        assert_eq!(pair_to_hex(&['A' as u8, 'A' as u8]).unwrap(), 0xAA);
        assert_eq!(pair_to_hex(&['f' as u8, 'f' as u8]).unwrap(), 0xFF);
        assert_eq!(pair_to_hex(&['F' as u8, 'F' as u8]).unwrap(), 0xFF);

        assert!(pair_to_hex(&['0' as u8, '/' as u8]).is_err());
        assert!(pair_to_hex(&['0' as u8, ':' as u8]).is_err());

        assert!(pair_to_hex(&['0' as u8, '`' as u8]).is_err());
        assert!(pair_to_hex(&['0' as u8, 'g' as u8]).is_err());

        assert!(pair_to_hex(&['0' as u8, '@' as u8]).is_err());
        assert!(pair_to_hex(&['0' as u8, 'G' as u8]).is_err());
    }
}
<commit_msg>{de,en}code is now generic over Read/Write. This makes writing tests easier, because stdin/stdout handling is not needed.<commit_after>extern crate docopt;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate error_chain;

use docopt::Docopt;
use std::fs::File;
use std::io::{Read, Write, stdin, stdout, stderr};

mod errors {
    error_chain!{
        foreign_links {
            Io(::std::io::Error);
        }
    }
}

use errors::*;

const USAGE: &str = "
hexlify 0.0.1

Perform bytes-to-hexstring conversion and vice-versa as implemented
in Python's binascii.{un,}hexlify. Read from stdin if <file> is \"-\"
or not specified. Whitespace is ignored during decoding.

Usage: hexlify [options] [<file>]

Options:
  -d --decode          Decode stream.
  -i --ignore-garbage  Ignore non-hex values.
  -h --help            Show this help screen.
";

const ERR_NOT_IN_HEX: &str = "\
Character does not match hex-alphabet. Only a-z, A-Z and 0-9 are allowed.
Make sure not to confuse decoding with encoding or use -i to ignore non-hex characters.\
";

const ERR_ODD_CHARS: &str = "\
Input had odd number of characters, please be cautious.\
";

#[derive(Deserialize)]
struct Args {
    arg_file: Option<String>,
    flag_decode: bool,
    flag_ignore_garbage: bool,
}

fn to_hex_value(c: u8) -> Result<u8> {
    match c as char {
        '0'...'9' => Ok(c - '0' as u8),
        'a'...'f' => Ok(c - 'a' as u8 + 10),
        'A'...'F' => Ok(c - 'A' as u8 + 10),
        _ => Err(ERR_NOT_IN_HEX.into()),
    }
}

fn pair_to_hex(pair: &[u8; 2]) -> Result<u8> {
    let l = to_hex_value(pair[0])?;
    let r = to_hex_value(pair[1])?;
    Ok(l * 16 + r)
}

fn decode<R: Read, W: Write>(src: &mut R, dst: &mut W, ignore_garbage: bool) -> Result<()> {
    let mut side = 0;
    let mut pair = [0 as u8; 2];

    for byte in src.bytes() {
        let byte = byte?;

        if (byte as char).is_whitespace() {
            continue;
        }

        if to_hex_value(byte).is_err() && ignore_garbage {
            continue;
        }

        pair[side] = byte;

        if side == 0 {
            side = 1;
        } else {
            dst.write(&[pair_to_hex(&pair)?])?;
            side = 0;
        }
    }

    if side == 1 {
        return Err(ERR_ODD_CHARS.into());
    }

    Ok(())
}

fn encode<R: Read, W: Write>(src: &mut R, dst: &mut W) -> Result<()> {
    for byte in src.bytes() {
        write!(dst, "{:02X}", byte?)?;
    }

    Ok(())
}

fn run(file: Option<String>, flag_decode: bool, flag_ignore_garbage: bool) -> Result<()> {
    // Choose input (file or stdin)
    let mut src: Box<Read> = match file {
        Some(path) => {
            if path != "-" {
                Box::new(File::open(&path).chain_err(|| format!("can't open \"{}\"", path))?)
            } else {
                Box::new(stdin())
            }
        }
        None => Box::new(stdin()),
    };

    // Choose output (stdout)
    let mut dst = stdout();

    if flag_decode {
        decode(&mut src, &mut dst, flag_ignore_garbage)?;
    } else {
        encode(&mut src, &mut dst)?;
    }

    Ok(())
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    if let Err(ref e) = run(args.arg_file, args.flag_decode, args.flag_ignore_garbage) {
        writeln!(stderr(), "error: {}", e).unwrap();

        for e in e.iter().skip(1) {
            writeln!(stderr(), "caused by: {}", e).unwrap();
        }

        if let Some(backtrace) = e.backtrace() {
            writeln!(stderr(), "backtrace: {:?}", backtrace).unwrap();
        }

        ::std::process::exit(1);
    }
}

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

    #[test]
    fn test_to_hex_value() {
        assert_eq!(to_hex_value('0' as u8).unwrap(), 0);
        assert_eq!(to_hex_value('9' as u8).unwrap(), 9);
        assert_eq!(to_hex_value('a' as u8).unwrap(), 10);
        assert_eq!(to_hex_value('f' as u8).unwrap(), 15);
        assert_eq!(to_hex_value('A' as u8).unwrap(), 10);
        assert_eq!(to_hex_value('F' as u8).unwrap(), 15);
    }

    #[test]
    fn test_pair_to_hex() {
        assert_eq!(pair_to_hex(&['0' as u8, '0' as u8]).unwrap(), 0x00);
        assert_eq!(pair_to_hex(&['9' as u8, '9' as u8]).unwrap(), 0x99);
        assert_eq!(pair_to_hex(&['a' as u8, 'a' as u8]).unwrap(), 0xAA);
        assert_eq!(pair_to_hex(&['A' as u8, 'A' as u8]).unwrap(), 0xAA);
        assert_eq!(pair_to_hex(&['f' as u8, 'f' as u8]).unwrap(), 0xFF);
        assert_eq!(pair_to_hex(&['F' as u8, 'F' as u8]).unwrap(), 0xFF);

        assert!(pair_to_hex(&['0' as u8, '/' as u8]).is_err());
        assert!(pair_to_hex(&['0' as u8, ':' as u8]).is_err());

        assert!(pair_to_hex(&['0' as u8, '`' as u8]).is_err());
        assert!(pair_to_hex(&['0' as u8, 'g' as u8]).is_err());

        assert!(pair_to_hex(&['0' as u8, '@' as u8]).is_err());
        assert!(pair_to_hex(&['0' as u8, 'G' as u8]).is_err());
    }

    #[test]
    fn test_decode() {
        let samples = vec![
            ("AA", vec![0xAA]),
            ("AABB", vec![0xAA, 0xBB]),
            ("AABBCCD", vec![0xAA, 0xBB, 0xCC]),
            ("AABBCCDD", vec![0xAA, 0xBB, 0xCC, 0xDD]),
            ("00gf", vec![0x00]),
            ("00g  ff", vec![0x00, 0xff]),
            ("---f-..,f aa b", vec![0xff, 0xaa]),
            ("²¹²³fa„“", vec![0xfa]),
            ("¹¸^afg01", vec![0xaf, 0x01]),
            ("´#*-_ff:;:;:1234", vec![0xff, 0x12, 0x34]),
            ("", vec![]),
            ("a1¹²}ff™± ¡¿⅛°±£™⅞`ff¿¡⅛°±£™01`", vec![0xa1, 0xff, 0xff, 0x01]),
        ];

        for (mut sample, expected) in samples.into_iter().map(|(s, e)| (std::io::Cursor::new(s), e)) {
            let got = {
                let mut tmp = Vec::new();
                let _ = decode(&mut sample, &mut tmp, true);
                tmp
            };

            assert_eq!(expected, got);
        }
    }

    #[test]
    fn test_encode() {
        let samples = vec![
            (vec![], ""),
            (vec![0x00], "00"),
            (vec![0x00, 0x99, 0xAA, 0xFF], "0099AAFF"),
            (vec![0x00, 0x99, 0xAA, 0xFF, 0x00, 0x00], "0099AAFF0000"),
        ];

        for (mut sample, expected) in samples.into_iter().map(|(s, e)| (std::io::Cursor::new(s), e)) {
            let got = {
                let mut tmp = Vec::new();
                let _ = encode(&mut sample, &mut tmp);
                String::from_utf8(tmp).unwrap()
            };

            assert_eq!(expected, got);
        }
    }
}
<|endoftext|>


<commit_before>extern crate dotenv;
extern crate rand;
extern crate serenity;

use std::env;
use std::sync::Arc;

use dotenv::dotenv;
use rand::Rng;
use serenity::framework::standard::*;
use serenity::framework::StandardFramework;
use serenity::model::prelude::*;
use serenity::prelude::*;

struct Handler;
impl EventHandler for Handler {}

fn main() {
    let _ = dotenv();

    let creds = env::var("Z_CREDENTIALS")
        .expect("Unspecified credentials: set Z_CREDENTIALS in the environment (or in `.env`).");
    let mut client = Client::new(&creds, Handler).unwrap();
    client.with_framework(
        StandardFramework::new()
            .configure(|c| c.depth(1).prefix("!"))
            .group("Rng", |g| g.cmd("flip", Flip).cmd("roll", Roll))
            .on_dispatch_error(dispatch_error_handler)
            .unrecognised_command(unrecognised_command_handler)
            .customised_help(help_commands::plain, |h| {
                h.striked_commands_tip(None)
                    .dm_and_guilds_text("Everywhere")
                    .guild_only_text("Only in text channels")
            }),
    );
    client.start().unwrap();
}

fn dispatch_error_handler(_ctx: Context, msg: Message, err: DispatchError) {
    msg.reply(&format!("{:?}", err)).unwrap();
}

fn unrecognised_command_handler(_ctx: &mut Context, msg: &Message, cmd: &str) {
    msg.reply(&format!("{} is not a command.", cmd)).unwrap();
}

struct Flip;
impl Command for Flip {
    fn options(&self) -> Arc<CommandOptions> {
        let mut options = CommandOptions::default();
        options.desc = Some("Flips a coin.".to_owned());
        options.min_args = Some(0);
        options.max_args = Some(0);
        Arc::new(options)
    }

    fn execute(&self, _ctx: &mut Context, msg: &Message, _args: Args) -> Result<(), CommandError> {
        let result = if rand::thread_rng().gen() {
            "Heads!"
        } else {
            "Tails!"
        };

        msg.reply(result).unwrap();
        Ok(())
    }
}

struct Roll;
impl Command for Roll {
    fn options(&self) -> Arc<CommandOptions> {
        let mut options = CommandOptions::default();
        options.desc = Some("Rolls a die.".to_owned());
        options.min_args = Some(0);
        options.max_args = Some(1);
        Arc::new(options)
    }

    fn execute(
        &self,
        _ctx: &mut Context,
        msg: &Message,
        mut args: Args,
    ) -> Result<(), CommandError> {
        let upper_bound = match args.single() {
            Ok(num) => Ok(num),
            Err(ArgError::Eos) => Ok(6),
            Err(ArgError::Parse(err)) => Err(CommandError(format!("{}", err))),
        };

        upper_bound.map(|up| {
            let result = rand::thread_rng().gen_range(0, up) + 1;
            msg.reply(&format!("{}", result)).unwrap();
        })
    }
}
<commit_msg>Shorten some arg names.<commit_after>extern crate dotenv;
extern crate rand;
extern crate serenity;

use std::env;
use std::sync::Arc;

use dotenv::dotenv;
use rand::Rng;
use serenity::framework::standard::*;
use serenity::framework::StandardFramework;
use serenity::model::prelude::*;
use serenity::prelude::*;

struct Handler;
impl EventHandler for Handler {}

fn main() {
    let _ = dotenv();

    let creds = env::var("Z_CREDENTIALS")
        .expect("Unspecified credentials: set Z_CREDENTIALS in the environment (or in `.env`).");
    let mut client = Client::new(&creds, Handler).unwrap();
    client.with_framework(
        StandardFramework::new()
            .configure(|c| c.depth(1).prefix("!"))
            .group("Rng", |g| g.cmd("flip", Flip).cmd("roll", Roll))
            .on_dispatch_error(dispatch_error_handler)
            .unrecognised_command(unrecognised_command_handler)
            .customised_help(help_commands::plain, |h| {
                h.striked_commands_tip(None)
                    .dm_and_guilds_text("Everywhere")
                    .guild_only_text("Only in text channels")
            }),
    );
    client.start().unwrap();
}

fn dispatch_error_handler(_: Context, msg: Message, err: DispatchError) {
    msg.reply(&format!("{:?}", err)).unwrap();
}

fn unrecognised_command_handler(_: &mut Context, msg: &Message, cmd: &str) {
    msg.reply(&format!("{} is not a command.", cmd)).unwrap();
}

struct Flip;
impl Command for Flip {
    fn options(&self) -> Arc<CommandOptions> {
        let mut options = CommandOptions::default();
        options.desc = Some("Flips a coin.".to_owned());
        options.min_args = Some(0);
        options.max_args = Some(0);
        Arc::new(options)
    }

    fn execute(&self, _: &mut Context, msg: &Message, _: Args) -> Result<(), CommandError> {
        let result = if rand::thread_rng().gen() {
            "Heads!"
        } else {
            "Tails!"
        };

        msg.reply(result).unwrap();
        Ok(())
    }
}

struct Roll;
impl Command for Roll {
    fn options(&self) -> Arc<CommandOptions> {
        let mut options = CommandOptions::default();
        options.desc = Some("Rolls a die.".to_owned());
        options.min_args = Some(0);
        options.max_args = Some(1);
        Arc::new(options)
    }

    fn execute(&self, _: &mut Context, msg: &Message, mut args: Args) -> Result<(), CommandError> {
        let upper_bound = match args.single() {
            Ok(num) => Ok(num),
            Err(ArgError::Eos) => Ok(6),
            Err(ArgError::Parse(err)) => Err(CommandError(format!("{}", err))),
        };

        upper_bound.map(|up| {
            let result = rand::thread_rng().gen_range(0, up) + 1;
            msg.reply(&format!("{}", result)).unwrap();
        })
    }
}
<|endoftext|>


<commit_before>
// YAML files as iptables configuration sources

// Rust Core
use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::process::exit;

// Crate.io
extern crate clap;
extern crate yaml_rust;

use clap::{Arg, App};
use yaml_rust::{Yaml, YamlLoader};

fn main() {

    let app = App::new("iptables-init")
        .version("1.0.0")
        .about("\nYAML files as iptables configuration sources")
        .arg(Arg::with_name("CONFIG")
             .multiple(true)
             .short("c")
             .long("config")
             .help("yaml file as iptables configuration source")
             .conflicts_with("license")
             .takes_value(true))
        .args_from_usage("-l --license 'Prints License'");

    let matches = app.get_matches();

    if let Some(ref f_paths) = matches.values_of("CONFIG") {
        for f_path in f_paths.iter() {
            read_yaml(f_path);
        }
    }

    if matches.is_present("license") {
        print_license();
    }

}

fn print_license() {
    let s:String = "
The MIT License (MIT)

Copyright (c) 2015 Samuel <sam@infinitely.io>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the \"Software\"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
".to_string();
    println!("{}", s);
}

fn read_yaml(f_path: &str) {

    // Create a path for yaml configuration file
    let path = Path::new(&f_path);
    let display = path.display();

    // Open file
    let mut file = match File::open(&path) {
        Err(_)   => {
            println!("Failed to open file \"{}\"", display);
            exit(1);
        },
        Ok(file) => file,
    };

    // Create buffer string and read content
    let mut buffer_str = String::new();
    match file.read_to_string(&mut buffer_str) {
        Err(_)  => {
            println!("Failed to read file \"{}\"", display);
            exit(1);
        },
        Ok(str) => str,
    };

    // Parse yaml file
    let yaml = match YamlLoader::load_from_str(&buffer_str) {
        Err(_)  => {
            println!("Failed to parse yaml data: \"{}\"", display);
            exit(1);
        },
        Ok(yaml) => yaml,
    };

    // Check if yaml file has no data, exits program if no data
    if yaml.is_empty() {
        println!("\"{}\" has no configuration(s)", display);
        exit(1);
    };

    // Validate configuration template format
    match yaml[0].as_hash() {
        Some(doc) => doc,
        None => {
            println!("Configuration template format is invalid: {}", display);
            exit(1);
        }
    };

    // Parse configuration from YAML configuration file
    parse_yaml(&yaml[0]);

}

fn parse_yaml(doc: &Yaml) {
    // Reset all rules
    reset_rules();
    // Read all rules from yaml
    match doc {
        // Parse if template data is a hash object
        &Yaml::Hash(ref h) => {
            for (k, v) in h {
                match k.as_str().unwrap() {
                    // Parse `filter` rules
                    "filter" | "FILTER" => parse_filter(k, v),
                    // Parse custom section
                    _ => parse_section(k, v)
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}

fn reset_rules() {
    let mut s:String = "# reset all rules".to_string();
    s.push_str("\niptables -F");
    s.push_str("\niptables -t nat -F");
    s.push_str("\niptables -t mangle -F");
    s.push_str("\niptables -X");
    println!("{}", s);
}

fn parse_section(id: &Yaml, doc: &Yaml) {
    match doc {
        &Yaml::Hash(ref h) => {
            println!("# {} rules", id.as_str().unwrap());
            for (k, v) in h {
                let k = k.as_str().unwrap();
                println!("{:?} - {:?}", k, v);
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}

fn parse_filter(id: &Yaml, doc: &Yaml) {
    // Check if filter section exists
    match doc {
        &Yaml::Hash(ref h) => {
            println!("# {} rules", id.as_str().unwrap());
            for (k, v) in h {
                let k = k.as_str().unwrap();
                let v = v.as_str().unwrap();
                match k {
                    "input" | "output" | "forward" | "INPUT" | "OUTPUT" | "FORWARD" => {
                        match v {
                            "drop" | "reject" | "accept" | "DROP" | "REJECT" | "ACCEPT" => println!("iptables -P {} {}", k, v),
                            _ => {
                                println!("Rules \"{}\" only accept options of \"drop\",\"reject\" or \"accept\"", k);
                                exit(1);
                            }
                        }
                    },
                    _ => println!("iptables -P {}", k)
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}
<commit_msg>Changed app name, improved yaml parser<commit_after>
// YAML files as iptables configuration sources

// Rust Core
use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::process::exit;

// Crate.io
extern crate clap;
extern crate yaml_rust;

use clap::{Arg, App};
use yaml_rust::{Yaml, YamlLoader};

fn main() {

    let app = App::new("iptables-compose")
        .version("1.0.0")
        .global_version(true)
        .unified_help_message(true)
        .arg_required_else_help(true)
        .about("\nYAML files as iptables configuration sources")
        .arg(Arg::with_name("CONFIG")
             .multiple(true)
             .short("c")
             .long("config")
             .help("yaml file as iptables configuration source\n")
             .conflicts_with("license")
             .takes_value(true))
        .args_from_usage("-l --license 'Prints License'");

    let matches = app.get_matches();

    if let Some(ref f_paths) = matches.values_of("CONFIG") {
        for f_path in f_paths.iter() {
            read_yaml(f_path);
        }
    }

    if matches.is_present("license") {
        print_license();
    }

}

fn print_license() {
    let s:String = "
The MIT License (MIT)

Copyright (c) 2015 Samuel <sam@infinitely.io>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the \"Software\"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
".to_string();
    println!("{}", s);
}

fn read_yaml(f_path: &str) {

    // Create a path for yaml configuration file
    let path = Path::new(&f_path);
    let display = path.display();

    // Open file
    let mut file = match File::open(&path) {
        Err(_)   => {
            println!("Failed to open file \"{}\"", display);
            exit(1);
        },
        Ok(file) => file,
    };

    // Create buffer string and read content
    let mut buffer_str = String::new();
    match file.read_to_string(&mut buffer_str) {
        Err(_)  => {
            println!("Failed to read file \"{}\"", display);
            exit(1);
        },
        Ok(str) => str,
    };

    // Parse yaml file
    let yaml = match YamlLoader::load_from_str(&buffer_str) {
        Err(_)  => {
            println!("Failed to parse yaml data: \"{}\"", display);
            exit(1);
        },
        Ok(yaml) => yaml,
    };

    // Check if yaml file has no data, exits program if no data
    if yaml.is_empty() {
        println!("\"{}\" has no configuration(s)", display);
        exit(1);
    };

    // Validate configuration template format
    match yaml[0].as_hash() {
        Some(doc) => doc,
        None => {
            println!("Configuration template format is invalid: {}", display);
            exit(1);
        }
    };

    // Parse configuration from YAML configuration file
    parse_yaml(&yaml[0]);

}

fn parse_yaml(doc: &Yaml) {
    // Reset all rules
    reset_rules();
    // Read all rules from yaml
    match doc {
        // Parse if template data is a hash object
        &Yaml::Hash(ref h) => {
            for (k, v) in h {
                match k.as_str().unwrap() {
                    // Parse `filter` rules
                    "filter" | "FILTER" => parse_filter(k, v),
                    // Parse custom section
                    _ => parse_section(k, v)
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}

fn reset_rules() {
    let mut s:String = "# reset all rules".to_string();
    s.push_str("\niptables -F");
    s.push_str("\niptables -t nat -F");
    s.push_str("\niptables -t mangle -F");
    s.push_str("\niptables -X");
    println!("{}", s);
}

fn parse_section(id: &Yaml, doc: &Yaml) {
    match doc {
        &Yaml::Hash(ref h) => {
            println!("# {} rules", id.as_str().unwrap());
            for (k, v) in h {
                let k = k.as_str().unwrap();
                match k {
                    "ports" | "PORTS" => parse_ports(v),
                    _ => {
                        println!("Rules \"{}\" is not available", k);
                        exit(1);
                    }
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}

fn parse_ports(doc: &Yaml) {
    match doc {
        &Yaml::Array(ref v) => {
            for x in v {
                println!("{:?}", x);
            }
        },
        _ => {
            println!("Configuration \"ports\" format is invalid");
            exit(1);
        }
    }
}

fn parse_filter(id: &Yaml, doc: &Yaml) {
    // Check if filter section exists
    match doc {
        &Yaml::Hash(ref h) => {
            println!("# {} rules", id.as_str().unwrap());
            for (k, v) in h {
                let k = k.as_str().unwrap();
                let v = v.as_str().unwrap();
                match k {
                    "input" | "output" | "forward" | "INPUT" | "OUTPUT" | "FORWARD" => {
                        match v {
                            "drop" | "reject" | "accept" | "DROP" | "REJECT" | "ACCEPT" => println!("iptables -P {} {}", k, v),
                            _ => {
                                println!("Rules \"{}\" only accept options of \"drop\",\"reject\" or \"accept\"", k);
                                exit(1);
                            }
                        }
                    },
                    _ => println!("iptables -P {}", k)
                }
            }
        },
        _ => {
            println!("Configuration template format is invalid");
            exit(1);
        }
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate gradr_backend;

use hyper::Ipv4Addr;
use gradr_backend::notification_listener::GitHubServer;

#[cfg(not(test))]
fn main() {
    let server = GitHubServer::new(Ipv4Addr(0, 0, 0, 0), 1337);
    let running_server = server.event_loop().unwrap();
    loop {
        println!("{}", running_server.get_notification());
    }
}
<commit_msg>Added missing includes.  Now compiles, and testing reveals that it is connected properly.<commit_after>extern crate hyper;
extern crate gradr_backend;

use hyper::Ipv4Addr;
use gradr_backend::notification_listener::{GitHubServer, NotificationSource};

#[cfg(not(test))]
fn main() {
    let server = GitHubServer::new(Ipv4Addr(0, 0, 0, 0), 1337);
    let running_server = server.event_loop().unwrap();
    loop {
        println!("{}", running_server.get_notification());
    }
}
<|endoftext|>


<commit_before>extern crate iron;
#[macro_use]
extern crate router;
extern crate serde_json;
extern crate sbb_telegram_bot;
extern crate reqwest;
extern crate dotenv;
#[macro_use]
extern crate maplit;

use dotenv::dotenv;
use std::env;

use iron::prelude::*;
use iron::status;
use iron::{Iron, Request, Response, IronResult, AfterMiddleware, Chain};
use router::NoRoute;
use std::io::Read;
use sbb_telegram_bot::model::telegram;
struct Custom404;

impl AfterMiddleware for Custom404 {
    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<Response> {
        if let Some(_) = err.error.downcast::<NoRoute>() {
            Ok(Response::with((status::NotFound, format!("Invalid request: {}", req.url))))
        } else {
            Err(err)
        }
    }
}

fn main() {
    dotenv().ok();
    let router = router!(telegram: post "/sbb/telegram" => telegram);
    let mut chain = Chain::new(router);
    chain.link_after(Custom404);
    Iron::new(chain).http("localhost:3001").unwrap();

    fn telegram(req: &mut Request) -> IronResult<Response> {
        let mut body = String::new();
        req.body
            .read_to_string(&mut body)
            .map_err(|e| IronError::new(e, (status::BadRequest, "Error reading request")))?;
        let update: telegram::Update = serde_json::from_str(&body).unwrap();
        if let Some(msg) = update.message {
            if let Some(txt) = msg.text {
                if txt == "/start" {
                    let url = format!("{}{}{}",
                                      "https://api.telegram.org/bot",
                                      bot_token(),
                                      "/sendMessage");
                    let params = hashmap![
                        "chat_id" => format!("{}", msg.chat.id),
                        "text" => "If the bot was already working, \
                                    you would now have registered yourself. \
                                    Alas, as this is only a placeholder text, \
                                    nothing happened".to_owned(),
                    ];
                    let client = reqwest::Client::new().map_err(|e| {
                                     IronError::new(e,
                                                    (status::BadRequest, "Error reading request"))
                                 })?;
                    client.post(&url)
                        .json(&params)
                        .send()
                        .unwrap();
                }
            }
        }
        Ok(Response::with((status::Ok, "ok")))
    }
}

fn bot_token() -> String {
    env::var_os("TELEGRAM_BOT_TOKEN")
        .expect("TELEGRAM_BOT_TOKEN must be specified. \
                Did you forget to add it to your .env file?")
        .into_string()
        .expect("TELEGRAM_BOT_TOKEN does not contain a valid UTF8 string")
}
<commit_msg>Remove custom 404 response<commit_after>extern crate iron;
#[macro_use]
extern crate router;
extern crate serde_json;
extern crate sbb_telegram_bot;
extern crate reqwest;
extern crate dotenv;
#[macro_use]
extern crate maplit;

use dotenv::dotenv;
use std::env;

use iron::prelude::*;
use iron::status;
use iron::{Iron, Request, Response, IronResult, AfterMiddleware, Chain};
use router::NoRoute;
use std::io::Read;
use sbb_telegram_bot::model::telegram;
struct Custom404;

fn main() {
    dotenv().ok();
    let router = router!(telegram: post "/sbb/telegram" => telegram);
    Iron::new(router).http("localhost:3001").unwrap();

    fn telegram(req: &mut Request) -> IronResult<Response> {
        let mut body = String::new();
        req.body
            .read_to_string(&mut body)
            .map_err(|e| IronError::new(e, (status::BadRequest, "Error reading request")))?;
        let update: telegram::Update = serde_json::from_str(&body).unwrap();
        if let Some(msg) = update.message {
            if let Some(txt) = msg.text {
                if txt == "/start" {
                    let url = format!("{}{}{}",
                                      "https://api.telegram.org/bot",
                                      bot_token(),
                                      "/sendMessage");
                    let params = hashmap![
                        "chat_id" => format!("{}", msg.chat.id),
                        "text" => "If the bot was already working, \
                                    you would now have registered yourself. \
                                    Alas, as this is only a placeholder text, \
                                    nothing happened".to_owned(),
                    ];
                    let client = reqwest::Client::new().map_err(|e| {
                                     IronError::new(e,
                                                    (status::InternalServerError, "Error setting up HTTP client"))
                                 })?;
                    client.post(&url)
                        .json(&params)
                        .send()
                        .unwrap();
                }
            }
        }
        Ok(Response::with((status::Ok, "ok")))
    }
}

fn bot_token() -> String {
    env::var_os("TELEGRAM_BOT_TOKEN")
        .expect("TELEGRAM_BOT_TOKEN must be specified. \
                Did you forget to add it to your .env file?")
        .into_string()
        .expect("TELEGRAM_BOT_TOKEN does not contain a valid UTF8 string")
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
use std::collections::HashMap;
use std::env;

include!("./sentiment.rs");

// Gets a normalized nick from a log nick.
// - Removes beginning '+' and '@'
// - Removes trailing '_' and '-'
fn get_nick(s: &str) -> &str {
    let ignored_prefixes: &[_] = &['+', '@', ' '];
    let ignored_suffixes: &[_] = &['_', '-', ' '];

    return &s.trim_left_matches(ignored_prefixes)
        .trim_right_matches(ignored_suffixes);
}

fn main() {
    // Parse arguments.
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
       panic!("Usage: {} [log file name]", args[0]);
    }
    let path = args[1].clone();

    // Read file.
    let f = match File::open(&path) {
        Err(why) => panic!("Couldn't open {}: {}", path, why),
        Ok(f) => f,
    };
    let f = BufReader::new(f);

    let analizer = sentiment::new("./src/wordlist.txt");

    // Fold all lines to a hashmap containing username -> score.
    let result = f.lines().fold(HashMap::new(), |mut res, line| {
        let line = line.expect("Couldn't parse line");

        let parts = line.split("\t").collect::<Vec<&str>>();
        let who = get_nick(parts[1]).to_string();
        let comment_value = analizer.analyze(parts[2]);

        if ["*", "**", "***", "--", "---", "-->", "<--", "-", "", "<-", "=!=", "<"]
            .contains(&who.as_str()) {
                return res;
            }

        let val = match res.get(&who) {
            Some(existing_comment_value) => existing_comment_value + comment_value,
            None => comment_value,
        };

        res.insert(who, val);
        return res;
    });

    for (key, value) in result {
        print!("{} {}\n", key, value);
    }
}
<commit_msg>Skip counting 0 sentiment value comments<commit_after>use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
use std::collections::HashMap;
use std::env;

include!("./sentiment.rs");

// Gets a normalized nick from a log nick.
// - Removes beginning '+' and '@'
// - Removes trailing '_' and '-'
fn get_nick(s: &str) -> &str {
    let ignored_prefixes: &[_] = &['+', '@', ' '];
    let ignored_suffixes: &[_] = &['_', '-', ' '];

    return &s.trim_left_matches(ignored_prefixes)
        .trim_right_matches(ignored_suffixes);
}

fn main() {
    // Parse arguments.
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
       panic!("Usage: {} [log file name]", args[0]);
    }
    let path = args[1].clone();

    // Read file.
    let f = match File::open(&path) {
        Err(why) => panic!("Couldn't open {}: {}", path, why),
        Ok(f) => f,
    };
    let f = BufReader::new(f);

    let analizer = sentiment::new("./src/wordlist.txt");

    // Fold all lines to a hashmap containing username -> score.
    let result = f.lines().fold(HashMap::new(), |mut res, line| {
        let line = line.expect("Couldn't parse line");

        let parts = line.split("\t").collect::<Vec<&str>>();
        let who = get_nick(parts[1]).to_string();
        let comment_value = analizer.analyze(parts[2]);

        if comment_value == 0.0 || ["*", "**", "***", "--", "---", "-->", "<--", "-", "", "<-", "=!=", "<"]
            .contains(&who.as_str()) {
                return res;
            }

        let val = match res.get(&who) {
            Some(existing_comment_value) => existing_comment_value + comment_value,
            None => comment_value,
        };

        res.insert(who, val);
        return res;
    });

    for (key, value) in result {
        print!("{} {}\n", key, value);
    }
}
<|endoftext|>


<commit_before>trait Normalize {
  fn normalize(&self) -> Self;
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Const {
  Data,
  Codata,
}

impl Normalize for Const {
  fn normalize(&self) -> Const {
    *self
  }
}

impl ToString for Const {
  fn to_string(&self) -> String {
    match *self {
      Const::Data => "data".to_string(),
      Const::Codata => "codata".to_string(),
    }
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
struct Var {
  name: String,
  idx: i32,
}

impl Var {
  pub fn new(name: &str, idx: i32) -> Var {
    Var {
      name: name.to_string(),
      idx: idx,
    }
  }
}

impl Normalize for Var {
  fn normalize(&self) -> Var {
    self.clone()
  }
}

impl ToString for Var {
  fn to_string(&self) -> String {
    if self.idx == 0 {
      self.name.clone()
    } else {
      self.name.clone() + "@" + &self.idx.to_string()
    }
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
enum Expr {
  // Type system constants
  Const(Const),
  // Bound variables
  Var(Var),
  // Lambda
  Lam(Var, Box<Expr>, Box<Expr>),
  // "forall"
  Pi(Var, Box<Expr>, Box<Expr>),
  // Function application
  App(Box<Expr>, Box<Expr>),
}

impl Expr {
  pub fn constant(constant: Const) -> Expr {
    Expr::Const(constant)
  }
  pub fn var(v: &Var) -> Expr {
    Expr::Var(v.clone())
  }
  pub fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Lam(var, Box::new(ty), Box::new(body))
  }
  pub fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Pi(var, Box::new(ty), Box::new(body))
  }
  pub fn app(f: Expr, arg: Expr) -> Expr {
    Expr::App(Box::new(f), Box::new(arg))
  }

  pub fn is_constant(&self) -> bool {
    match *self {
      Expr::Const(_) => true,
      _ => false,
    }
  }
  pub fn is_var(&self) -> bool {
    match *self {
      Expr::Var(_) => true,
      _ => false,
    }
  }
  pub fn is_lam(&self) -> bool {
    match *self {
      Expr::Lam(_, _, _) => true,
      _ => false,
    }
  }
  pub fn is_pi(&self) -> bool {
    match *self {
      Expr::Pi(_, _, _) => true,
      _ => false,
    }
  }
  pub fn is_app(&self) -> bool {
    match *self {
      Expr::App(_, _) => true,
      _ => false,
    }
  }
}

fn constant(c: Const) -> Expr {
  Expr::constant(c)
}
fn var(v: &Var) -> Expr {
  Expr::var(v)
}
fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
  Expr::lam(var, ty, body)
}
fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
  Expr::pi(var, ty, body)
}
fn app(f: Expr, arg: Expr) -> Expr {
  Expr::app(f, arg)
}

fn replace(val: &Var, with: &Expr, body: &Box<Expr>) -> Expr {
  use Expr::*;
  println!("replacing {} with {} in {}",
           val.to_string(),
           with.to_string(),
           body.to_string());
  // *body.clone()
  match **body {
    Const(ref constant) => *body.clone(), //Const(constant),
    Var(ref var) => {
      println!("replacing var");
      if var.to_string() == val.to_string() {
        println!("  eq -> replacing");
        with.clone()
      } else {
        println!("  no change");
        Var(var.clone())
      }
    }
    Lam(ref var, ref ty, ref body) => {
      // println!("  lam");
      let ty = Box::new(replace(val, &with.clone(), &ty));
      // let body = Box::new(replace(val, with, &body));
      Lam(var.clone(), ty.clone(), body.clone())
      // panic!("Lam")
      // Lam(var.clone(),
      //     Box::new(replace(val.clone(), with.clone(), ty.clone())),
      //     Box::new(replace(val.clone(), with.clone(), body.clone())))
    }
    Pi(ref var, ref ty, ref body) => panic!("Pi"),
    App(ref f, ref arg) => panic!("App"), //replace(val, with, f.clone()),
  }
}

impl Normalize for Expr {
  fn normalize(&self) -> Expr {
    use Expr::*;
    println!("normalize {}", self.to_string());
    match *self {
      Const(ref constant) => Const(constant.normalize()),
      Var(ref var) => Var(var.normalize()),
      Lam(ref var, ref ty, ref body) => {
        let l = &Lam(var.normalize(),
                     Box::new(ty.normalize()),
                     Box::new(body.normalize()));
        l.clone()
      }
      Pi(ref var, ref ty, ref body) => {
        let p = &Pi(var.normalize(),
                    Box::new(ty.normalize()),
                    Box::new(body.normalize()));
        p.clone()
      }
      App(ref f, ref arg) => {
        let f = f.normalize();
        let arg = arg.normalize();
        if let Lam(var, ty, body) = f {
          replace(&var, &arg, &body)
          // Lam(var, ty, body)
        } else if let Pi(var, ty, body) = f {
          replace(&var, &arg, &body)
        } else {
          panic!("f isn't a function {}", f.to_string())
        }
      }
    }
  }
}

impl ToString for Expr {
  fn to_string(&self) -> String {
    use Expr::*;
    match *self {
      Const(ref constant) => constant.to_string(),
      Var(ref var) => var.to_string(),
      Lam(ref var, ref ty, ref body) => {
        "(".to_string() + &var.to_string() + " : " + &ty.to_string() + ") -> " +
        &body.to_string()
      }
      Pi(ref var, ref ty, ref body) => {
        "forall (".to_string() + &var.to_string() + " : " + &ty.to_string() +
        ") -> " + &body.to_string()
      }
      App(ref f, ref arg) => {
        if f.is_lam() || f.is_pi() {
          "(".to_string() + &f.to_string() + ") " + &arg.to_string()
        } else {
          f.to_string() + " " + &arg.to_string()
        }
      }
    }
  }
}

fn main() {
  println!("{}", Const::Data.to_string());
  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let expra = var(&a);
  let exprx = var(&x);
  println!("{}", x.to_string());
  let id = pi(a.clone(), constant(Const::Data), lam(x, expra, exprx));
  println!("{}", id.to_string());
  let apply_int = app(id.clone(), var(&Var::new("int", 0)));
  println!("{}", apply_int.normalize().to_string());
  let apply_id = app(app(id, var(&Var::new("int", 0))), var(&Var::new("1", 0)));
  println!("{}", apply_id.to_string());
  println!("{}", apply_id.normalize().to_string());
}

#[test]
fn test_to_string() {
  let codata = Const::Codata;
  println!("{}", codata.to_string());
  assert_eq!("codata", codata.to_string());
  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let expra = var(&a);
  let exprx = var(&x);
  println!("{}", x.to_string());
  assert_eq!("x", x.to_string());
  let id = pi(a.clone(), constant(Const::Data), lam(x, expra, exprx));
  println!("{}", id.to_string());
  assert_eq!("forall (a : data) -> (x : a) -> x", id.to_string());
  let apply_id = app(app(id, var(&Var::new("int", 0))), var(&Var::new("1", 0)));
  println!("{}", apply_id.to_string());
  assert_eq!("(forall (a : data) -> (x : a) -> x) int 1",
             apply_id.to_string());
}
<commit_msg>Get normalize and replace working.<commit_after>trait Normalize {
  fn normalize(&self) -> Self;
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Const {
  Data,
  Codata,
}

impl Normalize for Const {
  fn normalize(&self) -> Const {
    *self
  }
}

impl ToString for Const {
  fn to_string(&self) -> String {
    match *self {
      Const::Data => "data".to_string(),
      Const::Codata => "codata".to_string(),
    }
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
struct Var {
  name: String,
  idx: i32,
}

impl Var {
  pub fn new(name: &str, idx: i32) -> Var {
    Var {
      name: name.to_string(),
      idx: idx,
    }
  }
}

impl Normalize for Var {
  fn normalize(&self) -> Var {
    self.clone()
  }
}

impl ToString for Var {
  fn to_string(&self) -> String {
    if self.idx == 0 {
      self.name.clone()
    } else {
      self.name.clone() + "@" + &self.idx.to_string()
    }
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
enum Expr {
  // Type system constants
  Const(Const),
  // Bound variables
  Var(Var),
  // Lambda
  Lam(Var, Box<Expr>, Box<Expr>),
  // "forall"
  Pi(Var, Box<Expr>, Box<Expr>),
  // Function application
  App(Box<Expr>, Box<Expr>),
}

impl Expr {
  pub fn constant(constant: Const) -> Expr {
    Expr::Const(constant)
  }
  pub fn var(v: &Var) -> Expr {
    Expr::Var(v.clone())
  }
  pub fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Lam(var, Box::new(ty), Box::new(body))
  }
  pub fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Pi(var, Box::new(ty), Box::new(body))
  }
  pub fn app(f: Expr, arg: Expr) -> Expr {
    Expr::App(Box::new(f), Box::new(arg))
  }

  pub fn is_constant(&self) -> bool {
    match *self {
      Expr::Const(_) => true,
      _ => false,
    }
  }
  pub fn is_var(&self) -> bool {
    match *self {
      Expr::Var(_) => true,
      _ => false,
    }
  }
  pub fn is_lam(&self) -> bool {
    match *self {
      Expr::Lam(_, _, _) => true,
      _ => false,
    }
  }
  pub fn is_pi(&self) -> bool {
    match *self {
      Expr::Pi(_, _, _) => true,
      _ => false,
    }
  }
  pub fn is_app(&self) -> bool {
    match *self {
      Expr::App(_, _) => true,
      _ => false,
    }
  }
}

fn constant(c: Const) -> Expr {
  Expr::constant(c)
}
fn var(v: &Var) -> Expr {
  Expr::var(v)
}
fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
  Expr::lam(var, ty, body)
}
fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
  Expr::pi(var, ty, body)
}
fn app(f: Expr, arg: Expr) -> Expr {
  Expr::app(f, arg)
}

fn replace(val: &Var, with: &Expr, body: &Expr) -> Expr {
  use Expr::*;
  match *body {
    Const(ref constant) => body.clone(),
    Var(ref var) => {
      if *var == *val {
        with.clone()
      } else {
        body.clone()
      }
    }
    Lam(ref var, ref ty, ref body) => {
      // TODO: Check var == val
      let ty = Box::new(replace(val, with, &ty));
      let body = Box::new(replace(val, with, &body));
      Lam(var.clone(), ty, body)
    }
    Pi(ref var, ref ty, ref body) => {
      // TODO: Check var == val
      let ty = Box::new(replace(val, with, &ty));
      let body = Box::new(replace(val, with, &body));
      Pi(var.clone(), ty, body)
    }
    App(ref f, ref arg) => {
      let f = Box::new(replace(val, with, &f));
      let arg = Box::new(replace(val, with, &arg));
      App(f, arg)
    }
  }
}

impl Normalize for Expr {
  fn normalize(&self) -> Expr {
    use Expr::*;
    // println!("normalize {}", self.to_string());
    match *self {
      Const(ref constant) => Const(constant.normalize()),
      Var(ref var) => Var(var.normalize()),
      Lam(ref var, ref ty, ref body) => {
        let l = &Lam(var.normalize(),
                     Box::new(ty.normalize()),
                     Box::new(body.normalize()));
        l.clone()
      }
      Pi(ref var, ref ty, ref body) => {
        let p = &Pi(var.normalize(),
                    Box::new(ty.normalize()),
                    Box::new(body.normalize()));
        p.clone()
      }
      App(ref f, ref arg) => {
        let f = f.normalize();
        let arg = arg.normalize();
        if let Lam(var, ty, body) = f {
          replace(&var, &arg, &body)
        } else if let Pi(var, ty, body) = f {
          replace(&var, &arg, &body)
        } else {
          panic!("f isn't a function {}", f.to_string())
        }
      }
    }
  }
}

impl ToString for Expr {
  fn to_string(&self) -> String {
    use Expr::*;
    match *self {
      Const(ref constant) => constant.to_string(),
      Var(ref var) => var.to_string(),
      Lam(ref var, ref ty, ref body) => {
        "(".to_string() + &var.to_string() + " : " + &ty.to_string() + ") -> " +
        &body.to_string()
      }
      Pi(ref var, ref ty, ref body) => {
        "forall (".to_string() + &var.to_string() + " : " + &ty.to_string() +
        ") -> " + &body.to_string()
      }
      App(ref f, ref arg) => {
        if f.is_lam() || f.is_pi() {
          "(".to_string() + &f.to_string() + ") " + &arg.to_string()
        } else {
          f.to_string() + " " + &arg.to_string()
        }
      }
    }
  }
}

fn main() {
  println!("{}", Const::Data.to_string());
  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let expra = var(&a);
  let exprx = var(&x);
  println!("{}", x.to_string());
  let id = pi(a.clone(), constant(Const::Data), lam(x, expra, exprx));
  println!("{}", id.to_string());
  println!("{}", id.normalize().to_string());
  let apply_int = app(id.clone(), var(&Var::new("int", 0)));
  println!("{}", apply_int.to_string());
  println!("{}", apply_int.normalize().to_string());
  let apply_id = app(app(id, var(&Var::new("int", 0))), var(&Var::new("1", 0)));
  println!("{}", apply_id.to_string());
  println!("{}", apply_id.normalize().to_string());
}

#[test]
fn test_to_string() {
  let codata = Const::Codata;
  println!("{}", codata.to_string());
  assert_eq!("codata", codata.to_string());
  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let expra = var(&a);
  let exprx = var(&x);
  println!("{}", x.to_string());
  assert_eq!("x", x.to_string());
  let id = pi(a.clone(), constant(Const::Data), lam(x, expra, exprx));
  println!("{}", id.to_string());
  assert_eq!("forall (a : data) -> (x : a) -> x", id.to_string());
  let apply_id = app(app(id, var(&Var::new("int", 0))), var(&Var::new("1", 0)));
  println!("{}", apply_id.to_string());
  assert_eq!("(forall (a : data) -> (x : a) -> x) int 1",
             apply_id.to_string());
}
<|endoftext|>


<commit_before>
#[macro_use]
extern crate clap;
use clap::{Arg, App};

use std::io::{self, Read};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::error::Error;
use std::env;

use std::collections::HashSet;

extern crate petgraph;

extern crate walkdir;
use walkdir::WalkDir;

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

mod path_utils;

mod dot_writer;

mod file_node;
use file_node::FileNode;

mod hash_graph;
use hash_graph::HashGraph;

extern crate itertools;
use itertools::Itertools;

// ----------------------------------------------------------------------------

// Convert a relative include path (e.g. <Windows.h>) into an absolute path.
fn find_absolute_include_path(include: &FileNode,
                              parent_file: &Path,
                              system_search_paths: &[PathBuf])
                              -> FileNode {

    let local_dir = parent_file.parent().unwrap(); // strip the file name

    let normalized_path = path_utils::normalize_path_separators(&include.path);

    match path_utils::convert_to_absolute_path(&normalized_path, local_dir, system_search_paths) {
        None => {

            println!("Unable to locate {:?}", &include.path);
            println!("Included from file {:?}\n", parent_file);
            include.clone()
        }
        Some(path_buf) => FileNode::from_path(&path_buf, include.is_system),
    }
}

// -----------------------------------------------------------------------------

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<FileNode>, io::Error> {
    let mut f = File::open(file)?;
    let mut text = String::new();
    f.read_to_string(&mut text)?;

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The second (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        // Notes:
        // (?m:^[[:blank:]]*) => empty space at line start, multi-line mode, non-capturing group.
        static ref RE: Regex =
        Regex::new(r##"(?m:^[[:blank:]]*)#[[:blank:]]*include[[:blank:]]*([<"])(.*)[>"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    if let Some(cap) = RE.captures(&text) {
        let is_system_include = cap.get(1).map_or(false, |sym| sym.as_str() == "<");

        if let Some(include_name) = cap.get(2) {
            includes.push(FileNode::new(include_name.as_str(), is_system_include));
        }
    }

    // println!("Found {} includes in {}", includes.len(), &file.display());

    Ok(includes)
}

// -----------------------------------------------------------------------------

// TODO: implement the command line arguments from the original 'cinclude2dot' project.
// Output from "cinclude2dot.pl --help":
// --debug       Display various debug info
// --exclude     Specify a regular expression of filenames to ignore
//              For example, ignore your test harnesses.
// --merge       Granularity of the diagram:
//              file - the default, treats each file as separate
//              module - merges .c/.cc/.cpp/.cxx and .h/.hpp/.hxx pairs
//              directory - merges directories into one node
// --groups      Cluster files or modules into directory groups
// --help        Display this help page.
// --include     Followed by a comma separated list of include search paths.
// --paths       Leaves relative paths in displayed filenames.
// --quotetypes  Select for parsing the files included by strip quotes or angle brackets:
//              both - the default, parse all headers.
//              angle - include only "system" headers included by anglebrackets (<>)
//              quote - include only "user" headers included by strip quotes ("")
// --src         Followed by a path to the source code, defaults to current directory

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    enum MergeType {
        file,
        module,
        directory
    }
}

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    enum QuoteTypes {
        both,
        angle,
        quote
    }
}

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("debug")
//            .long("debug")
//            .help("Display extra debug info")
//            .takes_value(true))
        .arg(Arg::with_name("exclude")
            .long("exclude")
            .help("Specify a regular expression of filenames to ignore. \nRust/RE2 \
                   syntax.\n\tExample: --exclude=\"test_|noisyFile\"")
            .takes_value(true))
//        .arg(Arg::with_name("merge")
//            .long("merge")
//            .help("Granularity of the diagram: \nfile - the default, treats each file as \
//                   separate \nmodule - merges .c/.cc/.cpp/.cxx and .h/.hpp/.hxx pairs \
//                   \ndirectory - merges directories into one node\n")
//            .possible_values(&MergeType::variants())
//            .default_value("file")
//            .takes_value(true))
//        .arg(Arg::with_name("groups")
//            .long("groups")
//            .help("Cluster files or modules into directory groups")
//            .takes_value(true))
        .arg(Arg::with_name("include")
            .long("include")
            .help("Space separated list of include search paths. (e.g. ./*/include)")
            .multiple(true)
            .takes_value(true))
//        .arg(Arg::with_name("paths")
//            .long("paths")
//            .help("Leaves relative paths in displayed filenames.")
//            .takes_value(true))
        .arg(Arg::with_name("quotetypes")
            .long("quotetypes")
            .help("Select which type of includes to parse:\nboth - parse all \
                   includes. \nangle - parse only \"system\" includes (<>) \nquote - parse only \
                   \"user\" includes (\"\")\n")
            .possible_values(&QuoteTypes::variants())
            .default_value("quote")
            .multiple(false)
            .takes_value(true))
        .arg(Arg::with_name("src")
            .long("src")
            .help("Path to the source code, defaults to current directory.")
            .multiple(false)
            .takes_value(true))
        .get_matches();

    let root_dir = match args.value_of("src") {
        Some(path) => PathBuf::from(path),
        None => env::current_dir().unwrap()
    };

    if !root_dir.exists() {
        panic!("Unable to access directory: {}", root_dir.display());
    }
    //println!("Scanning directory: {}", root_dir.display());

    // Collect a list of include paths to search.
    let mut search_paths = Vec::new();
    if let Some(values) = args.values_of("include") {
        for string in values {
            println!("Using include: {}", &string);
            search_paths.push(PathBuf::from(string));
        }
    }

    // Add a list of default system include paths.
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Collect the type of includes to scan (<> vs "")
    let quote_types = args.value_of("quotetypes").unwrap_or("both");
    let (parse_user_includes, parse_system_includes) = match quote_types {
        "angle" => (false, true),
        "quote" => (true, false),
        _ => (true, true), // both
    };

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsStr::new("c"));
    extensions.insert(OsStr::new("cc"));
    extensions.insert(OsStr::new("cpp"));
    extensions.insert(OsStr::new("cxx"));
    extensions.insert(OsStr::new("h"));
    extensions.insert(OsStr::new("hpp"));
    extensions.insert(OsStr::new("hxx"));

    // Regular expression of files to exclude. Skip if exclude string is empty.
    let exclude_regex = args.value_of("exclude")
        .and_then(|regex_str| {
            Regex::new(regex_str)
                .map_err(|err| panic!("Unable to parse exclude regex: {}", err.description()))
                .ok() // Converts successful result to Some(), discarding errors.
        });

    // Collect all the files to scan in a HashSet
    // Note: is_hidden() is currently hiding paths that start with './', so don't use it yet.
    let input_queue = WalkDir::new(root_dir).into_iter()
        //.filter_entry(|e| !path_utils::is_hidden(e))
        .filter_map(|entry| match entry {
            Err(what) => {println!("Error reading directory: {}", what.description()); None},
            Ok(val) => Some(val),
        })
        .map(|entry| PathBuf::from(entry.path()))
        .filter(|path| path.extension().map_or(false, |ext| extensions.contains(ext)))
        .filter(|path| !path_utils::filename_matches_regex(&exclude_regex, path))
        .collect::<HashSet<_>>();

    // Graph of all the tracked files
    let mut hash_graph = HashGraph::<FileNode>::new();

    for path_buf in input_queue {
        let parent_file = path_buf.as_path();
        let includes_result = scan_file_for_includes(parent_file);
        match includes_result {
            Ok(includes) => {

                // Convert relative includes to absolute includes
                includes.iter()
                    .filter(|inc| (!inc.is_system && parse_user_includes)
                                    || (inc.is_system && parse_system_includes))
                    .filter(|inc| !path_utils::name_matches_regex(&exclude_regex, &inc.path))
                    .map(|inc| find_absolute_include_path(inc, parent_file, &search_paths))
                    .foreach(|inc| {
                        // Add an edge to the graph
                        let src_node = FileNode::from_path(parent_file, false);
                        let dst_node = FileNode::from_path(&inc.path,inc.is_system);
                        hash_graph.add_edge(src_node, dst_node);
                    });
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", parent_file, err);
            }
        };
    }

    // Write the graph to a dot file.
    let _ = dot_writer::write_dot_with_header("./graph.dot", &hash_graph.graph);

    // Print summary stats
    println!("Generated graph with {} nodes and {} edges.",
             &hash_graph.graph.node_count(), &hash_graph.graph.edge_count());

    println!("Now run \"dot -Tpdf graph.dot > graph.pdf\" to render the graph.");
}
<commit_msg>Moved search loop to function for easier testing<commit_after>
#[macro_use]
extern crate clap;
use clap::{Arg, App};

use std::io::{self, Read};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::OsString;
use std::error::Error;
use std::env;

use std::collections::HashSet;

extern crate petgraph;

extern crate walkdir;
use walkdir::WalkDir;

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

mod path_utils;

mod dot_writer;

mod file_node;
use file_node::FileNode;

mod hash_graph;
use hash_graph::HashGraph;

extern crate itertools;
use itertools::Itertools;

// ----------------------------------------------------------------------------

// Convert a relative include path (e.g. <Windows.h>) into an absolute path.
fn find_absolute_include_path(include: &FileNode,
                              parent_file: &Path,
                              system_search_paths: &[PathBuf])
                              -> FileNode {

    let local_dir = parent_file.parent().unwrap(); // strip the file name

    let normalized_path = path_utils::normalize_path_separators(&include.path);

    match path_utils::convert_to_absolute_path(&normalized_path, local_dir, system_search_paths) {
        None => {

            println!("Unable to locate {:?}", &include.path);
            println!("Included from file {:?}\n", parent_file);
            include.clone()
        }
        Some(path_buf) => FileNode::from_path(&path_buf, include.is_system),
    }
}

// -----------------------------------------------------------------------------

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<FileNode>, io::Error> {
    let mut f = File::open(file)?;
    let mut text = String::new();
    f.read_to_string(&mut text)?;

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The second (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        // Notes:
        // (?m:^[[:blank:]]*) => empty space at line start, multi-line mode, non-capturing group.
        static ref RE: Regex =
        Regex::new(r##"(?m:^[[:blank:]]*)#[[:blank:]]*include[[:blank:]]*([<"])(.*)[>"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    if let Some(cap) = RE.captures(&text) {
        let is_system_include = cap.get(1).map_or(false, |sym| sym.as_str() == "<");

        if let Some(include_name) = cap.get(2) {
            includes.push(FileNode::new(include_name.as_str(), is_system_include));
        }
    }

    // println!("Found {} includes in {}", includes.len(), &file.display());

    Ok(includes)
}

// -----------------------------------------------------------------------------

// TODO: implement the command line arguments from the original 'cinclude2dot' project.
// Output from "cinclude2dot.pl --help":
// --debug       Display various debug info
// --exclude     Specify a regular expression of filenames to ignore
//              For example, ignore your test harnesses.
// --merge       Granularity of the diagram:
//              file - the default, treats each file as separate
//              module - merges .c/.cc/.cpp/.cxx and .h/.hpp/.hxx pairs
//              directory - merges directories into one node
// --groups      Cluster files or modules into directory groups
// --help        Display this help page.
// --include     Followed by a comma separated list of include search paths.
// --paths       Leaves relative paths in displayed filenames.
// --quotetypes  Select for parsing the files included by strip quotes or angle brackets:
//              both - the default, parse all headers.
//              angle - include only "system" headers included by anglebrackets (<>)
//              quote - include only "user" headers included by strip quotes ("")
// --src         Followed by a path to the source code, defaults to current directory

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    enum MergeType {
        file,
        module,
        directory
    }
}

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    enum QuoteTypes {
        both,
        angle,
        quote
    }
}

// Core include searching loop
fn find_includes(root_dir: &Path,
                 search_paths: &Vec<PathBuf>,
                 extensions: &HashSet<OsString>,
                 parse_user_includes: bool,
                 parse_system_includes: bool,
                 exclude_regex: &Option<Regex>) -> HashGraph<FileNode> {
    // Collect all the files to scan in a HashSet
    // Note: is_hidden() is currently hiding paths that start with './', so don't use it yet.
    let input_queue = WalkDir::new(root_dir).into_iter()
        //.filter_entry(|e| !path_utils::is_hidden(e))
        .filter_map(|entry| match entry {
            Err(what) => {println!("Error reading directory: {}", what.description()); None},
            Ok(val) => Some(val),
        })
        .map(|entry| PathBuf::from(entry.path()))
        .filter(|path| path.extension().map_or(false, |ext| extensions.contains(ext)))
        .filter(|path| !path_utils::filename_matches_regex(&exclude_regex, path))
        .collect::<HashSet<_>>();

    // Graph of all the tracked files
    let mut hash_graph = HashGraph::<FileNode>::new();

    for path_buf in input_queue {
        let parent_file = path_buf.as_path();
        let includes_result = scan_file_for_includes(parent_file);
        match includes_result {
            Ok(includes) => {

                // Convert relative includes to absolute includes
                includes.iter()
                    .filter(|inc| (!inc.is_system && parse_user_includes)
                        || (inc.is_system && parse_system_includes))
                    .filter(|inc| !path_utils::name_matches_regex(&exclude_regex, &inc.path))
                    .map(|inc| find_absolute_include_path(inc, parent_file, &search_paths))
                    .foreach(|inc| {
                        // Add an edge to the graph
                        let src_node = FileNode::from_path(parent_file, false);
                        let dst_node = FileNode::from_path(&inc.path,inc.is_system);
                        hash_graph.add_edge(src_node, dst_node);
                    });
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", parent_file, err);
            }
        }
    }

    hash_graph
}

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("debug")
//            .long("debug")
//            .help("Display extra debug info")
//            .takes_value(true))
        .arg(Arg::with_name("exclude")
            .long("exclude")
            .help("Specify a regular expression of filenames to ignore. \nRust/RE2 \
                   syntax.\n\tExample: --exclude=\"test_|noisyFile\"")
            .takes_value(true))
//        .arg(Arg::with_name("merge")
//            .long("merge")
//            .help("Granularity of the diagram: \nfile - the default, treats each file as \
//                   separate \nmodule - merges .c/.cc/.cpp/.cxx and .h/.hpp/.hxx pairs \
//                   \ndirectory - merges directories into one node\n")
//            .possible_values(&MergeType::variants())
//            .default_value("file")
//            .takes_value(true))
//        .arg(Arg::with_name("groups")
//            .long("groups")
//            .help("Cluster files or modules into directory groups")
//            .takes_value(true))
        .arg(Arg::with_name("include")
            .long("include")
            .help("Space separated list of include search paths. (e.g. ./*/include)")
            .multiple(true)
            .takes_value(true))
//        .arg(Arg::with_name("paths")
//            .long("paths")
//            .help("Leaves relative paths in displayed filenames.")
//            .takes_value(true))
        .arg(Arg::with_name("quotetypes")
            .long("quotetypes")
            .help("Select which type of includes to parse:\nboth - parse all \
                   includes. \nangle - parse only \"system\" includes (<>) \nquote - parse only \
                   \"user\" includes (\"\")\n")
            .possible_values(&QuoteTypes::variants())
            .default_value("quote")
            .multiple(false)
            .takes_value(true))
        .arg(Arg::with_name("src")
            .long("src")
            .help("Path to the source code, defaults to current directory.")
            .multiple(false)
            .takes_value(true))
        .get_matches();

    let root_dir = match args.value_of("src") {
        Some(path) => PathBuf::from(path),
        None => env::current_dir().unwrap()
    };

    if !root_dir.exists() {
        panic!("Unable to access directory: {}", root_dir.display());
    }
    //println!("Scanning directory: {}", root_dir.display());

    // Collect a list of include paths to search.
    let mut search_paths = Vec::new();
    if let Some(values) = args.values_of("include") {
        for string in values {
            println!("Using include: {}", &string);
            search_paths.push(PathBuf::from(string));
        }
    }

    // Add a list of default system include paths.
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Collect the type of includes to scan (<> vs "")
    let quote_types = args.value_of("quotetypes").unwrap_or("both");
    let (parse_user_includes, parse_system_includes) = match quote_types {
        "angle" => (false, true),
        "quote" => (true, false),
        _ => (true, true), // both
    };

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsString::from("c"));
    extensions.insert(OsString::from("cc"));
    extensions.insert(OsString::from("cpp"));
    extensions.insert(OsString::from("cxx"));
    extensions.insert(OsString::from("h"));
    extensions.insert(OsString::from("hpp"));
    extensions.insert(OsString::from("hxx"));

    // Regular expression of files to exclude. Skip if exclude string is empty.
    let exclude_regex = args.value_of("exclude")
        .and_then(|regex_str| {
            Regex::new(regex_str)
                .map_err(|err| panic!("Unable to parse exclude regex: {}", err.description()))
                .ok() // Converts successful result to Some(), discarding errors.
        });

    let hash_graph = find_includes(&root_dir,
                                   &search_paths,
                                   &extensions,
                                   parse_user_includes,
                                   parse_system_includes,
                                   &exclude_regex);

    // Write the graph to a dot file.
    let _ = dot_writer::write_dot_with_header("./graph.dot", &hash_graph.graph);

    // Print summary stats
    println!("Generated graph with {} nodes and {} edges.",
             &hash_graph.graph.node_count(), &hash_graph.graph.edge_count());

    println!("Now run \"dot -Tpdf graph.dot > graph.pdf\" to render the graph.");
}
<|endoftext|>


<commit_before>fn main() {
    let logfile_path = match std::os::homedir() {
        Some(p) => p.join(".eyl-shell.log"),
        None    => fail!("finding homedir")
    };
    let mut logfile = std::io::File::create(&logfile_path);
    match logfile.write(b"It works!\n") {
        Ok(()) => (),
        Err(_) => fail!("writing to logfile")
    }
}
<commit_msg>Take homedir by reference instead of copying<commit_after>fn main() {
    let logfile_path = match std::os::homedir() {
        Some(ref p) => p.join(".eyl-shell.log"),
        None        => fail!("finding homedir")
    };
    let mut logfile = std::io::File::create(&logfile_path);
    match logfile.write(b"It works!\n") {
        Ok(()) => (),
        Err(_) => fail!("writing to logfile")
    }
}
<|endoftext|>