<commit_before>use rand::{
    Rng,
    seq::SliceRandom,
    thread_rng,
};
use crate::{
    game::GameState,
    ship::{
        Ship,
        ShipDirection,
    },
    space::Space,
};

pub struct Player {
    pub is_cpu: bool,
    spaces: Vec<Space>,
    ships: Vec<Ship>,
    grid_size: [u8; 2],
    grid_cursor: [u8; 2],
    pub temp_ship_pos: Vec<[u8; 2]>,
    pub temp_ship_dir: ShipDirection,
}

impl Player {
    pub fn new(grid_size: [u8; 2], is_cpu: bool) -> Player {
        let spaces_x = grid_size[0] as usize;
        let spaces_y = grid_size[1] as usize;
        let mut spaces = Vec::with_capacity(spaces_x * spaces_y);

        for col in 0..grid_size[0] {
            for row in 0..grid_size[1] {
                spaces.push(Space::new([col, row]));
            }
        }

        Player {
            is_cpu: is_cpu,
            spaces: spaces,
            ships: vec![],
            grid_size: grid_size,
            grid_cursor: [0, 0],
            temp_ship_pos: vec![[0, 0], [1, 0]],
            temp_ship_dir: ShipDirection::West,
        }
    }

    /// Selects a space and checks the status of ships if there's a hit.
    pub fn select_space(&mut self, pos: &[u8; 2]) -> GameState {
        let mut game_state = GameState::Active;
        let ship_hit = self.ships.iter().position(|s| s.pos().contains(pos));

        let space_index = self.space_index(pos);
        self.spaces[space_index].set_checked(ship_hit.is_some());

        if let Some(ship) = ship_hit {
            let ship_sunk = self.ships[ship].pos().iter()
                .all(|p| self.space(p).is_hit());

            if ship_sunk {
                self.ships[ship].sink();
            }

            let all_sunk = self.ships.iter().all(|s| !s.is_active());
            if all_sunk {
                game_state = GameState::Over;
            }
        }

        game_state
    }

    /// Determines the next space a CPU player will select.
    pub fn cpu_select_space(&self) -> [u8; 2] {
        let mut rng = thread_rng();
        let mut select = Vec::new();
        let mut directions = [
            ShipDirection::North,
            ShipDirection::East,
            ShipDirection::South,
            ShipDirection::West
        ];

        directions.shuffle(&mut rng);

        let mut hit_spaces = self.spaces.iter()
            .filter(|s| s.is_hit())
            .filter(|s| self.ship(s.pos()).unwrap().is_active())
            .collect::<Vec<&Space>>();

        hit_spaces.shuffle(&mut rng);

        // Check for a line of hit spaces.
        for space in &hit_spaces {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(
                    space.pos(),
                    *direction,
                    true
                );

                if let Some(pos) = unchecked {
                    if !select.contains(&pos) {
                        select.push(pos);
                    }
                }
            }
        }

        // If a hit space was found, but no hit spaces next to it, find any
        // unchecked spaces next to it.
        if hit_spaces.len() > 0 && select.is_empty() {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(
                    hit_spaces[0].pos(),
                    *direction,
                    false
                );

                if let Some(pos) = unchecked {
                    select.push(pos);
                }
            }
        }

        // If no spaces have been selected, just select any available space.
        if select.is_empty() {
            let mut pos: Option<[u8; 2]> = None;
            while pos.is_none() {
                let space = self.rng_pos();

                if self.space(&space).is_unchecked() {
                    pos = Some(space);
                }
            }

            select.push(pos.unwrap());
        }

        if select.len() > 1 {
            select.shuffle(&mut rng);
        }

        select[0]
    }

    fn rng_pos(&self) -> [u8; 2] {
        let mut rng = thread_rng();

        [
            rng.gen_range(0, self.grid_size[0]),
            rng.gen_range(0, self.grid_size[1]),
        ]
    }

    pub fn move_temp_ship(&mut self, direction: ShipDirection) {
        let old_head = self.temp_ship_pos[0];

        if let Some(new_head) = self.movement(&old_head, direction) {
            if let Some(ship) = self.get_ship_position(
                new_head,
                self.temp_ship_dir,
                self.temp_ship_pos.len() as u8
            ) {
                self.temp_ship_pos = ship;
            }
        }
    }

    pub fn place_temp_ship(&mut self) {
        let ship = self.temp_ship_pos.clone();

        if self.valid_ship_position(&ship) {
            self.ships.push(Ship::new(ship));

            self.temp_ship_dir = ShipDirection::West;
            self.temp_ship_pos = self.get_ship_position(
                [0, 0],
                self.temp_ship_dir,
                self.temp_ship_pos.len() as u8 + 1
            ).unwrap();
        }
    }

    pub fn rotate_temp_ship(&mut self) {
        let direction = match self.temp_ship_dir {
            ShipDirection::North => ShipDirection::East,
            ShipDirection::East => ShipDirection::South,
            ShipDirection::South => ShipDirection::West,
            ShipDirection::West => ShipDirection::North,
        };

        if let Some(ship) = self.get_ship_position(
            self.temp_ship_pos[0],
            direction,
            self.temp_ship_pos.len() as u8
        ) {
            self.temp_ship_pos = ship;
            self.temp_ship_dir = direction;
        }
    }

    pub fn cpu_place_ships(&mut self) {
        for length in 2..6 {
            let ship_pos = self.cpu_place_ship(length);
            self.ships.push(Ship::new(ship_pos));
        }
    }

    /// RNGs ship locations for CPU players.
    fn cpu_place_ship(&self, length: u8) -> Vec<[u8; 2]> {
        let mut ship = vec![];
        let mut valid = false;
        let mut direction: ShipDirection;
        let mut rng = thread_rng();

        // RNG a position and direction, then make sure it's valid.
        while !valid {
            let pos = self.rng_pos();
            direction = match rng.gen_range(0, 4) {
                0 => ShipDirection::North,
                1 => ShipDirection::East,
                2 => ShipDirection::South,
                3 => ShipDirection::West,
                _ => unreachable!()
            };

            if let Some(s) = self.get_ship_position(pos, direction, length) {
                valid = self.valid_ship_position(&s);

                if valid {
                    ship = s;
                }
            }
        }

        ship
    }

    /// Returns a ship position, given its head position, direction and length.
    ///
    /// `direction` refers to the direction the ship is facing, not the
    /// direction in which positions are generated.
    /// Returns `None` if the resulting ship position would not be contained
    /// within the grid.
    pub fn get_ship_position(
        &self,
        head: [u8; 2],
        direction: ShipDirection,
        length: u8
    ) -> Option<Vec<[u8; 2]>> {
        let valid = match direction {
            ShipDirection::North => head[1] + length <= self.grid_size[1],
            ShipDirection::East => head[0] >= length - 1,
            ShipDirection::South => head[1] >= length - 1,
            ShipDirection::West => head[0] + length <= self.grid_size[0],
        };

        let ship_opt = if valid {
            let mut ship = vec![head];

            for pos in 1..length {
                let pos_u8 = pos as u8;
                let space = match direction {
                    ShipDirection::North => [head[0], head[1] + pos_u8],
                    ShipDirection::East => [head[0] - pos_u8, head[1]],
                    ShipDirection::South => [head[0], head[1] - pos_u8],
                    ShipDirection::West => [head[0] + pos_u8, head[1]],
                };
                ship.push(space);
            }
            Some(ship)
        } else {
            None
        };

        ship_opt
    }

    /// Checks that the given ship position is valid.
    ///
    /// If the player is CPU-controlled, a ship in a space next to another ship
    /// will be considered invalid.
    pub fn valid_ship_position(&self, new_ship: &Vec<[u8; 2]>) -> bool {
        new_ship.iter()
            .all(|s| self.valid_space(s) && !self.ship_is_in_space(s)
                 && !(self.ship_is_next_to(s) && self.is_cpu))
    }

    /// Gets a reference to the ships.
    pub fn ships(&self) -> &Vec<Ship> {
        &self.ships
    }

    /// Gets a reference to a ship if it is in the given position.
    fn ship(&self, pos: &[u8; 2]) -> Option<&Ship> {
        self.ships.iter().find(|s| s.pos().contains(pos))
    }

    /// Returns whether a ship occupies the specified grid coordinates.
    pub fn ship_is_in_space(&self, pos: &[u8; 2]) -> bool {
        self.ships.iter().any(|s| s.pos().contains(pos))
    }

    /// Returns whether there is a ship next to the specified grid coordinates.
    fn ship_is_next_to(&self, pos: &[u8; 2]) -> bool {
        let mut result = false;
        let x = pos[0];
        let y = pos[1];

        // Left
        if x > 0 {
            result = self.ship_is_in_space(&[x - 1, y]);
        }

        // Right
        if x < self.grid_size[0] - 1 && !result {
            result = self.ship_is_in_space(&[x + 1, y]);
        }

        // Above
        if y > 0 && !result {
            result = self.ship_is_in_space(&[x, y - 1]);
        }

        // Below
        if y < self.grid_size[1] - 1 && !result {
            result = self.ship_is_in_space(&[x, y + 1]);
        }

        result
    }

    /// Gets a reference to the spaces.
    pub fn spaces(&self) -> &Vec<Space> {
        &self.spaces
    }

    /// Returns whether the given position is valid.
    fn valid_space(&self, pos: &[u8; 2]) -> bool {
        pos[0] < self.grid_size[0] && pos[1] < self.grid_size[1]
    }

    /// Gets a reference to the space with the given position.
    pub fn space(&self, pos: &[u8; 2]) -> &Space {
        self.spaces.get(self.space_index(pos)).unwrap()
    }

    /// Calculates the index of the given position in the spaces vector.
    fn space_index(&self, pos: &[u8; 2]) -> usize {
        self.grid_size[0] as usize * pos[0] as usize + pos[1] as usize
    }

    /// Returns the coordinates of the player's grid cursor.
    pub fn get_grid_cursor(&self) -> [u8; 2] {
        self.grid_cursor.clone()
    }

    /// Returns the coordinates of a movement from `pos` in a `direction`.
    /// Returns `None` if the movement is not possible.
    pub fn movement(&self, pos: &[u8; 2], direction: ShipDirection) -> Option<[u8; 2]> {
        let valid = match direction {
            ShipDirection::North => pos[1] > 0,
            ShipDirection::East => pos[0] < self.grid_size[0] - 1,
            ShipDirection::South => pos[1] < self.grid_size[1] - 1,
            ShipDirection::West => pos[0] > 0,
        };

        if valid {
            let movement = match direction {
                ShipDirection::North => [pos[0], pos[1] - 1],
                ShipDirection::East => [pos[0] + 1, pos[1]],
                ShipDirection::South => [pos[0], pos[1] + 1],
                ShipDirection::West => [pos[0] - 1, pos[1]],
            };
            Some(movement)
        } else {
            None
        }
    }

    /// Finds the first non-hit, unchecked space in a `direction` from `pos`.
    /// Can also make sure the space is at the end of a `line` of hit spaces.
    /// Returns `None` if the first non-hit space has been checked or if a grid
    /// boundary is reached.
    fn find_unchecked_space(
        &self,
        pos: &[u8; 2],
        direction: ShipDirection,
        check_for_line: bool
    ) -> Option<[u8; 2]> {
        let mut check_pos = self.movement(pos, direction);

        while let Some(next_pos) = check_pos {
            let next_space = self.space(&next_pos);

            match next_space.is_hit() {
                true => check_pos = self.movement(&next_pos, direction),
                false => {
                    if !next_space.is_unchecked() {
                        check_pos = None;
                    }
                    break;
                }
            };
        }

        if check_for_line && check_pos.is_some() {
            let unchecked = check_pos.unwrap();
            let opposite_dir = direction.opposite();
            let prev_pos = self.movement(&unchecked, opposite_dir).unwrap();

            if &prev_pos == pos {
                check_pos = None;
            }
        }

        check_pos
    }

    /// Moves the player's grid cursor in the given `direction` if possible.
    pub fn move_grid_cursor(&mut self, direction: ShipDirection) {
        if let Some(new_cursor) = self.movement(&self.grid_cursor, direction) {
            self.set_grid_cursor(&new_cursor);
        }
    }

    /// Sets the player's grid cursor coordinates.
    pub fn set_grid_cursor(&mut self, new_cursor: &[u8; 2]) {
        self.grid_cursor = *new_cursor;
    }
}

<commit_msg>Allow full ship rotation near grid edges during placement<commit_after>use std::cmp;
use rand::{
    Rng,
    seq::SliceRandom,
    thread_rng,
};
use crate::{
    game::GameState,
    ship::{
        Ship,
        ShipDirection,
    },
    space::Space,
};

pub struct Player {
    pub is_cpu: bool,
    spaces: Vec<Space>,
    ships: Vec<Ship>,
    grid_size: [u8; 2],
    grid_cursor: [u8; 2],
    pub temp_ship_pos: Vec<[u8; 2]>,
    pub temp_ship_dir: ShipDirection,
}

impl Player {
    pub fn new(grid_size: [u8; 2], is_cpu: bool) -> Player {
        let spaces_x = grid_size[0] as usize;
        let spaces_y = grid_size[1] as usize;
        let mut spaces = Vec::with_capacity(spaces_x * spaces_y);

        for col in 0..grid_size[0] {
            for row in 0..grid_size[1] {
                spaces.push(Space::new([col, row]));
            }
        }

        Player {
            is_cpu: is_cpu,
            spaces: spaces,
            ships: vec![],
            grid_size: grid_size,
            grid_cursor: [0, 0],
            temp_ship_pos: vec![[0, 0], [1, 0]],
            temp_ship_dir: ShipDirection::West,
        }
    }

    /// Selects a space and checks the status of ships if there's a hit.
    pub fn select_space(&mut self, pos: &[u8; 2]) -> GameState {
        let mut game_state = GameState::Active;
        let ship_hit = self.ships.iter().position(|s| s.pos().contains(pos));

        let space_index = self.space_index(pos);
        self.spaces[space_index].set_checked(ship_hit.is_some());

        if let Some(ship) = ship_hit {
            let ship_sunk = self.ships[ship].pos().iter()
                .all(|p| self.space(p).is_hit());

            if ship_sunk {
                self.ships[ship].sink();
            }

            let all_sunk = self.ships.iter().all(|s| !s.is_active());
            if all_sunk {
                game_state = GameState::Over;
            }
        }

        game_state
    }

    /// Determines the next space a CPU player will select.
    pub fn cpu_select_space(&self) -> [u8; 2] {
        let mut rng = thread_rng();
        let mut select = Vec::new();
        let mut directions = [
            ShipDirection::North,
            ShipDirection::East,
            ShipDirection::South,
            ShipDirection::West
        ];

        directions.shuffle(&mut rng);

        let mut hit_spaces = self.spaces.iter()
            .filter(|s| s.is_hit())
            .filter(|s| self.ship(s.pos()).unwrap().is_active())
            .collect::<Vec<&Space>>();

        hit_spaces.shuffle(&mut rng);

        // Check for a line of hit spaces.
        for space in &hit_spaces {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(
                    space.pos(),
                    *direction,
                    true
                );

                if let Some(pos) = unchecked {
                    if !select.contains(&pos) {
                        select.push(pos);
                    }
                }
            }
        }

        // If a hit space was found, but no hit spaces next to it, find any
        // unchecked spaces next to it.
        if hit_spaces.len() > 0 && select.is_empty() {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(
                    hit_spaces[0].pos(),
                    *direction,
                    false
                );

                if let Some(pos) = unchecked {
                    select.push(pos);
                }
            }
        }

        // If no spaces have been selected, just select any available space.
        if select.is_empty() {
            let mut pos: Option<[u8; 2]> = None;
            while pos.is_none() {
                let space = self.rng_pos();

                if self.space(&space).is_unchecked() {
                    pos = Some(space);
                }
            }

            select.push(pos.unwrap());
        }

        if select.len() > 1 {
            select.shuffle(&mut rng);
        }

        select[0]
    }

    fn rng_pos(&self) -> [u8; 2] {
        let mut rng = thread_rng();

        [
            rng.gen_range(0, self.grid_size[0]),
            rng.gen_range(0, self.grid_size[1]),
        ]
    }

    pub fn move_temp_ship(&mut self, direction: ShipDirection) {
        let old_head = self.temp_ship_pos[0];

        if let Some(new_head) = self.movement(&old_head, direction) {
            if let Some(ship) = self.get_ship_position(
                new_head,
                self.temp_ship_dir,
                self.temp_ship_pos.len() as u8
            ) {
                self.temp_ship_pos = ship;
            }
        }
    }

    pub fn place_temp_ship(&mut self) {
        let ship = self.temp_ship_pos.clone();

        if self.valid_ship_position(&ship) {
            self.ships.push(Ship::new(ship));

            self.temp_ship_dir = ShipDirection::West;
            self.temp_ship_pos = self.get_ship_position(
                [0, 0],
                self.temp_ship_dir,
                self.temp_ship_pos.len() as u8 + 1
            ).unwrap();
        }
    }

    /// Rotates a ship during the ship placement game state.
    pub fn rotate_temp_ship(&mut self) {
        let ship_len = self.temp_ship_pos.len() as u8;
        let dir = match self.temp_ship_dir {
            ShipDirection::North => ShipDirection::East,
            ShipDirection::East => ShipDirection::South,
            ShipDirection::South => ShipDirection::West,
            ShipDirection::West => ShipDirection::North,
        };

        // If the current starting position would cause the rotation to position
        // the ship partially out of bounds, adjust the starting position such
        // that the ship will be entirely within bounds.
        let old_start_pos = self.temp_ship_pos[0];
        let start_pos = match dir {
            ShipDirection::North => [
                self.temp_ship_pos[0][0],
                cmp::min(old_start_pos[1], self.grid_size[1] - ship_len),
            ],
            ShipDirection::East => [
                cmp::max(old_start_pos[0], ship_len - 1),
                self.temp_ship_pos[0][1],
            ],
            ShipDirection::South => [
                self.temp_ship_pos[0][0],
                cmp::max(old_start_pos[1], ship_len - 1),
            ],
            ShipDirection::West => [
                cmp::min(old_start_pos[0], self.grid_size[0] - ship_len),
                self.temp_ship_pos[0][1],
            ],
        };

        if let Some(ship) = self.get_ship_position(start_pos, dir, ship_len) {
            self.temp_ship_pos = ship;
            self.temp_ship_dir = dir;
        }
    }

    pub fn cpu_place_ships(&mut self) {
        for length in 2..6 {
            let ship_pos = self.cpu_place_ship(length);
            self.ships.push(Ship::new(ship_pos));
        }
    }

    /// RNGs ship locations for CPU players.
    fn cpu_place_ship(&self, length: u8) -> Vec<[u8; 2]> {
        let mut ship = vec![];
        let mut valid = false;
        let mut direction: ShipDirection;
        let mut rng = thread_rng();

        // RNG a position and direction, then make sure it's valid.
        while !valid {
            let pos = self.rng_pos();
            direction = match rng.gen_range(0, 4) {
                0 => ShipDirection::North,
                1 => ShipDirection::East,
                2 => ShipDirection::South,
                3 => ShipDirection::West,
                _ => unreachable!()
            };

            if let Some(s) = self.get_ship_position(pos, direction, length) {
                valid = self.valid_ship_position(&s);

                if valid {
                    ship = s;
                }
            }
        }

        ship
    }

    /// Returns a ship position, given its head position, direction and length.
    ///
    /// `direction` refers to the direction the ship is facing, not the
    /// direction in which positions are generated.
    /// Returns `None` if the resulting ship position would not be contained
    /// within the grid.
    pub fn get_ship_position(
        &self,
        head: [u8; 2],
        direction: ShipDirection,
        length: u8
    ) -> Option<Vec<[u8; 2]>> {
        let valid = match direction {
            ShipDirection::North => head[1] + length <= self.grid_size[1],
            ShipDirection::East => head[0] >= length - 1,
            ShipDirection::South => head[1] >= length - 1,
            ShipDirection::West => head[0] + length <= self.grid_size[0],
        };

        let ship_opt = if valid {
            let mut ship = vec![head];

            for pos in 1..length {
                let pos_u8 = pos as u8;
                let space = match direction {
                    ShipDirection::North => [head[0], head[1] + pos_u8],
                    ShipDirection::East => [head[0] - pos_u8, head[1]],
                    ShipDirection::South => [head[0], head[1] - pos_u8],
                    ShipDirection::West => [head[0] + pos_u8, head[1]],
                };
                ship.push(space);
            }
            Some(ship)
        } else {
            None
        };

        ship_opt
    }

    /// Checks that the given ship position is valid.
    ///
    /// If the player is CPU-controlled, a ship in a space next to another ship
    /// will be considered invalid.
    pub fn valid_ship_position(&self, new_ship: &Vec<[u8; 2]>) -> bool {
        new_ship.iter()
            .all(|s| self.valid_space(s) && !self.ship_is_in_space(s)
                 && !(self.ship_is_next_to(s) && self.is_cpu))
    }

    /// Gets a reference to the ships.
    pub fn ships(&self) -> &Vec<Ship> {
        &self.ships
    }

    /// Gets a reference to a ship if it is in the given position.
    fn ship(&self, pos: &[u8; 2]) -> Option<&Ship> {
        self.ships.iter().find(|s| s.pos().contains(pos))
    }

    /// Returns whether a ship occupies the specified grid coordinates.
    pub fn ship_is_in_space(&self, pos: &[u8; 2]) -> bool {
        self.ships.iter().any(|s| s.pos().contains(pos))
    }

    /// Returns whether there is a ship next to the specified grid coordinates.
    fn ship_is_next_to(&self, pos: &[u8; 2]) -> bool {
        let mut result = false;
        let x = pos[0];
        let y = pos[1];

        // Left
        if x > 0 {
            result = self.ship_is_in_space(&[x - 1, y]);
        }

        // Right
        if x < self.grid_size[0] - 1 && !result {
            result = self.ship_is_in_space(&[x + 1, y]);
        }

        // Above
        if y > 0 && !result {
            result = self.ship_is_in_space(&[x, y - 1]);
        }

        // Below
        if y < self.grid_size[1] - 1 && !result {
            result = self.ship_is_in_space(&[x, y + 1]);
        }

        result
    }

    /// Gets a reference to the spaces.
    pub fn spaces(&self) -> &Vec<Space> {
        &self.spaces
    }

    /// Returns whether the given position is valid.
    fn valid_space(&self, pos: &[u8; 2]) -> bool {
        pos[0] < self.grid_size[0] && pos[1] < self.grid_size[1]
    }

    /// Gets a reference to the space with the given position.
    pub fn space(&self, pos: &[u8; 2]) -> &Space {
        self.spaces.get(self.space_index(pos)).unwrap()
    }

    /// Calculates the index of the given position in the spaces vector.
    fn space_index(&self, pos: &[u8; 2]) -> usize {
        self.grid_size[0] as usize * pos[0] as usize + pos[1] as usize
    }

    /// Returns the coordinates of the player's grid cursor.
    pub fn get_grid_cursor(&self) -> [u8; 2] {
        self.grid_cursor.clone()
    }

    /// Returns the coordinates of a movement from `pos` in a `direction`.
    /// Returns `None` if the movement is not possible.
    pub fn movement(&self, pos: &[u8; 2], direction: ShipDirection) -> Option<[u8; 2]> {
        let valid = match direction {
            ShipDirection::North => pos[1] > 0,
            ShipDirection::East => pos[0] < self.grid_size[0] - 1,
            ShipDirection::South => pos[1] < self.grid_size[1] - 1,
            ShipDirection::West => pos[0] > 0,
        };

        if valid {
            let movement = match direction {
                ShipDirection::North => [pos[0], pos[1] - 1],
                ShipDirection::East => [pos[0] + 1, pos[1]],
                ShipDirection::South => [pos[0], pos[1] + 1],
                ShipDirection::West => [pos[0] - 1, pos[1]],
            };
            Some(movement)
        } else {
            None
        }
    }

    /// Finds the first non-hit, unchecked space in a `direction` from `pos`.
    /// Can also make sure the space is at the end of a `line` of hit spaces.
    /// Returns `None` if the first non-hit space has been checked or if a grid
    /// boundary is reached.
    fn find_unchecked_space(
        &self,
        pos: &[u8; 2],
        direction: ShipDirection,
        check_for_line: bool
    ) -> Option<[u8; 2]> {
        let mut check_pos = self.movement(pos, direction);

        while let Some(next_pos) = check_pos {
            let next_space = self.space(&next_pos);

            match next_space.is_hit() {
                true => check_pos = self.movement(&next_pos, direction),
                false => {
                    if !next_space.is_unchecked() {
                        check_pos = None;
                    }
                    break;
                }
            };
        }

        if check_for_line && check_pos.is_some() {
            let unchecked = check_pos.unwrap();
            let opposite_dir = direction.opposite();
            let prev_pos = self.movement(&unchecked, opposite_dir).unwrap();

            if &prev_pos == pos {
                check_pos = None;
            }
        }

        check_pos
    }

    /// Moves the player's grid cursor in the given `direction` if possible.
    pub fn move_grid_cursor(&mut self, direction: ShipDirection) {
        if let Some(new_cursor) = self.movement(&self.grid_cursor, direction) {
            self.set_grid_cursor(&new_cursor);
        }
    }

    /// Sets the player's grid cursor coordinates.
    pub fn set_grid_cursor(&mut self, new_cursor: &[u8; 2]) {
        self.grid_cursor = *new_cursor;
    }
}

<|endoftext|>


<commit_before>use crate::rparser::*;
use radius_parser::parse_radius_data;

pub struct RadiusBuilder {}
impl RBuilder for RadiusBuilder {
    fn build(&self) -> Box<dyn RParser> {
        Box::new(RadiusParser::new(b"Radius"))
    }
    fn get_l4_probe(&self) -> Option<ProbeL4> {
        Some(radius_probe)
    }
}

pub struct RadiusParser<'a> {
    _name: Option<&'a [u8]>,
}

impl<'a> RadiusParser<'a> {
    pub fn new(name: &'a [u8]) -> RadiusParser<'a> {
        RadiusParser { _name: Some(name) }
    }
}

impl<'a> RParser for RadiusParser<'a> {
    fn parse_l4(&mut self, data: &[u8], _direction: Direction) -> ParseResult {
        match parse_radius_data(data) {
            Ok((rem, ref rad)) => {
                debug!("parse_radius_data: {:?}", rad);
                if !rem.is_empty() {
                    warn!("Extra bytes after Radius data");
                }
                ParseResult::Ok
            }
            _ => ParseResult::Error,
        }
    }
}

// #[allow(dead_code)]
pub fn radius_probe(i: &[u8], _l4info: &L4Info) -> ProbeResult {
    if i.len() <= 2 {
        return ProbeResult::NotForUs;
    }
    match parse_radius_data(i) {
        Ok((rem, _)) => {
            if !rem.is_empty() {
                warn!("Extra bytes after Radius data");
                ProbeResult::NotForUs
            } else {
                ProbeResult::Certain
            }
        }
        _ => ProbeResult::NotForUs,
    }
}
<commit_msg>Radius: be stricter when probing (test code and length)<commit_after>use crate::rparser::*;
use radius_parser::parse_radius_data;

pub struct RadiusBuilder {}
impl RBuilder for RadiusBuilder {
    fn build(&self) -> Box<dyn RParser> {
        Box::new(RadiusParser::new(b"Radius"))
    }
    fn get_l4_probe(&self) -> Option<ProbeL4> {
        Some(radius_probe)
    }
}

pub struct RadiusParser<'a> {
    _name: Option<&'a [u8]>,
}

impl<'a> RadiusParser<'a> {
    pub fn new(name: &'a [u8]) -> RadiusParser<'a> {
        RadiusParser { _name: Some(name) }
    }
}

impl<'a> RParser for RadiusParser<'a> {
    fn parse_l4(&mut self, data: &[u8], _direction: Direction) -> ParseResult {
        match parse_radius_data(data) {
            Ok((rem, ref rad)) => {
                debug!("parse_radius_data: {:?}", rad);
                if !rem.is_empty() {
                    warn!("Extra bytes after Radius data");
                }
                ParseResult::Ok
            }
            _ => ParseResult::Error,
        }
    }
}

#[allow(clippy::manual_range_contains)]
pub fn radius_probe(i: &[u8], _l4info: &L4Info) -> ProbeResult {
    let sz = i.len();
    if sz <= 2 {
        return ProbeResult::NotForUs;
    }
    // RFC 2865: size must be >= 20 and <= 4096
    if sz < 20 || sz > 4096 {
        return ProbeResult::NotForUs;
    }
    match parse_radius_data(i) {
        Ok((rem, r)) => {
            if r.code.0 == 0 || r.code.0 > 13 || r.length as usize != i.len() {
                return ProbeResult::NotForUs;
            }
            if !rem.is_empty() {
                warn!("Extra bytes after Radius data");
                ProbeResult::NotForUs
            } else {
                ProbeResult::Certain
            }
        }
        _ => ProbeResult::NotForUs,
    }
}
<|endoftext|>


<commit_before>use glium::VertexBuffer;
use glium::IndexBuffer;
use glium::Program;
use glium::DrawParameters;
use glium::Surface;
use glium::Frame;
use glium::Rect;
use glium::Texture2d;
use glium::Blend;
use glium::index::PrimitiveType;
use glium::backend::Facade;

use nalgebra::Mat4;
use nalgebra::OrthoMat3;

use mem::MemSection;
use mem::AddressSpace;

pub const LCD_WIDTH: u32    = 160;
pub const LCD_HEIGHT: u32   = 144;
pub const LCD_ASPECT: f32   = (LCD_WIDTH as f32) / (LCD_HEIGHT as f32);

pub const BG_SIZE: u32      = 256;

/// Period of the V-Blank in ns. V-Blank frequency is ~59.7 Hz
pub const VBLANK_PERIOD: u64 = 16_750_419;

/// Period of the H-Blank in ns. H-Blank frequency is ~9198 Hz
pub const HBLANK_PERIOD: u64 = 108_719;

static SIMPLE_VERT: &'static str = r#"
#version 140

in vec2 coord;
in vec2 tcoord;

out vec2 tex_coord;

uniform mat4 projection;
uniform vec2 translate;

void main() {
    tex_coord = tcoord;
    gl_Position = projection * vec4(coord.x + translate.x, coord.y + translate.y, 0.0, 1.0);
}
"#;

static COLOR_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform vec4 color;
uniform vec2 translate;

void main() {
    frag_color = color;
}
"#;

static TEXTURE_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform sampler2D tex;

void main() {
    frag_color = texture(tex, tex_coord);
}
"#;

#[derive(Copy, Clone)]
struct Vertex {
    coord: [f32; 2],
    tcoord: [f32; 2],
}

implement_vertex!(Vertex, coord, tcoord);

pub struct GbDisplay {
    simple_surface: VertexBuffer<Vertex>,
    simple_surface_idx: IndexBuffer<u32>,
    color_prog: Program,
    tex_prog: Program,
    projection: Mat4<f32>,
    ly_counter: u8,
}

impl GbDisplay {

    pub fn new<F>(display: &F) -> GbDisplay where F: Facade {
        let (vertbuf, idxbuf) = {
            let topleft = Vertex {
                coord: [0.0, 0.0],
                tcoord: [0.0, 1.0],
            };
            let topright = Vertex {
                coord: [BG_SIZE as f32, 0.0],
                tcoord: [1.0, 1.0],
            };
            let bottomright = Vertex {
                coord: [BG_SIZE as f32, BG_SIZE as f32],
                tcoord: [1.0, 0.0],
            };
            let bottomleft = Vertex {
                coord: [0.0, BG_SIZE as f32],
                tcoord: [0.0, 0.0],
            };
            let vertices = vec![topleft, topright, bottomright, bottomleft];
            let indices = vec![0, 1, 3, 1, 2, 3];
            (
                VertexBuffer::new(display, &vertices).unwrap(),
                IndexBuffer::immutable(display, PrimitiveType::TrianglesList, &indices).unwrap()
            )
        };
        let colorprog = Program::from_source(display, SIMPLE_VERT, COLOR_FRAG, None).unwrap();
        let texprog = Program::from_source(display, SIMPLE_VERT, TEXTURE_FRAG, None).unwrap();
        let projection = {
            let orthomat = OrthoMat3::new(LCD_WIDTH as f32, LCD_HEIGHT as f32, 0.0, 1.0);
            // Reverse y coord, and translate origin to top left
            let adjust = Mat4::new(
                1.0f32, 0.0, 0.0, (LCD_WIDTH as f32) / -2.0,
                0.0, -1.0, 0.0, (LCD_HEIGHT as f32) / 2.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0
                );
            orthomat.to_mat() * adjust
        };
        GbDisplay {
            simple_surface: vertbuf,
            simple_surface_idx: idxbuf,
            color_prog: colorprog,
            tex_prog: texprog,
            projection: projection,
            ly_counter: 0,
        }
    }

    pub fn inc_ly_counter(&mut self) -> u8 {
        if self.ly_counter >= 153 {
            self.ly_counter = 0;
        } else {
            self.ly_counter += 1;
        }
        self.ly_counter
    }

    pub fn set_ly_vblank(&mut self) -> u8 {
        self.ly_counter = 144;
        self.ly_counter
    }

    pub fn clear_viewport(&mut self, frame: &mut Frame, view: Rect, color: (f32, f32, f32, f32)) {
        let params = DrawParameters {
            viewport: Some(view),
            .. Default::default()
        };
        let uniforms = uniform! {
            projection: self.projection,
            translate: (0.0f32, 0.0),
            color: color,
        };
        frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.color_prog, &uniforms, &params);
    }

    pub fn draw<F>(&mut self, display: &F, frame: &mut Frame, view: Rect, mem: &AddressSpace) where F: Facade {
        let lcdc_reg = mem[0xFF40];
        let lcd_on                  = (lcdc_reg & 0x80) != 0;
        let win_map_addr            = if (lcdc_reg & 0x40) == 0 { 0x9800 } else { 0x9C00 };
        let win_on                  = (lcdc_reg & 0x20) != 0;
        let (tile_data, signed_idx) = if (lcdc_reg & 0x10) == 0 { (0x9000, true) }
                                        else { (0x8000, false) };
        let bg_map_addr             = if (lcdc_reg & 0x08) == 0 { 0x9800 } else { 0x9C00 };
        let sprite_height           = if (lcdc_reg & 0x04) == 0 { 8 } else { 16 };
        let sprite_on               = (lcdc_reg & 0x02) != 0;
        let bg_on                   = (lcdc_reg & 0x01) != 0;
        let scroll_y = mem[0xFF42];
        let scroll_x = mem[0xFF43];
        let bg_palette = build_palette(mem[0xFF47]);
        let sprite_palette0 = build_palette(mem[0xFF48]);
        let sprite_palette1 = build_palette(mem[0xFF49]);
        let win_y = mem[0xFF4A];
        let win_x = mem[0xFF4B];

        let params = DrawParameters {
            blend: Blend::alpha_blending(),
            viewport: Some(view),
            .. Default::default()
        };

        // Draw BG
        if bg_on {
            let opts = TileOpts {
                map_addr: bg_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let bg_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (-(scroll_x as f32), -(scroll_y as f32)),
                tex: &bg_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }

        // Draw window
        if win_on {
            let opts = TileOpts {
                map_addr: win_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let win_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (win_x as f32, win_y as f32),
                tex: &win_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }
    }
}

const PALETTE_COLOR0: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 1.0);
const PALETTE_COLOR1: (f32, f32, f32, f32) = (0.25, 0.25, 0.25, 1.0);
const PALETTE_COLOR2: (f32, f32, f32, f32) = (0.75, 0.75, 0.75, 1.0);
const PALETTE_COLOR3: (f32, f32, f32, f32) = (0.0, 0.0, 0.0, 1.0);
const PALETTE_COLORTEST: (f32, f32, f32, f32) = (1.0, 0.5, 0.75, 1.0);

fn build_palette(bits: u8) -> [(f32, f32, f32, f32); 4] {
    let c0 = (bits & 0x03);
    let c1 = (bits & 0x0C) >> 2;
    let c2 = (bits & 0x30) >> 4;
    let c3 = (bits & 0xC0) >> 6;
    let mut colors = [PALETTE_COLOR0; 4];
    colors[0] = match c0 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[1] = match c1 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[2] = match c2 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[3] = match c3 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors
}

struct TileOpts {
    map_addr: u16,
    tile_addr: u16,
    signed_idx: bool,
    palette: [(f32, f32, f32, f32); 4],
}

fn build_tile_tex<F>(display: &F, mem: &AddressSpace, opts: &TileOpts) -> Texture2d where F: Facade {
    // Data layout: data[y][x], origin top left
    let mut data: Vec<Vec<(f32, f32, f32, f32)>> = Vec::with_capacity(256);
    for i in 0..256 {
        data.push(Vec::with_capacity(256));
    }
    for i in 0..1024 {
        let offset: i32 = if opts.signed_idx {
            // Abuse conversion operations
            let tmp = mem[opts.map_addr + i] as i8;
            tmp as i32
        } else {
            mem[opts.map_addr + i] as i32
        };
        let addr = (opts.tile_addr as i32 + offset*16) as u16;
        let y_coord = (i / 32) as usize;
        // Pixel format: big endian pixel pos
        for j in 0..8 {
            let lo = mem[addr + j*2];
            let hi = mem[addr + j*2 + 1];
            for k in (0..8).rev() {
                let mask = 1 << k;
                let color = ((lo & mask) >> k) | (((hi & mask) >> k) << 1);
                data[y_coord*8 + j as usize].push(opts.palette[color as usize]);
            }
        }
    }
    Texture2d::new(display, data).unwrap()
}

pub fn calculate_viewport(width: u32, height: u32) -> Rect {
    let aspect = (width as f32) / (height as f32);
    if aspect > LCD_ASPECT {
        let fixwidth = ((height as f32) * LCD_ASPECT) as u32;
        Rect {
            left: (width - fixwidth) / 2,
            bottom: 0,
            width: fixwidth,
            height: height,
        }
    } else {
        let fixheight = ((width as f32) / LCD_ASPECT) as u32;
        Rect {
            left: 0,
            bottom: (height - fixheight) / 2,
            width: width,
            height: fixheight,
        }
    }
}
<commit_msg>Make various improvements to render module<commit_after>use glium::VertexBuffer;
use glium::IndexBuffer;
use glium::Program;
use glium::DrawParameters;
use glium::Surface;
use glium::Frame;
use glium::Rect;
use glium::Texture2d;
use glium::Blend;
use glium::index::PrimitiveType;
use glium::backend::Facade;
use glium::texture::MipmapsOption;
use glium::uniforms::{Sampler, MagnifySamplerFilter, MinifySamplerFilter};

use nalgebra::Mat4;
use nalgebra::OrthoMat3;

use mem::MemSection;
use mem::AddressSpace;

pub const LCD_WIDTH: u32    = 160;
pub const LCD_HEIGHT: u32   = 144;
pub const LCD_ASPECT: f32   = (LCD_WIDTH as f32) / (LCD_HEIGHT as f32);

pub const BG_SIZE: u32      = 256;

/// Period of the V-Blank in ns. V-Blank frequency is ~59.7 Hz
pub const VBLANK_PERIOD: u64 = 16_750_419;

/// Period of the H-Blank in ns. H-Blank frequency is ~9198 Hz
pub const HBLANK_PERIOD: u64 = 108_719;

static SIMPLE_VERT: &'static str = r#"
#version 140

in vec2 coord;
in vec2 tcoord;

out vec2 tex_coord;

uniform mat4 projection;
uniform vec2 translate = vec2(0.0,  0.0);
uniform vec2 tex_scroll = vec2(0.0, 0.0);

void main() {
    tex_coord = tcoord + tex_scroll;
    gl_Position = projection * vec4(coord + translate, 0.0, 1.0);
}
"#;

static COLOR_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform vec4 color;

void main() {
    frag_color = color;
}
"#;

static TEXTURE_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform sampler2D tex;

void main() {
    frag_color = texture(tex, tex_coord);
}
"#;

#[derive(Copy, Clone)]
struct Vertex {
    coord: [f32; 2],
    tcoord: [f32; 2],
}

implement_vertex!(Vertex, coord, tcoord);

pub struct GbDisplay {
    simple_surface: VertexBuffer<Vertex>,
    simple_surface_idx: IndexBuffer<u32>,
    scroll_surface: VertexBuffer<Vertex>,
    scroll_surface_idx: IndexBuffer<u32>,
    color_prog: Program,
    tex_prog: Program,
    projection: Mat4<f32>,
    ly_counter: u8,
}

impl GbDisplay {

    pub fn new<F>(display: &F) -> GbDisplay where F: Facade {
        // Full texture surface
        let (vertbuf, idxbuf) = {
            let topleft = Vertex {
                coord: [0.0, 0.0],
                tcoord: [0.0, 0.0],
            };
            let topright = Vertex {
                coord: [BG_SIZE as f32, 0.0],
                tcoord: [1.0, 0.0],
            };
            let bottomright = Vertex {
                coord: [BG_SIZE as f32, BG_SIZE as f32],
                tcoord: [1.0, 1.0],
            };
            let bottomleft = Vertex {
                coord: [0.0, BG_SIZE as f32],
                tcoord: [0.0, 1.0],
            };
            let vertices = vec![topleft, topright, bottomright, bottomleft];
            let indices = vec![0, 1, 3, 1, 2, 3];
            (
                VertexBuffer::new(display, &vertices).unwrap(),
                IndexBuffer::immutable(display, PrimitiveType::TrianglesList, &indices).unwrap()
            )
        };
        // Texture scrolling surface
        let (scroll_buff, scroll_idx) = {
            let texw = (LCD_WIDTH as f32) / (BG_SIZE as f32);
            let texh = (LCD_HEIGHT as f32) / (BG_SIZE as f32);
            let topleft = Vertex {
                coord: [0.0, 0.0],
                tcoord: [0.0, 0.0],
            };
            let topright = Vertex {
                coord: [LCD_WIDTH as f32, 0.0],
                tcoord: [texw, 0.0],
            };
            let bottomright = Vertex {
                coord: [LCD_WIDTH as f32, LCD_HEIGHT as f32],
                tcoord: [texw, texh],
            };
            let bottomleft = Vertex {
                coord: [0.0, LCD_HEIGHT as f32],
                tcoord: [0.0, texh],
            };
            let vertices = vec![topleft, topright, bottomright, bottomleft];
            let indices = vec![0, 1, 3, 1, 2, 3];
            (
                VertexBuffer::new(display, &vertices).unwrap(),
                IndexBuffer::immutable(display, PrimitiveType::TrianglesList, &indices).unwrap()
            )
        };
        // Shaders
        let colorprog = Program::from_source(display, SIMPLE_VERT, COLOR_FRAG, None).unwrap();
        let texprog = Program::from_source(display, SIMPLE_VERT, TEXTURE_FRAG, None).unwrap();
        // Projection matrices
        let projection = {
            let orthomat = OrthoMat3::new(LCD_WIDTH as f32, LCD_HEIGHT as f32, 0.0, 1.0);
            // Reverse y coord, and translate origin to top left
            let adjust = Mat4::new(
                1.0f32, 0.0, 0.0, (LCD_WIDTH as f32) / -2.0,
                0.0, -1.0, 0.0, (LCD_HEIGHT as f32) / 2.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0
                );
            orthomat.to_mat() * adjust
        };
        // Result
        GbDisplay {
            simple_surface: vertbuf,
            simple_surface_idx: idxbuf,
            scroll_surface: scroll_buff,
            scroll_surface_idx: scroll_idx,
            color_prog: colorprog,
            tex_prog: texprog,
            projection: projection,
            ly_counter: 0,
        }
    }

    pub fn inc_ly_counter(&mut self) -> u8 {
        if self.ly_counter >= 153 {
            self.ly_counter = 0;
        } else {
            self.ly_counter += 1;
        }
        self.ly_counter
    }

    pub fn set_ly_vblank(&mut self) -> u8 {
        self.ly_counter = 144;
        self.ly_counter
    }

    pub fn clear_viewport(&mut self, frame: &mut Frame, view: Rect, color: (f32, f32, f32, f32)) {
        let params = DrawParameters {
            viewport: Some(view),
            .. Default::default()
        };
        let uniforms = uniform! {
            projection: self.projection,
            color: color,
        };
        frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.color_prog, &uniforms, &params);
    }

    pub fn draw<F>(&mut self, display: &F, frame: &mut Frame, view: Rect, mem: &AddressSpace) where F: Facade {
        let lcdc_reg = mem[0xFF40];
        let lcd_on                  = (lcdc_reg & 0x80) != 0;
        let win_map_addr            = if (lcdc_reg & 0x40) == 0 { 0x9800 } else { 0x9C00 };
        let win_on                  = (lcdc_reg & 0x20) != 0;
        let (tile_data, signed_idx) = if (lcdc_reg & 0x10) == 0 { (0x9000, true) }
                                        else { (0x8000, false) };
        let bg_map_addr             = if (lcdc_reg & 0x08) == 0 { 0x9800 } else { 0x9C00 };
        let sprite_height           = if (lcdc_reg & 0x04) == 0 { 8 } else { 16 };
        let sprite_on               = (lcdc_reg & 0x02) != 0;
        let bg_on                   = (lcdc_reg & 0x01) != 0;
        let scroll_y = mem[0xFF42];
        let scroll_x = mem[0xFF43];
        let bg_palette = build_palette(mem[0xFF47]);
        let sprite_palette0 = build_palette(mem[0xFF48]);
        let sprite_palette1 = build_palette(mem[0xFF49]);
        let win_y = mem[0xFF4A];
        let win_x = mem[0xFF4B];

        let params = DrawParameters {
            blend: Blend::alpha_blending(),
            viewport: Some(view),
            .. Default::default()
        };

        // Draw BG
        if bg_on {
            let opts = TileOpts {
                map_addr: bg_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let bg_tex = build_tile_tex(display, mem, &opts);
            let tsx = (scroll_x as f32) / (BG_SIZE as f32);
            let tsy = (scroll_y as f32) / (BG_SIZE as f32);
            let uniforms = uniform! {
                projection: self.projection,
                tex_scroll: (tsx, tsy),
                tex: Sampler::new(&bg_tex)
                    .magnify_filter(MagnifySamplerFilter::Nearest)
                    .minify_filter(MinifySamplerFilter::Nearest),
            };
            frame.draw(&self.scroll_surface, &self.scroll_surface_idx, &self.tex_prog, &uniforms, &params);
        }

        // Draw window
        if win_on {
            let opts = TileOpts {
                map_addr: win_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let win_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (win_x as f32, win_y as f32),
                tex: Sampler::new(&win_tex)
                    .magnify_filter(MagnifySamplerFilter::Nearest)
                    .minify_filter(MinifySamplerFilter::Nearest),
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }
    }
}

const PALETTE_COLOR0: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 1.0);
const PALETTE_COLOR1: (f32, f32, f32, f32) = (0.25, 0.25, 0.25, 1.0);
const PALETTE_COLOR2: (f32, f32, f32, f32) = (0.75, 0.75, 0.75, 1.0);
const PALETTE_COLOR3: (f32, f32, f32, f32) = (0.0, 0.0, 0.0, 1.0);
const PALETTE_COLORTEST: (f32, f32, f32, f32) = (1.0, 0.5, 0.75, 1.0);

fn build_palette(bits: u8) -> [(f32, f32, f32, f32); 4] {
    let c0 = (bits & 0x03);
    let c1 = (bits & 0x0C) >> 2;
    let c2 = (bits & 0x30) >> 4;
    let c3 = (bits & 0xC0) >> 6;
    let mut colors = [PALETTE_COLOR0; 4];
    colors[0] = match c0 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[1] = match c1 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[2] = match c2 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[3] = match c3 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors
}

struct TileOpts {
    map_addr: u16,
    tile_addr: u16,
    signed_idx: bool,
    palette: [(f32, f32, f32, f32); 4],
}

fn build_tile_tex<F>(display: &F, mem: &AddressSpace, opts: &TileOpts) -> Texture2d where F: Facade {
    // Data layout: data[y][x], origin top left
    let mut data: Vec<Vec<(f32, f32, f32, f32)>> = Vec::with_capacity(256);
    for i in 0..256 {
        data.push(Vec::with_capacity(256));
    }
    for i in 0..1024 {
        let offset: i32 = if opts.signed_idx {
            // Abuse conversion operations
            let tmp = mem[opts.map_addr + i] as i8;
            tmp as i32
        } else {
            mem[opts.map_addr + i] as i32
        };
        let addr = (opts.tile_addr as i32 + offset*16) as u16;
        let y_coord = (i / 32) as usize;
        // Pixel format: big endian pixel pos
        for j in 0..8 {
            let lo = mem[addr + j*2];
            let hi = mem[addr + j*2 + 1];
            for k in (0..8).rev() {
                let mask = 1 << k;
                let color = ((lo & mask) >> k) | (((hi & mask) >> k) << 1);
                data[y_coord*8 + j as usize].push(opts.palette[color as usize]);
            }
        }
    }
    Texture2d::with_mipmaps(display, data, MipmapsOption::NoMipmap).unwrap()
}

pub fn calculate_viewport(width: u32, height: u32) -> Rect {
    let aspect = (width as f32) / (height as f32);
    if aspect > LCD_ASPECT {
        let fixwidth = ((height as f32) * LCD_ASPECT) as u32;
        Rect {
            left: (width - fixwidth) / 2,
            bottom: 0,
            width: fixwidth,
            height: height,
        }
    } else {
        let fixheight = ((width as f32) / LCD_ASPECT) as u32;
        Rect {
            left: 0,
            bottom: (height - fixheight) / 2,
            width: width,
            height: fixheight,
        }
    }
}
<|endoftext|>


<commit_before>use std::path:: { PathBuf };
use coverage:: { Coverage };

#[derive(Debug)]
pub struct Report {
    files: Vec<FileResult>
}

impl Report {
    pub fn new(files: Vec<FileResult>) -> Self {
        Report { files: files }
    }
    pub fn files(&self) -> Vec<FileResult> {
        self.files.to_vec()
    }
    pub fn sorted_files(&self) -> Vec<FileResult> {
        let mut files = self.files();
        files.sort_by(|a, b| { b.coverage.partial_cmp(&a.coverage).unwrap() });
        files
    }
}

#[derive(Debug, Clone)]
pub struct FileResult {
    path: PathBuf,
    coverage: Coverage
}

impl FileResult {
    pub fn new(path: PathBuf, coverage: Coverage) -> Self {
        FileResult { path: path, coverage: coverage }
    }
    pub fn coverage(&self) -> &Coverage {
        &self.coverage
    }
}

#[cfg(test)]
mod tests {
    use std::path:: { PathBuf };
    use report:: { Report, FileResult };
    use coverage:: { Coverage };

    #[test]
    fn test_sorted_files() {
        let report = Report::new(vec!(
            FileResult::new(PathBuf::from("test1.rs"), Coverage::new(0.1)),
            FileResult::new(PathBuf::from("test2.rs"), Coverage::new(0.2)),
            FileResult::new(PathBuf::from("test3.rs"), Coverage::new(0.3))
        ));
        let files = report.sorted_files();

        assert_eq!(&Coverage::new(0.3), files.get(0).unwrap().coverage());
        assert_eq!(&Coverage::new(0.2), files.get(1).unwrap().coverage());
        assert_eq!(&Coverage::new(0.1), files.get(2).unwrap().coverage());
    }
}
<commit_msg>to AsRef<Path><commit_after>use std::path:: { Path, PathBuf };
use coverage:: { Coverage };

#[derive(Debug)]
pub struct Report {
    files: Vec<FileResult>
}

impl Report {
    pub fn new(files: Vec<FileResult>) -> Self {
        Report { files: files }
    }
    pub fn files(&self) -> Vec<FileResult> {
        self.files.to_vec()
    }
    pub fn sorted_files(&self) -> Vec<FileResult> {
        let mut files = self.files();
        files.sort_by(|a, b| { b.coverage.partial_cmp(&a.coverage).unwrap() });
        files
    }
}

#[derive(Debug, Clone)]
pub struct FileResult {
    path: PathBuf,
    coverage: Coverage
}

impl FileResult {
    pub fn new<P: AsRef<Path>>(path: P, coverage: Coverage) -> Self {
        FileResult { path: path.as_ref().to_path_buf(), coverage: coverage }
    }
    pub fn coverage(&self) -> &Coverage {
        &self.coverage
    }
}

#[cfg(test)]
mod tests {
    use report:: { Report, FileResult };
    use coverage:: { Coverage };

    #[test]
    fn test_sorted_files() {
        let report = Report::new(vec!(
            FileResult::new("test1.rs", Coverage::new(0.1)),
            FileResult::new("test2.rs", Coverage::new(0.2)),
            FileResult::new("test3.rs", Coverage::new(0.3))
        ));
        let files = report.sorted_files();

        assert_eq!(&Coverage::new(0.3), files.get(0).unwrap().coverage());
        assert_eq!(&Coverage::new(0.2), files.get(1).unwrap().coverage());
        assert_eq!(&Coverage::new(0.1), files.get(2).unwrap().coverage());
    }
}
<|endoftext|>


<commit_before>//!Router asigns handlers to paths and resolves them per request

#[cfg(test)]
use http::method;
use http::method::{ Method, Get, Post, Put, Delete };
use http::server::request::{AbsolutePath};
use regex::Regex;
use std::collections::hashmap::HashMap;
use request::Request;
use response::Response;
use middleware::{ Middleware, Action, Halt, Continue };
use nickel_error::NickelError;

/// A Route is the basic data structure that stores both the path
/// and the handler that gets executed for the route.
/// The path can contain variable pattern such as `user/:userid/invoices`
pub struct Route {
    pub path: String,
    pub method: Method,
    pub handler: fn(request: &Request, response: &mut Response),
    pub variables: HashMap<String, uint>,
    matcher: Regex
}

impl Clone for Route {
    fn clone(&self) -> Route {
        Route {
            path: self.path.clone(),
            method: self.method.clone(),
            handler: self.handler,
            matcher: self.matcher.clone(),
            variables: self.variables.clone()
        }
    }
}

/// A RouteResult is what the router returns when `match_route` is called.
/// It contains the matched `route` and also a `params` property holding
/// a HashMap with the keys being the variable names and the value being the
/// evaluated string
pub struct RouteResult<'a> {
    pub route: &'a Route,
    pub params: HashMap<String, String>
}

/// The PathUtils collects some small helper methods that operate on the path
struct PathUtils;

// matches named variables (e.g. :userid)
static REGEX_VAR_SEQ: Regex                 = regex!(r":([,a-zA-Z0-9_-]*)");
static VAR_SEQ:&'static str                 = "[,a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_SLASH:&'static str      = "[,/a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_CAPTURE:&'static str    = "([,a-zA-Z0-9%_-]*)";
// matches request params (e.g. ?foo=true&bar=false)
static REGEX_PARAM_SEQ:&'static str         = "(\\?[a-zA-Z0-9%_=&-]*)?";
static REGEX_START:&'static str             = "^";
static REGEX_END:&'static str               = "$";

impl PathUtils {
    fn create_regex (route_path: &str) -> Regex {

        let updated_path = route_path.to_string()
                                     // first mark all double wildcards for replacement. We can't directly replace them
                                     // since the replacement does contain the * symbol as well, which would get overwritten
                                     // with the next replace call
                                     .replace("**", "___DOUBLE_WILDCARD___")
                                     // then replace the regular wildcard symbols (*) with the appropriate regex
                                     .replace("*", VAR_SEQ)
                                     // now replace the previously marked double wild cards (**)
                                     .replace("___DOUBLE_WILDCARD___", VAR_SEQ_WITH_SLASH);

        // then replace the variable symbols (:variable) with the appropriate regex
        let result = REGEX_START.to_string()
                                .append(REGEX_VAR_SEQ.replace_all(updated_path.as_slice(), VAR_SEQ_WITH_CAPTURE).as_slice())
                                .append(REGEX_PARAM_SEQ)
                                .append(REGEX_END);

        match Regex::new(result.as_slice()) {
            Ok(re) => re,
            Err(err) => fail!("{}", err)
        }
    }

    fn get_variable_info (route_path: &str) -> HashMap<String, uint> {
        REGEX_VAR_SEQ.captures_iter(route_path)
             .enumerate()
             .map(|(i, matched)| (matched.at(1).to_string(), i))
             .collect()
    }
}

/// The Router's job is it to hold routes and to resolve them later against
/// concrete URLs. The router is also a regular middleware and needs to be
/// added to the middleware stack with `server.utilize(router)`.

#[deriving(Clone)]
pub struct Router{
    routes: Vec<Route>,
}

impl Router {
    pub fn new () -> Router {
        Router {
            routes: Vec::new()
        }
    }

    /// Registers a handler to be used for a specific GET request.
    /// Handlers are assigned to paths and paths are allowed to contain
    /// variables and wildcards.
    ///
    /// # Example without variables and wildcards
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user");
    /// };
    /// router.get("/user", handler);
    /// ```
    /// # Example with variables
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     let text = format!("This is user: {}", request.params.get(&"userid".to_string()));
    ///     response.send(text.as_slice());
    /// };
    /// router.get("/user/:userid", handler);
    /// ```
    /// # Example with simple wildcard
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user/list/4711 but not /user/extended/list/4711");
    /// };
    /// router.get("/user/*/:userid", handler);
    /// ```
    /// # Example with double wildcard
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user/list/4711 and also /user/extended/list/4711");
    /// };
    /// router.get("/user/**/:userid", handler);
    /// ```
    pub fn get(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Get, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific POST request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a POST request to /a/post/request");
    /// };
    /// router.post("/a/post/request", handler);
    /// ```
    /// Take a look at `get()` for a more detailed description.
    pub fn post(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Post, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific PUT request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a POST request to /a/put/request");
    /// };
    /// router.put("/a/put/request", handler);
    /// ```
    /// Take a look at `get(..)` for a more detailed description.
    pub fn put(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Put, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific DELETE request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a DELETE request to /a/delete/request");
    /// };
    /// router.delete("/a/delete/request", handler);
    /// ```
    /// Take a look at `get(...)` for a more detailed description.
    pub fn delete(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Delete, String::from_str(uri), handler);
    }

    pub fn add_route (&mut self, method: Method, path: String, handler: fn(request: &Request, response: &mut Response)) -> () {
        let matcher = PathUtils::create_regex(path.as_slice());
        let variable_infos = PathUtils::get_variable_info(path.as_slice());
        let route = Route {
            path: path,
            method: method,
            matcher: matcher,
            handler: handler,
            variables: variable_infos
        };
        self.routes.push(route);
    }

    pub fn match_route(&self, method: Method, path: String) -> Option<RouteResult> {
        self.routes.iter().find(|item| item.method == method && item.matcher.is_match(path.as_slice()))
            .and_then(|route| {
                match route.matcher.captures(path.as_slice()) {
                    Some(captures) => {
                        let mut map = HashMap::new();
                        for (name, pos) in route.variables.iter() {
                            map.insert(name.to_string(), captures.at(pos + 1).to_string());
                        }
                        Some(RouteResult {
                            route: route,
                            params: map
                        })
                    },
                    None => Some(RouteResult{
                        route: route,
                        params: HashMap::new()
                    })
                }
            })
    }
}

impl Middleware for Router {
    fn invoke (&self, req: &mut Request, res: &mut Response) -> Result<Action, NickelError> {
        match &req.origin.request_uri {
            &AbsolutePath(ref url) => {
                match self.match_route(req.origin.method.clone(), url.clone()) {
                    Some(route_result) => {
                        res.origin.status = ::http::status::Ok;
                        req.params = route_result.params.clone();
                        (route_result.route.handler)(req, res);
                        Ok(Halt)
                    },
                    None => Ok(Continue)
                }
            },
            _ => Ok(Continue)
        }
    }
}

#[test]
fn creates_map_with_var_variable_infos () {
    let map = PathUtils::get_variable_info("foo/:uid/bar/:groupid");

    assert_eq!(map.len(), 2);
    assert_eq!(map.get(&"uid".to_string()), &0);
    assert_eq!(map.get(&"groupid".to_string()), &1);
}

#[test]
fn creates_regex_with_captures () {
    let regex = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let caps = regex.captures("foo/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/*/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/**/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");
}

#[test]
fn creates_valid_regex_for_routes () {
    let regex1 = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let regex2 = PathUtils::create_regex("foo/*/bar");
    let regex3 = PathUtils::create_regex("foo/**/bar");

    assert_eq!(regex1.is_match("foo/4711/bar/5490"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=true&bar=false"), false);
    assert_eq!(regex1.is_match("foo/4711/bar/test%20spacing"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5281?foo=test%20spacing&bar=false"), true);

    assert_eq!(regex2.is_match("foo/4711/bar"), true);
    assert_eq!(regex2.is_match("foo/4711/barr"), false);
    assert_eq!(regex2.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex2.is_match("foo/4711/4712/bar"), false);
    assert_eq!(regex2.is_match("foo/4711/4712/bar?foo=true&bar=false"), false);

    assert_eq!(regex3.is_match("foo/4711/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar?foo=true&bar=false"), true);

    //ensure that this works with commas too
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=1,2,3&bar=false"), false);
}

#[test]
fn can_match_var_routes () {
    let route_store = &mut Router::new();

    fn handler (_request: &Request, response: &mut Response) -> () {
        let _ = response.origin.write("hello from foo".as_bytes());
    };

    route_store.add_route(method::Get, "/foo/:userid".to_string(), handler);
    route_store.add_route(method::Get, "/bar".to_string(), handler);

    let route_result = route_store.match_route(method::Get, "/foo/4711".to_string()).unwrap();
    let route = route_result.route;

    assert_eq!(route_result.params.get(&"userid".to_string()), &"4711".to_string());

    //assert the route has identified the variable
    assert_eq!(route.variables.len(), 1);
    assert_eq!(route.variables.get(&"userid".to_string()), &0);

    let route_result = route_store.match_route(method::Get, "/bar/4711".to_string());
    assert!(route_result.is_none());

    let route_result = route_store.match_route(method::Get, "/foo".to_string());
    assert!(route_result.is_none());

    //ensure that this will work with commas too
    let route_result = route_store.match_route(method::Get, "/foo/123,456".to_string());
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.params.get(&"userid".to_string()), &"123,456".to_string());

    //ensure that this will work with spacing too
    let route_result = route_store.match_route(method::Get, "/foo/John%20Doe".to_string());
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.params.get(&"userid".to_string()), &"John%20Doe".to_string());
}
<commit_msg>Collect is slightly more efficient here.<commit_after>//!Router asigns handlers to paths and resolves them per request

#[cfg(test)]
use http::method;
use http::method::{ Method, Get, Post, Put, Delete };
use http::server::request::{AbsolutePath};
use regex::Regex;
use std::collections::hashmap::HashMap;
use request::Request;
use response::Response;
use middleware::{ Middleware, Action, Halt, Continue };
use nickel_error::NickelError;

/// A Route is the basic data structure that stores both the path
/// and the handler that gets executed for the route.
/// The path can contain variable pattern such as `user/:userid/invoices`
pub struct Route {
    pub path: String,
    pub method: Method,
    pub handler: fn(request: &Request, response: &mut Response),
    pub variables: HashMap<String, uint>,
    matcher: Regex
}

impl Clone for Route {
    fn clone(&self) -> Route {
        Route {
            path: self.path.clone(),
            method: self.method.clone(),
            handler: self.handler,
            matcher: self.matcher.clone(),
            variables: self.variables.clone()
        }
    }
}

/// A RouteResult is what the router returns when `match_route` is called.
/// It contains the matched `route` and also a `params` property holding
/// a HashMap with the keys being the variable names and the value being the
/// evaluated string
pub struct RouteResult<'a> {
    pub route: &'a Route,
    pub params: HashMap<String, String>
}

/// The PathUtils collects some small helper methods that operate on the path
struct PathUtils;

// matches named variables (e.g. :userid)
static REGEX_VAR_SEQ: Regex                 = regex!(r":([,a-zA-Z0-9_-]*)");
static VAR_SEQ:&'static str                 = "[,a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_SLASH:&'static str      = "[,/a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_CAPTURE:&'static str    = "([,a-zA-Z0-9%_-]*)";
// matches request params (e.g. ?foo=true&bar=false)
static REGEX_PARAM_SEQ:&'static str         = "(\\?[a-zA-Z0-9%_=&-]*)?";
static REGEX_START:&'static str             = "^";
static REGEX_END:&'static str               = "$";

impl PathUtils {
    fn create_regex (route_path: &str) -> Regex {

        let updated_path = route_path.to_string()
                                     // first mark all double wildcards for replacement. We can't directly replace them
                                     // since the replacement does contain the * symbol as well, which would get overwritten
                                     // with the next replace call
                                     .replace("**", "___DOUBLE_WILDCARD___")
                                     // then replace the regular wildcard symbols (*) with the appropriate regex
                                     .replace("*", VAR_SEQ)
                                     // now replace the previously marked double wild cards (**)
                                     .replace("___DOUBLE_WILDCARD___", VAR_SEQ_WITH_SLASH);

        // then replace the variable symbols (:variable) with the appropriate regex
        let result = REGEX_START.to_string()
                                .append(REGEX_VAR_SEQ.replace_all(updated_path.as_slice(), VAR_SEQ_WITH_CAPTURE).as_slice())
                                .append(REGEX_PARAM_SEQ)
                                .append(REGEX_END);

        match Regex::new(result.as_slice()) {
            Ok(re) => re,
            Err(err) => fail!("{}", err)
        }
    }

    fn get_variable_info (route_path: &str) -> HashMap<String, uint> {
        REGEX_VAR_SEQ.captures_iter(route_path)
             .enumerate()
             .map(|(i, matched)| (matched.at(1).to_string(), i))
             .collect()
    }
}

/// The Router's job is it to hold routes and to resolve them later against
/// concrete URLs. The router is also a regular middleware and needs to be
/// added to the middleware stack with `server.utilize(router)`.

#[deriving(Clone)]
pub struct Router{
    routes: Vec<Route>,
}

impl Router {
    pub fn new () -> Router {
        Router {
            routes: Vec::new()
        }
    }

    /// Registers a handler to be used for a specific GET request.
    /// Handlers are assigned to paths and paths are allowed to contain
    /// variables and wildcards.
    ///
    /// # Example without variables and wildcards
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user");
    /// };
    /// router.get("/user", handler);
    /// ```
    /// # Example with variables
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     let text = format!("This is user: {}", request.params.get(&"userid".to_string()));
    ///     response.send(text.as_slice());
    /// };
    /// router.get("/user/:userid", handler);
    /// ```
    /// # Example with simple wildcard
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user/list/4711 but not /user/extended/list/4711");
    /// };
    /// router.get("/user/*/:userid", handler);
    /// ```
    /// # Example with double wildcard
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user/list/4711 and also /user/extended/list/4711");
    /// };
    /// router.get("/user/**/:userid", handler);
    /// ```
    pub fn get(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Get, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific POST request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a POST request to /a/post/request");
    /// };
    /// router.post("/a/post/request", handler);
    /// ```
    /// Take a look at `get()` for a more detailed description.
    pub fn post(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Post, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific PUT request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a POST request to /a/put/request");
    /// };
    /// router.put("/a/put/request", handler);
    /// ```
    /// Take a look at `get(..)` for a more detailed description.
    pub fn put(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Put, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific DELETE request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a DELETE request to /a/delete/request");
    /// };
    /// router.delete("/a/delete/request", handler);
    /// ```
    /// Take a look at `get(...)` for a more detailed description.
    pub fn delete(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Delete, String::from_str(uri), handler);
    }

    pub fn add_route (&mut self, method: Method, path: String, handler: fn(request: &Request, response: &mut Response)) -> () {
        let matcher = PathUtils::create_regex(path.as_slice());
        let variable_infos = PathUtils::get_variable_info(path.as_slice());
        let route = Route {
            path: path,
            method: method,
            matcher: matcher,
            handler: handler,
            variables: variable_infos
        };
        self.routes.push(route);
    }

    pub fn match_route(&self, method: Method, path: String) -> Option<RouteResult> {
        self.routes.iter().find(|item| item.method == method && item.matcher.is_match(path.as_slice()))
            .and_then(|route| {
                let map = match route.matcher.captures(path.as_slice()) {
                    Some(captures) => {
                        route.variables.iter().map(|(name, pos)| {
                            (name.to_string(), captures.at(pos + 1).to_string())
                        }).collect()
                    },
                    None => HashMap::new(),
                };
                Some(RouteResult {
                    route: route,
                    params: map
                })
            })
    }
}

impl Middleware for Router {
    fn invoke (&self, req: &mut Request, res: &mut Response) -> Result<Action, NickelError> {
        match &req.origin.request_uri {
            &AbsolutePath(ref url) => {
                match self.match_route(req.origin.method.clone(), url.clone()) {
                    Some(route_result) => {
                        res.origin.status = ::http::status::Ok;
                        req.params = route_result.params.clone();
                        (route_result.route.handler)(req, res);
                        Ok(Halt)
                    },
                    None => Ok(Continue)
                }
            },
            _ => Ok(Continue)
        }
    }
}

#[test]
fn creates_map_with_var_variable_infos () {
    let map = PathUtils::get_variable_info("foo/:uid/bar/:groupid");

    assert_eq!(map.len(), 2);
    assert_eq!(map.get(&"uid".to_string()), &0);
    assert_eq!(map.get(&"groupid".to_string()), &1);
}

#[test]
fn creates_regex_with_captures () {
    let regex = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let caps = regex.captures("foo/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/*/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/**/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");
}

#[test]
fn creates_valid_regex_for_routes () {
    let regex1 = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let regex2 = PathUtils::create_regex("foo/*/bar");
    let regex3 = PathUtils::create_regex("foo/**/bar");

    assert_eq!(regex1.is_match("foo/4711/bar/5490"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=true&bar=false"), false);
    assert_eq!(regex1.is_match("foo/4711/bar/test%20spacing"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5281?foo=test%20spacing&bar=false"), true);

    assert_eq!(regex2.is_match("foo/4711/bar"), true);
    assert_eq!(regex2.is_match("foo/4711/barr"), false);
    assert_eq!(regex2.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex2.is_match("foo/4711/4712/bar"), false);
    assert_eq!(regex2.is_match("foo/4711/4712/bar?foo=true&bar=false"), false);

    assert_eq!(regex3.is_match("foo/4711/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar?foo=true&bar=false"), true);

    //ensure that this works with commas too
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=1,2,3&bar=false"), false);
}

#[test]
fn can_match_var_routes () {
    let route_store = &mut Router::new();

    fn handler (_request: &Request, response: &mut Response) -> () {
        let _ = response.origin.write("hello from foo".as_bytes());
    };

    route_store.add_route(method::Get, "/foo/:userid".to_string(), handler);
    route_store.add_route(method::Get, "/bar".to_string(), handler);

    let route_result = route_store.match_route(method::Get, "/foo/4711".to_string()).unwrap();
    let route = route_result.route;

    assert_eq!(route_result.params.get(&"userid".to_string()), &"4711".to_string());

    //assert the route has identified the variable
    assert_eq!(route.variables.len(), 1);
    assert_eq!(route.variables.get(&"userid".to_string()), &0);

    let route_result = route_store.match_route(method::Get, "/bar/4711".to_string());
    assert!(route_result.is_none());

    let route_result = route_store.match_route(method::Get, "/foo".to_string());
    assert!(route_result.is_none());

    //ensure that this will work with commas too
    let route_result = route_store.match_route(method::Get, "/foo/123,456".to_string());
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.params.get(&"userid".to_string()), &"123,456".to_string());

    //ensure that this will work with spacing too
    let route_result = route_store.match_route(method::Get, "/foo/John%20Doe".to_string());
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.params.get(&"userid".to_string()), &"John%20Doe".to_string());
}
<|endoftext|>


<commit_before>// Copyright 2017 tokio-jsonrpc Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! The [`Server`](trait.Server.html) trait and helpers.
//!
//! The `Server` trait for the use by the [`Endpoint`](../endpoint/struct.Endpoint.html) is defined
//! here. Furthermore, some helpers for convenient creation and composition of servers are
//! available. Note that not all of these helpers are necessarily zero-cost, at least at this time.

use futures::IntoFuture;
use serde::Serialize;
use serde_json::Value;

use endpoint::ServerCtl;
use message::RPCError;

/// The server endpoint.
///
/// This is usually implemented by the end application and provides the actual functionality of the
/// RPC server. It allows composition of more servers together.
///
/// The default implementations of the callbacks return None, indicating that the given method is
/// not known. It allows implementing only RPCs or only notifications without having to worry about
/// the other callback. If you want a server that does nothing at all, use
/// [`Empty`](struct.Empty.html).
pub trait Server {
    /// The successfull result of the RPC call.
    type Success: Serialize;
    /// The result of the RPC call
    ///
    /// Once the future resolves, the value or error is sent to the client as the reply. The reply
    /// is wrapped automatically.
    type RPCCallResult: IntoFuture<Item = Self::Success, Error = RPCError>;
    /// The result of the RPC call.
    ///
    /// As the client doesn't expect anything in return, both the success and error results are
    /// thrown away and therefore (). However, it still makes sense to distinguish success and
    /// error.
    type NotificationResult: IntoFuture<Item = (), Error = ()> + 'static;
    /// Called when the client requests something.
    ///
    /// This is a callback from the [endpoint](struct.Endpoint.html) when the client requests
    /// something. If the method is unknown, it shall return `None`. This allows composition of
    /// servers.
    ///
    /// Conversion of parameters and handling of errors is up to the implementer of this trait.
    fn rpc(&self, _ctl: &ServerCtl, _method: &str, _params: &Option<Value>) -> Option<Self::RPCCallResult> {
        None
    }
    /// Called when the client sends a notification.
    ///
    /// This is a callback from the [endpoint](struct.Endpoint.html) when the client requests
    /// something. If the method is unknown, it shall return `None`. This allows composition of
    /// servers.
    ///
    /// Conversion of parameters and handling of errors is up to the implementer of this trait.
    fn notification(&self, _ctl: &ServerCtl, _method: &str, _params: &Option<Value>) -> Option<Self::NotificationResult> {
        None
    }
    /// Called when the endpoint is initialized.
    ///
    /// It provides a default empty implementation, which can be overriden to hook onto the
    /// initialization.
    fn initialized(&self, _ctl: &ServerCtl) {}
}

/// A RPC server that knows no methods.
///
/// You can use this if you want to have a client-only [Endpoint](struct.Endpoint.html). It simply
/// terminates the server part right away. Or, more conveniently, use `Endpoint`'s
/// [`client_only`](struct.Endpoint.html#method.client_only) method.
pub struct Empty;

impl Server for Empty{
    type Success = ();
    type RPCCallResult = Result<(), RPCError>;
    type NotificationResult = Result<(), ()>;
    fn initialized(&self, ctl: &ServerCtl) {
        ctl.terminate();
    }
}

#[cfg(test)]
mod tests {
    use futures::Future;

    use super::*;

    /// Check the empty server is somewhat sane.
    #[test]
    fn empty() {
        let server = Empty;
        let (ctl, dropped, _killed) = ServerCtl::new_test();
        // As we can't reasonably check all possible method names, do so for just a bunch
        for method in ["method", "notification", "check"].iter() {
            assert!(server.rpc(&ctl, method, &None).is_none());
            assert!(server.notification(&ctl, method, &None).is_none());
        }
        // It terminates the ctl on the server side on initialization
        server.initialized(&ctl);
        dropped.wait().unwrap();
    }
}
<commit_msg>The AbstractServer wrapper<commit_after>// Copyright 2017 tokio-jsonrpc Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! The [`Server`](trait.Server.html) trait and helpers.
//!
//! The `Server` trait for the use by the [`Endpoint`](../endpoint/struct.Endpoint.html) is defined
//! here. Furthermore, some helpers for convenient creation and composition of servers are
//! available. Note that not all of these helpers are necessarily zero-cost, at least at this time.

use futures::{Future, IntoFuture};
use serde::Serialize;
use serde_json::{Value, to_value};

use endpoint::ServerCtl;
use message::RPCError;

/// The server endpoint.
///
/// This is usually implemented by the end application and provides the actual functionality of the
/// RPC server. It allows composition of more servers together.
///
/// The default implementations of the callbacks return None, indicating that the given method is
/// not known. It allows implementing only RPCs or only notifications without having to worry about
/// the other callback. If you want a server that does nothing at all, use
/// [`Empty`](struct.Empty.html).
pub trait Server {
    /// The successfull result of the RPC call.
    type Success: Serialize;
    /// The result of the RPC call
    ///
    /// Once the future resolves, the value or error is sent to the client as the reply. The reply
    /// is wrapped automatically.
    type RPCCallResult: IntoFuture<Item = Self::Success, Error = RPCError> + 'static;
    /// The result of the RPC call.
    ///
    /// As the client doesn't expect anything in return, both the success and error results are
    /// thrown away and therefore (). However, it still makes sense to distinguish success and
    /// error.
    type NotificationResult: IntoFuture<Item = (), Error = ()> + 'static;
    /// Called when the client requests something.
    ///
    /// This is a callback from the [endpoint](struct.Endpoint.html) when the client requests
    /// something. If the method is unknown, it shall return `None`. This allows composition of
    /// servers.
    ///
    /// Conversion of parameters and handling of errors is up to the implementer of this trait.
    fn rpc(&self, _ctl: &ServerCtl, _method: &str, _params: &Option<Value>) -> Option<Self::RPCCallResult> {
        None
    }
    /// Called when the client sends a notification.
    ///
    /// This is a callback from the [endpoint](struct.Endpoint.html) when the client requests
    /// something. If the method is unknown, it shall return `None`. This allows composition of
    /// servers.
    ///
    /// Conversion of parameters and handling of errors is up to the implementer of this trait.
    fn notification(&self, _ctl: &ServerCtl, _method: &str, _params: &Option<Value>) -> Option<Self::NotificationResult> {
        None
    }
    /// Called when the endpoint is initialized.
    ///
    /// It provides a default empty implementation, which can be overriden to hook onto the
    /// initialization.
    fn initialized(&self, _ctl: &ServerCtl) {}
}

/// A RPC server that knows no methods.
///
/// You can use this if you want to have a client-only [Endpoint](struct.Endpoint.html). It simply
/// terminates the server part right away. Or, more conveniently, use `Endpoint`'s
/// [`client_only`](struct.Endpoint.html#method.client_only) method.
pub struct Empty;

impl Server for Empty {
    type Success = ();
    type RPCCallResult = Result<(), RPCError>;
    type NotificationResult = Result<(), ()>;
    fn initialized(&self, ctl: &ServerCtl) {
        ctl.terminate();
    }
}

/// An RPC server wrapper with dynamic dispatch.
///
/// This server wraps another server and converts it into a common ground, so multiple different
/// servers can be used as trait objects. Basically, it boxes the futures it returns and converts
/// the result into `serde_json::Value`. It can then be used together with
/// [`ServerChain`](struct.ServerChain.html) easilly. Note that this conversion incurs
/// runtime costs.
pub struct AbstractServer<S: Server>(S);

impl<S: Server> AbstractServer<S> {
    /// Wraps another server into an abstract server.
    pub fn new(server: S) -> Self {
        AbstractServer(server)
    }
    /// Unwraps the abstract server and provides the one inside back.
    pub fn into_inner(self) -> S {
        self.0
    }
}

impl<S: Server> Server for AbstractServer<S> {
    type Success = Value;
    type RPCCallResult = Box<Future<Item = Value, Error = RPCError>>;
    type NotificationResult = Box<Future<Item = (), Error = ()>>;
    fn rpc(&self, ctl: &ServerCtl, method: &str, params: &Option<Value>) -> Option<Self::RPCCallResult> {
        self.0.rpc(ctl, method, params).map(|f| -> Box<Future<Item = Value, Error = RPCError>> {
            Box::new(f.into_future().map(|result| to_value(result).expect("Your result type is not convertible to JSON, which is a bug")))
        })
    }
    fn notification(&self, ctl: &ServerCtl, method: &str, params: &Option<Value>) -> Option<Self::NotificationResult> {
        // It seems the type signature is computed from inside the closure and it doesn't fit on
        // the outside, so we need to declare it manually :-(
        self.0.notification(ctl, method, params).map(|f| -> Box<Future<Item = (), Error = ()>> {
            Box::new(f.into_future())
        })
    }
    fn initialized(&self, ctl: &ServerCtl) {
        self.0.initialized(ctl)
    }
}

#[cfg(test)]
mod tests {
    use std::cell::{Cell, RefCell};

    use super::*;

    /// Check the empty server is somewhat sane.
    #[test]
    fn empty() {
        let server = Empty;
        let (ctl, dropped, _killed) = ServerCtl::new_test();
        // As we can't reasonably check all possible method names, do so for just a bunch
        for method in ["method", "notification", "check"].iter() {
            assert!(server.rpc(&ctl, method, &None).is_none());
            assert!(server.notification(&ctl, method, &None).is_none());
        }
        // It terminates the ctl on the server side on initialization
        server.initialized(&ctl);
        dropped.wait().unwrap();
    }

    /// A server that logs what has been called.
    #[derive(Default, Debug, PartialEq)]
    struct LogServer {
        serial: Cell<usize>,
        rpc: RefCell<Vec<usize>>,
        notification: RefCell<Vec<usize>>,
        initialized: RefCell<Vec<usize>>,
    }

    impl LogServer {
        fn update(&self, what: &RefCell<Vec<usize>>) {
            let serial = self.serial.get() + 1;
            self.serial.set(serial);
            what.borrow_mut().push(serial);
        }
    }

    impl Server for LogServer {
        type Success = bool;
        type RPCCallResult = Result<bool, RPCError>;
        type NotificationResult = Result<(), ()>;
        fn rpc(&self, _ctl: &ServerCtl, method: &str, params: &Option<Value>) -> Option<Self::RPCCallResult> {
            self.update(&self.rpc);
            assert!(params.is_none());
            match method {
                "test" => Some(Ok(true)),
                _ => None
            }
        }
        fn notification(&self, _ctl: &ServerCtl, method: &str, params: &Option<Value>) -> Option<Self::NotificationResult> {
            self.update(&self.notification);
            assert!(params.is_none());
            match method {
                "notification" => Some(Ok(())),
                _ => None,
            }
        }
        fn initialized(&self, _ctl: &ServerCtl) {
            self.update(&self.initialized);
        }
    }

    /// Testing of the abstract server
    ///
    /// Just checking the data gets through and calling everything, there's nothing much to test
    /// anyway.
    #[test]
    fn abstract_server() {
        let log_server = LogServer::default();
        let abstract_server = AbstractServer::new(log_server);
        let (ctl, _, _) = ServerCtl::new_test();
        let rpc_result = abstract_server.rpc(&ctl, "test", &None).unwrap().wait().unwrap();
        assert_eq!(Value::Bool(true), rpc_result);
        abstract_server.notification(&ctl, "notification", &None).unwrap().wait().unwrap();
        assert!(abstract_server.rpc(&ctl, "another", &None).is_none());
        assert!(abstract_server.notification(&ctl, "another", &None).is_none());
        abstract_server.initialized(&ctl);
        let log_server = abstract_server.into_inner();
        let expected = LogServer {
            serial: Cell::new(5),
            rpc: RefCell::new(vec![1, 3]),
            notification: RefCell::new(vec![2, 4]),
            initialized: RefCell::new(vec![5]),
        };
        assert_eq!(expected, log_server);
    }
}
<|endoftext|>