<commit_before>/*
  Copyright (c) 2015, Al Poole <netstar@gmail.com>

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
*/

//   This is a HTTP proxy written in Rust!
//   DONE: POST+GET+OPTIONS

use std::string::{String};
use std::net::{SocketAddrV4,Ipv4Addr,TcpListener,TcpStream};
use std::io::{Write, Read};

const REQUEST_LEN: usize = 65535;

fn substr(buf: [u8;REQUEST_LEN], needle: &str, byte: u8) -> String
{
    let mut i = 0;
    let mut x = 0;
    let mut request = String::new();

    let srch = needle.as_bytes();

    if srch.len() == 0
    {
        return request;
    }

    while buf[i] as char != '\0'
    {
        if buf[i] as char == srch[0] as char
        {
            while x < srch.len() && buf[i] as char  == srch[x] as char
            {
                i = i + 1;
                x = x + 1;
            }
            break;
        } else {
            i = i + 1;
        }
    }

    if x == srch.len()
    {
        //println!("match");
        let mut end = i;
        while buf[end] as char != byte as char && buf[end] as char != '\n'
        {
            end = end + 1;

        }

        for y in (i..end)
        {
            request.push(buf[y] as char);
        }
    }

    return request;
}

const CHUNK: usize = 4096; // PAGESIZEish

fn get_with_content_length(mut instream: &TcpStream, mut outstream: &TcpStream, total: usize) {
    let mut current = 0;
    let mut buf = [0u8; CHUNK];

    while current < total
    {
        let bytes = outstream.read(&mut buf). unwrap();
        if bytes == 0
        {
            break;
        }

        let mut chunk = 0;

        while chunk < bytes
        {
            let sent = instream.write(&mut buf[0..bytes]).unwrap();
            if sent <= 0
            {
                break;
            }
            chunk = chunk + sent;
            current = current + chunk;
        }
    }
}

fn get_with_no_content_length(mut instream: &TcpStream, mut outstream: &TcpStream)
{
    loop {
        let mut buf = [0u8; CHUNK];

        let bytes = outstream.read(&mut buf).unwrap();
        if bytes == 0
        {
            break; // please!!!
        }

        let mut chunk = 0;

        while chunk < bytes
        {
            let sent = instream.write(&mut buf[0..bytes]).unwrap();
            if sent == 0
            {
                break;
            }

            chunk = chunk + sent;
        }
    }
}

fn http_options_request(mut instream: TcpStream)
{
    let allow = "GET,POST,HEAD,OPTIONS";
    let code: String = "HTTP/1.1 200 OK\r\n".to_string();
    let request: String  = "Allow: ".to_string() + allow + "\r\n\r\n";

    let response = format!("{}{}", code, request);

    instream.write(response.as_bytes()).unwrap();
}

fn http_head_request(hdr: Header)
{
    let bogus_fix = &format!("{}:{}", hdr.hostname, 80);

    let mut outstream = TcpStream::connect::<(&str)>(bogus_fix).unwrap();

    let query: String = format!("HEAD /{} HTTP/1.1\r\nHost: {}\r\n\r\n", hdr.resource, hdr.hostname);

    outstream.write(query.as_bytes()).unwrap();
}

fn http_post_request(mut instream: TcpStream, headers: Header)
{
    let bogus_fix = &format!("{}:{}", headers.hostname, 80);
    let mut outstream = TcpStream::connect::<(&str)>(bogus_fix).unwrap();

    let query: String = format!("POST {}Host: {}\r\nContent-Type: {}\r\nContent-Length: {}\r\n\r\n", headers.resource, headers.hostname, headers.content, headers.length);
    outstream.write(query.as_bytes()).unwrap();

    println!("QUERY: {}", query);

    let mut buf = [0u8; CHUNK];
    let mut current = 0;

    while current < headers.length
    {
        let bytes = instream.read(&mut buf).unwrap();
        if bytes == 0
        {
            break;
        }

        let mut chunk = 0;

        while chunk < bytes
        {
            let sent = outstream.write(&mut buf[0..bytes]).unwrap();
            if sent == 0
            {
                break;
            }

            chunk += sent;
            current = current + chunk;
        }
    }

    let terminate = "\r\n\r\n";

    outstream.write(terminate.as_bytes()).unwrap();

    get_with_no_content_length(&instream, &outstream);
}

fn http_get_request(instream: TcpStream, headers: Header)
{
    let bogus_fix = &format!("{}:{}", headers.hostname, 80);

    let mut outstream = TcpStream::connect::<(&str)>(bogus_fix).unwrap();

    let query: String = format!("GET {}Host: {}\r\nConnection: close\r\n\r\n", headers.resource, headers.hostname);
    outstream.write(query.as_bytes()).unwrap();

    println!("QUERY: {}", query);

    if headers.length > 0
    {
        get_with_content_length(&instream, &outstream, headers.length);
    }
    else
    {
        get_with_no_content_length(&instream, &outstream);
    }
}

fn req_resource(buf: [u8;REQUEST_LEN]) -> String
{
    let mut i = 0;
    let bytes = buf;
    let mut request: String = String::new();

    // FIXME WORDPRESS DOESN't WORK!
    while bytes[i] as char != '\0'
    {
        request.push(bytes[i] as char);
        i += 1;
    }

    return request;
}

fn req_content(buf: [u8;REQUEST_LEN]) -> String
{
    let content = substr(buf, "Content-Type: ", '\r' as u8);

    return content;
}

fn req_hostname(buf: [u8;REQUEST_LEN]) -> String
{
    let hostname: String = substr(buf, "http://", '/' as u8);

    return hostname;
}

fn req_length(buf: [u8;REQUEST_LEN]) -> usize
{
    let request: String = substr(buf, "Content-Length: ", '\r' as u8);
    if ! request.is_empty()
    {
        return request.trim().parse().unwrap();
    }

    return 0;
}

struct Header {
    pub hostname: String,
    pub resource: String,
    pub content: String,
    pub length: usize,
    pub method: String,
}

impl Header {

pub fn new() -> Header
{
    let hostname = String::new();
    let resource = String::new();
    let content  = String::new();
    let method   = String::new();
    let length   = 0;

    let h = Header
        { hostname: hostname, resource: resource , content: content, length: length, method: method};

    return h;
}

}

fn check_headers(buffer: [u8;REQUEST_LEN], headers: &mut Header) -> bool
{

    if headers.hostname.is_empty()
    {
        headers.hostname = req_hostname(buffer);
    }

    if headers.resource.is_empty()
    {
        headers.resource = req_resource(buffer);
    }

    if headers.content.is_empty()
    {
        headers.content = req_content(buffer);
    }

    if headers.length == 0
    {
        headers.length = req_length(buffer);
    }

    if headers.hostname.is_empty() || headers.resource.is_empty() || headers.method.is_empty()
    {
        return false;
    }

    return true;
}

fn request_headers(mut instream: &TcpStream, headers: &mut Header)
{
	let mut byte = [0u8;1];

	// bit better for now it'll do!
	while byte[0] as char != ' '
	{
		instream.read(&mut byte).unwrap();
		if byte[0] as char != ' '
		{
			headers.method.push(byte[0] as char);
		}
	}

	match headers.method.as_ref()
	{
		"GET" | "POST" | "OPTIONS" | "HEAD" =>
		{
			// all good do not return!!!
		}

		_ =>
		{
			return;
		}
	}

     loop
    {
	let mut buffer = [0u8; REQUEST_LEN];
        let mut byte = [0u8; 1];
        let mut len = 0;

        while byte[0] as char != '\n'
        {
            let bytes = instream.read(&mut byte).unwrap();
            buffer[len] = byte[0];
            len += bytes;
        }

        buffer[len] = 0;

        if check_headers(buffer, headers) && len == 2
        {
            return;
        }
    }
}

fn proxy(stream: TcpStream) {
    let mut headers: Header = Header::new();
    request_headers(&stream, &mut headers);

    match headers.method.as_ref() {
            "GET" =>
            {
                http_get_request(stream, headers);
            }

            "POST" =>
            {
                http_post_request(stream, headers);
            }

            "HEAD" =>
            {
                http_head_request(headers);
            }

            "OPTIONS" =>
            {
                http_options_request(stream);
            }

            _ =>
            {
                println!("REQUEST UNKNOWN");
            }
        }
}

extern crate threadpool;
use threadpool::ThreadPool;

fn proxy_time(port: u16, threads: usize) {
    let ip = Ipv4Addr::new(127, 0, 0, 1);
    let host = SocketAddrV4::new(ip, port);
    let listener = TcpListener::bind(host).unwrap();

    let pool = ThreadPool::new(threads);

    for stream in listener.incoming()
    {
        match stream
        {
            Ok(stream) =>
            {
                pool.execute(move || { proxy(stream); });
            }

            Err(_) =>
            {
                println!("NET error");
                1 << 7;
            }
        }
    }

    drop (listener);
}

fn main () {
    let threads = 128;
    let port: u16 = 9999;

    proxy_time(port, threads);
    println!("Blocking all badness");

}

<commit_msg>put back CONNECT<commit_after>/*
  Copyright (c) 2015, Al Poole <netstar@gmail.com>

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
*/

//   This is a HTTP proxy written in Rust!
//   DONE: POST+GET+OPTIONS

use std::string::{String};
use std::net::{SocketAddrV4,Ipv4Addr,TcpListener,TcpStream};
use std::io::{Write, Read};

const REQUEST_LEN: usize = 65535;

fn substr(buf: [u8;REQUEST_LEN], needle: &str, byte: u8) -> String
{
    let mut i = 0;
    let mut x = 0;
    let mut request = String::new();

    let srch = needle.as_bytes();

    if srch.len() == 0
    {
        return request;
    }

    while buf[i] as char != '\0'
    {
        if buf[i] as char == srch[0] as char
        {
            while x < srch.len() && buf[i] as char  == srch[x] as char
            {
                i = i + 1;
                x = x + 1;
            }
            break;
        } else {
            i = i + 1;
        }
    }

    if x == srch.len()
    {
        //println!("match");
        let mut end = i;
        while buf[end] as char != byte as char && buf[end] as char != '\n'
        {
            end = end + 1;

        }

        for y in (i..end)
        {
            request.push(buf[y] as char);
        }
    }

    return request;
}

const CHUNK: usize = 4096; // PAGESIZEish

fn get_with_content_length(mut instream: &TcpStream, mut outstream: &TcpStream, total: usize) {
    let mut current = 0;
    let mut buf = [0u8; CHUNK];

    while current < total
    {
        let bytes = outstream.read(&mut buf). unwrap();
        if bytes == 0
        {
            break;
        }

        let mut chunk = 0;

        while chunk < bytes
        {
            let sent = instream.write(&mut buf[0..bytes]).unwrap();
            if sent <= 0
            {
                break;
            }
            chunk = chunk + sent;
            current = current + chunk;
        }
    }
}

fn get_with_no_content_length(mut instream: &TcpStream, mut outstream: &TcpStream)
{
    loop {
        let mut buf = [0u8; CHUNK];

        let bytes = outstream.read(&mut buf).unwrap();
        if bytes == 0
        {
            break; // please!!!
        }

        let mut chunk = 0;

        while chunk < bytes
        {
            let sent = instream.write(&mut buf[0..bytes]).unwrap();
            if sent == 0
            {
                break;
            }

            chunk = chunk + sent;
        }
    }
}

fn http_options_request(mut instream: TcpStream)
{
    let allow = "GET,POST,HEAD,CONNECT,OPTIONS";
    let code: String = "HTTP/1.1 200 OK\r\n".to_string();
    let request: String  = "Allow: ".to_string() + allow + "\r\n\r\n";

    let response = format!("{}{}", code, request);

    instream.write(response.as_bytes()).unwrap();
}

fn http_head_request(hdr: Header)
{
    let bogus_fix = &format!("{}:{}", hdr.hostname, 80);

    let mut outstream = TcpStream::connect::<(&str)>(bogus_fix).unwrap();

    let query: String = format!("HEAD /{} HTTP/1.1\r\nHost: {}\r\n\r\n", hdr.resource, hdr.hostname);

    outstream.write(query.as_bytes()).unwrap();
}

fn http_post_request(mut instream: TcpStream, headers: Header)
{
    let bogus_fix = &format!("{}:{}", headers.hostname, 80);
    let mut outstream = TcpStream::connect::<(&str)>(bogus_fix).unwrap();

    let query: String = format!("POST {}Host: {}\r\nContent-Type: {}\r\nContent-Length: {}\r\n\r\n", headers.resource, headers.hostname, headers.content, headers.length);
    outstream.write(query.as_bytes()).unwrap();

    println!("QUERY: {}", query);

    let mut buf = [0u8; CHUNK];
    let mut current = 0;

    while current < headers.length
    {
        let bytes = instream.read(&mut buf).unwrap();
        if bytes == 0
        {
            break;
        }

        let mut chunk = 0;

        while chunk < bytes
        {
            let sent = outstream.write(&mut buf[0..bytes]).unwrap();
            if sent == 0
            {
                break;
            }

            chunk += sent;
            current = current + chunk;
        }
    }

    let terminate = "\r\n\r\n";

    outstream.write(terminate.as_bytes()).unwrap();

    get_with_no_content_length(&instream, &outstream);
}

fn http_get_request(instream: TcpStream, headers: Header)
{
    let bogus_fix = &format!("{}:{}", headers.hostname, 80);

    let mut outstream = TcpStream::connect::<(&str)>(bogus_fix).unwrap();

    let query: String = format!("GET {}Host: {}\r\nConnection: close\r\n\r\n", headers.resource, headers.hostname);
    outstream.write(query.as_bytes()).unwrap();

    println!("QUERY: {}", query);

    if headers.length > 0
    {
        get_with_content_length(&instream, &outstream, headers.length);
    }
    else
    {
        get_with_no_content_length(&instream, &outstream);
    }
}

fn http_connect_request(instream: TcpStream, hdr: Header)
{
    let bogus_fix = &format!("{}:{}", hdr.hostname, 443);
	// this needs fixing!!!

    let outstream = TcpStream::connect::<(&str)>(bogus_fix).unwrap();

    get_with_no_content_length(&instream, &outstream);
}

fn req_resource(buf: [u8;REQUEST_LEN]) -> String
{
    let mut i = 0;
    let bytes = buf;
    let mut request: String = String::new();

    // FIXME WORDPRESS DOESN't WORK!
    while bytes[i] as char != '\0'
    {
        request.push(bytes[i] as char);
        i += 1;
    }

    return request;
}

fn req_content(buf: [u8;REQUEST_LEN]) -> String
{
    let content = substr(buf, "Content-Type: ", '\r' as u8);

    return content;
}

fn req_hostname(buf: [u8;REQUEST_LEN]) -> String
{
    let hostname: String = substr(buf, "http://", '/' as u8);

    return hostname;
}

fn req_length(buf: [u8;REQUEST_LEN]) -> usize
{
    let request: String = substr(buf, "Content-Length: ", '\r' as u8);
    if ! request.is_empty()
    {
        return request.trim().parse().unwrap();
    }

    return 0;
}

struct Header {
    pub hostname: String,
    pub resource: String,
    pub content: String,
    pub length: usize,
    pub method: String,
}

impl Header {

pub fn new() -> Header
{
    let hostname = String::new();
    let resource = String::new();
    let content  = String::new();
    let method   = String::new();
    let length   = 0;

    let h = Header
        { hostname: hostname, resource: resource , content: content, length: length, method: method};

    return h;
}

}

fn check_headers(buffer: [u8;REQUEST_LEN], headers: &mut Header) -> bool
{
    if headers.hostname.is_empty()
    {
        headers.hostname = req_hostname(buffer);
    }

    if headers.resource.is_empty()
    {
        headers.resource = req_resource(buffer);
    }

    if headers.content.is_empty()
    {
        headers.content = req_content(buffer);
    }

    if headers.length == 0
    {
        headers.length = req_length(buffer);
    }

    if headers.hostname.is_empty() || headers.resource.is_empty() || headers.method.is_empty()
    {
        return false;
    }

    return true;
}

fn request_headers(mut instream: &TcpStream, headers: &mut Header)
{
	let mut byte = [0u8;1];

	// bit better for now it'll do!
	while byte[0] as char != ' '
	{
		instream.read(&mut byte).unwrap();
		if byte[0] as char != ' '
		{
			headers.method.push(byte[0] as char);
		}
	}

	match headers.method.as_ref()
	{
		"GET" | "POST" | "OPTIONS" | "HEAD" | "CONNECT" =>
		{
			// all good do not return!!!
		}

		_ =>
		{
			return;
		}
	}

     loop
    {
		let mut buffer = [0u8; REQUEST_LEN];
        let mut byte = [0u8; 1];
		let mut len = 0;

        while byte[0] as char != '\n'
        {
            let bytes = instream.read(&mut byte).unwrap();
            buffer[len] = byte[0];
            len += bytes;
        }

        buffer[len] = '\0' as u8;

        if check_headers(buffer, headers) && len == 2
        {
			let mut i = 0;

			while i < REQUEST_LEN
			{
				buffer[i] = 0;
				i += 1;
			}

            return;
        }
    }
}

fn proxy(stream: TcpStream) {
    let mut headers: Header = Header::new();
    request_headers(&stream, &mut headers);

    match headers.method.as_ref() {
            "GET" =>
            {
                http_get_request(stream, headers);
            }

            "POST" =>
            {
                http_post_request(stream, headers);
            }

            "HEAD" =>
            {
                http_head_request(headers);
            }

            "OPTIONS" =>
            {
                http_options_request(stream);
            }

			"CONNECT" =>
			{
				println!("here {} and {}", headers.hostname, headers.resource);
				http_connect_request(stream, headers);
			}

            _ =>
            {
                println!("REQUEST UNKNOWN");
            }
        }
}

extern crate threadpool;
use threadpool::ThreadPool;

fn proxy_time(port: u16, threads: usize) {
    let ip = Ipv4Addr::new(127, 0, 0, 1);
    let host = SocketAddrV4::new(ip, port);
    let listener = TcpListener::bind(host).unwrap();

    let pool = ThreadPool::new(threads);

    for stream in listener.incoming()
    {
        match stream
        {
            Ok(stream) =>
            {
                pool.execute(move || { proxy(stream); });
            }

            Err(_) =>
            {
                println!("NET error");
                1 << 7;
            }
        }
    }

    drop (listener);
}

fn main () {
    let threads = 128;
    let port: u16 = 9999;

    proxy_time(port, threads);
    println!("Blocking all badness");

}

<|endoftext|>


<commit_before>extern crate chrono;
extern crate colored;

use std::fmt;
use std::process::Command;
use std::str;
use std::iter;
use colored::Colorize;
use chrono::datetime::DateTime;
use chrono::Local;
use chrono::FixedOffset;

fn main() {
    let mut v = get_git_data();
    v.sort();
    print_git_data(v);
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Branch {
    user: String,
    date: DateTime<FixedOffset>,
    branch: String,
}

impl fmt::Display for Branch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "[{}] {} {}",
               self.date.format("%Y-%m-%d"),
               self.user,
               self.branch)
    }
}

impl fmt::Debug for Branch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "user: {}\nbranch: {}\ndate: {}",
               self.user,
               self.branch,
               self.date)
    }
}

fn parse_repo(line: String) -> Option<Branch> {
    if !line.contains("origin/HEAD") && !line.contains("origin/master") {
        let mut user = String::new();
        let mut date = String::new();
        let mut branch = String::new();
        for (i, part) in line.split("^~^").enumerate() {
            match i {
                0 => user = part.to_string(),
                1 => branch = part.to_string().replace("origin/", ""),
                2 => date = part.to_string(),
                _ => {}
            }
        }
        Some(Branch {
            user: user,
            branch: branch,
            date: DateTime::parse_from_rfc2822(&date).unwrap(),
        })
    } else {
        None
    }
}

fn print_git_data(v: Vec<Branch>) {
    let max = v.iter().fold(0, |max, branch| {
        let len = branch.user.len();
        if len > max {
            len
        } else {
            max
        }
    });
    let now = Local::now();

    for branch in v {
        let padding_count = max - branch.user.len();
        let padding: String = iter::repeat(" ").take(padding_count).collect();

        println!("[{}] {}{} {}",
                 coloured_date(now, branch.date),
                 branch.user,
                 padding,
                 branch.branch);
    }
}

fn coloured_date(now: DateTime<Local>, date: DateTime<FixedOffset>) -> String {
    let str_date = date.format("%Y-%m-%d").to_string();

    let diff = now - date;

    if diff.num_days() >= 180 {
        str_date.red().to_string()
    } else if diff.num_days() >= 90 {
        str_date.yellow().to_string()
    } else {
        str_date.green().to_string()
    }
}

fn get_git_data() -> Vec<Branch> {
    let output = Command::new("git")
        .arg("for-each-ref")
        .arg("--format=%(authorname)^~^%(refname:short)^~^%(committerdate:rfc2822)")
        .arg("refs/remotes/origin/")
        .output()
        .expect("failed to execute process");
    let git_output = str::from_utf8(&output.stdout).unwrap();

    let mut v: Vec<Branch> = Vec::new();
    for line in git_output.lines() {
        parse_repo(line.to_string()).map(|x| v.push(x));
    }
    v
}
<commit_msg>cleanup<commit_after>extern crate chrono;
extern crate colored;

use std::fmt;
use std::process::Command;
use std::str;
use std::iter;
use colored::Colorize;
use chrono::datetime::DateTime;
use chrono::Local;
use chrono::FixedOffset;

fn main() {
    let mut v = get_git_data();
    v.sort();
    print_git_data(v);
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Branch {
    user: String,
    date: DateTime<FixedOffset>,
    branch: String,
}

impl fmt::Display for Branch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "[{}] {} {}",
               self.date.format("%Y-%m-%d"),
               self.user,
               self.branch)
    }
}

impl fmt::Debug for Branch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "user: {}\nbranch: {}\ndate: {}",
               self.user,
               self.branch,
               self.date)
    }
}

fn parse_repo(line: String) -> Option<Branch> {
    if !line.contains("origin/HEAD") && !line.contains("origin/master") {
        let mut user = String::new();
        let mut date = String::new();
        let mut branch = String::new();
        for (i, part) in line.split("^~^").enumerate() {
            match i {
                0 => user = part.to_string(),
                1 => branch = part.to_string().replace("origin/", ""),
                2 => date = part.to_string(),
                _ => {}
            }
        }
        Some(Branch {
            user: user,
            branch: branch,
            date: DateTime::parse_from_rfc2822(&date).unwrap(),
        })
    } else {
        None
    }
}

fn print_git_data(v: Vec<Branch>) {
    let max = v.iter().fold(0, |max, branch| {
        let len = branch.user.len();
        if len > max {
            len
        } else {
            max
        }
    });
    let now = Local::now();

    for branch in v {
        let padding_count = max - branch.user.len();
        let padding: String = iter::repeat(" ").take(padding_count).collect();

        println!("[{}] {}{} {}",
                 coloured_date(now, branch.date),
                 branch.user,
                 padding,
                 branch.branch);
    }
}

fn coloured_date(now: DateTime<Local>, date: DateTime<FixedOffset>) -> String {
    let str_date = date.format("%Y-%m-%d").to_string();
    let diff = (now - date).num_days();

    if diff >= 180 {
        str_date.red().to_string()
    } else if diff >= 90 {
        str_date.yellow().to_string()
    } else {
        str_date.green().to_string()
    }
}

fn get_git_data() -> Vec<Branch> {
    let output = Command::new("git")
        .arg("for-each-ref")
        .arg("--format=%(authorname)^~^%(refname:short)^~^%(committerdate:rfc2822)")
        .arg("refs/remotes/origin/")
        .output()
        .expect("failed to execute process");
    let git_output = str::from_utf8(&output.stdout).unwrap();

    let mut v: Vec<Branch> = Vec::new();
    for line in git_output.lines() {
        parse_repo(line.to_string()).map(|x| v.push(x));
    }
    v
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017  Sebastian Wiesner <swiesner@lunaryorn.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

fn main() {
    println!("hello world")
}
<commit_msg>Generate a single password<commit_after>/*
 * Copyright (C) 2017  Sebastian Wiesner <swiesner@lunaryorn.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

extern crate rand;

static EFF_WORDLIST: &'static str = include_str!(concat!(env!("OUT_DIR"), "/eff_wordlist.txt"));

fn parse_diceware_list(input: &str) -> Vec<&str> {
    input.lines().map(|l| l.split_whitespace().last().unwrap()).collect()
}

fn main() {
    let words = parse_diceware_list(EFF_WORDLIST);
    println!("{}", rand::sample(&mut rand::thread_rng(), words, 4).join(" "));
}
<|endoftext|>


<commit_before>extern crate ppbert;
#[macro_use] extern crate clap;

use std::io::{self, Read, Write};
use std::fs::File;
use std::process::exit;
use std::time::Instant;

use clap::{Arg, App};

use ppbert::parser;
use ppbert::bertterm::{
    BertTerm,
    PrettyPrinter,
    DEFAULT_INDENT_WIDTH,
    DEFAULT_MAX_TERMS_PER_LINE
};
use ppbert::error::Result;

fn main() {
    let matches = App::new("ppbert")
        .version(crate_version!())
        .author("Vincent Foley")
        .about("Pretty print structure encoded in Erlang's External Term Format")
        .arg(Arg::with_name("input_files")
             .value_name("FILES")
             .multiple(true))
        .arg(Arg::with_name("indent_width")
             .value_name("num")
             .short("i")
             .long("--indent-width")
             .takes_value(true))
        .arg(Arg::with_name("max_per_line")
             .value_name("num")
             .short("m")
             .long("--max-terms-per-line")
             .takes_value(true))
        .arg(Arg::with_name("verbose")
             .short("v")
             .long("--verbose"))
        .arg(Arg::with_name("skip_pretty_print")
             .short("s")
             .long("--skip-pretty-print"))
        .get_matches();

    let files: Vec<&str> = match matches.values_of("input_files") {
        Some(files) => files.collect(),
        None => vec!["-"]
    };

    let indent_level = value_t!(matches, "indent_width", usize)
        .unwrap_or(DEFAULT_INDENT_WIDTH);
    let max_per_line = value_t!(matches, "max_per_line", usize)
        .unwrap_or(DEFAULT_MAX_TERMS_PER_LINE);
    let verbose = matches.is_present("verbose");
    let skip_pretty_print = matches.is_present("skip_pretty_print");

    let mut return_code = 0;
    for file in files {
        let now = Instant::now();
        let parse_res = parse_file(file);
        if verbose {
            let dur = now.elapsed();
            let _ = writeln!(&mut io::stderr(),
                             "ppbert: parse time: {}.{}s",
                             dur.as_secs(), dur.subsec_nanos());
        }

        if skip_pretty_print {
            continue;
        }

        let _ = parse_res
            .map(|ref t| {
                let now = Instant::now();
                let pp = PrettyPrinter::new(t, indent_level, max_per_line);
                println!("{}", pp);
                if verbose {
                    let dur = now.elapsed();
                    let _ = writeln!(&mut io::stderr(),
                                     "ppbert: pretty print time: {}.{}s",
                                     dur.as_secs(), dur.subsec_nanos());
                }

            })
            .map_err(|ref e| {
                return_code = 1;
                writeln!(&mut io::stderr(), "ppbert: {}: {}", file, e)
            });
    }
    exit(return_code);
}

fn parse_file(file: &str) -> Result<BertTerm> {
    let mut buf: Vec<u8> = Vec::new();
    if file == "-" {
        let mut stdin = io::stdin();
        stdin.read_to_end(&mut buf)?;
    } else {
        let mut f = File::open(file)?;
        f.read_to_end(&mut buf)?;
    }
    let mut parser = parser::Parser::new(buf);
    return parser.parse();
}
<commit_msg>Add help strings to all command-line flags<commit_after>extern crate ppbert;
#[macro_use] extern crate clap;

use std::io::{self, Read, Write};
use std::fs::File;
use std::process::exit;
use std::time::Instant;

use clap::{Arg, App};

use ppbert::parser;
use ppbert::bertterm::{
    BertTerm,
    PrettyPrinter,
    DEFAULT_INDENT_WIDTH,
    DEFAULT_MAX_TERMS_PER_LINE
};
use ppbert::error::Result;

fn main() {
    let matches = App::new("ppbert")
        .version(crate_version!())
        .author("Vincent Foley")
        .about("Pretty print structure encoded in Erlang's External Term Format")
        .arg(Arg::with_name("input_files")
             .value_name("FILES")
             .multiple(true))
        .arg(Arg::with_name("indent_width")
             .help("Number of spaces to indent nested terms")
             .value_name("num")
             .short("i")
             .long("--indent-width")
             .takes_value(true))
        .arg(Arg::with_name("max_per_line")
             .help("Number of spaces to indent nested terms")
             .value_name("num")
             .short("m")
             .long("--max-terms-per-line")
             .takes_value(true))
        .arg(Arg::with_name("verbose")
             .help("Enable verbose mode")
             .short("v")
             .long("--verbose"))
        .arg(Arg::with_name("skip_pretty_print")
             .help("Only parse the file(s)")
             .short("s")
             .long("--skip-pretty-print"))
        .get_matches();

    let files: Vec<&str> = match matches.values_of("input_files") {
        Some(files) => files.collect(),
        None => vec!["-"]
    };

    let indent_level = value_t!(matches, "indent_width", usize)
        .unwrap_or(DEFAULT_INDENT_WIDTH);
    let max_per_line = value_t!(matches, "max_per_line", usize)
        .unwrap_or(DEFAULT_MAX_TERMS_PER_LINE);
    let verbose = matches.is_present("verbose");
    let skip_pretty_print = matches.is_present("skip_pretty_print");

    let mut return_code = 0;
    for file in files {
        let now = Instant::now();
        let parse_res = parse_file(file);
        if verbose {
            let dur = now.elapsed();
            let _ = writeln!(&mut io::stderr(),
                             "ppbert: parse time: {}.{}s",
                             dur.as_secs(), dur.subsec_nanos());
        }

        if skip_pretty_print {
            continue;
        }

        let _ = parse_res
            .map(|ref t| {
                let now = Instant::now();
                let pp = PrettyPrinter::new(t, indent_level, max_per_line);
                println!("{}", pp);
                if verbose {
                    let dur = now.elapsed();
                    let _ = writeln!(&mut io::stderr(),
                                     "ppbert: pretty print time: {}.{}s",
                                     dur.as_secs(), dur.subsec_nanos());
                }

            })
            .map_err(|ref e| {
                return_code = 1;
                writeln!(&mut io::stderr(), "ppbert: {}: {}", file, e)
            });
    }
    exit(return_code);
}

fn parse_file(file: &str) -> Result<BertTerm> {
    let mut buf: Vec<u8> = Vec::new();
    if file == "-" {
        let mut stdin = io::stdin();
        stdin.read_to_end(&mut buf)?;
    } else {
        let mut f = File::open(file)?;
        f.read_to_end(&mut buf)?;
    }
    let mut parser = parser::Parser::new(buf);
    return parser.parse();
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate chrono;
extern crate clap;
extern crate curl;
extern crate fern;
extern crate flate2;
extern crate rustc_version;
extern crate tar;
extern crate tempdir;
extern crate toml;

#[macro_use]
extern crate log;

use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::Command;

use chrono::NaiveDate;
use clap::{App, AppSettings, Arg, SubCommand};
use curl::http;
use rustc_version::Channel;

// TODO proper error reporting
macro_rules! try {
    ($e:expr) => {
        $e.unwrap_or_else(|e| panic!("{} with {}", stringify!($e), e))
    }
}

enum Target<'a> {
    /// `path/to/some/target.json`
    Spec(&'a Path),
    /// `arm-unknown-linux-gnueabihf`
    Triple(&'a str),
}

struct Context<'a> {
    commit_date: NaiveDate,
    commit_hash: &'a str,
    host: &'a str,
    out_dir: &'a Path,
    profile: &'static str,
    target: Target<'a>,
    verbose: bool,
}

struct Config {
    table: Option<toml::Value>,
}

impl Config {
    /// Parses `sysroot.toml`
    fn parse() -> Config {
        let path = Path::new("sysroot.toml");

        let table = if path.exists() {
            let ref mut toml = String::new();

            try!(try!(File::open(path)).read_to_string(toml));

            toml::Parser::new(toml).parse().map(toml::Value::Table)
        } else {
            info!("no sysroot.toml found, using default configuration");

            None
        };

        Config { table: table }
    }

    /// Crates to build for this target, returns `["core"]` if not specified
    fn crates(&self, target: &str) -> Vec<String> {
        let ref key = format!("target.{}.crates", target);

        let mut crates = self.table
                             .as_ref()
                             .and_then(|t| t.lookup(key))
                             .and_then(|v| v.as_slice())
                             .and_then(|vs| {
                                 vs.iter().map(|v| v.as_str().map(|s| s.to_owned())).collect()
                             })
                             .unwrap_or_else(|| vec!["core".to_owned()]);

        crates.push("core".to_owned());
        crates.sort();
        crates.dedup();
        crates
    }
}

fn main() {
    let rustc_version::VersionMeta { ref host, ref commit_date, ref commit_hash, ref channel, .. } =
        rustc_version::version_meta();
    let commit_hash = commit_hash.as_ref().unwrap();

    match *channel {
        Channel::Nightly => {}
        _ => panic!("only the nightly channel is supported at this time (see issue #5)"),
    }

    let ref matches = App::new("cargo-sysroot")
                          .bin_name("cargo")
                          .settings(&[AppSettings::SubcommandRequired])
                          .subcommand(SubCommand::with_name("sysroot")
                                          .about("Builds a sysroot with cross compiled standard \
                                                  crates")
                                          .arg(Arg::with_name("triple")
                                                   .help("Target triple to compile for")
                                                   .long("target")
                                                   .required(true)
                                                   .takes_value(true))
                                          .arg(Arg::with_name("release")
                                                   .help("Build artifacts in release mode, \
                                                          with optimizations")
                                                   .long("release"))
                                          .arg(Arg::with_name("out_dir")
                                                   .help("Output directory")
                                                   .required(true))
                                          .arg(Arg::with_name("verbose")
                                                   .help("Verbose cargo builds")
                                                   .long("verbose"))
                                          .author("Jorge Aparicio <japaricious@gmail.com>"))
                          .version(env!("CARGO_PKG_VERSION"))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("sysroot") {
        if let (Some(target), Some(out_dir)) = (matches.value_of("triple"),
                                                matches.value_of("out_dir")) {
            if host == target {
                panic!("`cargo sysroot` for host has not been implement yet");
            }

            let target = if target.ends_with("json") {
                Target::Spec(Path::new(target))
            } else {
                Target::Triple(target)
            };

            let ref ctx = Context {
                commit_date: NaiveDate::parse_from_str(commit_date.as_ref().unwrap(), "%Y-%m-%d")
                                 .unwrap(),
                commit_hash: commit_hash,
                host: host,
                out_dir: Path::new(out_dir),
                profile: if matches.is_present("release") {
                    "release"
                } else {
                    "debug"
                },
                target: target,
                verbose: matches.is_present("verbose"),
            };

            init_logger();
            fetch_source(ctx);
            symlink_host_crates(ctx);
            build_target_crates(ctx);
        }
    }
}

fn init_logger() {
    let config = fern::DispatchConfig {
        format: Box::new(|msg, level, _| format!("{}: {}", level, msg)),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    try!(fern::init_global_logger(config, log::LogLevelFilter::Trace));
}

// TODO Ultimately, we want to use `multirust fetch-source` for 100% correctness
fn fetch_source(ctx: &Context) {
    // XXX There doesn't seem to be way to get the _nightly_ date from the output of `rustc -Vv`
    // So we _assume_ the nightly day is the day after the commit-date found in `rustc -Vv` which
    // seems to be the common case, but it could be wrong and we'll end up building unusable crates
    let date = ctx.commit_date.succ();
    let hash = ctx.commit_hash;
    let ref src_dir = ctx.out_dir.join("src");
    let ref hash_file = src_dir.join(".commit-hash");

    if hash_file.exists() {
        let ref mut old_hash = String::with_capacity(hash.len());
        try!(try!(File::open(hash_file)).read_to_string(old_hash));

        if old_hash == hash {
            info!("source up to date");
            return;
        }
    }

    if src_dir.exists() {
        info!("purging the src directory");
        try!(fs::remove_dir_all(src_dir));
    }

    try!(fs::create_dir_all(src_dir));

    info!("fetching source tarball");
    let handle = http::Handle::new();
    let url = format!("http://static.rust-lang.org/dist/{}/rustc-nightly-src.tar.gz",
                      date.format("%Y-%m-%d"));
    let resp = try!(handle.timeout(300_000).get(&url[..]).follow_redirects(true).exec());

    assert_eq!(resp.get_code(), 200);

    info!("unpacking source tarball");
    let decoder = try!(flate2::read::GzDecoder::new(resp.get_body()));
    let mut archive = tar::Archive::new(decoder);
    for entry in try!(archive.entries()) {
        let mut entry = try!(entry);
        let path = {
            let path = try!(entry.path());
            let mut components = path.components();
            components.next(); // skip rust-lang-rust-<...>
            let next = components.next().and_then(|s| s.as_os_str().to_str());
            if next != Some("src") {
                continue;
            }
            components.as_path().to_path_buf()
        };
        try!(entry.unpack(&src_dir.join(path)));
    }

    info!("creating .commit-hash file");
    try!(try!(File::create(hash_file)).write_all(hash.as_bytes()));
}

fn symlink_host_crates(ctx: &Context) {
    info!("symlinking host crates");

    let sys_root = try!(String::from_utf8(try!(Command::new("rustc")
                                                   .args(&["--print", "sysroot"])
                                                   .output())
                                              .stdout));
    let ref src = Path::new(sys_root.trim_right()).join(format!("lib/rustlib/{}", ctx.host));
    let ref rustlib_dir = ctx.out_dir.join(format!("{}/lib/rustlib", ctx.profile));
    let ref dst = rustlib_dir.join(ctx.host);

    try!(fs::create_dir_all(rustlib_dir));

    if dst.exists() {
        try!(fs::remove_dir_all(dst));
    }

    link_dirs(&src, &dst);
}

fn link_dirs(src: &Path, dst: &Path) {
    try!(fs::create_dir(&dst));

    for entry in try!(src.read_dir()) {
        let entry = try!(entry);
        let src = entry.path();
        let dst = dst.join(entry.file_name());
        if try!(entry.file_type()).is_dir() {
            link_dirs(&src, &dst);
        } else {
            try!(fs::hard_link(&src, &dst).or_else(|_| fs::copy(&src, &dst).map(|_| ())));
        }
    }
}

fn build_target_crates(ctx: &Context) {
    const LIB_RS: &'static [u8] = b"#![no_std]";

    let ref config = Config::parse();

    let temp_dir = try!(tempdir::TempDir::new("sysroot"));
    let temp_dir = temp_dir.path();

    // Create Cargo project
    let ref mut cargo = Command::new("cargo");
    cargo.current_dir(temp_dir);
    cargo.args(&["new", "--vcs", "none"]);
    if ctx.verbose {
        cargo.arg("--verbose");
    }
    assert!(try!(cargo.arg("sysroot").status()).success());

    let ref cargo_dir = temp_dir.join("sysroot");
    let ref src_dir = try!(fs::canonicalize(ctx.out_dir)).join("src");

    let (ref triple, ref spec_file): (String, _) = match ctx.target {
        Target::Spec(path) => {
            let path = try!(fs::canonicalize(path));
            let triple = path.file_stem().unwrap().to_str().unwrap().into();

            (triple, path)
        }
        Target::Triple(triple) => (triple.into(), PathBuf::from(format!("{}.json", triple))),
    };

    // Add crates to build as dependencies
    let ref mut toml = try!(OpenOptions::new()
                                .write(true)
                                .append(true)
                                .open(cargo_dir.join("Cargo.toml")));
    let ref crates = config.crates(triple);
    info!("will build the following crates: {:?}", crates);
    for ref krate in crates {
        try!(writeln!(toml,
                      "{} = {{ path = \"{}\" }}",
                      krate,
                      src_dir.join(format!("lib{}", krate)).display()))
    }

    {
        let ref mut toml = String::new();
        try!(try!(File::open(cargo_dir.join("Cargo.toml"))).read_to_string(toml));
        debug!("sysroot's Cargo.toml: {}", toml);
    }

    // Rewrite lib.rs to only depend on libcore
    try!(try!(OpenOptions::new().write(true).truncate(true).open(cargo_dir.join("src/lib.rs")))
             .write_all(LIB_RS));

    if spec_file.exists() {
        info!("copy target specification file");
        try!(fs::copy(spec_file, cargo_dir.join(format!("{}.json", triple))));
    }

    info!("building the target crates");
    let ref mut cargo = Command::new("cargo");
    cargo.current_dir(cargo_dir);
    cargo.args(&["build", "--target", triple]);
    if ctx.profile == "release" {
        cargo.arg("--release");
    }
    if ctx.verbose {
        cargo.arg("--verbose");
    }
    assert!(try!(cargo.status()).success());

    info!("copy the target crates to the sysroot");
    let ref libdir = ctx.out_dir.join(format!("{}/lib/rustlib/{}/lib", ctx.profile, triple));
    let ref deps_dir = cargo_dir.join(format!("target/{}/{}/deps", triple, ctx.profile));

    try!(fs::create_dir_all(libdir));
    for entry in try!(fs::read_dir(deps_dir)) {
        let entry = try!(entry);

        try!(fs::copy(entry.path(), libdir.join(entry.file_name())));
    }
}
<commit_msg>use current_dir.join instead of canonicalize to get the absolute path of the output directory<commit_after>#![deny(warnings)]

extern crate chrono;
extern crate clap;
extern crate curl;
extern crate fern;
extern crate flate2;
extern crate rustc_version;
extern crate tar;
extern crate tempdir;
extern crate toml;

#[macro_use]
extern crate log;

use std::env;
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::Command;

use chrono::NaiveDate;
use clap::{App, AppSettings, Arg, SubCommand};
use curl::http;
use rustc_version::Channel;

// TODO proper error reporting
macro_rules! try {
    ($e:expr) => {
        $e.unwrap_or_else(|e| panic!("{} with {}", stringify!($e), e))
    }
}

enum Target<'a> {
    /// `path/to/some/target.json`
    Spec(&'a Path),
    /// `arm-unknown-linux-gnueabihf`
    Triple(&'a str),
}

struct Context<'a> {
    commit_date: NaiveDate,
    commit_hash: &'a str,
    host: &'a str,
    out_dir: &'a Path,
    profile: &'static str,
    target: Target<'a>,
    verbose: bool,
}

struct Config {
    table: Option<toml::Value>,
}

impl Config {
    /// Parses `sysroot.toml`
    fn parse() -> Config {
        let path = Path::new("sysroot.toml");

        let table = if path.exists() {
            let ref mut toml = String::new();

            try!(try!(File::open(path)).read_to_string(toml));

            toml::Parser::new(toml).parse().map(toml::Value::Table)
        } else {
            info!("no sysroot.toml found, using default configuration");

            None
        };

        Config { table: table }
    }

    /// Crates to build for this target, returns `["core"]` if not specified
    fn crates(&self, target: &str) -> Vec<String> {
        let ref key = format!("target.{}.crates", target);

        let mut crates = self.table
                             .as_ref()
                             .and_then(|t| t.lookup(key))
                             .and_then(|v| v.as_slice())
                             .and_then(|vs| {
                                 vs.iter().map(|v| v.as_str().map(|s| s.to_owned())).collect()
                             })
                             .unwrap_or_else(|| vec!["core".to_owned()]);

        crates.push("core".to_owned());
        crates.sort();
        crates.dedup();
        crates
    }
}

fn main() {
    let rustc_version::VersionMeta { ref host, ref commit_date, ref commit_hash, ref channel, .. } =
        rustc_version::version_meta();
    let commit_hash = commit_hash.as_ref().unwrap();

    match *channel {
        Channel::Nightly => {}
        _ => panic!("only the nightly channel is supported at this time (see issue #5)"),
    }

    let ref matches = App::new("cargo-sysroot")
                          .bin_name("cargo")
                          .settings(&[AppSettings::SubcommandRequired])
                          .subcommand(SubCommand::with_name("sysroot")
                                          .about("Builds a sysroot with cross compiled standard \
                                                  crates")
                                          .arg(Arg::with_name("triple")
                                                   .help("Target triple to compile for")
                                                   .long("target")
                                                   .required(true)
                                                   .takes_value(true))
                                          .arg(Arg::with_name("release")
                                                   .help("Build artifacts in release mode, \
                                                          with optimizations")
                                                   .long("release"))
                                          .arg(Arg::with_name("out_dir")
                                                   .help("Output directory")
                                                   .required(true))
                                          .arg(Arg::with_name("verbose")
                                                   .help("Verbose cargo builds")
                                                   .long("verbose"))
                                          .author("Jorge Aparicio <japaricious@gmail.com>"))
                          .version(env!("CARGO_PKG_VERSION"))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("sysroot") {
        if let (Some(target), Some(out_dir)) = (matches.value_of("triple"),
                                                matches.value_of("out_dir")) {
            if host == target {
                panic!("`cargo sysroot` for host has not been implement yet");
            }

            let target = if target.ends_with("json") {
                Target::Spec(Path::new(target))
            } else {
                Target::Triple(target)
            };

            let ref ctx = Context {
                commit_date: NaiveDate::parse_from_str(commit_date.as_ref().unwrap(), "%Y-%m-%d")
                                 .unwrap(),
                commit_hash: commit_hash,
                host: host,
                out_dir: Path::new(out_dir),
                profile: if matches.is_present("release") {
                    "release"
                } else {
                    "debug"
                },
                target: target,
                verbose: matches.is_present("verbose"),
            };

            init_logger();
            fetch_source(ctx);
            symlink_host_crates(ctx);
            build_target_crates(ctx);
        }
    }
}

fn init_logger() {
    let config = fern::DispatchConfig {
        format: Box::new(|msg, level, _| format!("{}: {}", level, msg)),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    try!(fern::init_global_logger(config, log::LogLevelFilter::Trace));
}

// TODO Ultimately, we want to use `multirust fetch-source` for 100% correctness
fn fetch_source(ctx: &Context) {
    // XXX There doesn't seem to be way to get the _nightly_ date from the output of `rustc -Vv`
    // So we _assume_ the nightly day is the day after the commit-date found in `rustc -Vv` which
    // seems to be the common case, but it could be wrong and we'll end up building unusable crates
    let date = ctx.commit_date.succ();
    let hash = ctx.commit_hash;
    let ref src_dir = ctx.out_dir.join("src");
    let ref hash_file = src_dir.join(".commit-hash");

    if hash_file.exists() {
        let ref mut old_hash = String::with_capacity(hash.len());
        try!(try!(File::open(hash_file)).read_to_string(old_hash));

        if old_hash == hash {
            info!("source up to date");
            return;
        }
    }

    if src_dir.exists() {
        info!("purging the src directory");
        try!(fs::remove_dir_all(src_dir));
    }

    try!(fs::create_dir_all(src_dir));

    info!("fetching source tarball");
    let handle = http::Handle::new();
    let url = format!("http://static.rust-lang.org/dist/{}/rustc-nightly-src.tar.gz",
                      date.format("%Y-%m-%d"));
    let resp = try!(handle.timeout(300_000).get(&url[..]).follow_redirects(true).exec());

    assert_eq!(resp.get_code(), 200);

    info!("unpacking source tarball");
    let decoder = try!(flate2::read::GzDecoder::new(resp.get_body()));
    let mut archive = tar::Archive::new(decoder);
    for entry in try!(archive.entries()) {
        let mut entry = try!(entry);
        let path = {
            let path = try!(entry.path());
            let mut components = path.components();
            components.next(); // skip rust-lang-rust-<...>
            let next = components.next().and_then(|s| s.as_os_str().to_str());
            if next != Some("src") {
                continue;
            }
            components.as_path().to_path_buf()
        };
        try!(entry.unpack(&src_dir.join(path)));
    }

    info!("creating .commit-hash file");
    try!(try!(File::create(hash_file)).write_all(hash.as_bytes()));
}

fn symlink_host_crates(ctx: &Context) {
    info!("symlinking host crates");

    let sys_root = try!(String::from_utf8(try!(Command::new("rustc")
                                                   .args(&["--print", "sysroot"])
                                                   .output())
                                              .stdout));
    let ref src = Path::new(sys_root.trim_right()).join(format!("lib/rustlib/{}", ctx.host));
    let ref rustlib_dir = ctx.out_dir.join(format!("{}/lib/rustlib", ctx.profile));
    let ref dst = rustlib_dir.join(ctx.host);

    try!(fs::create_dir_all(rustlib_dir));

    if dst.exists() {
        try!(fs::remove_dir_all(dst));
    }

    link_dirs(&src, &dst);
}

fn link_dirs(src: &Path, dst: &Path) {
    try!(fs::create_dir(&dst));

    for entry in try!(src.read_dir()) {
        let entry = try!(entry);
        let src = entry.path();
        let dst = dst.join(entry.file_name());
        if try!(entry.file_type()).is_dir() {
            link_dirs(&src, &dst);
        } else {
            try!(fs::hard_link(&src, &dst).or_else(|_| fs::copy(&src, &dst).map(|_| ())));
        }
    }
}

fn build_target_crates(ctx: &Context) {
    const LIB_RS: &'static [u8] = b"#![no_std]";

    let ref config = Config::parse();

    let temp_dir = try!(tempdir::TempDir::new("sysroot"));
    let temp_dir = temp_dir.path();

    // Create Cargo project
    let ref mut cargo = Command::new("cargo");
    cargo.current_dir(temp_dir);
    cargo.args(&["new", "--vcs", "none"]);
    if ctx.verbose {
        cargo.arg("--verbose");
    }
    assert!(try!(cargo.arg("sysroot").status()).success());

    let ref cargo_dir = temp_dir.join("sysroot");
    let ref src_dir = env::current_dir().unwrap().join(ctx.out_dir).join("src");

    let (ref triple, ref spec_file): (String, _) = match ctx.target {
        Target::Spec(path) => {
            let path = try!(fs::canonicalize(path));
            let triple = path.file_stem().unwrap().to_str().unwrap().into();

            (triple, path)
        }
        Target::Triple(triple) => (triple.into(), PathBuf::from(format!("{}.json", triple))),
    };

    // Add crates to build as dependencies
    let ref mut toml = try!(OpenOptions::new()
                                .write(true)
                                .append(true)
                                .open(cargo_dir.join("Cargo.toml")));
    let ref crates = config.crates(triple);
    info!("will build the following crates: {:?}", crates);
    for ref krate in crates {
        try!(writeln!(toml,
                      "{} = {{ path = \"{}\" }}",
                      krate,
                      src_dir.join(format!("lib{}", krate)).display()))
    }

    {
        let ref mut toml = String::new();
        try!(try!(File::open(cargo_dir.join("Cargo.toml"))).read_to_string(toml));
        debug!("sysroot's Cargo.toml: {}", toml);
    }

    // Rewrite lib.rs to only depend on libcore
    try!(try!(OpenOptions::new().write(true).truncate(true).open(cargo_dir.join("src/lib.rs")))
             .write_all(LIB_RS));

    if spec_file.exists() {
        info!("copy target specification file");
        try!(fs::copy(spec_file, cargo_dir.join(format!("{}.json", triple))));
    }

    info!("building the target crates");
    let ref mut cargo = Command::new("cargo");
    cargo.current_dir(cargo_dir);
    cargo.args(&["build", "--target", triple]);
    if ctx.profile == "release" {
        cargo.arg("--release");
    }
    if ctx.verbose {
        cargo.arg("--verbose");
    }
    assert!(try!(cargo.status()).success());

    info!("copy the target crates to the sysroot");
    let ref libdir = ctx.out_dir.join(format!("{}/lib/rustlib/{}/lib", ctx.profile, triple));
    let ref deps_dir = cargo_dir.join(format!("target/{}/{}/deps", triple, ctx.profile));

    try!(fs::create_dir_all(libdir));
    for entry in try!(fs::read_dir(deps_dir)) {
        let entry = try!(entry);

        try!(fs::copy(entry.path(), libdir.join(entry.file_name())));
    }
}
<|endoftext|>


<commit_before>use std::io::{self, BufReader, Write};
use std::io::BufRead;
use std::net::TcpStream;
use std::fs::File;

extern crate chrono;
use chrono::*;

extern crate stats;
use stats::median;

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

mod percentile;
use percentile::percentile;

mod http_status;

mod request_response;
use request_response::*;

pub fn open_logfile(path: &str, time_filter: Option<Duration>) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(Request::new_from_log_line(&line_value));

            if time_filter.is_none() ||
              (time_filter.is_some() && r.is_between_times(UTC::now().with_timezone(&r.time.timezone()) - time_filter.unwrap(), UTC::now().with_timezone(&r.time.timezone()))) {
                requests.push(r);
            }
        }

        if line_value.contains("<-") {
            let r = try!(Response::new_from_log_line(&line_value));
            responses.push(r);
        }

    }

    responses.sort_by_key(|r| r.id);

    Ok((requests, responses))
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct RequestLogAnalyzerResult {
    count: usize,
    max: usize,
    min: usize,
    avg: usize,
    median: usize,
    percentile90: usize,
}

pub fn analyze(request_response_pairs: &Vec<RequestResponsePair>) -> Option<RequestLogAnalyzerResult> {
    if request_response_pairs.len() == 0 {
        return None;
    }

    let times: Vec<i64> = request_response_pairs.iter()
        .map(|rr: &RequestResponsePair| -> i64 {rr.response.response_time.num_milliseconds() })
        .collect();

    let sum: usize = times.iter().sum::<i64>() as usize;
    let avg: usize = sum / times.len();

    let max: usize = *times.iter().max().unwrap() as usize;
    let min: usize = *times.iter().min().unwrap() as usize;

    let percentile90: usize = percentile(&times, 0.9) as usize;

    let median = median(times.into_iter()).unwrap() as usize;

    Some(RequestLogAnalyzerResult {
        count: request_response_pairs.len().into(),
        max: max,
        min: min,
        avg: avg,
        median: median,
        percentile90: percentile90,
    })
}

fn render_terminal(result: RequestLogAnalyzerResult) {
    println!("count:\t{}", result.count);
    println!("time.avg:\t{}", result.avg);
    println!("time.min:\t{}", result.min);
    println!("time.median:\t{}", result.median);
    println!("time.90percent:\t{}", result.percentile90);
    println!("time.max:\t{}", result.max);
}

pub fn render_graphite<T: Write>(result: RequestLogAnalyzerResult, time: DateTime<FixedOffset>, mut stream: T) {
    let mut write = |text: String| {
        stream.write(
            format!("{} {}\n", text, time.timestamp() )
            .as_bytes()
        );
    };

    write(format!("requests.count {}", result.count));
    write(format!("requests.time.max {}", result.max));
    write(format!("requests.time.min {}", result.min));
    write(format!("requests.time.avg {}", result.avg));
    write(format!("requests.time.median {}", result.median));
    write(format!("requests.time.90percent {}", result.percentile90));
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(true)
            .help("Log file to analyze")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("graphite-server")
            .value_name("GRAPHITE_SERVER")
            .long("graphite-server")
            .help("Send values to this Graphite server instead of stdout")
            .takes_value(true))
        .arg(Arg::with_name("graphite-port")
            .value_name("GRAPHITE_PORT")
            .long("graphite-port")
            .takes_value(true)
            .default_value("2003"))
        .arg(Arg::with_name("graphite-prefix")
            .value_name("GRAPHITE_PREFIX")
            .long("graphite-prefix")
            .help("Prefix for Graphite key, e.g. 'servers.prod.publisher1'")
            .takes_value(true))
        .get_matches()
}

fn main() {
    let args = parse_args();

    let filename = args.value_of("filename").unwrap();

    let time_filter = match args.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let lines = open_logfile(filename, time_filter);
    let (requests, responses) = lines.unwrap();

    let time_zone = &requests[0].time.timezone();

    let pairs: Vec<RequestResponsePair> = pair_requests_responses(requests, responses)
        .into_iter()
        .filter(|rr| rr.matches_include_exclude_filter(
            args.value_of("include_term"),
            args.value_of("exclude_term")
        ))
        .collect();

    if args.is_present("graphite-server") {
        let stream = TcpStream::connect(
            (
                args.value_of("graphite-server").unwrap(),
                args.value_of("graphite-port").unwrap().parse().unwrap()
            )
        ).unwrap();

        match analyze(&pairs) {
            Some(result) => render_graphite(result, UTC::now().with_timezone(time_zone), stream),
            None => println!("No matching log lines in file.")
        }
    } else {
        match analyze(&pairs) {
            Some(result) => render_terminal(result),
            None => println!("No matching log lines in file.")
        }
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    use request_response::*;
    extern crate chrono;
    use chrono::*;
    use std::str;
    use std::io::prelude::Write;
    use std::io::{self, BufReader};

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_open_logfile_time_filter() {
        let time_filter: Duration = Duration::minutes(1);
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 0);

        let time_filter: Duration = Duration::minutes(52560000); // 100 years
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
    }

    #[test]
    fn test_pair_requests_resonses() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        let result = pair_requests_responses(requests, responses);

        assert_eq!(result.len(), 2);

        assert_eq!(result[0].request.id, result[0].response.id);
        assert_eq!(result[1].request.id, result[1].response.id);
    }

    #[test]
    fn test_request_log_analyzer_result() {
        let lines = open_logfile("src/test/response-time-calculations.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = Some(RequestLogAnalyzerResult {
            count: 3,
            max: 100,
            min: 1,
            avg: 37,
            median: 10,
            percentile90: 100,
        });

        assert_eq!(result, expected);
    }

    #[test]
    fn test_request_log_analyze_none_matching() {
        let lines = open_logfile("src/test/simple-1.log", Some(Duration::minutes(0)));
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = None;

        assert_eq!(result, expected);
    }

    #[test]
    fn test_90_percentile_calculation() {
        let lines = open_logfile("src/test/percentile.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result: RequestLogAnalyzerResult = analyze(&request_response_pairs).unwrap();

        assert_eq!(result.percentile90, 9);
    }

    struct MockTcpStream {
        write_calls: Vec<String>,
    }

    impl Write for MockTcpStream {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.write_calls.push(str::from_utf8(buf).unwrap().to_string());
            Ok(1)
        }

        fn flush(&mut self) -> io::Result<()> { Ok(()) }
    }

    #[test]
    fn test_render_graphite() {
        let mut mock_tcp_stream = MockTcpStream {
            write_calls: vec![]
        };

        render_graphite(RequestLogAnalyzerResult {
                count: 3,
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            },
            DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200", "%d/%b/%Y:%H:%M:%S %z").unwrap(),
            &mut mock_tcp_stream
        );

        assert_eq!(&mock_tcp_stream.write_calls[0], "requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1], "requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2], "requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3], "requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4], "requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5], "requests.time.90percent 100 1474576919\n");
    }
}
<commit_msg>Enable prefix use (optional)<commit_after>use std::io::{self, BufReader, Write};
use std::io::BufRead;
use std::net::TcpStream;
use std::fs::File;

extern crate chrono;
use chrono::*;

extern crate stats;
use stats::median;

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

mod percentile;
use percentile::percentile;

mod http_status;

mod request_response;
use request_response::*;

pub fn open_logfile(path: &str, time_filter: Option<Duration>) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(Request::new_from_log_line(&line_value));

            if time_filter.is_none() ||
              (time_filter.is_some() && r.is_between_times(UTC::now().with_timezone(&r.time.timezone()) - time_filter.unwrap(), UTC::now().with_timezone(&r.time.timezone()))) {
                requests.push(r);
            }
        }

        if line_value.contains("<-") {
            let r = try!(Response::new_from_log_line(&line_value));
            responses.push(r);
        }

    }

    responses.sort_by_key(|r| r.id);

    Ok((requests, responses))
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct RequestLogAnalyzerResult {
    count: usize,
    max: usize,
    min: usize,
    avg: usize,
    median: usize,
    percentile90: usize,
}

pub fn analyze(request_response_pairs: &Vec<RequestResponsePair>) -> Option<RequestLogAnalyzerResult> {
    if request_response_pairs.len() == 0 {
        return None;
    }

    let times: Vec<i64> = request_response_pairs.iter()
        .map(|rr: &RequestResponsePair| -> i64 {rr.response.response_time.num_milliseconds() })
        .collect();

    let sum: usize = times.iter().sum::<i64>() as usize;
    let avg: usize = sum / times.len();

    let max: usize = *times.iter().max().unwrap() as usize;
    let min: usize = *times.iter().min().unwrap() as usize;

    let percentile90: usize = percentile(&times, 0.9) as usize;

    let median = median(times.into_iter()).unwrap() as usize;

    Some(RequestLogAnalyzerResult {
        count: request_response_pairs.len().into(),
        max: max,
        min: min,
        avg: avg,
        median: median,
        percentile90: percentile90,
    })
}

fn render_terminal(result: RequestLogAnalyzerResult) {
    println!("count:\t{}", result.count);
    println!("time.avg:\t{}", result.avg);
    println!("time.min:\t{}", result.min);
    println!("time.median:\t{}", result.median);
    println!("time.90percent:\t{}", result.percentile90);
    println!("time.max:\t{}", result.max);
}

pub fn render_graphite<T: Write>(result: RequestLogAnalyzerResult, time: DateTime<FixedOffset>, prefix: Option<&str>, mut stream: T) {
    let prefix_text: &str;
    let prefix_separator: &str;

    match prefix {
        Some(p) => {
            prefix_text = p;
            prefix_separator = ".";
        }
        None => {
            prefix_text = "";
            prefix_separator = "";
        }
    };

    let mut write = |text: String| {
        stream.write(
            format!("{}{}{} {}\n", prefix_text, prefix_separator, text, time.timestamp() )
            .as_bytes()
        );
    };

    write(format!("requests.count {}", result.count));
    write(format!("requests.time.max {}", result.max));
    write(format!("requests.time.min {}", result.min));
    write(format!("requests.time.avg {}", result.avg));
    write(format!("requests.time.median {}", result.median));
    write(format!("requests.time.90percent {}", result.percentile90));
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(true)
            .help("Log file to analyze")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("graphite-server")
            .value_name("GRAPHITE_SERVER")
            .long("graphite-server")
            .help("Send values to this Graphite server instead of stdout")
            .takes_value(true))
        .arg(Arg::with_name("graphite-port")
            .value_name("GRAPHITE_PORT")
            .long("graphite-port")
            .takes_value(true)
            .default_value("2003"))
        .arg(Arg::with_name("graphite-prefix")
            .value_name("GRAPHITE_PREFIX")
            .long("graphite-prefix")
            .help("Prefix for Graphite key, e.g. 'servers.prod.publisher1'")
            .takes_value(true))
        .get_matches()
}

fn main() {
    let args = parse_args();

    let filename = args.value_of("filename").unwrap();

    let time_filter = match args.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let lines = open_logfile(filename, time_filter);
    let (requests, responses) = lines.unwrap();

    let time_zone = &requests[0].time.timezone();

    let pairs: Vec<RequestResponsePair> = pair_requests_responses(requests, responses)
        .into_iter()
        .filter(|rr| rr.matches_include_exclude_filter(
            args.value_of("include_term"),
            args.value_of("exclude_term")
        ))
        .collect();

    if args.is_present("graphite-server") {
        let stream = TcpStream::connect(
            (
                args.value_of("graphite-server").unwrap(),
                args.value_of("graphite-port").unwrap().parse().unwrap()
            )
        ).unwrap();

        match analyze(&pairs) {
            Some(result) => render_graphite(result, UTC::now().with_timezone(time_zone), args.value_of("graphite-prefix"), stream),
            None => println!("No matching log lines in file.")
        }
    } else {
        match analyze(&pairs) {
            Some(result) => render_terminal(result),
            None => println!("No matching log lines in file.")
        }
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    use request_response::*;
    extern crate chrono;
    use chrono::*;
    use std::str;
    use std::io::prelude::Write;
    use std::io::{self, BufReader};

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_open_logfile_time_filter() {
        let time_filter: Duration = Duration::minutes(1);
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 0);

        let time_filter: Duration = Duration::minutes(52560000); // 100 years
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
    }

    #[test]
    fn test_pair_requests_resonses() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        let result = pair_requests_responses(requests, responses);

        assert_eq!(result.len(), 2);

        assert_eq!(result[0].request.id, result[0].response.id);
        assert_eq!(result[1].request.id, result[1].response.id);
    }

    #[test]
    fn test_request_log_analyzer_result() {
        let lines = open_logfile("src/test/response-time-calculations.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = Some(RequestLogAnalyzerResult {
            count: 3,
            max: 100,
            min: 1,
            avg: 37,
            median: 10,
            percentile90: 100,
        });

        assert_eq!(result, expected);
    }

    #[test]
    fn test_request_log_analyze_none_matching() {
        let lines = open_logfile("src/test/simple-1.log", Some(Duration::minutes(0)));
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = None;

        assert_eq!(result, expected);
    }

    #[test]
    fn test_90_percentile_calculation() {
        let lines = open_logfile("src/test/percentile.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result: RequestLogAnalyzerResult = analyze(&request_response_pairs).unwrap();

        assert_eq!(result.percentile90, 9);
    }

    struct MockTcpStream {
        write_calls: Vec<String>,
    }

    impl Write for MockTcpStream {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.write_calls.push(str::from_utf8(buf).unwrap().to_string());
            Ok(1)
        }

        fn flush(&mut self) -> io::Result<()> { Ok(()) }
    }

    #[test]
    fn test_render_graphite() {
        let mut mock_tcp_stream = MockTcpStream {
            write_calls: vec![]
        };

        render_graphite(RequestLogAnalyzerResult {
                count: 3,
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            },
            DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200", "%d/%b/%Y:%H:%M:%S %z").unwrap(),
            None,
            &mut mock_tcp_stream
        );

        assert_eq!(&mock_tcp_stream.write_calls[0], "requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1], "requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2], "requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3], "requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4], "requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5], "requests.time.90percent 100 1474576919\n");
    }

    #[test]
    fn test_render_graphite_prefix() {
        let mut mock_tcp_stream = MockTcpStream {
            write_calls: vec![]
        };

        render_graphite(RequestLogAnalyzerResult {
                count: 3,
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            },
            DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200", "%d/%b/%Y:%H:%M:%S %z").unwrap(),
            Some("my.prefix"),
            &mut mock_tcp_stream
        );

        assert_eq!(&mock_tcp_stream.write_calls[0], "my.prefix.requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1], "my.prefix.requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2], "my.prefix.requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3], "my.prefix.requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4], "my.prefix.requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5], "my.prefix.requests.time.90percent 100 1474576919\n");
    }
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;

use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use piston::input::{ Button };

pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    rotation: f64,   // Rotation for the square.
    position: [f64; 2],
    target: [f64; 2],
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
        const RED:   [f32; 4] = [1.0, 0.0, 0.0, 1.0];

        let square = rectangle::square(0.0, 0.0, 50.0);
        let rotation = self.rotation;
        let pos = self.position;

        self.gl.draw(args.viewport(), |c, gl| {
            // Clear the screen.
            clear(GREEN, gl);

            let transform = c.transform.trans(pos[0], pos[1])
                                       .rot_rad(rotation)
                                       .trans(-25.0, -25.0);

            // Draw a box rotating around the middle of the screen.
            rectangle(RED, square, transform, gl);
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        // Rotate 2 radians per second.
        self.rotation += 2.0 * args.dt;

        let diff = [self.target[0]-self.position[0], self.target[1]-self.position[1]];
        self.position[0] += diff[0]/2.0*args.dt;
        self.position[1] += diff[1]/2.0*args.dt;
    }
}

fn main() {
    let opengl = OpenGL::_3_2;

    // Create an Glutin window.
    let window = Window::new(
        WindowSettings::new(
            "spinning-square",
            [200, 200]
        )
        .exit_on_esc(true)
    );

    // Create a new game and run it.
    let mut app = App {
        gl: GlGraphics::new(opengl),
        rotation: 0.0,
        position: [50.0, 50.0],
        target: [50.0, 50.0],
    };

    let mut cursor = [0.0,0.0];

    for e in window.events() {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }
        if let Some(Button::Mouse(button)) = e.press_args() {
            println!("Pressed mouse button '{:?}'", button);
            app.target = cursor;
        }
        e.mouse_cursor(|x, y| {
            cursor = [x, y];
            println!("Mouse moved '{} {}'", x, y);
        });

        if let Some(u) = e.update_args() {
            app.update(&u);
        }
    }
}

<commit_msg>Move rectangle into its own struct<commit_after>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;

use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use piston::input::{ Button };
use piston::input::mouse::MouseButton;

pub struct Square {
    rotation: f64,   // Rotation for the square.
    position: [f64; 2],
    target: [f64; 2],
    selected: bool,
}

impl Square {
    fn new(position: [f64;2]) -> Square {
        Square {
            rotation: 0.0,
            position: position,
            target: position,
            selected: false
        }
    }
}

pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    square: Square,
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
        const RED:   [f32; 4] = [1.0, 0.0, 0.0, 1.0];
        const BLUE:  [f32; 4] = [0.0, 0.0, 1.0, 1.0];

        let square = rectangle::square(0.0, 0.0, 50.0);
        let rotation = self.square.rotation;
        let pos = self.square.position;
        let selected = self.square.selected;

        self.gl.draw(args.viewport(), |c, gl| {
            // Clear the screen.
            clear(GREEN, gl);

            let transform = c.transform.trans(pos[0], pos[1])
                                       .rot_rad(rotation)
                                       .trans(-25.0, -25.0);

            // Draw a box rotating around the middle of the screen.
            if selected {
                rectangle(RED, square, transform, gl);
            } else {
                rectangle(BLUE, square, transform, gl);
            }
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        // Rotate 2 radians per second.
        self.square.rotation += 2.0 * args.dt;

        let diff = [self.square.target[0]-self.square.position[0], self.square.target[1]-self.square.position[1]];
        self.square.position[0] += diff[0]/2.0*args.dt;
        self.square.position[1] += diff[1]/2.0*args.dt;
    }
}

fn main() {
    let opengl = OpenGL::_3_2;

    // Create an Glutin window.
    let window = Window::new(
        WindowSettings::new(
            "spinning-square",
            [200, 200]
        )
        .exit_on_esc(true)
    );

    // Create a new game and run it.
    let mut app = App {
        gl: GlGraphics::new(opengl),
        square: Square::new([50.0, 50.0]),
    };

    let mut cursor = [0.0,0.0];

    for e in window.events() {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }
        if let Some(Button::Mouse(button)) = e.press_args() {
            match button {
                MouseButton::Left  => println!("Pressed left mouse button '{:?}'", button),
                MouseButton::Right => app.square.target = cursor,
                _ => println!("Pressed mouse button '{:?}'", button),
            }
        }
        e.mouse_cursor(|x, y| {
            cursor = [x, y];
            println!("Mouse moved '{} {}'", x, y);
        });

        if let Some(u) = e.update_args() {
            app.update(&u);
        }
    }
}

<|endoftext|>


<commit_before>#![feature(convert)]
extern crate byteorder;
extern crate rocksdb;

use byteorder::{BigEndian, WriteBytesExt};

struct Error(i32, String);
type Result<T> = std::result::Result<T, Error>;

fn encode(key :&[u8], ver :u64) -> Result<Vec<u8>> {
    let mut data = key;
    let mut new_key = Vec::with_capacity(key.len() + 20);

    if data.len() > 0 && data[0] == b'\xff' {
		// we must escape 0xFF here to guarantee encoded value < InfiniteValue \xFF\xFF.
        new_key.push(b'\xff');
        new_key.push(b'\x00');
        data = &data[1..];
	}
    for c in data {
        match *c {
            b'\x00' =>  {
                new_key.push(b'\x00');
                new_key.push(b'\xff');
            }
            _ => {
                new_key.push(*c);
            }
        }
    }
    new_key.push(b'\x00');
    new_key.push(b'\x01');

    // put version
    let mut wtr = Vec::with_capacity(8);
    wtr.write_u64::<BigEndian>(ver).unwrap();
    new_key.extend(&*wtr);

    Ok(new_key)
}

fn decode(encoded :&[u8]) -> Result<(Vec<u8>, u64)> {
    let mut data = encoded;
    let mut key = Vec::new();

    if data[0] == b'\xff' {
        if data[1] != b'\x00' {
            return Err(Error(-1, "invalid format".to_string()))
        }
        key.push(data[0]);
        data = &data[2..]
    }
    for (i, c) in data.iter().enumerate() {
        // find \x00 => {\xff, \x01 -> term}
        match *c {
            b'\x00' => {
                if i + 1 >= data.len() {
                    return Err(Error(-2, "invalid format".to_string()))
                }
                match data[i+1] {
                    b'\xff' => {
                        key.push(b'\x00');
                        continue
                    }
                    b'\x01' => {
                        break
                    }
                    _ => {
                        return Err(Error(-3, "invalid format".to_string()))
                    }
                }
            }
            _ => {
                key.push(*c);
            }
        }
    }
    Ok((key, 0))
}

trait MvccDB {
    fn put(&self, key :&[u8], val :&[u8], ver :u64) -> Result<()>;
    fn get(&self, key :&[u8], ver :u64) -> Result<&[u8]>;
}

struct MvccRocks;
impl MvccDB for MvccRocks {
    fn put(&self, key :&[u8], val :&[u8], ver :u64) -> Result<()> {
        println!("put {:?}, {:?}, {}", key, val, ver);
        Ok(())
    }

    fn get(&self, key :&[u8], ver :u64) -> Result<&[u8]> {
        println!("get {:?}, {}", key, ver);
        Ok(b"hello")
    }
}

fn main() {
    let b = Box::new(MvccRocks);
    match b.put(b"hello", b"world", 1) {
        Ok(()) => println!("ok"),
        Err(Error(code, msg)) => println!("err code: {:?}, msg: {:?}", code, msg),
    }

    match encode(b"\xffhe\x00llo", 23) {
        Ok(key) => {
            println!("encoded: {:?}", key);
            match decode(key.as_slice()) {
                Ok((ori, ver)) => {
                    println!("decoded: {:?}", ori);
                }
                _ => (),
            }
        }
        _ => (),
    }
}
<commit_msg>*: add auto fmt to vim<commit_after>#![feature(convert)]
extern crate byteorder;
extern crate rocksdb;

use byteorder::{BigEndian, WriteBytesExt};

struct Error(i32, String);
type Result<T> = std::result::Result<T, Error>;

fn encode(key :&[u8], ver :u64) -> Result<Vec<u8>> {
    let mut data = key;
    let mut new_key = Vec::with_capacity(key.len() + 20);

    if data.len() > 0 && data[0] == b'\xff' {
        // we must escape 0xFF here to guarantee encoded value < InfiniteValue \xFF\xFF.
        new_key.push(b'\xff');
        new_key.push(b'\x00');
        data = &data[1..];
    }
    for c in data {
        match *c {
            b'\x00' =>  {
                new_key.push(b'\x00');
                new_key.push(b'\xff');
            }
            _ => {
                new_key.push(*c);
            }
        }
    }
    new_key.push(b'\x00');
    new_key.push(b'\x01');

    // put version
    let mut wtr = Vec::with_capacity(8);
    wtr.write_u64::<BigEndian>(ver).unwrap();
    new_key.extend(&*wtr);

    Ok(new_key)
}

fn decode(encoded :&[u8]) -> Result<(Vec<u8>, u64)> {
    let mut data = encoded;
    let mut key = Vec::new();

    if data[0] == b'\xff' {
        if data[1] != b'\x00' {
            return Err(Error(-1, "invalid format".to_string()))
        }
        key.push(data[0]);
        data = &data[2..]
    }
    for (i, c) in data.iter().enumerate() {
        // find \x00 => {\xff, \x01 -> term}
        match *c {
            b'\x00' => {
                if i + 1 >= data.len() {
                    return Err(Error(-2, "invalid format".to_string()))
                }
                match data[i+1] {
                    b'\xff' => {
                        key.push(b'\x00');
                        continue
                    }
                    b'\x01' => {
                        break
                    }
                    _ => {
                        return Err(Error(-3, "invalid format".to_string()))
                    }
                }
            }
            _ => {
                key.push(*c);
            }
        }
    }
    Ok((key, 0))
}

trait MvccDB {
    fn put(&self, key :&[u8], val :&[u8], ver :u64) -> Result<()>;
    fn get(&self, key :&[u8], ver :u64) -> Result<&[u8]>;
}

struct MvccRocks;
impl MvccDB for MvccRocks {
    fn put(&self, key :&[u8], val :&[u8], ver :u64) -> Result<()> {
        println!("put {:?}, {:?}, {}", key, val, ver);
        Ok(())
    }

    fn get(&self, key :&[u8], ver :u64) -> Result<&[u8]> {
        println!("get {:?}, {}", key, ver);
        Ok(b"hello")
    }
}

fn main() {
    let b = Box::new(MvccRocks);
    match b.put(b"hello", b"world", 1) {
        Ok(()) => println!("ok"),
        Err(Error(code, msg)) => println!("err code: {:?}, msg: {:?}", code, msg),
    }

    match encode(b"\xffhe\x00llo", 23) {
        Ok(key) => {
            println!("encoded: {:?}", key);
            match decode(key.as_slice()) {
                Ok((ori, ver)) => {
                    println!("decoded: {:?}", ori);
                }
                _ => (),
            }
        }
        _ => (),
    }
}
<|endoftext|>