<commit_before>use std::env;
use std::io::prelude::*;
use std::fs::File;
use std::str::Chars;
use std::rc::Rc;
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq)]
enum Builtin {
    dot,
    plus,
    minus,
    times,
    divide,
}

impl Builtin {
    fn call(&self, stack: &mut Vec<i32>) {
        match *self {
            Builtin::dot => print!("{}", stack.pop().unwrap()),
            Builtin::plus => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 + n2);
            },
            Builtin::minus => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 - n2);
            },
            Builtin::times => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 * n2);
            },
            Builtin::divide => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 / n2);
            },
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
enum Word {
    custom(Rc<Vec<Branch>>),
    builtin(Builtin),
    int(i32),
    float(f32),
    parenthesis,
    colon,
    semicolon,
    _if,
    then,
    _else,
    dotquote,
}

#[derive(Debug, PartialEq)]
enum Branch {
    custom(Rc<Vec<Branch>>),
    builtin(Builtin),
    int(i32),
    float(f32),
    ifelse(Vec<Branch>, Vec<Branch>),
    dotquote(String)
}

struct Dictionary {
    items: HashMap<String, Word>
}

impl Dictionary {
    fn get(&self, name: &str) -> Option<Word> {
        if let Some(word) = self.items.get(name) {
            Some(word.clone())
        } else if let Ok(num) = name.parse::<i32>() {
            Some(Word::int(num))
        } else if let Ok(num) = name.parse::<f32>() {
            Some(Word::float(num))
        } else {
            None
        }
    }
    fn set(&mut self, name: &str, value: Word) {
        if self.items.contains_key(name) {
            // XXX display debug message another way
            println!("Redefined {}", name);
        }
        self.items.insert(name.to_owned(), value);
    }
}

impl Default for Dictionary {
    fn default() -> Dictionary {
        let mut dict = HashMap::new();
        dict.insert(".\"".to_owned(), Word::dotquote);
        dict.insert(".".to_owned(), Word::builtin(Builtin::dot));
        dict.insert("+".to_owned(), Word::builtin(Builtin::plus));
        dict.insert("-".to_owned(), Word::builtin(Builtin::minus));
        dict.insert("*".to_owned(), Word::builtin(Builtin::times));
        dict.insert("/".to_owned(), Word::builtin(Builtin::divide));
        dict.insert(":".to_owned(), Word::colon);
        dict.insert(";".to_owned(), Word::semicolon);
        dict.insert("if".to_owned(), Word::_if);
        dict.insert("then".to_owned(), Word::then);
        dict.insert("else".to_owned(), Word::_else);
        dict.insert("(".to_owned(), Word::parenthesis);
        Dictionary{items: dict}
    }
}

fn next_word(chars: &mut Chars) -> Option<String> {
    let mut word = String::new();

    while let Some(c) = chars.next() {
        if (c == ' ' || c == '\n') {
            if word.is_empty() {
                continue;
            }
            break;
        }
        word.push(c);
    }

    if word.is_empty() {
        // No input left
        None
    } else {
        Some(word)
    }
}

fn parse_word(word_str: &str, chars: &mut Chars, dict: &mut Dictionary) -> Option<Branch> {
    if let Some(word) = dict.get(word_str) {
        match word {
            Word::custom(x) => Some(Branch::custom(x)),
            Word::builtin(x) => Some(Branch::builtin(x)),
            Word::int(x) => Some(Branch::int(x)),
            Word::float(x) => Some(Branch::float(x)),
            Word::dotquote => {
                let text = chars.take_while(|x| *x != '"').collect();
                Some(Branch::dotquote(text))
            },
            Word::parenthesis => {
                // Consumes all characters up to next )
                chars.take_while(|x| *x != ')').count();
                None
            },
            Word::colon => {
                let name = next_word(chars).unwrap();
                let mut inner_branches: Vec<Branch> = Vec::new();
                while let Some(inner_word_str) = next_word(chars) {
                    if dict.get(&inner_word_str) == Some(Word::semicolon) {
                        break;
                    }
                    if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                        inner_branches.push(branch);
                    }
                }
                dict.set(&name, Word::custom(Rc::new(inner_branches)));
                None
            },
            Word::_if => {
                let mut ifbranches = Vec::new();
                let mut elsebranches = Vec::new();
                while let Some(inner_word_str) = next_word(chars) {
                    if dict.get(&inner_word_str) == Some(Word::_else) {
                        while let Some(inner_word_str) = next_word(chars) {
                            if dict.get(&inner_word_str) == Some(Word::then) {
                                break;
                            }
                    }
                        if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                            elsebranches.push(branch);
                        }
                        break;
                    } else if dict.get(&inner_word_str) == Some(Word::then) {
                        break;
                    }
                    if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                        ifbranches.push(branch);
                    }
                }
                Some(Branch::ifelse(ifbranches, elsebranches))
            },
            Word::semicolon | Word::_else | Word::then => {
                panic!("Invalid here");
            }
        }
    } else {
        panic!("Word '{}' not in dictionary.", word_str);
    }
}

fn parse(code: &mut Chars) -> Vec<Branch> {
    let mut branches: Vec<Branch> = Vec::new();
    let mut dict = Dictionary::default();
    while let Some(word_str) = next_word(code) {
        if let Some(branch) = parse_word(&word_str, code, &mut dict) {
            branches.push(branch);
        }
    }

    branches
}

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("{:#?}", parse(&mut code.chars()));
}
<commit_msg>Basic execution now works<commit_after>use std::env;
use std::io::prelude::*;
use std::fs::File;
use std::str::Chars;
use std::rc::Rc;
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq)]
enum Builtin {
    dot,
    plus,
    minus,
    times,
    divide,
}

impl Builtin {
    fn call(&self, stack: &mut Vec<i32>) {
        match *self {
            Builtin::dot => print!("{}", stack.pop().unwrap()),
            Builtin::plus => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 + n2);
            },
            Builtin::minus => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 - n2);
            },
            Builtin::times => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 * n2);
            },
            Builtin::divide => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 / n2);
            },
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
enum Word {
    custom(Rc<Vec<Branch>>),
    builtin(Builtin),
    int(i32),
    float(f32),
    parenthesis,
    colon,
    semicolon,
    _if,
    then,
    _else,
    dotquote,
}

#[derive(Debug, PartialEq)]
enum Branch {
    custom(Rc<Vec<Branch>>),
    builtin(Builtin),
    int(i32),
    float(f32),
    ifelse(Vec<Branch>, Vec<Branch>),
    dotquote(String)
}

impl Branch {
    fn call(&self, stack: &mut Vec<i32>) {
        match self {
            &Branch::custom(ref branches) => {
                for branch in branches.iter() {
                    branch.call(stack);
                }
            },
            &Branch::builtin(ref builtin) => builtin.call(stack),
            &Branch::int(int) => stack.push(int),
            &Branch::float(float) => stack.push(float as i32), //XXX
            &Branch::ifelse(ref ifbranches, ref elsebranches) => {
                if stack.pop().unwrap() == 0 {
                    for branch in ifbranches.iter() {
                        branch.call(stack);
                    }
                } else {
                    for branch in elsebranches.iter() {
                        branch.call(stack);
                    }
                }
            },
            &Branch::dotquote(ref text) => print!("{}", text),
        }
    }
}

struct Dictionary {
    items: HashMap<String, Word>
}

impl Dictionary {
    fn get(&self, name: &str) -> Option<Word> {
        if let Some(word) = self.items.get(name) {
            Some(word.clone())
        } else if let Ok(num) = name.parse::<i32>() {
            Some(Word::int(num))
        } else if let Ok(num) = name.parse::<f32>() {
            Some(Word::float(num))
        } else {
            None
        }
    }
    fn set(&mut self, name: &str, value: Word) {
        if self.items.contains_key(name) {
            // XXX display debug message another way
            println!("Redefined {}", name);
        }
        self.items.insert(name.to_owned(), value);
    }
}

impl Default for Dictionary {
    fn default() -> Dictionary {
        let mut dict = HashMap::new();
        dict.insert(".\"".to_owned(), Word::dotquote);
        dict.insert(".".to_owned(), Word::builtin(Builtin::dot));
        dict.insert("+".to_owned(), Word::builtin(Builtin::plus));
        dict.insert("-".to_owned(), Word::builtin(Builtin::minus));
        dict.insert("*".to_owned(), Word::builtin(Builtin::times));
        dict.insert("/".to_owned(), Word::builtin(Builtin::divide));
        dict.insert(":".to_owned(), Word::colon);
        dict.insert(";".to_owned(), Word::semicolon);
        dict.insert("if".to_owned(), Word::_if);
        dict.insert("then".to_owned(), Word::then);
        dict.insert("else".to_owned(), Word::_else);
        dict.insert("(".to_owned(), Word::parenthesis);
        Dictionary{items: dict}
    }
}

fn next_word(chars: &mut Chars) -> Option<String> {
    let mut word = String::new();

    while let Some(c) = chars.next() {
        if c == ' ' || c == '\n' {
            if word.is_empty() {
                continue;
            }
            break;
        }
        word.push(c);
    }

    if word.is_empty() {
        // No input left
        None
    } else {
        Some(word)
    }
}

fn parse_word(word_str: &str, chars: &mut Chars, dict: &mut Dictionary) -> Option<Branch> {
    if let Some(word) = dict.get(word_str) {
        match word {
            Word::custom(x) => Some(Branch::custom(x)),
            Word::builtin(x) => Some(Branch::builtin(x)),
            Word::int(x) => Some(Branch::int(x)),
            Word::float(x) => Some(Branch::float(x)),
            Word::dotquote => {
                let text = chars.take_while(|x| *x != '"').collect();
                Some(Branch::dotquote(text))
            },
            Word::parenthesis => {
                // Consumes all characters up to next )
                chars.take_while(|x| *x != ')').count();
                None
            },
            Word::colon => {
                let name = next_word(chars).unwrap();
                let mut inner_branches: Vec<Branch> = Vec::new();
                while let Some(inner_word_str) = next_word(chars) {
                    if dict.get(&inner_word_str) == Some(Word::semicolon) {
                        break;
                    }
                    if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                        inner_branches.push(branch);
                    }
                }
                dict.set(&name, Word::custom(Rc::new(inner_branches)));
                None
            },
            Word::_if => {
                let mut ifbranches = Vec::new();
                let mut elsebranches = Vec::new();
                while let Some(inner_word_str) = next_word(chars) {
                    if dict.get(&inner_word_str) == Some(Word::_else) {
                        while let Some(inner_word_str) = next_word(chars) {
                            if dict.get(&inner_word_str) == Some(Word::then) {
                                break;
                            }
                    }
                        if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                            elsebranches.push(branch);
                        }
                        break;
                    } else if dict.get(&inner_word_str) == Some(Word::then) {
                        break;
                    }
                    if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                        ifbranches.push(branch);
                    }
                }
                Some(Branch::ifelse(ifbranches, elsebranches))
            },
            Word::semicolon | Word::_else | Word::then => {
                panic!("Invalid here");
            }
        }
    } else {
        panic!("Word '{}' not in dictionary.", word_str);
    }
}

fn parse(code: &mut Chars) -> Vec<Branch> {
    let mut branches: Vec<Branch> = Vec::new();
    let mut dict = Dictionary::default();
    while let Some(word_str) = next_word(code) {
        if let Some(branch) = parse_word(&word_str, code, &mut dict) {
            branches.push(branch);
        }
    }

    branches
}

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let branches = parse(&mut code.chars());
    //println!("{:#?}", parse(&mut code.chars()));
    let mut stack = Vec::new();
    for branch in branches {
        branch.call(&mut stack);
    }
}
<|endoftext|>


<commit_before>extern crate sysfs_pwm;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate clap;

mod error;
mod rover;

use error::*;
use clap::ArgMatches;

const PWM_CHIP: u32 = 0;
const LEFT_PWM: u32 = 0;
const RIGHT_PWM: u32 = 1;

fn run(matches: ArgMatches) -> Result<()> {
    use rover::Rover;
    let rover = Rover::new(PWM_CHIP, LEFT_PWM, RIGHT_PWM)?;

    if let Some(_) = matches.subcommand_matches("disable") {
        rover.enable(false)
    } else if let Some(_) = matches.subcommand_matches("enable") {
        rover.enable(true)
    } else if let Some(_) = matches.subcommand_matches("stop") {
        rover.stop()
    } else if let Some(matches) = matches.subcommand_matches("speed") {
        let left = matches.value_of("LEFT").unwrap();
        let right = matches.value_of("RIGHT").unwrap_or(left);
        let left: i8 = left.parse::<i8>().chain_err(|| "failed to parse left speed")?;
        let right: i8 = right.parse::<i8>().chain_err(|| "failed to parse right speed")?;

        rover.set_speed(left, right)?;
        if !matches.is_present("dont-enable") {
            rover.enable(true)?;
        }
        Ok(())

    } else if let Some(_) = matches.subcommand_matches("unexport") {
        rover.unexport()
    } else {
        panic!("subcommand not recognised");
    }
}

fn main() {
    use clap::App;

    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml).get_matches();

    if let Err(ref e) = run(matches) {
        use std::io::Write;
        let stderr = &mut ::std::io::stderr();
        let errmsg = "Error writing to stderr";

        writeln!(stderr, "error: {}", e).expect(errmsg);

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

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

        ::std::process::exit(1);
    }
}
<commit_msg>Moves argument handling to the run function<commit_after>extern crate sysfs_pwm;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate clap;

mod error;
mod rover;

use error::*;

const PWM_CHIP: u32 = 0;
const LEFT_PWM: u32 = 0;
const RIGHT_PWM: u32 = 1;

fn run() -> Result<()> {
    use clap::App;
    use rover::Rover;

    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml).get_matches();

    let rover = Rover::new(PWM_CHIP, LEFT_PWM, RIGHT_PWM)?;

    if let Some(_) = matches.subcommand_matches("disable") {
        rover.enable(false)
    } else if let Some(_) = matches.subcommand_matches("enable") {
        rover.enable(true)
    } else if let Some(_) = matches.subcommand_matches("stop") {
        rover.stop()
    } else if let Some(matches) = matches.subcommand_matches("speed") {
        let left = matches.value_of("LEFT").unwrap();
        let right = matches.value_of("RIGHT").unwrap_or(left);
        let left: i8 = left.parse::<i8>().chain_err(|| "failed to parse left speed")?;
        let right: i8 = right.parse::<i8>().chain_err(|| "failed to parse right speed")?;

        rover.set_speed(left, right)?;
        if !matches.is_present("dont-enable") {
            rover.enable(true)?;
        }
        Ok(())

    } else if let Some(_) = matches.subcommand_matches("unexport") {
        rover.unexport()
    } else {
        panic!("subcommand not recognised");
    }
}

fn main() {
    if let Err(ref e) = run(matches) {
        use std::io::Write;
        let stderr = &mut ::std::io::stderr();
        let errmsg = "Error writing to stderr";

        writeln!(stderr, "error: {}", e).expect(errmsg);

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

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

        ::std::process::exit(1);
    }
}
<|endoftext|>


<commit_before>extern crate actix_web;
extern crate cookie;
extern crate get_if_addrs;
extern crate hyper;
extern crate itertools;
extern crate json;
extern crate log4rs;
#[macro_use]
extern crate log;
extern crate mustache;
#[cfg(feature = "mocking")]
extern crate reqwest_mock;
#[macro_use]
extern crate serde_derive;
extern crate time;
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;

#[cfg(test)]
extern crate crossbeam;
extern crate env_logger;

#[cfg(feature = "lambda")]
extern crate reqwest;
#[cfg(feature = "lambda")]
use reqwest::{Client, RedirectPolicy};

#[cfg(feature = "lambda")]
#[macro_use]
extern crate lambda_http;
#[cfg(feature = "lambda")]
use lambda_http::RequestExt;
#[cfg(feature = "lambda")]
use url::percent_encoding::percent_decode;
#[cfg(feature = "lambda")]
extern crate aws_lambda_events;
#[cfg(test)]
#[macro_use]
extern crate maplit;

use actix_web::{http::Method, server, App, HttpRequest, HttpResponse};
#[cfg(not(feature = "lambda"))]
use std::env;

#[cfg(feature = "lambda")]
use std::thread;

mod arrivals;
mod common;
mod favourite;
mod id;
mod nearby;
mod search;

fn root_handler(request: HttpRequest) -> HttpResponse {
    let data = common::mustache_favourites(&request);
    common::render_to_response("resources/templates/root.mustache", &data)
}

fn app() -> App {
    return App::new()
        .route("/", Method::GET, root_handler)
        .route("/search", Method::GET, search::search_handler)
        .route("/id/{id}", Method::GET, id::id_handler)
        .route("/nearby", Method::GET, nearby::nearby_handler)
        .route("/favourites", Method::GET, favourite::list_favourites)
        .route("/favourites", Method::POST, favourite::add_favourite)
        .route("/favourite-remove", Method::POST, favourite::remove_favourite)
        .route("/arrivals/{stopid}", Method::GET, arrivals::arrivals_handler);
}

#[cfg(not(feature = "lambda"))]
fn main() {
    log4rs::init_file("log.yaml", Default::default()).unwrap();
    let port = env::var("PORT")
        .unwrap_or("8000".to_string())
        .parse::<u16>()
        .unwrap();
    let mut server = server::new(|| app().finish());
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        let ip = iface.ip();
        info!("Listening on {}:{} for {}", ip, port, iface.name);
        server = server.bind((ip, port)).unwrap();
    }
    server.run();
}

#[cfg(feature = "lambda")]
fn main() {
    env_logger::init();
    thread::spawn(move || server::new(|| app().finish()).bind("0.0.0.0:3457").unwrap().run());
    // Don't worry about any redirects, as we need loops for /favourite to work
    let client = Client::builder()
        .redirect(RedirectPolicy::custom(|attempt| attempt.follow()))
        .build()
        .unwrap();
    lambda!(|request: lambda_http::Request, _context| {
        debug!("Req to inner: {:?}", request);
        let uri = &format!(
            "http://127.0.0.1:3457{}",
            &request
                .uri()
                .clone()
                .into_parts()
                .path_and_query
                .unwrap()
                .as_str()
        );
        debug!("Uri for inner: {}", uri);
        let mut req = client.clone().request(request.method().clone(), uri);
        for (key, value) in request.headers() {
            req = req.header(key, value);
        }
        for (key, value) in request.query_string_parameters().iter() {
            // ALB encodes the query parameters, and reqwest will do it again, so need to stop doing it twice!
            let mut value = percent_decode(value.as_bytes())
                .decode_utf8()
                .unwrap()
                .to_string();
            value = value.replace("+", " "); // Also need to decode the + characters, which percent_decode doesn't
            debug!("Query param: '{}' = '{}'", key, value);
            req = req.query(&[(key, value)]);
        }
        match request.body() {
            lambda_http::Body::Empty => {}
            lambda_http::Body::Text(val) => {
                req = req.body(val.clone());
            }
            lambda_http::Body::Binary(val) => {
                req = req.body(val.clone());
            }
        }
        debug!("New req: {:?}", req);
        let mut res = req.send().unwrap();
        debug!("Res: {:?}", res);
        let mut lambda_res = lambda_http::Response::builder();
        lambda_res.status(res.status());
        for (key, value) in res.headers() {
            lambda_res.header(key, value);
        }
        Ok(lambda_res.body(res.text().unwrap()).unwrap())
    });
}

#[cfg(test)]
mod tests {
    use super::{app, common, main};
    use actix_web::actix::System;
    use actix_web::{http, server, test, App, HttpMessage, HttpRequest, HttpResponse, State};
    #[cfg(feature = "lambda")]
    use aws_lambda_events::event::alb;
    use crossbeam::channel::unbounded;
    use crossbeam::{Receiver, Sender};
    use std::collections::HashMap;
    use std::{env, thread};

    #[derive(Debug, Clone)]
    struct AppState {
        req: Receiver<Result<(i32, serde_json::Value), ()>>,
        res: Sender<String>,
    }

    fn test_app(req: Receiver<Result<(i32, serde_json::Value), ()>>, res: Sender<String>) -> App<AppState> {
        App::with_state(AppState { req, res })
            .resource("/2018-06-01/runtime/invocation/1234/response", |r| {
                r.method(http::Method::POST)
                    .with(|(body, state): (String, State<AppState>)| {
                        debug!("Response body: {}", body);
                        state.res.send(body).unwrap();
                        HttpResponse::Ok()
                    })
            })
            .resource("/2018-06-01/runtime/invocation/next", |r| {
                r.method(http::Method::GET).with(|state: State<AppState>| {
                    let (id, body) = match state.req.clone().recv().unwrap() {
                        Ok(val) => val,
                        Err(_) => {
                            System::current().stop();
                            thread::park();
                            unimplemented!();
                        }
                    };
                    debug!("Got req: {}", id);
                    HttpResponse::Ok()
                        .header("Lambda-Runtime-Aws-Request-Id", id.to_string())
                        .header("Lambda-Runtime-Invoked-Function-Arn", "an-arn")
                        .header("Lambda-Runtime-Deadline-Ms", "1000")
                        .json(body)
                })
            })
            .default_resource(|r| {
                r.route().with(|(r, body): (HttpRequest<AppState>, String)| {
                    warn!("{:?}", r);
                    warn!("Body: {}", body);
                    HttpResponse::NotFound()
                })
            })
    }

    #[cfg(feature = "lambda")]
    #[test]
    fn lambda_test() {
        let (req_send, req_recv) = unbounded();
        let (res_send, res_recv) = unbounded();
        req_send.send(Ok((1234,
            serde_json::to_value(
            alb::AlbTargetGroupRequest {
                request_context: alb::AlbTargetGroupRequestContext {
                    elb: alb::ElbContext {
                        target_group_arn: Some("arn:aws:elasticloadbalancing:region:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09".to_string())
                    }
                },
                http_method: Some("GET".to_string()),
                path: Some("/".to_string()),
                query_string_parameters: HashMap::new(),
                multi_value_query_string_parameters: HashMap::new(),
                headers: hashmap!{
                    "accept".to_string() => "text/html,application/xhtml+xml".to_string(),
                    "accept-language".to_string() => "en-US,en;q=0.8".to_string(),
                    "content-type".to_string() => "text/plain".to_string(),
                    "cookie".to_string() => "cookies".to_string(),
                    "host".to_string() => "lambda-846800462-us-east-2.elb.amazonaws.com".to_string(),
                    "user-agent".to_string() => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)".to_string(),
                    "x-amzn-trace-id".to_string() => "Root=1-5bdb40ca-556d8b0c50dc66f0511bf520".to_string(),
                    "x-forwarded-for".to_string() => "72.21.198.66".to_string(),
                    "x-forwarded-port".to_string() => "443".to_string(),
                    "x-forwarded-proto".to_string() => "https".to_string()
                },
                multi_value_headers: HashMap::new(),
                is_base64_encoded: false,
                body: Some("request_body".to_string())
}).unwrap()))).unwrap();
        env_logger::init();
        thread::spawn(move || {
            server::new(move || test_app(req_recv.clone(), res_send.clone()).finish())
                .bind("0.0.0.0:3456")
                .unwrap()
                .run()
        });
        env::set_var("AWS_LAMBDA_FUNCTION_NAME", "foo");
        env::set_var("AWS_LAMBDA_FUNCTION_VERSION", "1");
        env::set_var("AWS_LAMBDA_LOG_STREAM_NAME", "log");
        env::set_var("AWS_LAMBDA_LOG_GROUP_NAME", "lg");
        env::set_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128");
        env::set_var("AWS_LAMBDA_RUNTIME_API", "127.0.0.1:3456");
        thread::spawn(|| main());
        let resp_raw = res_recv.recv().unwrap();
        let resp: alb::AlbTargetGroupResponse = serde_json::from_str(&resp_raw).unwrap();
        debug!("Response to main: {:#?}", resp);
        // shutdown
        req_send.send(Err(())).unwrap();
    }

    #[test]
    fn simple_search() {
        let mut srv = test::TestServer::with_factory(app);
        common::set_client(common::ClientType::TESTING);
        let request = srv
            .client(http::Method::GET, "/search?query=foo")
            .finish()
            .unwrap();
        let response = srv.execute(request.send()).unwrap();
        let body: String = String::from_utf8(srv.execute(response.body()).unwrap().to_vec()).unwrap();
        assert!(body.find("<title>Search: foo</title>").is_some(), body);
        assert!(response.status().is_success(), response.status());
    }
}
<commit_msg>Don't do any redirects, as it breaks the cookies<commit_after>extern crate actix_web;
extern crate cookie;
extern crate get_if_addrs;
extern crate hyper;
extern crate itertools;
extern crate json;
extern crate log4rs;
#[macro_use]
extern crate log;
extern crate mustache;
#[cfg(feature = "mocking")]
extern crate reqwest_mock;
#[macro_use]
extern crate serde_derive;
extern crate time;
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;

#[cfg(test)]
extern crate crossbeam;
extern crate env_logger;

#[cfg(feature = "lambda")]
extern crate reqwest;
#[cfg(feature = "lambda")]
use reqwest::{Client, RedirectPolicy};

#[cfg(feature = "lambda")]
#[macro_use]
extern crate lambda_http;
#[cfg(feature = "lambda")]
use lambda_http::RequestExt;
#[cfg(feature = "lambda")]
use url::percent_encoding::percent_decode;
#[cfg(feature = "lambda")]
extern crate aws_lambda_events;
#[cfg(test)]
#[macro_use]
extern crate maplit;

use actix_web::{http::Method, server, App, HttpRequest, HttpResponse};
#[cfg(not(feature = "lambda"))]
use std::env;

#[cfg(feature = "lambda")]
use std::thread;

mod arrivals;
mod common;
mod favourite;
mod id;
mod nearby;
mod search;

fn root_handler(request: HttpRequest) -> HttpResponse {
    let data = common::mustache_favourites(&request);
    common::render_to_response("resources/templates/root.mustache", &data)
}

fn app() -> App {
    return App::new()
        .route("/", Method::GET, root_handler)
        .route("/search", Method::GET, search::search_handler)
        .route("/id/{id}", Method::GET, id::id_handler)
        .route("/nearby", Method::GET, nearby::nearby_handler)
        .route("/favourites", Method::GET, favourite::list_favourites)
        .route("/favourites", Method::POST, favourite::add_favourite)
        .route("/favourite-remove", Method::POST, favourite::remove_favourite)
        .route("/arrivals/{stopid}", Method::GET, arrivals::arrivals_handler);
}

#[cfg(not(feature = "lambda"))]
fn main() {
    log4rs::init_file("log.yaml", Default::default()).unwrap();
    let port = env::var("PORT")
        .unwrap_or("8000".to_string())
        .parse::<u16>()
        .unwrap();
    let mut server = server::new(|| app().finish());
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        let ip = iface.ip();
        info!("Listening on {}:{} for {}", ip, port, iface.name);
        server = server.bind((ip, port)).unwrap();
    }
    server.run();
}

#[cfg(feature = "lambda")]
fn main() {
    env_logger::init();
    thread::spawn(move || server::new(|| app().finish()).bind("0.0.0.0:3457").unwrap().run());
    // Don't do any redirects so we get the /favourite cookies
    let client = Client::builder()
        .redirect(RedirectPolicy::none())
        .build()
        .unwrap();
    lambda!(|request: lambda_http::Request, _context| {
        debug!("Req to inner: {:?}", request);
        let uri = &format!(
            "http://127.0.0.1:3457{}",
            &request
                .uri()
                .clone()
                .into_parts()
                .path_and_query
                .unwrap()
                .as_str()
        );
        debug!("Uri for inner: {}", uri);
        let mut req = client.clone().request(request.method().clone(), uri);
        for (key, value) in request.headers() {
            req = req.header(key, value);
        }
        for (key, value) in request.query_string_parameters().iter() {
            // ALB encodes the query parameters, and reqwest will do it again, so need to stop doing it twice!
            let mut value = percent_decode(value.as_bytes())
                .decode_utf8()
                .unwrap()
                .to_string();
            value = value.replace("+", " "); // Also need to decode the + characters, which percent_decode doesn't
            debug!("Query param: '{}' = '{}'", key, value);
            req = req.query(&[(key, value)]);
        }
        match request.body() {
            lambda_http::Body::Empty => {}
            lambda_http::Body::Text(val) => {
                req = req.body(val.clone());
            }
            lambda_http::Body::Binary(val) => {
                req = req.body(val.clone());
            }
        }
        debug!("New req: {:?}", req);
        let mut res = req.send().unwrap();
        debug!("Res: {:?}", res);
        let mut lambda_res = lambda_http::Response::builder();
        lambda_res.status(res.status());
        for (key, value) in res.headers() {
            lambda_res.header(key, value);
        }
        debug!("lambda_res: {:?}", lambda_res);
        Ok(lambda_res.body(res.text().unwrap()).unwrap())
    });
}

#[cfg(test)]
mod tests {
    use super::{app, common, main};
    use actix_web::actix::System;
    use actix_web::{http, server, test, App, HttpMessage, HttpRequest, HttpResponse, State};
    #[cfg(feature = "lambda")]
    use aws_lambda_events::event::alb;
    use crossbeam::channel::unbounded;
    use crossbeam::{Receiver, Sender};
    use std::collections::HashMap;
    use std::{env, thread};

    #[derive(Debug, Clone)]
    struct AppState {
        req: Receiver<Result<(i32, serde_json::Value), ()>>,
        res: Sender<String>,
    }

    fn test_app(req: Receiver<Result<(i32, serde_json::Value), ()>>, res: Sender<String>) -> App<AppState> {
        App::with_state(AppState { req, res })
            .resource("/2018-06-01/runtime/invocation/1234/response", |r| {
                r.method(http::Method::POST)
                    .with(|(body, state): (String, State<AppState>)| {
                        debug!("Response body: {}", body);
                        state.res.send(body).unwrap();
                        HttpResponse::Ok()
                    })
            })
            .resource("/2018-06-01/runtime/invocation/next", |r| {
                r.method(http::Method::GET).with(|state: State<AppState>| {
                    let (id, body) = match state.req.clone().recv().unwrap() {
                        Ok(val) => val,
                        Err(_) => {
                            System::current().stop();
                            thread::park();
                            unimplemented!();
                        }
                    };
                    debug!("Got req: {}", id);
                    HttpResponse::Ok()
                        .header("Lambda-Runtime-Aws-Request-Id", id.to_string())
                        .header("Lambda-Runtime-Invoked-Function-Arn", "an-arn")
                        .header("Lambda-Runtime-Deadline-Ms", "1000")
                        .json(body)
                })
            })
            .default_resource(|r| {
                r.route().with(|(r, body): (HttpRequest<AppState>, String)| {
                    warn!("{:?}", r);
                    warn!("Body: {}", body);
                    HttpResponse::NotFound()
                })
            })
    }

    #[cfg(feature = "lambda")]
    #[test]
    fn lambda_test() {
        let (req_send, req_recv) = unbounded();
        let (res_send, res_recv) = unbounded();
        req_send.send(Ok((1234,
            serde_json::to_value(
            alb::AlbTargetGroupRequest {
                request_context: alb::AlbTargetGroupRequestContext {
                    elb: alb::ElbContext {
                        target_group_arn: Some("arn:aws:elasticloadbalancing:region:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09".to_string())
                    }
                },
                http_method: Some("GET".to_string()),
                path: Some("/".to_string()),
                query_string_parameters: HashMap::new(),
                multi_value_query_string_parameters: HashMap::new(),
                headers: hashmap!{
                    "accept".to_string() => "text/html,application/xhtml+xml".to_string(),
                    "accept-language".to_string() => "en-US,en;q=0.8".to_string(),
                    "content-type".to_string() => "text/plain".to_string(),
                    "cookie".to_string() => "cookies".to_string(),
                    "host".to_string() => "lambda-846800462-us-east-2.elb.amazonaws.com".to_string(),
                    "user-agent".to_string() => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)".to_string(),
                    "x-amzn-trace-id".to_string() => "Root=1-5bdb40ca-556d8b0c50dc66f0511bf520".to_string(),
                    "x-forwarded-for".to_string() => "72.21.198.66".to_string(),
                    "x-forwarded-port".to_string() => "443".to_string(),
                    "x-forwarded-proto".to_string() => "https".to_string()
                },
                multi_value_headers: HashMap::new(),
                is_base64_encoded: false,
                body: Some("request_body".to_string())
}).unwrap()))).unwrap();
        env_logger::init();
        thread::spawn(move || {
            server::new(move || test_app(req_recv.clone(), res_send.clone()).finish())
                .bind("0.0.0.0:3456")
                .unwrap()
                .run()
        });
        env::set_var("AWS_LAMBDA_FUNCTION_NAME", "foo");
        env::set_var("AWS_LAMBDA_FUNCTION_VERSION", "1");
        env::set_var("AWS_LAMBDA_LOG_STREAM_NAME", "log");
        env::set_var("AWS_LAMBDA_LOG_GROUP_NAME", "lg");
        env::set_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128");
        env::set_var("AWS_LAMBDA_RUNTIME_API", "127.0.0.1:3456");
        thread::spawn(|| main());
        let resp_raw = res_recv.recv().unwrap();
        let resp: alb::AlbTargetGroupResponse = serde_json::from_str(&resp_raw).unwrap();
        debug!("Response to main: {:#?}", resp);
        // shutdown
        req_send.send(Err(())).unwrap();
    }

    #[test]
    fn simple_search() {
        let mut srv = test::TestServer::with_factory(app);
        common::set_client(common::ClientType::TESTING);
        let request = srv
            .client(http::Method::GET, "/search?query=foo")
            .finish()
            .unwrap();
        let response = srv.execute(request.send()).unwrap();
        let body: String = String::from_utf8(srv.execute(response.body()).unwrap().to_vec()).unwrap();
        assert!(body.find("<title>Search: foo</title>").is_some(), body);
        assert!(response.status().is_success(), response.status());
    }
}
<|endoftext|>


<commit_before>#![crate_id = "rboy"]
#![license = "MIT"]

#![feature(phase)]
#[phase(syntax, link)] extern crate log;

extern crate native;
extern crate getopts;
extern crate sync;
extern crate time;
extern crate sdl;
#[cfg(test)]
extern crate test;

use cpu::CPU;
use sync::DuplexStream;
use sync::{Arc,RWLock};
use std::comm::{Disconnected,Empty};

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;
mod util;

static SCALE: uint = 2;

#[cfg(not(test))]
#[start]
fn start(argc: int, argv: **u8) -> int { native::start(argc, argv, main) }

fn main() {
	let args = std::os::args();

	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f.to_err_msg()); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free.get(0).clone()
	} else {
		println!("{}", getopts::usage(StrBuf::from_str(*args.get(0)).append(" <filename>").into_owned(), opts));
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = sync::duplex();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();
	native::task::spawn(proc() cpuloop(&cpustream, arc2, filename, &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: ~str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); return; },
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let mut periodic = timer.periodic(8);

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				let mut data = arc.write();
				for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				if channel.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		periodic.recv();

		match channel.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => periodic = timer.periodic(1),
				SlowDown => periodic = timer.periodic(8),
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
}
<commit_msg>Now the only ~ is in ~[u8]<commit_after>#![crate_id = "rboy"]
#![license = "MIT"]

#![feature(phase)]
#[phase(syntax, link)] extern crate log;

extern crate native;
extern crate getopts;
extern crate sync;
extern crate time;
extern crate sdl;
#[cfg(test)]
extern crate test;

use cpu::CPU;
use sync::DuplexStream;
use sync::{Arc,RWLock};
use std::comm::{Disconnected,Empty};

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;
mod util;

static SCALE: uint = 2;

#[cfg(not(test))]
#[start]
fn start(argc: int, argv: **u8) -> int { native::start(argc, argv, main) }

fn main() {
	let args = std::os::args();

	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f.to_err_msg()); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free.get(0).clone()
	} else {
		println!("{}", getopts::usage(StrBuf::from_str(*args.get(0)).append(" <filename>").into_owned(), opts));
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = sync::duplex();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();
	native::task::spawn(proc() cpuloop(&cpustream, arc2, filename, &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); return; },
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let mut periodic = timer.periodic(8);

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				let mut data = arc.write();
				for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				if channel.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		periodic.recv();

		match channel.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => periodic = timer.periodic(1),
				SlowDown => periodic = timer.periodic(8),
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
}
<|endoftext|>


<commit_before>/*!
Hypervisor API for OSX

To use this library, you need

* OS X Yosemite (10.10) or newer

* an Intel processor with the VT-x feature set that includes Extended Page
Tables (EPT) and Unrestricted Mode. To verify this, run and expect the following
in your Terminal:

  ```shell
  $ sysctl kern.hv_support
  kern.hv_support: 1
  ```
!*/
//extern crate core;

use super::core::fmt;
use libc::*;

#[allow(non_camel_case_types)]
pub mod ffi;

use self::ffi::*;
use self::ffi::consts::*;

/// Error returned after every call
pub enum Error {
    /// Success
    Success,
    /// Error
    Error,
    /// Busy
    Busy,
    /// Bad argument
    BadArg,
    /// No resources
    NoRes,
    /// No device
    NoDev,
    /// Unsupported
    Unsupp
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Success => write!(f, "Success"),
            Error::Error => write!(f, "Error"),
            Error::Busy => write!(f, "Busy"),
            Error::BadArg => write!(f, "Bad argument"),
            Error::NoRes => write!(f, "No resources"),
            Error::NoDev => write!(f, "No device"),
            Error::Unsupp => write!(f, "Unsupported"),
        }
    }
}

fn match_error_code(code: hv_return_t) -> Error {
    match code {
        HV_SUCCESS => Error::Success,
        HV_BUSY => Error::Busy,
        HV_BAD_ARGUMENT => Error::BadArg,
        HV_NO_RESOURCES => Error::NoRes,
        HV_NO_DEVICE => Error::NoDev,
        HV_UNSUPPORTED => Error::Unsupp,
        _ => Error::Error
    }
}

/// Creates a VM instance for the current task
pub fn create_vm() -> Error {
    match_error_code(unsafe {
        hv_vm_create(0)
    })
}

/// Destroys the VM instance associated with the current task
pub fn destory_vm() -> Error {
    match_error_code(unsafe {
        hv_vm_destroy()
    })
}

/// Synchronizes the guest Timestamp-Counters (TSC) across all vCPUs
///
/// `tsc` Guest TSC value
pub fn sync_tsc(tsc: u64) -> Error {
    match_error_code(unsafe {
        hv_vm_sync_tsc(tsc as uint64_t)
    })
}

/// Virtual CPU
#[allow(non_camel_case_types)]
pub struct vCPU {
    /// Virtual CPU ID
    pub id: u32
}

/// x86 architectural register
#[allow(non_camel_case_types)]
pub enum x86Reg {
	RIP,
	RFLAGS,
	RAX,
	RCX,
	RDX,
	RBX,
	RSI,
	RDI,
	RSP,
	RBP,
	R8,
	R9,
	R10,
	R11,
	R12,
	R13,
	R14,
	R15,
	CS,
	SS,
	DS,
	ES,
	FS,
	GS,
	IDT_BASE,
	IDT_LIMIT,
	GDT_BASE,
	GDT_LIMIT,
	LDTR,
	LDT_BASE,
	LDT_LIMIT,
	LDT_AR,
	TR,
	TSS_BASE,
	TSS_LIMIT,
	TSS_AR,
	CR0,
	CR1,
	CR2,
	CR3,
	CR4,
	DR0,
	DR1,
	DR2,
	DR3,
	DR4,
	DR5,
	DR6,
	DR7,
	TPR,
	XCR0,
	REGISTERS_MAX,
}

#[allow(non_snake_case)]
fn match_x86Reg(reg_id: &x86Reg) -> hv_x86_reg_t {
    match reg_id {
        &x86Reg::RIP           => hv_x86_reg_t::HV_X86_RIP,
        &x86Reg::RFLAGS        => hv_x86_reg_t::HV_X86_RFLAGS,
        &x86Reg::RAX           => hv_x86_reg_t::HV_X86_RAX,
        &x86Reg::RCX           => hv_x86_reg_t::HV_X86_RCX,
        &x86Reg::RDX           => hv_x86_reg_t::HV_X86_RDX,
        &x86Reg::RBX           => hv_x86_reg_t::HV_X86_RBX,
        &x86Reg::RSI           => hv_x86_reg_t::HV_X86_RSI,
        &x86Reg::RDI           => hv_x86_reg_t::HV_X86_RDI,
        &x86Reg::RSP           => hv_x86_reg_t::HV_X86_RSP,
        &x86Reg::RBP           => hv_x86_reg_t::HV_X86_RBP,
        &x86Reg::R8            => hv_x86_reg_t::HV_X86_R8,
        &x86Reg::R9            => hv_x86_reg_t::HV_X86_R9,
        &x86Reg::R10           => hv_x86_reg_t::HV_X86_R10,
        &x86Reg::R11           => hv_x86_reg_t::HV_X86_R11,
        &x86Reg::R12           => hv_x86_reg_t::HV_X86_R12,
        &x86Reg::R13           => hv_x86_reg_t::HV_X86_R13,
        &x86Reg::R14           => hv_x86_reg_t::HV_X86_R14,
        &x86Reg::R15           => hv_x86_reg_t::HV_X86_R15,
        &x86Reg::CS            => hv_x86_reg_t::HV_X86_CS,
        &x86Reg::SS            => hv_x86_reg_t::HV_X86_SS,
        &x86Reg::DS            => hv_x86_reg_t::HV_X86_DS,
        &x86Reg::ES            => hv_x86_reg_t::HV_X86_ES,
        &x86Reg::FS            => hv_x86_reg_t::HV_X86_FS,
        &x86Reg::GS            => hv_x86_reg_t::HV_X86_GS,
        &x86Reg::IDT_BASE      => hv_x86_reg_t::HV_X86_IDT_BASE,
        &x86Reg::IDT_LIMIT     => hv_x86_reg_t::HV_X86_IDT_LIMIT,
        &x86Reg::GDT_BASE      => hv_x86_reg_t::HV_X86_GDT_BASE,
        &x86Reg::GDT_LIMIT     => hv_x86_reg_t::HV_X86_GDT_LIMIT,
        &x86Reg::LDTR          => hv_x86_reg_t::HV_X86_LDTR,
        &x86Reg::LDT_BASE      => hv_x86_reg_t::HV_X86_LDT_BASE,
        &x86Reg::LDT_LIMIT     => hv_x86_reg_t::HV_X86_LDT_LIMIT,
        &x86Reg::LDT_AR        => hv_x86_reg_t::HV_X86_LDT_AR,
        &x86Reg::TR            => hv_x86_reg_t::HV_X86_TR,
        &x86Reg::TSS_BASE      => hv_x86_reg_t::HV_X86_TSS_BASE,
        &x86Reg::TSS_LIMIT     => hv_x86_reg_t::HV_X86_TSS_LIMIT,
        &x86Reg::TSS_AR        => hv_x86_reg_t::HV_X86_TSS_AR,
        &x86Reg::CR0           => hv_x86_reg_t::HV_X86_CR0,
        &x86Reg::CR1           => hv_x86_reg_t::HV_X86_CR1,
        &x86Reg::CR2           => hv_x86_reg_t::HV_X86_CR2,
        &x86Reg::CR3           => hv_x86_reg_t::HV_X86_CR3,
        &x86Reg::CR4           => hv_x86_reg_t::HV_X86_CR4,
        &x86Reg::DR0           => hv_x86_reg_t::HV_X86_DR0,
        &x86Reg::DR1           => hv_x86_reg_t::HV_X86_DR1,
        &x86Reg::DR2           => hv_x86_reg_t::HV_X86_DR2,
        &x86Reg::DR3           => hv_x86_reg_t::HV_X86_DR3,
        &x86Reg::DR4           => hv_x86_reg_t::HV_X86_DR4,
        &x86Reg::DR5           => hv_x86_reg_t::HV_X86_DR5,
        &x86Reg::DR6           => hv_x86_reg_t::HV_X86_DR6,
        &x86Reg::DR7           => hv_x86_reg_t::HV_X86_DR7,
        &x86Reg::TPR           => hv_x86_reg_t::HV_X86_TPR,
        &x86Reg::XCR0          => hv_x86_reg_t::HV_X86_XCR0,
        &x86Reg::REGISTERS_MAX => hv_x86_reg_t::HV_X86_REGISTERS_MAX,
    }
}

impl vCPU {
    /// Creates a vCPU instance for the current thread
    pub fn new() -> Result<vCPU, Error> {
        let mut vcpuid: hv_vcpuid_t = 0;

        let error = match_error_code(unsafe {
            hv_vcpu_create(&mut vcpuid, 0)
        });

        match error {
            Error::Success => Ok(vCPU {
                id: vcpuid as u32
            }),
            _ => Err(error)
        }
    }

    /// Destroys the vCPU instance associated with the current thread
    pub fn destory(&self) -> Error {
        match_error_code(unsafe {
            hv_vcpu_destroy(self.id as hv_vcpuid_t)
        })
    }

    /// Executes the vCPU
    pub fn run(&self) -> Error {
        match_error_code(unsafe {
            hv_vcpu_run(self.id as hv_vcpuid_t)
        })
    }

    /// Returns the cumulative execution time of the vCPU in nanoseconds
    pub fn exec_time(&self) -> Result<u64, Error> {
        let mut exec_time: uint64_t = 0;

        let error = match_error_code(unsafe {
            hv_vcpu_get_exec_time(self.id, &mut exec_time)
        });

        match error {
            Error::Success => Ok(exec_time as u64),
            _ => Err(error)
        }
    }

    /// Forces flushing of cached vCPU state
    pub fn flush(&self) -> Error {
        match_error_code(unsafe {
            hv_vcpu_flush(self.id as hv_vcpuid_t)
        })
    }

    /// Invalidates the translation lookaside buffer (TLB) of a vCPU
    pub fn invalidate_tlb(&self) -> Error {
        match_error_code(unsafe {
            hv_vcpu_invalidate_tlb(self.id as hv_vcpuid_t)
        })
    }

    /// Enables an MSR to be used natively by the VM
    pub fn enable_native_msr(&self, msr: u32, enable: bool) -> Error {
        match_error_code(unsafe {
            hv_vcpu_enable_native_msr(self.id as hv_vcpuid_t, msr as uint32_t, enable)
        })
    }

    /// Returns the current value of an MSR of the vCPU
    pub fn read_msr(&self, msr: u32) -> Result<u64, Error> {
        let mut value: uint64_t = 0;

            let error = match_error_code(unsafe {
                hv_vcpu_read_msr(self.id as hv_vcpuid_t, msr as uint32_t, &mut value)
            });

        match error {
            Error::Success => Ok(value as u64),
            _ => Err(error)
        }
    }

    /// Set the value of an MSR of the vCPU
    pub fn write_msr(&self, msr: u32, value: u64) -> Error {
        match_error_code(unsafe {
            hv_vcpu_write_msr(self.id as hv_vcpuid_t, msr as uint32_t, &(value as uint64_t))
        })
    }

    /// Returns the current value of an architectural x86 register
    /// of the vCPU
    pub fn read_register(&self, reg: &x86Reg) -> Result<u64, Error> {
        let mut value: uint64_t = 0;

        let error = match_error_code(unsafe {
            hv_vcpu_read_register(self.id as hv_vcpuid_t, match_x86Reg(&reg), &mut value)
        });

        match error {
            Error::Success => Ok(value as u64),
            _ => Err(error)
        }
    }

    /// Sets the value of an architectural x86 register of the vCPU
    pub fn write_register(&self, reg: &x86Reg, value: u64) -> Error {
        match_error_code(unsafe {
            hv_vcpu_write_register(self.id as hv_vcpuid_t, match_x86Reg(&reg), value as uint64_t)
        })
    }
}

impl fmt::Debug for vCPU {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "vCPU ID: {}", (*self).id)
    }
}
<commit_msg>Update documentation of invalidate_tlb<commit_after>/*!
Hypervisor API for OSX

To use this library, you need

* OS X Yosemite (10.10) or newer

* an Intel processor with the VT-x feature set that includes Extended Page
Tables (EPT) and Unrestricted Mode. To verify this, run and expect the following
in your Terminal:

  ```shell
  $ sysctl kern.hv_support
  kern.hv_support: 1
  ```
!*/
//extern crate core;

use super::core::fmt;
use libc::*;

#[allow(non_camel_case_types)]
pub mod ffi;

use self::ffi::*;
use self::ffi::consts::*;

/// Error returned after every call
pub enum Error {
    /// Success
    Success,
    /// Error
    Error,
    /// Busy
    Busy,
    /// Bad argument
    BadArg,
    /// No resources
    NoRes,
    /// No device
    NoDev,
    /// Unsupported
    Unsupp
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Success => write!(f, "Success"),
            Error::Error => write!(f, "Error"),
            Error::Busy => write!(f, "Busy"),
            Error::BadArg => write!(f, "Bad argument"),
            Error::NoRes => write!(f, "No resources"),
            Error::NoDev => write!(f, "No device"),
            Error::Unsupp => write!(f, "Unsupported"),
        }
    }
}

fn match_error_code(code: hv_return_t) -> Error {
    match code {
        HV_SUCCESS => Error::Success,
        HV_BUSY => Error::Busy,
        HV_BAD_ARGUMENT => Error::BadArg,
        HV_NO_RESOURCES => Error::NoRes,
        HV_NO_DEVICE => Error::NoDev,
        HV_UNSUPPORTED => Error::Unsupp,
        _ => Error::Error
    }
}

/// Creates a VM instance for the current task
pub fn create_vm() -> Error {
    match_error_code(unsafe {
        hv_vm_create(0)
    })
}

/// Destroys the VM instance associated with the current task
pub fn destory_vm() -> Error {
    match_error_code(unsafe {
        hv_vm_destroy()
    })
}

/// Synchronizes the guest Timestamp-Counters (TSC) across all vCPUs
///
/// `tsc` Guest TSC value
pub fn sync_tsc(tsc: u64) -> Error {
    match_error_code(unsafe {
        hv_vm_sync_tsc(tsc as uint64_t)
    })
}

/// Virtual CPU
#[allow(non_camel_case_types)]
pub struct vCPU {
    /// Virtual CPU ID
    pub id: u32
}

/// x86 architectural register
#[allow(non_camel_case_types)]
pub enum x86Reg {
	RIP,
	RFLAGS,
	RAX,
	RCX,
	RDX,
	RBX,
	RSI,
	RDI,
	RSP,
	RBP,
	R8,
	R9,
	R10,
	R11,
	R12,
	R13,
	R14,
	R15,
	CS,
	SS,
	DS,
	ES,
	FS,
	GS,
	IDT_BASE,
	IDT_LIMIT,
	GDT_BASE,
	GDT_LIMIT,
	LDTR,
	LDT_BASE,
	LDT_LIMIT,
	LDT_AR,
	TR,
	TSS_BASE,
	TSS_LIMIT,
	TSS_AR,
	CR0,
	CR1,
	CR2,
	CR3,
	CR4,
	DR0,
	DR1,
	DR2,
	DR3,
	DR4,
	DR5,
	DR6,
	DR7,
	TPR,
	XCR0,
	REGISTERS_MAX,
}

#[allow(non_snake_case)]
fn match_x86Reg(reg_id: &x86Reg) -> hv_x86_reg_t {
    match reg_id {
        &x86Reg::RIP           => hv_x86_reg_t::HV_X86_RIP,
        &x86Reg::RFLAGS        => hv_x86_reg_t::HV_X86_RFLAGS,
        &x86Reg::RAX           => hv_x86_reg_t::HV_X86_RAX,
        &x86Reg::RCX           => hv_x86_reg_t::HV_X86_RCX,
        &x86Reg::RDX           => hv_x86_reg_t::HV_X86_RDX,
        &x86Reg::RBX           => hv_x86_reg_t::HV_X86_RBX,
        &x86Reg::RSI           => hv_x86_reg_t::HV_X86_RSI,
        &x86Reg::RDI           => hv_x86_reg_t::HV_X86_RDI,
        &x86Reg::RSP           => hv_x86_reg_t::HV_X86_RSP,
        &x86Reg::RBP           => hv_x86_reg_t::HV_X86_RBP,
        &x86Reg::R8            => hv_x86_reg_t::HV_X86_R8,
        &x86Reg::R9            => hv_x86_reg_t::HV_X86_R9,
        &x86Reg::R10           => hv_x86_reg_t::HV_X86_R10,
        &x86Reg::R11           => hv_x86_reg_t::HV_X86_R11,
        &x86Reg::R12           => hv_x86_reg_t::HV_X86_R12,
        &x86Reg::R13           => hv_x86_reg_t::HV_X86_R13,
        &x86Reg::R14           => hv_x86_reg_t::HV_X86_R14,
        &x86Reg::R15           => hv_x86_reg_t::HV_X86_R15,
        &x86Reg::CS            => hv_x86_reg_t::HV_X86_CS,
        &x86Reg::SS            => hv_x86_reg_t::HV_X86_SS,
        &x86Reg::DS            => hv_x86_reg_t::HV_X86_DS,
        &x86Reg::ES            => hv_x86_reg_t::HV_X86_ES,
        &x86Reg::FS            => hv_x86_reg_t::HV_X86_FS,
        &x86Reg::GS            => hv_x86_reg_t::HV_X86_GS,
        &x86Reg::IDT_BASE      => hv_x86_reg_t::HV_X86_IDT_BASE,
        &x86Reg::IDT_LIMIT     => hv_x86_reg_t::HV_X86_IDT_LIMIT,
        &x86Reg::GDT_BASE      => hv_x86_reg_t::HV_X86_GDT_BASE,
        &x86Reg::GDT_LIMIT     => hv_x86_reg_t::HV_X86_GDT_LIMIT,
        &x86Reg::LDTR          => hv_x86_reg_t::HV_X86_LDTR,
        &x86Reg::LDT_BASE      => hv_x86_reg_t::HV_X86_LDT_BASE,
        &x86Reg::LDT_LIMIT     => hv_x86_reg_t::HV_X86_LDT_LIMIT,
        &x86Reg::LDT_AR        => hv_x86_reg_t::HV_X86_LDT_AR,
        &x86Reg::TR            => hv_x86_reg_t::HV_X86_TR,
        &x86Reg::TSS_BASE      => hv_x86_reg_t::HV_X86_TSS_BASE,
        &x86Reg::TSS_LIMIT     => hv_x86_reg_t::HV_X86_TSS_LIMIT,
        &x86Reg::TSS_AR        => hv_x86_reg_t::HV_X86_TSS_AR,
        &x86Reg::CR0           => hv_x86_reg_t::HV_X86_CR0,
        &x86Reg::CR1           => hv_x86_reg_t::HV_X86_CR1,
        &x86Reg::CR2           => hv_x86_reg_t::HV_X86_CR2,
        &x86Reg::CR3           => hv_x86_reg_t::HV_X86_CR3,
        &x86Reg::CR4           => hv_x86_reg_t::HV_X86_CR4,
        &x86Reg::DR0           => hv_x86_reg_t::HV_X86_DR0,
        &x86Reg::DR1           => hv_x86_reg_t::HV_X86_DR1,
        &x86Reg::DR2           => hv_x86_reg_t::HV_X86_DR2,
        &x86Reg::DR3           => hv_x86_reg_t::HV_X86_DR3,
        &x86Reg::DR4           => hv_x86_reg_t::HV_X86_DR4,
        &x86Reg::DR5           => hv_x86_reg_t::HV_X86_DR5,
        &x86Reg::DR6           => hv_x86_reg_t::HV_X86_DR6,
        &x86Reg::DR7           => hv_x86_reg_t::HV_X86_DR7,
        &x86Reg::TPR           => hv_x86_reg_t::HV_X86_TPR,
        &x86Reg::XCR0          => hv_x86_reg_t::HV_X86_XCR0,
        &x86Reg::REGISTERS_MAX => hv_x86_reg_t::HV_X86_REGISTERS_MAX,
    }
}

impl vCPU {
    /// Creates a vCPU instance for the current thread
    pub fn new() -> Result<vCPU, Error> {
        let mut vcpuid: hv_vcpuid_t = 0;

        let error = match_error_code(unsafe {
            hv_vcpu_create(&mut vcpuid, 0)
        });

        match error {
            Error::Success => Ok(vCPU {
                id: vcpuid as u32
            }),
            _ => Err(error)
        }
    }

    /// Destroys the vCPU instance associated with the current thread
    pub fn destory(&self) -> Error {
        match_error_code(unsafe {
            hv_vcpu_destroy(self.id as hv_vcpuid_t)
        })
    }

    /// Executes the vCPU
    pub fn run(&self) -> Error {
        match_error_code(unsafe {
            hv_vcpu_run(self.id as hv_vcpuid_t)
        })
    }

    /// Returns the cumulative execution time of the vCPU in nanoseconds
    pub fn exec_time(&self) -> Result<u64, Error> {
        let mut exec_time: uint64_t = 0;

        let error = match_error_code(unsafe {
            hv_vcpu_get_exec_time(self.id, &mut exec_time)
        });

        match error {
            Error::Success => Ok(exec_time as u64),
            _ => Err(error)
        }
    }

    /// Forces flushing of cached vCPU state
    pub fn flush(&self) -> Error {
        match_error_code(unsafe {
            hv_vcpu_flush(self.id as hv_vcpuid_t)
        })
    }

    /// Invalidates the translation lookaside buffer (TLB) of the vCPU
    pub fn invalidate_tlb(&self) -> Error {
        match_error_code(unsafe {
            hv_vcpu_invalidate_tlb(self.id as hv_vcpuid_t)
        })
    }

    /// Enables an MSR to be used natively by the VM
    pub fn enable_native_msr(&self, msr: u32, enable: bool) -> Error {
        match_error_code(unsafe {
            hv_vcpu_enable_native_msr(self.id as hv_vcpuid_t, msr as uint32_t, enable)
        })
    }

    /// Returns the current value of an MSR of the vCPU
    pub fn read_msr(&self, msr: u32) -> Result<u64, Error> {
        let mut value: uint64_t = 0;

            let error = match_error_code(unsafe {
                hv_vcpu_read_msr(self.id as hv_vcpuid_t, msr as uint32_t, &mut value)
            });

        match error {
            Error::Success => Ok(value as u64),
            _ => Err(error)
        }
    }

    /// Set the value of an MSR of the vCPU
    pub fn write_msr(&self, msr: u32, value: u64) -> Error {
        match_error_code(unsafe {
            hv_vcpu_write_msr(self.id as hv_vcpuid_t, msr as uint32_t, &(value as uint64_t))
        })
    }

    /// Returns the current value of an architectural x86 register
    /// of the vCPU
    pub fn read_register(&self, reg: &x86Reg) -> Result<u64, Error> {
        let mut value: uint64_t = 0;

        let error = match_error_code(unsafe {
            hv_vcpu_read_register(self.id as hv_vcpuid_t, match_x86Reg(&reg), &mut value)
        });

        match error {
            Error::Success => Ok(value as u64),
            _ => Err(error)
        }
    }

    /// Sets the value of an architectural x86 register of the vCPU
    pub fn write_register(&self, reg: &x86Reg, value: u64) -> Error {
        match_error_code(unsafe {
            hv_vcpu_write_register(self.id as hv_vcpuid_t, match_x86Reg(&reg), value as uint64_t)
        })
    }
}

impl fmt::Debug for vCPU {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "vCPU ID: {}", (*self).id)
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(path)]
#![feature(collections)]

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex;
extern crate git2;
extern crate core;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Oid};
use docopt::Docopt;
use std::collections::HashMap;
// use std::old_io::BufferedReader;
// use std::old_io::File;
use std::str;
// use regex::Regex;
use rustc_serialize::json;
use std::fmt::Display;
use std::path::Path;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [--json] [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
    flag_json: bool,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(&args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[..]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[..]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            let output = find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
            make_output(vec![output]);
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        let mut output = vec![];
        // Walk through each pair of commits.
        for pair in history.windows(2) {
            let (old, new) = (&pair[1], &pair[0]);
            let detected = find_moves(&repo, old.clone(), new.clone()).unwrap();
            output.push(detected);
        }
        if args.flag_json {
            make_json(output);
        } else {
            make_output(output);
        }
    }
}

fn make_output(output: Vec<Vec<Output>>) {
    println!("make_output: output.len()={}", output.len());
    for o in output {
        for i in o {
                println!("\told_commit={}",         i.old_commit);
                println!("\tnew_commit={}",         i.new_commit);
                println!("\torigin_line={}",        i.origin_line);
                println!("\tdestintation_line={}",  i.destination_line);
                println!("\tnum_lines={}",          i.num_lines);
                println!("\tnew_filename={}",       i.new_filename);
                println!("\told_filename={}",       i.old_filename);
        }
    }
}

fn make_json(output: Vec<Vec<Output>>) {
    println!("{}", json::encode(&output).unwrap());
}

#[derive(Debug, PartialEq, Eq)]
enum FoundState {
    Added, Deleted
}

fn dump_diffline(line: &DiffLine) {
    // 'origin' is wrapped in pipes to ease displaying space characters.
    print!("line: old={:?} new={:?} offset={} |origin|=|{}|\n      content={}",
             line.old_lineno(), line.new_lineno(), line.content_offset(),
             line.origin(), str::from_utf8(line.content()).unwrap());
}

fn dump_diffdelta(delta: &DiffDelta) {
    println!("delta: nfiles={} status={:?} old_file=(id={} path_bytes={:?} path={:?} tsize={}) new_file=(id={} path_bytes={:?} path={:?} tsize={})",
            delta.nfiles(), delta.status(),
            delta.old_file().id(), delta.old_file().path_bytes(), delta.old_file().path(), delta.old_file().size(),
            delta.new_file().id(), delta.new_file().path_bytes(), delta.new_file().path(), delta.new_file().size());
}

fn dump_diffhunk(hunk: &DiffHunk) {
    println!("hunk: old_start={} old_lines={} new_start={} new_lines={} header={}",
            hunk.old_start(), hunk.old_lines(),
            hunk.new_start(), hunk.new_lines(),
            str::from_utf8(hunk.header()).unwrap());
}

fn format_key(key: String) -> String {
    let remove_whitespace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    let trim = regex!(r"^[\s]+|[\s]+$");
    let result = remove_whitespace.replace_all(key.as_slice(), "");
    trim.replace_all(result.as_slice(), "")
}

#[derive(Debug)]
struct Found {
    filename: String,
    key: String,
    state: FoundState,
    start_position: u32,
    line_count: u32,
}

fn find_additions_and_deletions(diff: Diff) -> Vec<Found> {

    #[derive(Debug)]
    enum State {
        Other, Addition, Deletion
    }

    let mut founds: Vec<Found> = Vec::new();

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();
    let mut old_path = String::new();
    let mut new_path = String::new();

    let mut line_count: u32 = 0;
    let mut start_position: u32 = 0;

    // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
    // It's a bit weird, but I think it will provide the necessary information.
    diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        // if delta.nfiles() != 2 {
        //     // This is diff with only one side, and thus can't have a move.
        //     return true;
        // }
        // assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files. Found {} files.", delta.nfiles());

        // Thinking:
        //  * If is not a hunk, keep going.
        //  * If it's a hunk, do regex magic.
        //  * Stick regex output into a hashmap as a hash.
        //  * Later, we will iterate through and output pased on the values.
        // Filter out all the boring context lines.
        // If we're not interested in this line just return since it will iterate to the next.
        if maybe_hunk.is_none() { return true }; // Return early.

        //dump_diffline(&line);
        //dump_diffdelta(&delta);
        //dump_diffhunk(&maybe_hunk.unwrap());

        old_path = match delta.old_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };
        new_path = match delta.new_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };

        match line.origin() {
            // Additions
            '+' | '>' => {
                added.push_str(str::from_utf8(line.content())
                    .ok().expect("Couldn't parse line content for added"));

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                        line_count = 0;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Addition => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Addition;
                true
            },
            // Deletions
            '-' | '<' => {
                deleted.push_str(str::from_utf8(line.content())
                    .ok().expect("Couldn't parse line content for deleted"));

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                        line_count = 0;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Deletion => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Deletion;
                true
            },
            // Other
            _         => {
                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
        }
    });

    // Grab last one.
    match state {
        State::Addition => {
            if added.len() > 0 {
                founds.push(Found {
                    filename: new_path.clone(),
                    key: format_key(added.clone()),
                    state: FoundState::Added,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        State::Deletion => {
            if deleted.len() > 0 {
                founds.push(Found {
                    filename: old_path.clone(),
                    key: format_key(deleted.clone()),
                    state: FoundState::Deleted,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        _ => (),
    }

    return founds;
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, git2::Error> {
    let old_tree = try!(old.tree());
    let new_tree = try!(new.tree());
    // Build up a diff of the two trees.
    let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let founds: Vec<Found> = find_additions_and_deletions(diff);

    let mut moves: Vec<Output> = Vec::new();
    let mut map: HashMap<String, Found> = HashMap::new();

    for f in founds {
        if f.key.len() == 0 { continue; }

        if map.contains_key(&f.key) {
            let q = map.get(&f.key).unwrap();

            // If both states are added, there are no moves.
            // assert!(f.state != q.state, format!("States {:?}, {:?}. Should be Addition/Deletion ", f.state, q.state));
            if f.state == q.state {
                return Ok(Vec::<Output>::new())
            }

            let output: Output;

            match f.state {
                FoundState::Added => {
                    output = Output {
                        old_commit: TransitOid(old.id()),
                        new_commit: TransitOid(new.id()),
                        old_filename: q.filename.clone(),
                        new_filename: f.filename.clone(),
                        origin_line: q.start_position,
                        destination_line: f.start_position,
                        num_lines: f.line_count,
                    };
                },
                FoundState::Deleted => {
                    output = Output {
                        old_commit: TransitOid(old.id()),
                        new_commit: TransitOid(new.id()),
                        old_filename: f.filename.clone(),
                        new_filename: q.filename.clone(),
                        origin_line: f.start_position,
                        destination_line: q.start_position,
                        num_lines: f.line_count,
                    };
                },
            }

            moves.push(output);
        } else {
            map.insert(f.key.clone(), f);
        }
    }

    return Ok(moves);
}

#[derive(Debug, RustcEncodable)]
struct Output {
    old_commit: TransitOid,
    new_commit: TransitOid,
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}

#[derive(Debug)]
struct TransitOid(Oid);
impl Display for TransitOid {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        Display::fmt(&self.0, f)
    }
}

impl rustc_serialize::Encodable for TransitOid {
    fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_str(&format!("{}", self)[..])
    }
}
<commit_msg>Remove unused dumps. Use println!("{:?}")<commit_after>#![feature(core)]
#![feature(path)]
#![feature(collections)]

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex;
extern crate git2;
extern crate core;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Oid};
use docopt::Docopt;
use std::collections::HashMap;
// use std::old_io::BufferedReader;
// use std::old_io::File;
use std::str;
// use regex::Regex;
use rustc_serialize::json;
use std::fmt::Display;
use std::path::Path;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [--json] [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
    flag_json: bool,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(&args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[..]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[..]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            let output = find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
            make_output(vec![output]);
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        let mut output = vec![];
        // Walk through each pair of commits.
        for pair in history.windows(2) {
            let (old, new) = (&pair[1], &pair[0]);
            let detected = find_moves(&repo, old.clone(), new.clone()).unwrap();
            output.push(detected);
        }
        if args.flag_json {
            make_json(output);
        } else {
            make_output(output);
        }
    }
}

fn make_output(output: Vec<Vec<Output>>) {
    println!("make_output: output.len()={}", output.len());
    for o in output {
        for i in o {
                println!("\told_commit={}",         i.old_commit);
                println!("\tnew_commit={}",         i.new_commit);
                println!("\torigin_line={}",        i.origin_line);
                println!("\tdestintation_line={}",  i.destination_line);
                println!("\tnum_lines={}",          i.num_lines);
                println!("\tnew_filename={}",       i.new_filename);
                println!("\told_filename={}",       i.old_filename);
        }
    }
}

fn make_json(output: Vec<Vec<Output>>) {
    println!("{}", json::encode(&output).unwrap());
}

#[derive(Debug, PartialEq, Eq)]
enum FoundState {
    Added, Deleted
}

fn format_key(key: String) -> String {
    let remove_whitespace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    let trim = regex!(r"^[\s]+|[\s]+$");
    let result = remove_whitespace.replace_all(key.as_slice(), "");
    trim.replace_all(result.as_slice(), "")
}

#[derive(Debug)]
struct Found {
    filename: String,
    key: String,
    state: FoundState,
    start_position: u32,
    line_count: u32,
}

fn find_additions_and_deletions(diff: Diff) -> Vec<Found> {

    #[derive(Debug)]
    enum State {
        Other, Addition, Deletion
    }

    let mut founds: Vec<Found> = Vec::new();

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();
    let mut old_path = String::new();
    let mut new_path = String::new();

    let mut line_count: u32 = 0;
    let mut start_position: u32 = 0;

    // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
    // It's a bit weird, but I think it will provide the necessary information.
    diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        // if delta.nfiles() != 2 {
        //     // This is diff with only one side, and thus can't have a move.
        //     return true;
        // }
        // assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files. Found {} files.", delta.nfiles());

        // Thinking:
        //  * If is not a hunk, keep going.
        //  * If it's a hunk, do regex magic.
        //  * Stick regex output into a hashmap as a hash.
        //  * Later, we will iterate through and output pased on the values.
        // Filter out all the boring context lines.
        // If we're not interested in this line just return since it will iterate to the next.
        if maybe_hunk.is_none() { return true }; // Return early.

        //dump_diffline(&line);
        //dump_diffdelta(&delta);
        //dump_diffhunk(&maybe_hunk.unwrap());

        old_path = match delta.old_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };
        new_path = match delta.new_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };

        match line.origin() {
            // Additions
            '+' | '>' => {
                added.push_str(str::from_utf8(line.content())
                    .ok().expect("Couldn't parse line content for added"));

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                        line_count = 0;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Addition => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Addition;
                true
            },
            // Deletions
            '-' | '<' => {
                deleted.push_str(str::from_utf8(line.content())
                    .ok().expect("Couldn't parse line content for deleted"));

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                        line_count = 0;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Deletion => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Deletion;
                true
            },
            // Other
            _         => {
                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
        }
    });

    // Grab last one.
    match state {
        State::Addition => {
            if added.len() > 0 {
                founds.push(Found {
                    filename: new_path.clone(),
                    key: format_key(added.clone()),
                    state: FoundState::Added,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        State::Deletion => {
            if deleted.len() > 0 {
                founds.push(Found {
                    filename: old_path.clone(),
                    key: format_key(deleted.clone()),
                    state: FoundState::Deleted,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        _ => (),
    }

    return founds;
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, git2::Error> {
    let old_tree = try!(old.tree());
    let new_tree = try!(new.tree());
    // Build up a diff of the two trees.
    let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let founds: Vec<Found> = find_additions_and_deletions(diff);

    let mut moves: Vec<Output> = Vec::new();
    let mut map: HashMap<String, Found> = HashMap::new();

    for f in founds {
        if f.key.len() == 0 { continue; }

        if map.contains_key(&f.key) {
            let q = map.get(&f.key).unwrap();

            // If both states are added, there are no moves.
            // assert!(f.state != q.state, format!("States {:?}, {:?}. Should be Addition/Deletion ", f.state, q.state));
            if f.state == q.state {
                return Ok(Vec::<Output>::new())
            }

            let output: Output;

            match f.state {
                FoundState::Added => {
                    output = Output {
                        old_commit: TransitOid(old.id()),
                        new_commit: TransitOid(new.id()),
                        old_filename: q.filename.clone(),
                        new_filename: f.filename.clone(),
                        origin_line: q.start_position,
                        destination_line: f.start_position,
                        num_lines: f.line_count,
                    };
                },
                FoundState::Deleted => {
                    output = Output {
                        old_commit: TransitOid(old.id()),
                        new_commit: TransitOid(new.id()),
                        old_filename: f.filename.clone(),
                        new_filename: q.filename.clone(),
                        origin_line: f.start_position,
                        destination_line: q.start_position,
                        num_lines: f.line_count,
                    };
                },
            }

            moves.push(output);
        } else {
            map.insert(f.key.clone(), f);
        }
    }

    return Ok(moves);
}

#[derive(Debug, RustcEncodable)]
struct Output {
    old_commit: TransitOid,
    new_commit: TransitOid,
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}

#[derive(Debug)]
struct TransitOid(Oid);
impl Display for TransitOid {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        Display::fmt(&self.0, f)
    }
}

impl rustc_serialize::Encodable for TransitOid {
    fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_str(&format!("{}", self)[..])
    }
}
<|endoftext|>


<commit_before>extern crate rff;

use std::env;

fn main() {
    if let Some(search_term) = env::args().nth(1) {
        let lines = rff::stdin::slurp();
        println!("Lines:\n{:?}", lines);
        println!("Search: {}", search_term);
    }
}
<commit_msg>Filter lines by search term<commit_after>extern crate rff;

use std::env;
use rff::{stdin, matcher};

fn main() {
    if let Some(search_term) = env::args().nth(1) {
        let lines = stdin::slurp();

        let lines: Vec<_> = lines.iter()
            .filter(|line| { matcher::matches(&search_term, line) })
            .collect();

        println!("Lines:\n{:?}", lines);
        println!("Search: {}", search_term);
    }
}
<|endoftext|>


<commit_before>extern crate lodepng;

struct Pixel {r: u8, g: u8, b: u8}

static h : u32 = 10;
static w : u32 = 10;
static pixel_count : uint = (w*h) as uint;

fn main() {
    let path = &Path::new("test.png");

    let pixel_data = [Pixel {r: 0, g: 0, b: 0}, ..pixel_count];
    let mut data = [0u8, ..pixel_count*3u];
    assert!(pixel_data.len()*3 == data.len());
    for (i, p) in pixel_data.iter().enumerate() {
        data[i*3 + 0] = p.r;
        data[i*3 + 1] = p.g;
        data[i*3 + 2] = p.b;
    }
    match lodepng::encode_file(path, data, w, h, lodepng::LCT_RGB, 8) {
        Err(e) => fail!("Error writing: {}", e),
        Ok(_)  => (),
    }
}
<commit_msg>Render pixels concurrently<commit_after>extern crate lodepng;

use std::comm;

#[deriving(Clone)]
struct Point {x: u32, y: u32}
#[deriving(Clone)]
struct Pixel {r: u8, g: u8, b: u8}

static h : u32 = 512;
static w : u32 = 512;
static pixel_count : uint = (w*h) as uint;

fn main() {
    let mut data = [0u8, ..pixel_count*3u];
    let (tx, rx) : (Sender<(Point, Pixel)>, Receiver<(Point, Pixel)>) = comm::channel();
    let mut points : Vec<Point> = Vec::with_capacity(pixel_count);

    for x in range(0, w) {
        for y in range(0, h) {
            points.push(Point {x: x, y: y});
        }
    }

    for p in points.iter() {
        let proc_tx = tx.clone();
        let point = p.clone();
        spawn(proc() {
            proc_tx.send((point, render(point)));
        });
    }

    for _ in range(0, pixel_count) {
        let (point, pixel) = rx.recv();
        let index : uint = ((point.x + point.y * w) * 3) as uint;
        data[index + 0] = pixel.r;
        data[index + 1] = pixel.g;
        data[index + 2] = pixel.b;
    }

    let path = &Path::new("test.png");
    match lodepng::encode_file(path, data, w, h, lodepng::LCT_RGB, 8) {
        Err(e) => fail!("Error writing: {}", e),
        Ok(_)  => (),
    }
}

fn render(point: Point) -> Pixel {
    Pixel {r: (point.x % 256) as u8,
           g: (point.y % 256) as u8,
           b: 128}
}
<|endoftext|>