<commit_before>use bson::{Bson, Document};
use mongodb::client::coll::options::WriteModel;
use mongodb::client::MongoClient;

#[test]
fn bulk_ordered_insert_only() {
    let client = MongoClient::new("localhost", 27017).unwrap();
    let db = client.db("test");
    let coll = db.collection("bulk_ordered_insert_only");

    coll.drop().unwrap();

    let models = (1..5).map(|i| WriteModel::InsertOne { document: doc! {
        "_id" => (i),
        "x" => (i * 11)
    }}).collect();

    coll.bulk_write(models, true);

    let cursor : Vec<_> = coll.find(None, None).unwrap().collect();

    assert_eq!(cursor.len(), 4);

    for (i, result) in cursor.into_iter().enumerate() {
        let doc = result.unwrap();
        let expected_id = i + 1;

        match doc.get("_id") {
            Some(&Bson::I32(j)) => assert_eq!(expected_id as i32, j),
            _ => panic!("Invalid id: {:?}", doc)
        }

        match doc.get("x") {
            Some(&Bson::I32(j)) => assert_eq!(11 * expected_id as i32, j),
            _ => panic!("Invalid id: {:?}", doc)
        }
    }
}

#[test]
fn bulk_unordered_insert_only() {
    let client = MongoClient::new("localhost", 27017).unwrap();
    let db = client.db("test");
    let coll = db.collection("bulk_unordered_insert_only");

    coll.drop().unwrap();

    let models = (1..5).map(|i| WriteModel::InsertOne { document: doc! {
        "_id" => (i),
        "x" => (i * 11)
    }}).collect();

    coll.bulk_write(models, false);

    let cursor : Vec<_> = coll.find(None, None).unwrap().collect();

    assert_eq!(cursor.len(), 4);

    for (i, result) in cursor.into_iter().enumerate() {
        let doc = result.unwrap();
        let expected_id = i + 1;

        match doc.get("_id") {
            Some(&Bson::I32(j)) => assert_eq!(expected_id as i32, j),
            _ => panic!("Invalid id: {:?}", doc)
        }

        match doc.get("x") {
            Some(&Bson::I32(j)) => assert_eq!(11 * expected_id as i32, j),
            _ => panic!("Invalid id: {:?}", doc)
        }
    }
}

#[test]
fn bulk_ordered_mix() {
    let models = vec![
        WriteModel::InsertOne { document: doc! {
            "_id" => (1),
            "x" => (11)
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (2),
            "x" => (22)
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (3),
            "x" => (33)
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (4),
            "x" => (44)
        }},
        WriteModel::ReplaceOne {
            filter: doc! { "_id" => (3) },
            replacement: doc! { "x" => (37) },
            upsert: true,
        },
        WriteModel::UpdateMany {
            filter: doc! { "_id" => { "$lt" => (3) } },
            update: doc! { "$inc" => { "x" => (1) } },
            upsert: false,
        },
        WriteModel::DeleteOne { filter: doc! {
            "_id" => (4)
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (5),
            "x" => (55)
        }},
        WriteModel::UpdateOne {
            filter: doc! { "_id" => (6) },
            update: doc! { "$set" =>  { "x" => (62) } },
            upsert: true
        },
        WriteModel::InsertOne { document: doc! {
            "_id" => (101),
            "x" => ("dalmations")
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (102),
            "x" => ("strawberries")
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (103),
            "x" => ("blueberries")
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (104),
            "x" => ("bananas")
        }},
        WriteModel::DeleteMany { filter: doc! {
            "_id" => { "$gte" => (103) }
        }},
    ];

    let client = MongoClient::new("localhost", 27017).unwrap();
    let db = client.db("test");
    let coll = db.collection("bulk_ordered_mix");

    coll.drop().unwrap();

    let result = coll.bulk_write(models, true);

    assert_eq!(result.inserted_count, 9);
    assert_eq!(result.inserted_ids.len() as i32, result.inserted_count);
    assert_eq!(result.matched_count, 3);
    assert_eq!(result.modified_count, 3);
    assert_eq!(result.deleted_count, 3);
    assert_eq!(result.upserted_count, 1);
    assert_eq!(result.upserted_ids.len() as i32, result.upserted_count);

    match result.inserted_ids.get(&0).unwrap() {
        &Bson::I32(1) => (),
        &Bson::I64(1) => (),
        id => panic!("Invalid inserted id at index 0: {:?}", id)
    }

    match result.inserted_ids.get(&1).unwrap() {
        &Bson::I32(2) => (),
        &Bson::I64(2) => (),
        id => panic!("Invalid inserted id at index 1: {:?}", id)
    }

    match result.inserted_ids.get(&2).unwrap() {
        &Bson::I32(3) => (),
        &Bson::I64(3) => (),
        id => panic!("Invalid inserted id at index 2: {:?}", id)
    }

    match result.inserted_ids.get(&3).unwrap() {
        &Bson::I32(4) => (),
        &Bson::I64(4) => (),
        id => panic!("Invalid inserted id at index 3: {:?}", id)
    }

    match result.inserted_ids.get(&7).unwrap() {
        &Bson::I32(5) => (),
        &Bson::I64(5) => (),
        id => panic!("Invalid inserted id at index 7: {:?}", id)
    }

    match result.inserted_ids.get(&9).unwrap() {
        &Bson::I32(101) => (),
        &Bson::I64(101) => (),
        id => panic!("Invalid inserted id at index 9: {:?}", id)
    }

    match result.inserted_ids.get(&10).unwrap() {
        &Bson::I32(102) => (),
        &Bson::I64(102) => (),
        id => panic!("Invalid inserted id at index 10: {:?}", id)
    }

    match result.inserted_ids.get(&11).unwrap() {
        &Bson::I32(103) => (),
        &Bson::I64(103) => (),
        id => panic!("Invalid inserted id at index 11: {:?}", id)
    }

    match result.inserted_ids.get(&12).unwrap() {
        &Bson::I32(104) => (),
        &Bson::I64(104) => (),
        id => panic!("Invalid inserted id at index 12: {:?}", id)
    }

    match result.upserted_ids.get(&8).unwrap() {
        &Bson::I32(6) => (),
        &Bson::I64(6) => (),
        id => panic!("Invalid inserted id at index 8: {:?}", id)
    }
}
<commit_msg>condensed test<commit_after>use bson::{Bson, Document};
use mongodb::client::coll::options::WriteModel;
use mongodb::client::MongoClient;

#[test]
fn bulk_ordered_insert_only() {
    let client = MongoClient::new("localhost", 27017).unwrap();
    let db = client.db("test");
    let coll = db.collection("bulk_ordered_insert_only");

    coll.drop().unwrap();

    let models = (1..5).map(|i| WriteModel::InsertOne { document: doc! {
        "_id" => (i),
        "x" => (i * 11)
    }}).collect();

    coll.bulk_write(models, true);

    let cursor : Vec<_> = coll.find(None, None).unwrap().collect();

    assert_eq!(cursor.len(), 4);

    for (i, result) in cursor.into_iter().enumerate() {
        let doc = result.unwrap();
        let expected_id = i + 1;

        match doc.get("_id") {
            Some(&Bson::I32(j)) => assert_eq!(expected_id as i32, j),
            _ => panic!("Invalid id: {:?}", doc)
        }

        match doc.get("x") {
            Some(&Bson::I32(j)) => assert_eq!(11 * expected_id as i32, j),
            _ => panic!("Invalid id: {:?}", doc)
        }
    }
}

#[test]
fn bulk_unordered_insert_only() {
    let client = MongoClient::new("localhost", 27017).unwrap();
    let db = client.db("test");
    let coll = db.collection("bulk_unordered_insert_only");

    coll.drop().unwrap();

    let models = (1..5).map(|i| WriteModel::InsertOne { document: doc! {
        "_id" => (i),
        "x" => (i * 11)
    }}).collect();

    coll.bulk_write(models, false);

    let cursor : Vec<_> = coll.find(None, None).unwrap().collect();

    assert_eq!(cursor.len(), 4);

    for (i, result) in cursor.into_iter().enumerate() {
        let doc = result.unwrap();
        let expected_id = i + 1;

        match doc.get("_id") {
            Some(&Bson::I32(j)) => assert_eq!(expected_id as i32, j),
            _ => panic!("Invalid id: {:?}", doc)
        }

        match doc.get("x") {
            Some(&Bson::I32(j)) => assert_eq!(11 * expected_id as i32, j),
            _ => panic!("Invalid id: {:?}", doc)
        }
    }
}

#[test]
fn bulk_ordered_mix() {
    let models = vec![
        WriteModel::InsertOne { document: doc! {
            "_id" => (1),
            "x" => (11)
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (2),
            "x" => (22)
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (3),
            "x" => (33)
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (4),
            "x" => (44)
        }},
        WriteModel::ReplaceOne {
            filter: doc! { "_id" => (3) },
            replacement: doc! { "x" => (37) },
            upsert: true,
        },
        WriteModel::UpdateMany {
            filter: doc! { "_id" => { "$lt" => (3) } },
            update: doc! { "$inc" => { "x" => (1) } },
            upsert: false,
        },
        WriteModel::DeleteOne { filter: doc! {
            "_id" => (4)
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (5),
            "x" => (55)
        }},
        WriteModel::UpdateOne {
            filter: doc! { "_id" => (6) },
            update: doc! { "$set" =>  { "x" => (62) } },
            upsert: true
        },
        WriteModel::InsertOne { document: doc! {
            "_id" => (101),
            "x" => ("dalmations")
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (102),
            "x" => ("strawberries")
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (103),
            "x" => ("blueberries")
        }},
        WriteModel::InsertOne { document: doc! {
            "_id" => (104),
            "x" => ("bananas")
        }},
        WriteModel::DeleteMany { filter: doc! {
            "_id" => { "$gte" => (103) }
        }},
    ];

    let client = MongoClient::new("localhost", 27017).unwrap();
    let db = client.db("test");
    let coll = db.collection("bulk_ordered_mix");

    coll.drop().unwrap();

    let result = coll.bulk_write(models, true);

    assert_eq!(result.inserted_count, 9);
    assert_eq!(result.inserted_ids.len() as i32, result.inserted_count);
    assert_eq!(result.matched_count, 3);
    assert_eq!(result.modified_count, 3);
    assert_eq!(result.deleted_count, 3);
    assert_eq!(result.upserted_count, 1);
    assert_eq!(result.upserted_ids.len() as i32, result.upserted_count);

    macro_rules! check_value_in_tree {
        ($tree:expr, $key:expr, $value:expr) => {
            match $tree.get(&$key).unwrap() {
                &Bson::I32($value) => (),
                &Bson::I64($value) => (),
                id => panic!("Invalid inserted id at index {}: {:?}", $key, id)
            }
        };
    }

    check_value_in_tree!(result.inserted_ids, 0, 1);
    check_value_in_tree!(result.inserted_ids, 1, 2);
    check_value_in_tree!(result.inserted_ids, 2, 3);
    check_value_in_tree!(result.inserted_ids, 3, 4);
    check_value_in_tree!(result.inserted_ids, 7, 5);
    check_value_in_tree!(result.inserted_ids, 9, 101);
    check_value_in_tree!(result.inserted_ids, 10, 102);
    check_value_in_tree!(result.inserted_ids, 11, 103);
    check_value_in_tree!(result.inserted_ids, 12, 104);
    check_value_in_tree!(result.upserted_ids, 8, 6);
}
<|endoftext|>


<commit_before>/*
src/logic/mod.rs, 2017-07-23

Copyright (c) 2017 Juuso Tuononen

This file is licensed under

Apache License, Version 2.0

or

MIT License
*/

pub mod common;

use logic::common::*;

use input::Input;

use time::PreciseTime;
use Timer;

use std::f32::consts;

pub struct Logic {
    player: Player,
    enemy: Enemy,
}

impl Logic {
    pub fn new() -> Logic {
        let player = Player::new();
        let enemy = Enemy::new();
        Logic { player, enemy }
    }

    pub fn update<T: Input>(&mut self, input: &T) {
        self.player.update(input, &mut self.enemy);
        self.enemy.update(&mut self.player);
    }

    pub fn get_player(&self) -> &Player {
        &self.player
    }

    pub fn get_enemy(&self) -> &Enemy {
        &self.enemy
    }
}

pub struct Player {
    data: Data<f32>,
    speed: f32,
    lasers: Vec<Laser>,
    laser_timer: Timer,
    health: i32,
}

impl Player {
    fn new() -> Player {
        let data = Data::new(0.0, 0.0, 1.0, 1.0);
        let speed = 0.05;
        let lasers = vec![];
        let laser_timer = Timer::new();
        let health = 100;
        Player { data, speed, lasers, laser_timer, health }
    }

    fn update(&mut self, input: &Input, enemy: &mut Enemy) {
        let speed = self.speed;

        let mut y_speed = 0.0;
        if input.up() {
            y_speed = speed;
        } else if input.down() {
            y_speed = -speed;
        }

        let mut x_speed = 0.0;
        if input.left() {
            x_speed = -speed;
        } else if input.right(){
            x_speed = speed;
        }

        self.move_position(x_speed, y_speed);

        if input.shoot() && self.laser_timer.check(PreciseTime::now(), 400) {
            let laser = Laser::new(self.data().position.x + 1.0, self.data().position.y);
            self.lasers.push(laser);
        }

        let width = 5.0;
        let height = 4.0;
        let area = Rectangle::new(-width, width, -height, height);
        self.stay_at_area(&area);

        self.clean_and_update_lasers(enemy);

        if self.circle_collision(enemy) {
            self.update_health(-1);
        }
    }

    pub fn get_lasers(&self) -> &Vec<Laser> {
        &self.lasers
    }

    fn clean_and_update_lasers(&mut self, enemy: &mut Enemy) {
        let mut remove = (false, 0);

        for (i, laser) in self.lasers.iter_mut().enumerate() {
            laser.update();

            if laser.destroy() {
                remove = (true, i);
            } else if enemy.circle_collision(laser) {
                remove = (true, i);
                enemy.update_health(-laser.get_damage());
            }
        }

        if let (true, i) = remove {
            self.lasers.swap_remove(i);
        }
    }

    pub fn update_health(&mut self, amount: i32) {
        self.health += amount;

        if self.health < 0 {
            self.health = 0;
        }

        println!("player health: {}", self.health);
    }
}

impl GameObject for Player {}
impl ModelMatrix for Player {}

impl GameObjectData<f32> for Player {
    fn data(&self) -> &Data<f32> {
        &self.data
    }
    fn data_mut(&mut self) -> &mut Data<f32> {
        &mut self.data
    }
}

pub struct Laser {
    data: Data<f32>,
    speed: f32,
    destroy: bool,
    damage: i32,
}

impl Laser {
    fn new(x: f32, y: f32) -> Laser {
        let data = Data::new(x, y, 0.3, 0.1);
        let speed = 0.08;
        let destroy = false;
        let damage = 1;
        Laser { data, speed, destroy, damage }
    }

    fn update(&mut self) {
        let speed = self.speed;
        self.forward(speed);

        let width = 5.0;
        let height = 4.0;
        let area = Rectangle::new(-width, width, -height, height);

        if self.outside_allowed_area(&area) {
            self.destroy = true;
        }
    }

    fn get_damage(&self) -> i32 {
        self.damage
    }
}

impl CanDestroy for Laser {
    fn destroy(&self) -> bool {
        self.destroy
    }
}

impl GameObjectData<f32> for Laser {
    fn data(&self) -> &Data<f32> {
        &self.data
    }
    fn data_mut(&mut self) -> &mut Data<f32> {
        &mut self.data
    }
}

impl GameObject for Laser {}
impl ModelMatrix for Laser {}

pub struct Enemy {
    data: Data<f32>,
    speed: f32,
    lasers: Vec<Laser>,
    laser_timer: Timer,
    health: i32,
}

impl Enemy {
    fn new() -> Enemy {
        let data = Data::new(3.0, 0.0, 1.0, 1.0);

        let speed = 0.05;
        let lasers = vec![];
        let laser_timer = Timer::new();
        let health = 100;
        Enemy { data, speed, lasers, laser_timer, health }
    }

    fn update(&mut self, player: &mut Player) {
        let speed = self.speed;

        self.move_position(0.0, speed);

        let width = 5.0;
        let height = 4.0;
        let area = Rectangle::new(-width, width, -height, height);

        if self.stay_at_area(&area) {
            self.speed *= -1.0;
        }

        if self.laser_timer.check(PreciseTime::now(), 1000) {
            self.create_laser(consts::PI);
            if (self.health < 25) {
                self.create_laser(consts::PI * 0.9);
                self.create_laser(consts::PI * 1.1);
            } else if (self.health < 50) {
                self.create_laser(consts::PI * 0.9);
            }
        }

        self.clean_and_update_lasers(player);
    }

    pub fn get_lasers(&self) -> &Vec<Laser> {
        &self.lasers
    }

    fn clean_and_update_lasers(&mut self, player: &mut Player) {
        let mut remove = (false, 0);

        for (i, laser) in self.lasers.iter_mut().enumerate() {
            laser.update();

            if laser.destroy() {
                remove = (true, i);
            } else if player.circle_collision(laser) {
                remove = (true, i);
                player.update_health(-laser.get_damage());
            }
        }

        if let (true, i) = remove {
            self.lasers.swap_remove(i);
        }
    }

    fn create_laser(&mut self, rotation: f32) {
        let mut laser = Laser::new(self.data().position.x - 1.0, self.data().position.y);
        laser.turn(rotation);
        self.lasers.push(laser);
    }

    pub fn update_health(&mut self, amount: i32) {
        self.health += amount;

        if self.health < 0 {
            self.health = 0;
        }

        println!("enemy health: {}", self.health);
    }
}

impl GameObject for Enemy {}
impl ModelMatrix for Enemy {}

impl GameObjectData<f32> for Enemy {
    fn data(&self) -> &Data<f32> {
        &self.data
    }
    fn data_mut(&mut self) -> &mut Data<f32> {
        &mut self.data
    }
}
<commit_msg>Background and MovingBackground<commit_after>/*
src/logic/mod.rs, 2017-07-24

Copyright (c) 2017 Juuso Tuononen

This file is licensed under

Apache License, Version 2.0

or

MIT License
*/

pub mod common;

use logic::common::*;

use input::Input;

use time::PreciseTime;
use Timer;

use std::f32::consts;

pub struct Logic {
    player: Player,
    enemy: Enemy,
    moving_background: MovingBackground,
}

impl Logic {
    pub fn new() -> Logic {
        let player = Player::new();
        let enemy = Enemy::new();
        let moving_background = MovingBackground::new();
        Logic { player, enemy, moving_background }
    }

    pub fn update<T: Input>(&mut self, input: &T) {
        self.player.update(input, &mut self.enemy);
        self.enemy.update(&mut self.player);
        self.moving_background.update();
    }

    pub fn get_player(&self) -> &Player {
        &self.player
    }

    pub fn get_enemy(&self) -> &Enemy {
        &self.enemy
    }

    pub fn get_moving_background(&self) -> &MovingBackground {
        &self.moving_background
    }
}

pub struct Player {
    data: Data<f32>,
    speed: f32,
    lasers: Vec<Laser>,
    laser_timer: Timer,
    health: i32,
}

impl Player {
    fn new() -> Player {
        let data = Data::new(0.0, 0.0, 1.0, 1.0);
        let speed = 0.05;
        let lasers = vec![];
        let laser_timer = Timer::new();
        let health = 100;
        Player { data, speed, lasers, laser_timer, health }
    }

    fn update(&mut self, input: &Input, enemy: &mut Enemy) {
        let speed = self.speed;

        let mut y_speed = 0.0;
        if input.up() {
            y_speed = speed;
        } else if input.down() {
            y_speed = -speed;
        }

        let mut x_speed = 0.0;
        if input.left() {
            x_speed = -speed;
        } else if input.right(){
            x_speed = speed;
        }

        self.move_position(x_speed, y_speed);

        if input.shoot() && self.laser_timer.check(PreciseTime::now(), 400) {
            let laser = Laser::new(self.data().position.x + 1.0, self.data().position.y);
            self.lasers.push(laser);
        }

        let width = 5.0;
        let height = 4.0;
        let area = Rectangle::new(-width, width, -height, height);
        self.stay_at_area(&area);

        self.clean_and_update_lasers(enemy);

        if self.circle_collision(enemy) {
            self.update_health(-1);
        }
    }

    pub fn get_lasers(&self) -> &Vec<Laser> {
        &self.lasers
    }

    fn clean_and_update_lasers(&mut self, enemy: &mut Enemy) {
        let mut remove = (false, 0);

        for (i, laser) in self.lasers.iter_mut().enumerate() {
            laser.update();

            if laser.destroy() {
                remove = (true, i);
            } else if enemy.circle_collision(laser) {
                remove = (true, i);
                enemy.update_health(-laser.get_damage());
            }
        }

        if let (true, i) = remove {
            self.lasers.swap_remove(i);
        }
    }

    pub fn update_health(&mut self, amount: i32) {
        self.health += amount;

        if self.health < 0 {
            self.health = 0;
        }

        println!("player health: {}", self.health);
    }
}

impl GameObject for Player {}
impl ModelMatrix for Player {}

impl GameObjectData<f32> for Player {
    fn data(&self) -> &Data<f32> {
        &self.data
    }
    fn data_mut(&mut self) -> &mut Data<f32> {
        &mut self.data
    }
}

pub struct Laser {
    data: Data<f32>,
    speed: f32,
    destroy: bool,
    damage: i32,
}

impl Laser {
    fn new(x: f32, y: f32) -> Laser {
        let data = Data::new(x, y, 0.3, 0.1);
        let speed = 0.08;
        let destroy = false;
        let damage = 1;
        Laser { data, speed, destroy, damage }
    }

    fn update(&mut self) {
        let speed = self.speed;
        self.forward(speed);

        let width = 5.0;
        let height = 4.0;
        let area = Rectangle::new(-width, width, -height, height);

        if self.outside_allowed_area(&area) {
            self.destroy = true;
        }
    }

    fn get_damage(&self) -> i32 {
        self.damage
    }
}

impl CanDestroy for Laser {
    fn destroy(&self) -> bool {
        self.destroy
    }
}

impl GameObjectData<f32> for Laser {
    fn data(&self) -> &Data<f32> {
        &self.data
    }
    fn data_mut(&mut self) -> &mut Data<f32> {
        &mut self.data
    }
}

impl GameObject for Laser {}
impl ModelMatrix for Laser {}

pub struct Enemy {
    data: Data<f32>,
    speed: f32,
    lasers: Vec<Laser>,
    laser_timer: Timer,
    health: i32,
}

impl Enemy {
    fn new() -> Enemy {
        let data = Data::new(3.0, 0.0, 1.0, 1.0);

        let speed = 0.05;
        let lasers = vec![];
        let laser_timer = Timer::new();
        let health = 100;
        Enemy { data, speed, lasers, laser_timer, health }
    }

    fn update(&mut self, player: &mut Player) {
        let speed = self.speed;

        self.move_position(0.0, speed);

        let width = 5.0;
        let height = 4.0;
        let area = Rectangle::new(-width, width, -height, height);

        if self.stay_at_area(&area) {
            self.speed *= -1.0;
        }

        if self.laser_timer.check(PreciseTime::now(), 1000) {
            self.create_laser(consts::PI);
            if self.health < 25 {
                self.create_laser(consts::PI * 0.9);
                self.create_laser(consts::PI * 1.1);
            } else if self.health < 50 {
                self.create_laser(consts::PI * 0.9);
            }
        }

        self.clean_and_update_lasers(player);
    }

    pub fn get_lasers(&self) -> &Vec<Laser> {
        &self.lasers
    }

    fn clean_and_update_lasers(&mut self, player: &mut Player) {
        let mut remove = (false, 0);

        for (i, laser) in self.lasers.iter_mut().enumerate() {
            laser.update();

            if laser.destroy() {
                remove = (true, i);
            } else if player.circle_collision(laser) {
                remove = (true, i);
                player.update_health(-laser.get_damage());
            }
        }

        if let (true, i) = remove {
            self.lasers.swap_remove(i);
        }
    }

    fn create_laser(&mut self, rotation: f32) {
        let mut laser = Laser::new(self.data().position.x - 1.0, self.data().position.y);
        laser.turn(rotation);
        self.lasers.push(laser);
    }

    pub fn update_health(&mut self, amount: i32) {
        self.health += amount;

        if self.health < 0 {
            self.health = 0;
        }

        println!("enemy health: {}", self.health);
    }
}

impl GameObject for Enemy {}
impl ModelMatrix for Enemy {}

impl GameObjectData<f32> for Enemy {
    fn data(&self) -> &Data<f32> {
        &self.data
    }
    fn data_mut(&mut self) -> &mut Data<f32> {
        &mut self.data
    }
}

pub struct Background {
    data: Data<f32>,
    x_limit: f32,
    x_reset_position: f32,
    speed: f32,
}

impl Background {
    fn new(i: f32, side_length: f32) -> Background {
        let data = Data::new(i*side_length, 0.0, side_length, side_length);
        let x_limit = -2.0*side_length;
        let x_reset_position = 2.0*side_length;
        let speed = -0.04;

        Background { data, x_limit, x_reset_position, speed }
    }

    fn update(&mut self) {
        let speed = self.speed;
        self.move_position(speed, 0.0);

        if self.data().position.x <= self.x_limit {
            self.data_mut().position.x = self.x_reset_position;
        }
    }
}

impl GameObject for Background {}
impl ModelMatrix for Background {}

impl GameObjectData<f32> for Background {
    fn data(&self) -> &Data<f32> {
        &self.data
    }
    fn data_mut(&mut self) -> &mut Data<f32> {
        &mut self.data
    }
}

pub struct MovingBackground {
    backgrounds: [Background; 4],
}

impl MovingBackground {
    fn new() -> MovingBackground {
        let size = 9.0;
        let backgrounds = [
            Background::new(-1.0, size),
            Background::new(0.0, size),
            Background::new(1.0, size),
            Background::new(2.0, size),
        ];

        MovingBackground { backgrounds }
    }

    fn update(&mut self) {
       for background in &mut self.backgrounds {
           background.update();
       }
    }

    pub fn get_backgrounds(&self) -> &[Background; 4] {
        &self.backgrounds
    }
}<|endoftext|>


<commit_before>//! Sprite rendering

use super::{Ppu, Rgb};
use super::oam::OamEntry;

use flexvec::FlexVec;

/// Render state stored inside the `Ppu`. We use this to cache the visible sprites for each
/// scanline, just like the real PPU would.
pub struct SpriteRenderState {
    /// Caches a prerendered scanline of sprite pixels. This is rendered at the start of each
    /// scanline and contains, for each pixel on the scanline, the color of the OBJ (= sprite) layer
    /// and the priority of that pixel (`None` in case there's no opaque sprite pixel there).
    sprite_scanline: [(Option<(Rgb, u8)>); super::SCREEN_WIDTH as usize],
}

impl Default for SpriteRenderState {
    fn default() -> Self {
        SpriteRenderState {
            sprite_scanline: [None; super::SCREEN_WIDTH as usize],
        }
    }
}

/// Informations about a single tile of a sprite, needed for drawing.
#[derive(Copy, Clone, Default)]
struct SpriteTile<'a> {
    /// Address of character data for this tile
    chr_addr: u16,
    /// X position of the tile on the screen. Can be negative if the tile starts outside the screen.
    x: i16,
    /// Y position of the scanline inside the tile (0-7)
    /// FIXME Can we just store the pixel row that's on the scanline? (for each priority)
    y_off: u8,
    /// Reference to the sprite this tile is a part of. Note that tiles can be shared, but that
    /// doesn't matter for this.
    ///
    /// This is only an `Option` so it can implement `Default`. Integer generics should make this go
    /// away (since they'd make `arrayvec` more usable).
    sprite: Option<&'a OamEntry>,
}

impl<'a> SpriteTile<'a> {
    fn sprite(&self) -> &OamEntry {
        self.sprite.unwrap()
    }
}

impl Ppu {
    /// Collects visible sprites and sprite tiles for the current scanline.
    ///
    /// Called when rendering the first pixel on a scanline.
    pub fn collect_sprite_data_for_scanline(&mut self) {
        let first_sprite = if self.oamaddh & 0x80 == 0 {
            0
        } else {
            // Priority rotation enabled
            (self.oamaddl as u16 & 0xfe) >> 1
        };

        // Find the first 32 sprites on the current scanline (RANGE)
        // NB Priority is ignored for this step, it's only used for drawing, which isn't done here
        let mut visible_sprites = [OamEntry::default(); 32];
        let mut visible_sprites = FlexVec::new(&mut visible_sprites);
        for i in first_sprite..first_sprite+128 {
            let index = (i & 0x7f) as u8;   // limit to 127 and wrap back around
            let entry = self.oam.get_sprite(index);
            if self.sprite_on_scanline(&entry) {
                if visible_sprites.push(entry).is_err() {
                    // FIXME: Sprite overflow. Set bit 6 of $213e.
                    break;
                }
            }
        }

        // "Starting with the last sprite in Range, load up to 34 8x8 tiles (from left-to-right,
        // after flipping). If there are more than 34 tiles in Range, set bit 7 of $213e. Only
        // those tiles with -8 < X < 256 are counted."
        // A few notes:
        // * Sprite tiles are always 8x8 pixels
        // * Sprites do not have tile maps like BGs do
        // * "left-to-right" refers to how tiles of sprites are loaded, not the sprite order
        // * Tiles are loaded iff they are on the current scanline (and have `-8 < X < 256`)
        // FIXME Is this ^^ correct?

        let mut visible_tiles = [SpriteTile::default(); 34];
        let mut visible_tiles = FlexVec::new(&mut visible_tiles);

        // Word address of first sprite character table
        let name_base: u16 = (self.obsel as u16 & 0b111) << 13;
        let name_select: u16 = (self.obsel as u16 >> 3) & 0b11;

        // TIME: Start at the last sprite found, load up to 34 8x8 tiles (for each sprite from left
        // to right, after taking flip bits of the sprite into account)
        'collect_tiles: for sprite in visible_sprites.iter().rev() {
            // How many tiles are there?
            let (sprite_w, _) = self.obj_size(sprite.size_toggle);
            let sprite_w_tiles = sprite_w / 8;
            // Offset into the sprite
            let sprite_y_off = self.scanline - sprite.y as u16;
            // Tile Y coordinate of the tile row we're interested in (tiles on the scanline)
            let y_tile = sprite_y_off / 8;
            // Y offset into the tile row
            let tile_y_off = (sprite_y_off % 8) as u8;

            // Calculate VRAM word address of first tile. Depends on base/name bits in `$2101`.
            let tile_start_word_addr =
                (name_base |
                ((sprite.tile as u16) << 4) |
                (sprite.name_table as u16 * ((name_select + 1) << 12))) & 0x7fff;
            let tile_start_addr = tile_start_word_addr * 2;

            // The character data for the first tile is stored at `tile_start_addr`, in the same
            // format as BG character data (bitplanes, etc.). Keep in mind that sprites do not have
            // tilemaps.
            // One 8x8 tile is 32 Bytes large (4 bits per pixel).
            // Tiles in a single (1 tile or 8 pixel high) row of the sprite are stored sequentially:
            // Tile coord (1,0) is stored directly behind (0,0), which is stored at
            // `tile_start_addr`.
            // Rows of tiles, however, are always stored 512 Bytes (or 16 tiles/128 pixels) apart:
            // If tile (0,0) is at address $0000, tile (0,1) is at $0200. This is independent of
            // the sprite size, which means that there are "holes" in the sprite character data,
            // which are used to store the data of other sprites.

            // Start address of the row of tiles on the scanline
            let y_row_start_addr = tile_start_addr.wrapping_add(512 * y_tile);

            // FIXME "Only those tiles with -8 < X < 256 are counted."
            // Add all tiles in this row to our tile list (left to right)
            for i in 0..sprite_w_tiles as i16 {
                let flip_i = if sprite.hflip { sprite_w_tiles as i16 - i - 1 } else { i };
                let tile = SpriteTile {
                    chr_addr: y_row_start_addr + 32 * i as u16,
                    x: sprite.x + 8 * flip_i,
                    y_off: tile_y_off,
                    sprite: Some(sprite),
                };

                if visible_tiles.push(tile).is_err() {
                    // FIXME Set sprite tile overflow flag
                    break 'collect_tiles
                }
            }
        }

        // With all 34 visible sprite tiles collected, prerender them into a cache. We also store
        // the priority in the cache to correctly layer the OBJ layer between BGs. This also
        // emulates the "sprite priority quirk" (as it is known primarily on the NES, which seems to
        // share this behavior).
        // "Sprites with a lower index are always in front of sprites with a higher index."
        // Since the sprite list is already sorted by index, and the tile list is built up
        // backwards, we should be fine with iterating over all 34 (or less) tiles and rendering
        // them in order (overwriting what's already there).
        self.sprite_render_state.sprite_scanline = [None; super::SCREEN_WIDTH as usize];

        for tile in visible_tiles.iter() {
            for x_off in 0u8..8 {
                let screen_x = tile.x + x_off as i16;
                if screen_x >= 0 && screen_x < super::SCREEN_WIDTH as i16 {
                    // on-screen pixel (can write to buffer)
                    let color = self.read_sprite_tile_pixel(tile, x_off);
                    let buffer = &mut self.sprite_render_state.sprite_scanline;
                    match color {
                        Some(rgb) => {
                            buffer[screen_x as usize] = Some((rgb, tile.sprite().priority));
                        }
                        None => {
                            // do nothing (don't overwrite visible pixels with transparent ones)
                        }
                    }
                }
            }
        }
    }

    /// Determines if the given sprite has any tiles on the current scanline
    fn sprite_on_scanline(&self, sprite: &OamEntry) -> bool {
        let (w, h) = self.obj_size(sprite.size_toggle);
        let (w, h) = (w as i16, h as u16);

        // "If any OBJ is at X=256 (or X=-256, same difference), consider it as being at X=0 when
        // considering Range and Time."
        // X=256 can not occur, since X is a signed 9-bit value (range is -256 - 255)
        let x = if sprite.x == -256 { 0 } else { sprite.x };
        let y = sprite.y as u16;

        // "Only those sprites with -size < X < 256 are considered in Range." (`size` is `w` here)
        // We don't check `X < 256`, since that cannot occur (X is a signed 9-bit integer)
        // A sprite moved past the right edge of the screen will wrap to `-256`, which is handled
        // by this check.
        if -w < x {
            if y <= self.scanline && y + h >= self.scanline {
                // Sprite is on scanline
                true
            } else {
                false
            }
        } else {
            false
        }
    }

    fn read_sprite_tile_pixel(&self, tile: &SpriteTile, x_offset: u8) -> Option<Rgb> {
        debug_assert!(x_offset < 8);
        let rel_color = self.read_chr_entry(4,  // 16 colors
                                            tile.chr_addr,
                                            8,  // 8x8 tiles
                                            (x_offset as u8, tile.y_off),
                                            (tile.sprite().vflip, tile.sprite().hflip));
        debug_assert!(rel_color < 16, "rel_color = {} (but is 4-bit!)", rel_color);

        // color index 0 is always transparent
        if rel_color == 0 { return None }

        let abs_color = 128 + tile.sprite().palette * 16 + rel_color;
        // FIXME Color math
        let rgb = self.cgram.get_color(abs_color);

        Some(rgb)
    }

    /// Returns the value of the current pixel on the sprite layer if it has the given priority
    /// (and `None` otherwise).
    pub fn maybe_draw_sprite_pixel(&self, prio: u8) -> Option<Rgb> {
        if self.tm & 0x10 == 0 { return None }  // OBJ layer disabled

        match self.sprite_render_state.sprite_scanline[self.x as usize] {
            None => None,   // No pixel or transparent pixel on OBJ layer
            Some((rgb, obj_prio)) => if obj_prio == prio {
                Some(rgb)
            } else {
                None
            },
        }
    }
}
<commit_msg>Fix sprite_on_scanline and implement vflip<commit_after>//! Sprite rendering

use super::{Ppu, Rgb};
use super::oam::OamEntry;

use flexvec::FlexVec;

/// Render state stored inside the `Ppu`. We use this to cache the visible sprites for each
/// scanline, just like the real PPU would.
pub struct SpriteRenderState {
    /// Caches a prerendered scanline of sprite pixels. This is rendered at the start of each
    /// scanline and contains, for each pixel on the scanline, the color of the OBJ (= sprite) layer
    /// and the priority of that pixel (`None` in case there's no opaque sprite pixel there).
    sprite_scanline: [(Option<(Rgb, u8)>); super::SCREEN_WIDTH as usize],
}

impl Default for SpriteRenderState {
    fn default() -> Self {
        SpriteRenderState {
            sprite_scanline: [None; super::SCREEN_WIDTH as usize],
        }
    }
}

/// Informations about a single tile of a sprite, needed for drawing.
#[derive(Copy, Clone, Default)]
struct SpriteTile<'a> {
    /// Address of character data for this tile
    chr_addr: u16,
    /// X position of the tile on the screen. Can be negative if the tile starts outside the screen.
    x: i16,
    /// Y position of the scanline inside the tile (0-7)
    /// FIXME Can we just store the pixel row that's on the scanline? (for each priority)
    y_off: u8,
    /// Reference to the sprite this tile is a part of. Note that tiles can be shared, but that
    /// doesn't matter for this.
    ///
    /// This is only an `Option` so it can implement `Default`. Integer generics should make this go
    /// away (since they'd make `arrayvec` more usable).
    sprite: Option<&'a OamEntry>,
}

impl<'a> SpriteTile<'a> {
    fn sprite(&self) -> &OamEntry {
        self.sprite.unwrap()
    }
}

impl Ppu {
    /// Collects visible sprites and sprite tiles for the current scanline.
    ///
    /// Called when rendering the first pixel on a scanline.
    pub fn collect_sprite_data_for_scanline(&mut self) {
        let first_sprite = if self.oamaddh & 0x80 == 0 {
            0
        } else {
            // Priority rotation enabled
            (self.oamaddl as u16 & 0xfe) >> 1
        };

        // Find the first 32 sprites on the current scanline (RANGE)
        // NB Priority is ignored for this step, it's only used for drawing, which isn't done here
        let mut visible_sprites = [OamEntry::default(); 32];
        let mut visible_sprites = FlexVec::new(&mut visible_sprites);
        for i in first_sprite..first_sprite+128 {
            let index = (i & 0x7f) as u8;   // limit to 127 and wrap back around
            let entry = self.oam.get_sprite(index);
            if self.sprite_on_scanline(&entry) {
                if visible_sprites.push(entry).is_err() {
                    // FIXME: Sprite overflow. Set bit 6 of $213e.
                    break;
                }
            }
        }

        // "Starting with the last sprite in Range, load up to 34 8x8 tiles (from left-to-right,
        // after flipping). If there are more than 34 tiles in Range, set bit 7 of $213e. Only
        // those tiles with -8 < X < 256 are counted."
        // A few notes:
        // * Sprite tiles are always 8x8 pixels
        // * Sprites do not have tile maps like BGs do
        // * "left-to-right" refers to how tiles of sprites are loaded, not the sprite order
        // * Tiles are loaded iff they are on the current scanline (and have `-8 < X < 256`)
        // FIXME Is this ^^ correct?

        let mut visible_tiles = [SpriteTile::default(); 34];
        let mut visible_tiles = FlexVec::new(&mut visible_tiles);

        // Word address of first sprite character table
        let name_base: u16 = (self.obsel as u16 & 0b111) << 13;
        let name_select: u16 = (self.obsel as u16 >> 3) & 0b11;

        // TIME: Start at the last sprite found, load up to 34 8x8 tiles (for each sprite from left
        // to right, after taking flip bits of the sprite into account [FIXME Flip bits are ignored
        // I think])
        'collect_tiles: for sprite in visible_sprites.iter().rev() {
            // How many tiles are there?
            let (sprite_w, sprite_h) = self.obj_size(sprite.size_toggle);
            let sprite_w_tiles = sprite_w / 8;
            //let sprite_h_tiles = sprite_h / 8;
            // Offset into the sprite
            let sprite_y_off = self.scanline - sprite.y as u16;
            // Tile Y coordinate of the tile row we're interested in (tiles on the scanline)
            let y_tile = if sprite.vflip {
                (sprite_h as u16 - sprite_y_off - 1) / 8
            } else {
                sprite_y_off / 8
            };
            // Y offset into the tile row
            let tile_y_off = (sprite_y_off % 8) as u8;

            // Calculate VRAM word address of first tile. Depends on base/name bits in `$2101`.
            let tile_start_word_addr =
                (name_base |
                ((sprite.tile as u16) << 4) |
                (sprite.name_table as u16 * ((name_select + 1) << 12))) & 0x7fff;
            let tile_start_addr = tile_start_word_addr * 2;

            // The character data for the first tile is stored at `tile_start_addr`, in the same
            // format as BG character data (bitplanes, etc.). Keep in mind that sprites do not have
            // tilemaps.
            // One 8x8 tile is 32 Bytes large (4 bits per pixel).
            // Tiles in a single (1 tile or 8 pixel high) row of the sprite are stored sequentially:
            // Tile coord (1,0) is stored directly behind (0,0), which is stored at
            // `tile_start_addr`.
            // Rows of tiles, however, are always stored 512 Bytes (or 16 tiles/128 pixels) apart:
            // If tile (0,0) is at address $0000, tile (0,1) is at $0200. This is independent of
            // the sprite size, which means that there are "holes" in the sprite character data,
            // which are used to store the data of other sprites.

            // Start address of the row of tiles on the scanline
            let y_row_start_addr = tile_start_addr.wrapping_add(512 * y_tile);

            // FIXME "Only those tiles with -8 < X < 256 are counted."
            // Add all tiles in this row to our tile list (left to right)
            for i in 0..sprite_w_tiles as i16 {
                let flip_i = if sprite.hflip { sprite_w_tiles as i16 - i - 1 } else { i };
                let tile = SpriteTile {
                    chr_addr: y_row_start_addr + 32 * i as u16,
                    x: sprite.x + 8 * flip_i,
                    y_off: tile_y_off,
                    sprite: Some(sprite),
                };

                if visible_tiles.push(tile).is_err() {
                    // FIXME Set sprite tile overflow flag
                    break 'collect_tiles
                }
            }
        }

        // With all 34 visible sprite tiles collected, prerender them into a cache. We also store
        // the priority in the cache to correctly layer the OBJ layer between BGs. This also
        // emulates the "sprite priority quirk" (as it is known primarily on the NES, which seems to
        // share this behavior).
        // "Sprites with a lower index are always in front of sprites with a higher index."
        // Since the sprite list is already sorted by index, and the tile list is built up
        // backwards, we should be fine with iterating over all 34 (or less) tiles and rendering
        // them in order (overwriting what's already there).
        self.sprite_render_state.sprite_scanline = [None; super::SCREEN_WIDTH as usize];

        for tile in visible_tiles.iter() {
            for x_off in 0u8..8 {
                let screen_x = tile.x + x_off as i16;
                if screen_x >= 0 && screen_x < super::SCREEN_WIDTH as i16 {
                    // on-screen pixel (can write to buffer)
                    let color = self.read_sprite_tile_pixel(tile, x_off);
                    let buffer = &mut self.sprite_render_state.sprite_scanline;
                    match color {
                        Some(rgb) => {
                            buffer[screen_x as usize] = Some((rgb, tile.sprite().priority));
                        }
                        None => {
                            // do nothing (don't overwrite visible pixels with transparent ones)
                        }
                    }
                }
            }
        }
    }

    /// Determines if the given sprite has any tiles on the current scanline
    fn sprite_on_scanline(&self, sprite: &OamEntry) -> bool {
        let (w, h) = self.obj_size(sprite.size_toggle);
        let (w, h) = (w as i16, h as u16);

        // "If any OBJ is at X=256 (or X=-256, same difference), consider it as being at X=0 when
        // considering Range and Time."
        // X=256 can not occur, since X is a signed 9-bit value (range is -256 - 255)
        let x = if sprite.x == -256 { 0 } else { sprite.x };
        let y = sprite.y as u16;

        // "Only those sprites with -size < X < 256 are considered in Range." (`size` is `w` here)
        // We don't check `X < 256`, since that cannot occur (X is a signed 9-bit integer)
        // A sprite moved past the right edge of the screen will wrap to `-256`, which is handled
        // by this check.
        if -w < x {
            if y <= self.scanline && y + h > self.scanline {
                // Sprite is on scanline
                true
            } else {
                false
            }
        } else {
            false
        }
    }

    fn read_sprite_tile_pixel(&self, tile: &SpriteTile, x_offset: u8) -> Option<Rgb> {
        debug_assert!(x_offset < 8);
        let rel_color = self.read_chr_entry(4,  // 16 colors
                                            tile.chr_addr,
                                            8,  // 8x8 tiles
                                            (x_offset as u8, tile.y_off),
                                            (tile.sprite().vflip, tile.sprite().hflip));
        debug_assert!(rel_color < 16, "rel_color = {} (but is 4-bit!)", rel_color);

        // color index 0 is always transparent
        if rel_color == 0 { return None }

        let abs_color = 128 + tile.sprite().palette * 16 + rel_color;
        // FIXME Color math
        let rgb = self.cgram.get_color(abs_color);

        Some(rgb)
    }

    /// Returns the value of the current pixel on the sprite layer if it has the given priority
    /// (and `None` otherwise).
    pub fn maybe_draw_sprite_pixel(&self, prio: u8) -> Option<Rgb> {
        if self.tm & 0x10 == 0 { return None }  // OBJ layer disabled

        match self.sprite_render_state.sprite_scanline[self.x as usize] {
            None => None,   // No pixel or transparent pixel on OBJ layer
            Some((rgb, obj_prio)) => if obj_prio == prio {
                Some(rgb)
            } else {
                None
            },
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

fn global_test_root() -> PathBuf {
    let mut path = env::current_exe().unwrap();
    path.pop(); // chop off exe name
    path.pop(); // chop off 'debug'

    // If `cargo test` is run manually then our path looks like
    // `target/debug/foo`, in which case our `path` is already pointing at
    // `target`. If, however, `cargo test --target $target` is used then the
    // output is `target/$target/debug/foo`, so our path is pointing at
    // `target/$target`. Here we conditionally pop the `$target` name.
    if path.file_name().and_then(|s| s.to_str()) != Some("target") {
        path.pop();
    }

    path.join("int-test")
}

fn test_root() -> PathBuf {
    let root = global_test_root();

    static NEXT_TEST_NUM: AtomicUsize = ATOMIC_USIZE_INIT;
    thread_local!(static TEST_NUM: usize = NEXT_TEST_NUM.fetch_add(1, Ordering::SeqCst));
    let root = root.join(&TEST_NUM.with(|my_id| format!("t{}", my_id)));

    thread_local!(static TEST_ROOT_INIT: Cell<bool> = Cell::new(false));
    TEST_ROOT_INIT.with(|i| {
        if i.get() {
            return;
        }
        i.set(true);
        fs::remove_dir_all(&root).expect("removing root");
        debug!("deleted root {}", root.display());
    });

    root
}

pub fn test_home() -> PathBuf {
    test_root().join("home")
}
<commit_msg>Make testkit init cleanly<commit_after>use std::cell::Cell;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

fn global_test_root() -> PathBuf {
    let mut path = env::current_exe().unwrap();
    path.pop(); // chop off exe name
    path.pop(); // chop off 'debug'

    // If `cargo test` is run manually then our path looks like
    // `target/debug/foo`, in which case our `path` is already pointing at
    // `target`. If, however, `cargo test --target $target` is used then the
    // output is `target/$target/debug/foo`, so our path is pointing at
    // `target/$target`. Here we conditionally pop the `$target` name.
    if path.file_name().and_then(|s| s.to_str()) != Some("target") {
        path.pop();
    }

    path.join("int-test")
}

fn test_root() -> PathBuf {
    let root = global_test_root();

    static NEXT_TEST_NUM: AtomicUsize = ATOMIC_USIZE_INIT;
    thread_local!(static TEST_NUM: usize = NEXT_TEST_NUM.fetch_add(1, Ordering::SeqCst));
    let root = root.join(&TEST_NUM.with(|my_id| format!("t{}", my_id)));

    thread_local!(static TEST_ROOT_INIT: Cell<bool> = Cell::new(false));
    TEST_ROOT_INIT.with(|i| {
        if i.get() {
            return;
        }
        i.set(true);
        if root.exists() {
            fs::remove_dir_all(&root).expect("removing root");
            debug!("deleted root {}", root.display());
        }
    });

    root
}

pub fn test_home() -> PathBuf {
    test_root().join("home")
}
<|endoftext|>


<commit_before>// Originally written by JustAPerson (https://github.com/JustAPerson).
// Modified by Andrew Gallant (https://github.com/BurntSushi).

extern crate regexp;
extern crate sync;

use regexp::{Regexp, NoExpand};
use sync::Arc;

static VARIANTS: &'static [&'static str] = &'static [
    "agggtaaa|tttaccct",
    "[cgt]gggtaaa|tttaccc[acg]",
    "a[act]ggtaaa|tttacc[agt]t",
    "ag[act]gtaaa|tttac[agt]ct",
    "agg[act]taaa|ttta[agt]cct",
    "aggg[acg]aaa|ttt[cgt]ccct",
    "agggt[cgt]aa|tt[acg]accct",
    "agggta[cgt]a|t[acg]taccct",
    "agggtaa[cgt]|[acg]ttaccct",
];

struct Subst<'a>(&'a str, &'a str);

static SUBST: &'static [Subst<'static>] = &'static [
    Subst("B", "(c|g|t)"), Subst("D", "(a|g|t)"),   Subst("H", "(a|c|t)"),
    Subst("K", "(g|t)"),   Subst("M", "(a|c)"),     Subst("N", "(a|c|g|t)"),
    Subst("R", "(a|g)"),   Subst("S", "(c|g)"),     Subst("V", "(a|c|g)"),
    Subst("W", "(a|t)"),   Subst("Y", "(c|t)"),
];

fn replace(text: &str, regex: &str, rep: &str) -> ~str {
    let re = Regexp::new(regex).unwrap();
    re.replace_all(text, NoExpand(rep))
}

fn count_matches(seq: &str, variant: &str) -> int {
    let re = Regexp::new(variant).unwrap();
    let mut n = 0;
    for _ in re.find_iter(seq) {
        n += 1;
    }
    n
}

fn main() {
    let mut stdin =  std::io::stdio::stdin();
    let mut seq = stdin.read_to_str().unwrap();
    let ilen = seq.len();

    seq = Regexp::new(">[^\n]*\n|\n").unwrap().replace_all(seq, NoExpand(""));
    let seq_arc = Arc::new(seq.clone());
    let clen = seq.len();

    let mut counts = vec!();
    for &variant in VARIANTS.iter() {
        let seq_arc_copy = seq_arc.clone();
        let count = sync::Future::spawn(proc() {
            count_matches(*seq_arc_copy, variant)
        });
        counts.push(count);
    }

    let mut seqlen = sync::Future::spawn(proc() {
        let mut seq = seq;
        for &Subst(k, v) in SUBST.iter() {
            seq = replace(seq, k, v);
        }
        seq.len()
    });
    for (i, variant) in VARIANTS.iter().enumerate() {
        println!("{} {:?}", variant, counts.get_mut(i).get());
    }
    println!("");
    println!("{}", ilen);
    println!("{}", clen);
    println!("{}", seqlen.get());
}
<commit_msg>don't need :? to print an int<commit_after>// Originally written by JustAPerson (https://github.com/JustAPerson).
// Modified by Andrew Gallant (https://github.com/BurntSushi).

extern crate regexp;
extern crate sync;

use regexp::{Regexp, NoExpand};
use sync::Arc;

static VARIANTS: &'static [&'static str] = &'static [
    "agggtaaa|tttaccct",
    "[cgt]gggtaaa|tttaccc[acg]",
    "a[act]ggtaaa|tttacc[agt]t",
    "ag[act]gtaaa|tttac[agt]ct",
    "agg[act]taaa|ttta[agt]cct",
    "aggg[acg]aaa|ttt[cgt]ccct",
    "agggt[cgt]aa|tt[acg]accct",
    "agggta[cgt]a|t[acg]taccct",
    "agggtaa[cgt]|[acg]ttaccct",
];

struct Subst<'a>(&'a str, &'a str);

static SUBST: &'static [Subst<'static>] = &'static [
    Subst("B", "(c|g|t)"), Subst("D", "(a|g|t)"),   Subst("H", "(a|c|t)"),
    Subst("K", "(g|t)"),   Subst("M", "(a|c)"),     Subst("N", "(a|c|g|t)"),
    Subst("R", "(a|g)"),   Subst("S", "(c|g)"),     Subst("V", "(a|c|g)"),
    Subst("W", "(a|t)"),   Subst("Y", "(c|t)"),
];

fn replace(text: &str, regex: &str, rep: &str) -> ~str {
    let re = Regexp::new(regex).unwrap();
    re.replace_all(text, NoExpand(rep))
}

fn count_matches(seq: &str, variant: &str) -> int {
    let re = Regexp::new(variant).unwrap();
    let mut n = 0;
    for _ in re.find_iter(seq) {
        n += 1;
    }
    n
}

fn main() {
    let mut stdin =  std::io::stdio::stdin();
    let mut seq = stdin.read_to_str().unwrap();
    let ilen = seq.len();

    seq = Regexp::new(">[^\n]*\n|\n").unwrap().replace_all(seq, NoExpand(""));
    let seq_arc = Arc::new(seq.clone());
    let clen = seq.len();

    let mut counts = vec!();
    for &variant in VARIANTS.iter() {
        let seq_arc_copy = seq_arc.clone();
        let count = sync::Future::spawn(proc() {
            count_matches(*seq_arc_copy, variant)
        });
        counts.push(count);
    }

    let mut seqlen = sync::Future::spawn(proc() {
        let mut seq = seq;
        for &Subst(k, v) in SUBST.iter() {
            seq = replace(seq, k, v);
        }
        seq.len()
    });
    for (i, variant) in VARIANTS.iter().enumerate() {
        println!("{} {}", variant, counts.get_mut(i).get());
    }
    println!("");
    println!("{}", ilen);
    println!("{}", clen);
    println!("{}", seqlen.get());
}
<|endoftext|>


<commit_before>extern crate url;

use std::collections::hashmap::HashMap;
use std::io::TcpStream;

/// A structure which holds the data for a Phant data stream
///
/// # Example
/// ```
/// let mut phant = phant::Phant::new("data.sparkfun.com", "your_public_key", "your_private_key");
///
/// phant.add("computer_name", "my-computer");
/// phant.add("external_ip", "123.321.111.222");
/// phant.add("internal_ip", "192.168.1.104");
///
/// phant.push().ok().expect("Pushing to server did not succeed");
/// ```

#[deriving(Show)]
pub struct Phant {
    hostname: String,
    public_key: String,
    private_key: String,
    data: HashMap<String, String>
}

impl Phant {
    pub fn new(hostname: &str, public_key: &str, private_key: &str) -> Phant {
        Phant {
            hostname: String::from_str(hostname),
            public_key: String::from_str(public_key),
            private_key: String::from_str(private_key),
            data: HashMap::new()
        }
    }

    /// Adds a key-value pair to the current phant dataset.  The `key` is the
    /// phant column name.  The `value` is a `.to_string()`-able data item. This function
    /// does not manipulate server data.
    pub fn add<T: ToString>(&mut self, key: &str, value: T) {
        self.data.insert(key.to_string(), value.to_string());
    }

    /// Pushes data to the server. All the data that has been `add`ed will be pushed as a row
    /// to the phant server located at `hostname`. The local row will then be cleared so `add`
    /// can be called again to repeat the process.
    ///
    /// Returns an [IoResult](http://doc.rust-lang.org/std/io/type.IoResult.html) where a successful
    /// push will return the response from the server in `String` form.
    pub fn push(&mut self) -> ::std::io::IoResult<String> {
        let query_string = self.data_query_string();
        let request: String = format!("POST /input/{} HTTP/1.0\nPhant-Private-Key: {}\nContent-Type: application/x-www-form-urlencoded\nContent-Length: {}\n\n{}\n\n",
            self.public_key, self.private_key, query_string.len(), query_string);
        let mut socket = try!(TcpStream::connect(self.hostname.as_slice(), 80));
        try!(socket.write_str(request.as_slice()));
        let result = try!(socket.read_to_string());
        self.clear_local();
        Ok(result)
    }

    /// Clears the data locally.  Any key-value pairs that have been added since creation or
    /// previous clear will be removed.
    pub fn clear_local(&mut self) {
        self.data.clear();
    }

    /// Clears the data on the server.
    pub fn clear_server(&mut self) -> ::std::io::IoResult<String> {
        let request: String = format!("DELETE /input/{} HTTP/1.0\nPhant-Private-Key: {}\n\n", self.public_key, self.private_key);
        let mut socket = TcpStream::connect(self.hostname.as_slice(), 80).unwrap();
        try!(socket.write_str(request.as_slice()));
        socket.read_to_string()
    }

    /// Gets a formatted URL in string form for adding the current row to the server.
    /// Local data is _not_ cleared, unlike the `push()` method.
    ///
    /// # Example
    /// ```
    /// let mut phant = phant::Phant::new("data.sparkfun.com", "your_public_key", "your_private_key");
    ///
    /// phant.add("apple_color", "red");
    /// let url = phant.get_url();
    /// assert!(url.as_slice() == "http://data.sparkfun.com/input/your_public_key?private_key=your_private_key&apple_color=red")
    /// ```
    pub fn get_url(&self) -> String {
        format!("http://{}/input/{}?private_key={}&{}", self.hostname, self.public_key, self.private_key, self.data_query_string())
    }

    /// Returns a clone of the data for the current row being added to. Notice: it's a
    /// clone, so manipulations done to this returned object will _not_ modify the
    /// data being held locally in the `Phant` object.
    pub fn row_data(&self) -> HashMap<String, String> {
        self.data.clone()
    }

    fn data_query_string(&self) -> String {
        let mut result_list = Vec::new();
        for (key, value) in self.data.iter() {
            result_list.push(format!("{}={}", key, value));
        }
        url::encode(result_list.connect("&"))
    }
}

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

    #[test]
    fn test_get_url() {
        let expected = "http://data.com/input/pub?private_key=priv&color=red".to_string();
        let p = basic_phant();
        assert_eq!(p.row_data().len(), 1);
        assert_eq!(p.get_url(), expected);
        assert_eq!(p.row_data().len(), 1);
    }

    #[test]
    fn test_row_data() {
        let expected_key = "color".to_string();
        let expected_value = "red".to_string();
        let p = basic_phant();
        let mut row_data = p.row_data();
        assert_eq!(row_data.len(), 1);
        assert_eq!(*row_data.get(&expected_key), expected_value);

        // Test that row_data actually makes a clone of the data, and not returning it directly
        row_data.insert("size".to_string(), "large".to_string());
        assert_eq!(p.row_data().len(), 1);
    }

    #[test]
    fn test_overwrite_column() {
        let key = "color".to_string();
        let first_value = "red".to_string();
        let second_value = "green".to_string();
        let mut p = basic_phant();
        assert_eq!(*p.row_data().get(&key), first_value);

        p.add(key.as_slice(), second_value.as_slice());
        assert_eq!(p.row_data().len(), 1);
        assert_eq!(p.row_data().get(&key), &second_value);
    }

    fn basic_phant() -> Phant {
        let mut p = Phant::new("data.com", "pub", "priv");
        p.add("color", "red");
        p
    }
}
<commit_msg>added one more test<commit_after>extern crate url;

use std::collections::hashmap::HashMap;
use std::io::TcpStream;

/// A structure which holds the data for a Phant data stream
///
/// # Example
/// ```
/// let mut phant = phant::Phant::new("data.sparkfun.com", "your_public_key", "your_private_key");
///
/// phant.add("computer_name", "my-computer");
/// phant.add("external_ip", "123.321.111.222");
/// phant.add("internal_ip", "192.168.1.104");
///
/// phant.push().ok().expect("Pushing to server did not succeed");
/// ```

#[deriving(Show)]
pub struct Phant {
    hostname: String,
    public_key: String,
    private_key: String,
    data: HashMap<String, String>
}

impl Phant {
    pub fn new(hostname: &str, public_key: &str, private_key: &str) -> Phant {
        Phant {
            hostname: String::from_str(hostname),
            public_key: String::from_str(public_key),
            private_key: String::from_str(private_key),
            data: HashMap::new()
        }
    }

    /// Adds a key-value pair to the current phant dataset.  The `key` is the
    /// phant column name.  The `value` is a `.to_string()`-able data item. This function
    /// does not manipulate server data.
    pub fn add<T: ToString>(&mut self, key: &str, value: T) {
        self.data.insert(key.to_string(), value.to_string());
    }

    /// Pushes data to the server. All the data that has been `add`ed will be pushed as a row
    /// to the phant server located at `hostname`. The local row will then be cleared so `add`
    /// can be called again to repeat the process.
    ///
    /// Returns an [IoResult](http://doc.rust-lang.org/std/io/type.IoResult.html) where a successful
    /// push will return the response from the server in `String` form.
    pub fn push(&mut self) -> ::std::io::IoResult<String> {
        let query_string = self.data_query_string();
        let request: String = format!("POST /input/{} HTTP/1.0\nPhant-Private-Key: {}\nContent-Type: application/x-www-form-urlencoded\nContent-Length: {}\n\n{}\n\n",
            self.public_key, self.private_key, query_string.len(), query_string);
        let mut socket = try!(TcpStream::connect(self.hostname.as_slice(), 80));
        try!(socket.write_str(request.as_slice()));
        let result = try!(socket.read_to_string());
        self.clear_local();
        Ok(result)
    }

    /// Clears the data locally.  Any key-value pairs that have been added since creation or
    /// previous clear will be removed.
    pub fn clear_local(&mut self) {
        self.data.clear();
    }

    /// Clears the data on the server.
    pub fn clear_server(&mut self) -> ::std::io::IoResult<String> {
        let request: String = format!("DELETE /input/{} HTTP/1.0\nPhant-Private-Key: {}\n\n", self.public_key, self.private_key);
        let mut socket = TcpStream::connect(self.hostname.as_slice(), 80).unwrap();
        try!(socket.write_str(request.as_slice()));
        socket.read_to_string()
    }

    /// Gets a formatted URL in string form for adding the current row to the server.
    /// Local data is _not_ cleared, unlike the `push()` method.
    ///
    /// # Example
    /// ```
    /// let mut phant = phant::Phant::new("data.sparkfun.com", "your_public_key", "your_private_key");
    ///
    /// phant.add("apple_color", "red");
    /// let url = phant.get_url();
    /// assert!(url.as_slice() == "http://data.sparkfun.com/input/your_public_key?private_key=your_private_key&apple_color=red")
    /// ```
    pub fn get_url(&self) -> String {
        format!("http://{}/input/{}?private_key={}&{}", self.hostname, self.public_key, self.private_key, self.data_query_string())
    }

    /// Returns a clone of the data for the current row being added to. Notice: it's a
    /// clone, so manipulations done to this returned object will _not_ modify the
    /// data being held locally in the `Phant` object.
    pub fn row_data(&self) -> HashMap<String, String> {
        self.data.clone()
    }

    fn data_query_string(&self) -> String {
        let mut result_list = Vec::new();
        for (key, value) in self.data.iter() {
            result_list.push(format!("{}={}", key, value));
        }
        url::encode(result_list.connect("&"))
    }
}

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

    #[test]
    fn test_get_url() {
        let expected = "http://data.com/input/pub?private_key=priv&color=red".to_string();
        let p = basic_phant();
        assert_eq!(p.row_data().len(), 1);
        assert_eq!(p.get_url(), expected);
        assert_eq!(p.row_data().len(), 1);
    }

    #[test]
    fn test_row_data() {
        let expected_key = "color".to_string();
        let expected_value = "red".to_string();
        let p = basic_phant();
        let mut row_data = p.row_data();
        assert_eq!(row_data.len(), 1);
        assert_eq!(*row_data.get(&expected_key), expected_value);

        // Test that row_data actually makes a clone of the data, and not returning it directly
        row_data.insert("size".to_string(), "large".to_string());
        assert_eq!(p.row_data().len(), 1);
    }

    #[test]
    fn test_overwrite_column() {
        let key = "color".to_string();
        let first_value = "red".to_string();
        let second_value = "green".to_string();
        let mut p = basic_phant();
        assert_eq!(*p.row_data().get(&key), first_value);

        p.add(key.as_slice(), second_value.as_slice());
        assert_eq!(p.row_data().len(), 1);
        assert_eq!(p.row_data().get(&key), &second_value);
    }

    #[test]
    fn test_clear_local() {
        let mut p = basic_phant();
        assert!(p.row_data().len() > 0);
        p.clear_local();
        assert_eq!(p.row_data().len(), 0);
    }

    fn basic_phant() -> Phant {
        let mut p = Phant::new("data.com", "pub", "priv");
        p.add("color", "red");
        p
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::str::FromStr;

struct VM {
	stack: Vec<Operator>,
}

impl VM {
	fn pop <'a> (&'a mut self, ret: &mut Operator) {
		*ret = self.stack.pop().expect("gg reinstall");
	}

	fn push <'a> (&'a mut self, op: Operator) {
		self.stack.push(op);
	}

	fn eval <'a> (&'a mut self, op: Operator) {

	}

	fn add <'a> (&'a mut self) {
		let mut x = Operator::Other;
		let mut y = Operator::Other;
		self.pop(&mut x);
		self.pop(&mut y);
		let z = match x{
			Operator::Value(i) => match y{
				Operator::Value(j) => i+j,
				_ => panic!("can only add ints"),
			},
			_ => panic!("can only add ints"),
		};
		self.push(Operator::Value(z));
	}
}

enum Operator {
	//stack
	Dup,
	Drop,
	Swap,
	Over,
	Rot,

	//debug
	Print,

	//math
	Add,
	Subtract,
	Multiply,
	Divide,
	Mod,
	And,
	Or,
	Xor,
	Lshift,
	Rshift,
	Abs,
	Max,
	Min,
	Invert,
	Negate,

	//custom
	Define,

	//values and custom calls
	Value(i32),
	Other,
}

fn parse(s: &str) -> Operator {
	match s {
		//stack operations:
		"dup" => Operator::Dup,
		"drop" => Operator::Drop,
		"swap" => Operator::Swap,
		"over" => Operator::Over,
		"rot" => Operator::Rot,

		//debug operations:
		"." => Operator::Print,

		//math operations:
		"+" => Operator::Add,
		"-" => Operator::Subtract,
		"*" => Operator::Multiply,
		"/" => Operator::Divide,
		"mod" => Operator::Mod,
		"and" => Operator::And,
		"or" => Operator::Or,
		"xor" => Operator::Xor,
		"lshift" => Operator::Lshift,
		"<<" => Operator::Lshift,
		"rshift" => Operator::Rshift,
		">>" => Operator::Rshift,
		"abs" => Operator::Abs,
		"max" => Operator::Max,
		"min" => Operator::Min,
		"invert" => Operator::Invert,
		"negate" => Operator::Negate,

		//custom definitions
		":" => Operator::Define,

		//value or custom operator
		x => {
			let y: Option<i32> = FromStr::from_str(x).ok();
			match y{
				Some(i) => Operator::Value(i),
				None => Operator::Other,
			}
		},
	}
}

fn main() {
	let mut x = io::stdin();
	let mut vm = VM {stack: vec![Operator::Print]};

	loop{
		let mut line: String = "".to_string();
		let _ = x.read_line(&mut line);

		for o in line.split(" "){
			println!("evaling: {}", o.trim());
			let b = parse(o.trim());
			vm.eval(b);
		}
	}
}
<commit_msg>subtract, multiply, divide<commit_after>use std::io;
use std::str::FromStr;

struct VM {
	stack: Vec<Operator>,
}

impl VM {
	fn pop <'a> (&'a mut self, ret: &mut Operator) {
		*ret = self.stack.pop().expect("gg reinstall");
	}

	fn push <'a> (&'a mut self, op: Operator) {
		self.stack.push(op);
	}

	fn eval <'a> (&'a mut self, op: Operator) {

	}

	fn add <'a> (&'a mut self) {
		let mut x = Operator::Other;
		let mut y = Operator::Other;
		self.pop(&mut x);
		self.pop(&mut y);
		let z = match x{
			Operator::Value(i) => match y{
				Operator::Value(j) => i+j,
				_ => panic!("can only add ints"),
			},
			_ => panic!("can only add ints"),
		};
		self.push(Operator::Value(z));
	}

	fn subtract <'a> (&'a mut self) {
		let mut x = Operator::Other;
		let mut y = Operator::Other;
		self.pop(&mut x);
		self.pop(&mut y);
		let z = match x{
			Operator::Value(i) => match y{
				Operator::Value(j) => i-j,
				_ => panic!("can only add ints"),
			},
			_ => panic!("can only add ints"),
		};
		self.push(Operator::Value(z));
	}

	fn multiply <'a> (&'a mut self) {
		let mut x = Operator::Other;
		let mut y = Operator::Other;
		self.pop(&mut x);
		self.pop(&mut y);
		let z = match x{
			Operator::Value(i) => match y{
				Operator::Value(j) => i*j,
				_ => panic!("can only add ints"),
			},
			_ => panic!("can only add ints"),
		};
		self.push(Operator::Value(z));
	}

	fn divide <'a> (&'a mut self) {
		let mut x = Operator::Other;
		let mut y = Operator::Other;
		self.pop(&mut x);
		self.pop(&mut y);
		let z = match x{
			Operator::Value(i) => match y{
				Operator::Value(j) => i/j,
				_ => panic!("can only add ints"),
			},
			_ => panic!("can only add ints"),
		};
		self.push(Operator::Value(z));
	}
}

enum Operator {
	//stack
	Dup,
	Drop,
	Swap,
	Over,
	Rot,

	//debug
	Print,

	//math
	Add,
	Subtract,
	Multiply,
	Divide,
	Mod,
	And,
	Or,
	Xor,
	Lshift,
	Rshift,
	Abs,
	Max,
	Min,
	Invert,
	Negate,

	//custom
	Define,

	//values and custom calls
	Value(i32),
	Other,
}

fn parse(s: &str) -> Operator {
	match s {
		//stack operations:
		"dup" => Operator::Dup,
		"drop" => Operator::Drop,
		"swap" => Operator::Swap,
		"over" => Operator::Over,
		"rot" => Operator::Rot,

		//debug operations:
		"." => Operator::Print,

		//math operations:
		"+" => Operator::Add,
		"-" => Operator::Subtract,
		"*" => Operator::Multiply,
		"/" => Operator::Divide,
		"mod" => Operator::Mod,
		"and" => Operator::And,
		"or" => Operator::Or,
		"xor" => Operator::Xor,
		"lshift" => Operator::Lshift,
		"<<" => Operator::Lshift,
		"rshift" => Operator::Rshift,
		">>" => Operator::Rshift,
		"abs" => Operator::Abs,
		"max" => Operator::Max,
		"min" => Operator::Min,
		"invert" => Operator::Invert,
		"negate" => Operator::Negate,

		//custom definitions
		":" => Operator::Define,

		//value or custom operator
		x => {
			let y: Option<i32> = FromStr::from_str(x).ok();
			match y{
				Some(i) => Operator::Value(i),
				None => Operator::Other,
			}
		},
	}
}

fn main() {
	let mut x = io::stdin();
	let mut vm = VM {stack: vec![Operator::Print]};

	loop{
		let mut line: String = "".to_string();
		let _ = x.read_line(&mut line);

		for o in line.split(" "){
			println!("evaling: {}", o.trim());
			let b = parse(o.trim());
			vm.eval(b);
		}
	}
}
<|endoftext|>


<commit_before>use std::io::Cursor;
use outcome::Outcome::*;
use http::{hyper, Method};
use request::{Request, Data};
use Rocket;

pub struct MockRequest {
    request: Request,
    data: Data
}

impl MockRequest {
    pub fn new<S: AsRef<str>>(method: Method, uri: S) -> Self {
        MockRequest {
            request: Request::mock(method, uri.as_ref()),
            data: Data::new(vec![])
        }
    }

    pub fn headers<'h, H: AsRef<[(&'h str, &'h str)]>>(mut self, headers: H) -> Self {
        let mut hyp_headers = hyper::HyperHeaders::new();

        for &(name, fields) in headers.as_ref() {
            let mut vec_fields = vec![];
            for field in fields.split(";") {
                vec_fields.push(field.as_bytes().to_vec());
            }

            hyp_headers.set_raw(name.to_string(), vec_fields);
        }

        self.request.set_headers(hyp_headers);
        self
    }

    pub fn body<S: AsRef<str>>(mut self, body: S) -> Self {
        self.data = Data::new(body.as_ref().as_bytes().into());
        self
    }

    pub fn dispatch_with(mut self, rocket: &Rocket) -> Option<String> {
        let request = self.request;
        let data = ::std::mem::replace(&mut self.data, Data::new(vec![]));

        let mut response = Cursor::new(vec![]);

        // Create a new scope so we can get the inner from response later.
        let ok = {
            let mut h_h = hyper::HyperHeaders::new();
            let res = hyper::FreshHyperResponse::new(&mut response, &mut h_h);
            match rocket.dispatch(&request, data) {
                Ok(mut responder) => {
                    match responder.respond(res) {
                        Success(_) => true,
                        Failure(_) => false,
                        Forward((code, r)) => rocket.handle_error(code, &request, r)
                    }
                }
                Err(code) => rocket.handle_error(code, &request, res)
            }
        };

        if !ok {
            return None;
        }

        match String::from_utf8(response.into_inner()) {
            Ok(string) => {
                // TODO: Expose the full response (with headers) somewhow.
                string.find("\r\n\r\n").map(|i| string[(i + 4)..].to_string())
            }
            Err(e) => {
                error_!("Could not create string from response: {:?}", e);
                None
            }
        }
    }
}
<commit_msg>Document the testing module.<commit_after>//! A tiny module for testing Rocket applications.
//!
//! # Usage
//!
//! The testing methadology is simple:
//!
//!   1. Construct a `Rocket` instance
//!   2. Construct a request.
//!   3. Dispatch the request using the Rocket instance.
//!   4. Inspect, validate, and verify the response.
//!
//! ## Construct a `Rocket` Instance
//!
//! Constructing a `Rocket` instance for testing is identical to constructing
//! one for launching, except you should not call the `launch` method. That is,
//! use `rocket::ignite`, then mount routes and catchers. That's it!
//!
//! ## Construct a (Mock)Request
//!
//! The [MockRequest](struct.MockRequest.html) object enables the creation of an
//! HTTP request without using any networking. A `MockRequest` object is
//! constructed using the builder pattern. For example, the following code
//! builds a request for submitting a login form with three fields:
//!
//! ```rust
//! use rocket::http::Method::*;
//! use rocket::testing::MockRequest;
//!
//! let (username, password, age) = ("user", "password", 32);
//! MockRequest::new(Post, "/login")
//!     .headers(&[("Content-Type", "application/x-www-form-urlencoded")])
//!     .body(&format!("username={}&password={}&age={}", username, password, age));
//! ```
//!
//! ## Dispatch and Validate
//!
//! Finally, requests can be dispatched using the
//! [dispatch_with](struct.MockRequest.html#method.dispatch_with) method on the
//! contructed `MockRequest` instance. The method returns the body of the
//! response. At present, the API does not allow for headers in the response to
//! be examined.

use std::io::Cursor;
use outcome::Outcome::*;
use http::{hyper, Method};
use request::{Request, Data};
use Rocket;

/// A type for mocking requests for testing Rocket applications.
pub struct MockRequest {
    request: Request,
    data: Data
}

impl MockRequest {
    /// Constructs a new mocked request with the given `method` and `uri`.
    pub fn new<S: AsRef<str>>(method: Method, uri: S) -> Self {
        MockRequest {
            request: Request::mock(method, uri.as_ref()),
            data: Data::new(vec![])
        }
    }

    /// Sets the headers for this request.
    ///
    /// # Examples
    ///
    /// Set the Content-Type header:
    ///
    /// ```rust
    /// use rocket::http::Method::*;
    /// use rocket::testing::MockRequest;
    ///
    /// let req = MockRequest::new(Get, "/").headers(&[
    ///     ("Content-Type", "application/json")
    /// ]);
    /// ```
    pub fn headers<'h, H: AsRef<[(&'h str, &'h str)]>>(mut self, headers: H) -> Self {
        let mut hyp_headers = hyper::HyperHeaders::new();

        for &(name, fields) in headers.as_ref() {
            let mut vec_fields = vec![];
            for field in fields.split(";") {
                vec_fields.push(field.as_bytes().to_vec());
            }

            hyp_headers.set_raw(name.to_string(), vec_fields);
        }

        self.request.set_headers(hyp_headers);
        self
    }

    /// Set the body (data) of the request.
    ///
    /// # Examples
    ///
    /// Set the body to be a JSON structure; also sets the Content-Type.
    ///
    /// ```rust
    /// use rocket::http::Method::*;
    /// use rocket::testing::MockRequest;
    ///
    /// let req = MockRequest::new(Post, "/").headers(&[
    ///     ("Content-Type", "application/json")
    /// ]).body(r#"
    ///    {
    ///        "key": "value",
    ///        "array": [1, 2, 3],
    ///    }
    /// "#);
    /// ```
    pub fn body<S: AsRef<str>>(mut self, body: S) -> Self {
        self.data = Data::new(body.as_ref().as_bytes().into());
        self
    }

    /// Dispatch this request using a given instance of Rocket.
    ///
    /// Returns the body of the response if there was a response. The return
    /// value is `None` if any of the following occurs:
    ///
    ///   1. The returned body was not valid UTF8.
    ///   2. The application failed to respond.
    ///
    /// # Examples
    ///
    /// Dispatch to a Rocket instance with the `"Hello, world!"` example
    /// mounted:
    ///
    /// ```rust
    /// # #![feature(plugin)]
    /// # #![plugin(rocket_codegen)]
    /// # extern crate rocket;
    /// #
    /// #[get("/")]
    /// fn hello() -> &'static str {
    ///     "Hello, world!"
    /// }
    ///
    /// use rocket::testing::MockRequest;
    /// use rocket::http::Method::*;
    ///
    /// # fn main() {
    /// let rocket = rocket::ignite().mount("/", routes![hello]);
    /// let req = MockRequest::new(Get, "/");
    /// let result = req.dispatch_with(&rocket);
    /// assert_eq!(result.unwrap().as_str(), "Hello, world!");
    /// # }
    /// ```
    pub fn dispatch_with(mut self, rocket: &Rocket) -> Option<String> {
        let request = self.request;
        let data = ::std::mem::replace(&mut self.data, Data::new(vec![]));

        let mut response = Cursor::new(vec![]);

        // Create a new scope so we can get the inner from response later.
        let ok = {
            let mut h_h = hyper::HyperHeaders::new();
            let res = hyper::FreshHyperResponse::new(&mut response, &mut h_h);
            match rocket.dispatch(&request, data) {
                Ok(mut responder) => {
                    match responder.respond(res) {
                        Success(_) => true,
                        Failure(_) => false,
                        Forward((code, r)) => rocket.handle_error(code, &request, r)
                    }
                }
                Err(code) => rocket.handle_error(code, &request, res)
            }
        };

        if !ok {
            return None;
        }

        match String::from_utf8(response.into_inner()) {
            Ok(string) => {
                // TODO: Expose the full response (with headers) somewhow.
                string.find("\r\n\r\n").map(|i| string[(i + 4)..].to_string())
            }
            Err(e) => {
                error_!("Could not create string from response: {:?}", e);
                None
            }
        }
    }
}
<|endoftext|>