<commit_before>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 rng = thread_rng();

        // RNG a position and direction, then make sure it's valid.
        loop {
            let pos = self.rng_pos();
            let 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) {
                let valid = self.valid_ship_position(&s);

                if valid {
                    break s;
                }
            }
        }
    }

    /// 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: &[[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) -> &[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) -> &[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>CPU player select available space with loop rather than while<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 pos: [u8; 2] = loop {
                let space = self.rng_pos();

                if self.space(&space).is_unchecked() {
                    break space;
                }
            };

            select.push(pos);
        }

        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 rng = thread_rng();

        // RNG a position and direction, then make sure it's valid.
        loop {
            let pos = self.rng_pos();
            let 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) {
                let valid = self.valid_ship_position(&s);

                if valid {
                    break s;
                }
            }
        }
    }

    /// 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: &[[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) -> &[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) -> &[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 xplane_plugin::*;
use xpgl::gl;
use xplm;

use xplm::ui::*;
use xplm::graphics::*;
use xplm::graphics::window::*;

use mapcore::map::Map;
use mapcore::layer::Layer;
use mapcore::{Latitude, Longitude, LatLon, LatLonRect, Polygon};
use mapcore::projection::Projection;
use mapcore::equirectangular::EquirectangularProjection;

use std::rc::Rc;
use std::cell::RefCell;
use std::mem;

/// The graphics state used for 2D drawing
const GRAPHICS_STATE_2D : GraphicsState = GraphicsState {
    fog: false,
    lighting: false,
    alpha_testing: false,
    alpha_blending: true,
    depth_testing: false,
    depth_writing: false,
    textures: 0,
};

pub struct TerrainDisplayPlugin {
    /// The window that displays graphics
    window: Option<Rc<RefCell<Window>>>,
}

impl Plugin for TerrainDisplayPlugin {
    fn start() -> Option<Self> {
        // Load OpenGL symbols
        gl::load_with(|name| {
            unsafe { mem::transmute(xplm::find_symbol(name)) }
        });

        Some(TerrainDisplayPlugin {
            window: None,
        })
    }
    fn enable(&mut self) {
        self.window = Some(Window::new(&Rect { left: 100, top: 1000, right: 400, bottom: 600 }));
        let mut map = Map::new(EquirectangularProjection);
        map.add_layer(TestLayer);
        {
            let local_draw_window = move |window: &mut Window| {

                let rect = window.get_geometry();

                set_state(&GRAPHICS_STATE_2D);
                unsafe {
                    gl::Color3f(0.0, 0.5, 0.0);
                    gl::Begin(gl::QUADS);
                    gl::Vertex2i(rect.left, rect.bottom);
                    gl::Vertex2i(rect.left, rect.top);
                    gl::Vertex2i(rect.right, rect.top);
                    gl::Vertex2i(rect.right, rect.bottom);
                    gl::End();
                }
                map.draw(rect.left, rect.bottom, (rect.right - rect.left), (rect.top - rect.bottom));
            };

            let mut window = self.window.as_mut().unwrap().borrow_mut();
            window.set_draw_callback(local_draw_window);
            window.set_visible(true);
        }
    }
    fn disable(&mut self) {
        self.window = None;
    }

    fn info<'a, 'b, 'c>(&self) -> PluginInfo<'a, 'b, 'c> {
        PluginInfo {
            name: "Terrain Display",
            signature: "org.samcrow.dev.terrain_display",
            description: "Draws a display of the terrain around the aircraft",
        }
    }

    fn stop(&mut self) {

    }
}

struct TestLayer;

impl Layer for TestLayer {
    fn draw(&self, projection: &Projection, x: i32, y: i32, width: i32, height: i32) {
        set_state(&GRAPHICS_STATE_2D);
        unsafe {
            gl::Color3f(0.8, 0.3, 0.0);
            gl::Begin(gl::QUADS);
            gl::Vertex2i(x, y);
            gl::Vertex2i(x , y + height);
            gl::Vertex2i(x + width, y + height);
            gl::Vertex2i(x + width, y);
            gl::End();
        }
        // Draw a projected latitude-longitude rectangle
        let poly = Polygon::new(&[LatLon{ latitude: Latitude(0.0), longitude: Longitude(0.0) },
                                LatLon{ latitude: Latitude(10.0), longitude: Longitude(0.0) },
                                LatLon{ latitude: Latitude(10.0), longitude: Longitude(10.0) },
                                LatLon{ latitude: Latitude(0.0), longitude: Longitude(10.0) }]);
        let projected = projection.project_poly(&poly);
        unsafe {
            gl::Color3f(0.0, 0.0, 1.0);
            gl::Begin(gl::QUADS);
            for point in projected.points() {
                gl::Vertex2d((x as f64) + point.x, (y as f64) + point.y);
            }
            gl::End();
        }
    }

    fn bounds(&self) -> Option<LatLonRect> {
        None
    }
}
<commit_msg>Added test world-spanning overlay<commit_after>use xplane_plugin::*;
use xpgl::gl;
use xplm;

use xplm::ui::*;
use xplm::graphics::*;
use xplm::graphics::window::*;

use mapcore::map::Map;
use mapcore::layer::Layer;
use mapcore::{Latitude, Longitude, LatLon, LatLonRect, Polygon};
use mapcore::projection::Projection;
use mapcore::equirectangular::EquirectangularProjection;

use std::rc::Rc;
use std::cell::RefCell;
use std::mem;

/// The graphics state used for 2D drawing
const GRAPHICS_STATE_2D : GraphicsState = GraphicsState {
    fog: false,
    lighting: false,
    alpha_testing: false,
    alpha_blending: true,
    depth_testing: false,
    depth_writing: false,
    textures: 0,
};

pub struct TerrainDisplayPlugin {
    /// The window that displays graphics
    window: Option<Rc<RefCell<Window>>>,
}

impl Plugin for TerrainDisplayPlugin {
    fn start() -> Option<Self> {
        // Load OpenGL symbols
        gl::load_with(|name| {
            unsafe { mem::transmute(xplm::find_symbol(name)) }
        });

        Some(TerrainDisplayPlugin {
            window: None,
        })
    }
    fn enable(&mut self) {
        self.window = Some(Window::new(&Rect { left: 100, top: 1000, right: 400, bottom: 600 }));
        let mut map = Map::new(EquirectangularProjection);
        map.add_layer(WorldLayer::new());
        map.add_layer(TestLayer);
        {
            let local_draw_window = move |window: &mut Window| {

                let rect = window.get_geometry();

                set_state(&GRAPHICS_STATE_2D);
                unsafe {
                    gl::Color3f(0.0, 0.5, 0.0);
                    gl::Begin(gl::QUADS);
                    gl::Vertex2i(rect.left, rect.bottom);
                    gl::Vertex2i(rect.left, rect.top);
                    gl::Vertex2i(rect.right, rect.top);
                    gl::Vertex2i(rect.right, rect.bottom);
                    gl::End();
                }
                map.draw(rect.left, rect.bottom, (rect.right - rect.left), (rect.top - rect.bottom));
            };

            let mut window = self.window.as_mut().unwrap().borrow_mut();
            window.set_draw_callback(local_draw_window);
            window.set_visible(true);
        }
    }
    fn disable(&mut self) {
        self.window = None;
    }

    fn info<'a, 'b, 'c>(&self) -> PluginInfo<'a, 'b, 'c> {
        PluginInfo {
            name: "Terrain Display",
            signature: "org.samcrow.dev.terrain_display",
            description: "Draws a display of the terrain around the aircraft",
        }
    }

    fn stop(&mut self) {

    }
}

struct TestLayer;

impl Layer for TestLayer {
    fn draw(&self, projection: &Projection, x: i32, y: i32, width: i32, height: i32) {
        set_state(&GRAPHICS_STATE_2D);
        // Draw a projected latitude-longitude rectangle
        let poly = Polygon::new(&[LatLon{ latitude: Latitude(37.41), longitude: Longitude(-122.29) },
                                LatLon{ latitude: Latitude(47.66), longitude: Longitude(-122.27) },
                                LatLon{ latitude: Latitude(51.507222), longitude: Longitude(-0.1275) },
                                LatLon{ latitude: Latitude(40.383333), longitude: Longitude(-3.716667) }]);
        let projected = projection.project_poly(&poly);
        unsafe {
            gl::Color3f(0.0, 0.0, 1.0);
            gl::Begin(gl::QUADS);
            for point in projected.points() {
                gl::Vertex2d((x as f64) + point.x, (y as f64) + point.y);
            }
            gl::End();
        }
    }

    fn bounds(&self) -> Option<LatLonRect> {
        None
    }
}

///
/// Draws a rectangle covering the whole world
///
struct WorldLayer {
    /// A Lat/Lon polygon that spans the world
    world: Polygon<LatLon>
}

impl WorldLayer {
    pub fn new() -> WorldLayer {
        WorldLayer {
            world: Polygon::new(&[LatLon{ latitude: Latitude(-90.0), longitude: Longitude(-180.0) },
                                    LatLon{ latitude: Latitude(90.0), longitude: Longitude(-180.0) },
                                    LatLon{ latitude: Latitude(90.0), longitude: Longitude(180.0) },
                                    LatLon{ latitude: Latitude(-90.0), longitude: Longitude(180.0) }]),
        }
    }
}

impl Layer for WorldLayer {
    fn draw(&self, projection: &Projection, x: i32, y: i32, width: i32, height: i32) {
        set_state(&GRAPHICS_STATE_2D);
        let projected = projection.project_poly(&self.world);
        unsafe {
            gl::Color3f(0.0, 0.5, 1.0);
            gl::Begin(gl::QUADS);
            for point in projected.points() {
                gl::Vertex2d((x as f64) + point.x, (y as f64) + point.y);
            }
            gl::End();
        }
    }

    fn bounds(&self) -> Option<LatLonRect> {
        None
    }
}
<|endoftext|>


<commit_before>use euclid::{self, vec2};
use space::{CellVector, Transformation, Space};
use std::collections::{hash_map, HashMap};
use std::error::Error;
use std::fmt;
use std::i32;

/// The text map character coordinate space.
struct TextSpace;

pub type TextVector = euclid::TypedVector2D<i32, TextSpace>;

// |  2 0 |
// | -1 1 |
//
// | 1/2 0 |
// | 1/2 1 |

impl Transformation for TextSpace {
    type Element = i32;

    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] {
        let v = v.into();
        [(v[0] + v[1]) / 2, v[1]]
    }

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] {
        let v = v.into();
        [2 * v[0] - v[1], v[1]]
    }
}

impl TextSpace {
    /// Which of the two possible map lattices is this vector in?
    pub fn in_even_lattice(v: TextVector) -> bool { (v.x + v.y) % 2 == 0 }
}

/// The on-screen minimap pixel coordinate space.
struct MinimapSpace;

// |  2  1 |
// | -2  1 |
//
// | 1/4  -1/4 |
// | 1/2   1/2 |

impl Transformation for MinimapSpace {
    type Element = i32;

    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] {
        let v = v.into();
        [v[0] / 4 + v[1] / 2, v[1] / 2 - v[0] / 4]
    }

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] {
        let v = v.into();
        [2 * v[0] - 2 * v[1], v[0] + v[1]]
    }
}

// Idea was there'd be both 'Floating' and 'Anchored' prefabs, but I don't think I actually have an
// use case for the 'Floating' one. So just using the 'Anchored' one and calling it a generic
// Prefab.

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum PrefabError {
    InvalidInput,
    MissingAnchor,
}

impl fmt::Display for PrefabError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
}

impl Error for PrefabError {
    fn description(&self) -> &str {
        match self {
            &PrefabError::InvalidInput => "Invalid input",
            &PrefabError::MissingAnchor => "Anchor not found in input",
        }
    }
}

// TODO: Once `std::convert::TryFrom` becomes stable, use that instead of this custom trait.

/// A trait for types that can be parsed into a map `Prefab`.
pub trait IntoPrefab<T> {
    fn try_into(self) -> Result<Prefab<T>, PrefabError>;
}

/// A piece of 2D map data with a fixed origin position.
///
/// # Examples
///
/// ```
/// # extern crate euclid;
/// # extern crate calx;
/// # fn main() {
///
/// use euclid::vec2;
/// use calx::{Prefab, IntoPrefab};
///
/// let string_map = Prefab::parse(r#"
///   1 2
///  3[4]5
///   6 7"#).expect("Failed to parse string map");
///
/// for &(c, p) in &[
///   ('1', (-1, -1)),
///   ('2', ( 0, -1)),
///   ('3', (-1,  0)),
///   ('4', ( 0,  0)),
///   ('5', ( 1,  0)),
///   ('6', ( 0,  1)),
///   ('7', ( 1,  1))] {
///     assert_eq!(Some(&c), string_map.get(vec2(p.0, p.1)));
/// }
///
/// # }
/// ```
#[derive(Clone)]
pub struct Prefab<T> {
    points: HashMap<CellVector, T>,
}

impl<T> Prefab<T> {
    pub fn parse<U: IntoPrefab<T>>(value: U) -> Result<Prefab<T>, PrefabError> { value.try_into() }

    pub fn get(&self, pos: CellVector) -> Option<&T> { self.points.get(&pos) }

    pub fn iter(&self) -> hash_map::Iter<CellVector, T> { self.points.iter() }
}

// Not using FromStr even when the input type is String, since there's a conversion family here
// where the sources can be strings or images and I want the interface to be uniform for both.

impl<S: Into<String>> IntoPrefab<char> for S {
    fn try_into(self) -> Result<Prefab<char>, PrefabError> {
        /// Recognize the point set that only contains the origin markup and return the
        /// corresponding origin value.
        fn is_origin(points: &HashMap<TextVector, char>) -> Option<TextVector> {
            if points.len() != 2 {
                return None;
            }
            let mut it = points.iter();
            let (mut p1, mut p2) = (it.next().unwrap(), it.next().unwrap());

            if p1.0.x > p2.0.x {
                ::std::mem::swap(&mut p1, &mut p2);
            }

            if *p2.0 - *p1.0 == vec2(2, 0) && *p1.1 == '[' && *p2.1 == ']' {
                Some(*p1.0 + vec2(1, 0))
            } else {
                None
            }
        }

        let value: String = self.into();

        let mut odds = HashMap::new();
        let mut evens = HashMap::new();
        for (y, line) in value.lines().enumerate() {
            for (x, c) in line.chars().enumerate() {
                if !c.is_whitespace() {
                    let vec = vec2(x as i32, y as i32);
                    if TextSpace::in_even_lattice(vec) {
                        evens.insert(vec, c);
                    } else {
                        odds.insert(vec, c);
                    }
                }
            }
        }

        let (offset, data) = if let Some(offset) = is_origin(&odds) {
            (offset, &evens)
        } else if let Some(offset) = is_origin(&evens) {
            (offset, &odds)
        } else {
            return Err(PrefabError::InvalidInput);
        };

        let mut points = HashMap::new();
        for text_data in data.iter() {
            // Set origin
            let text_pos = *text_data.0 - offset;
            // Store into cell space.
            points.insert(text_pos.to_cell_space(), *text_data.1);
        }

        Ok(Prefab { points })
    }
}

impl From<Prefab<char>> for String {
    fn from(prefab: Prefab<char>) -> String {
        // How far left does the textmap go?
        //
        // Subtract 1 from the result so that we can always fit the origin brackets on the leftmost
        // char if need be.
        let min_x = prefab
            .iter()
            .map(|(&pos, _)| TextVector::from_cell_space(pos).x)
            .min()
            .unwrap_or(0) - 1;

        unimplemented!();
    }
}
<commit_msg>Print text prefab as hexmap<commit_after>use euclid::{self, vec2};
use space::{CellVector, Transformation, Space};
use std::collections::{hash_map, HashMap};
use std::error::Error;
use std::fmt;
use std::i32;

/// The text map character coordinate space.
struct TextSpace;

pub type TextVector = euclid::TypedVector2D<i32, TextSpace>;

// |  2 0 |
// | -1 1 |
//
// | 1/2 0 |
// | 1/2 1 |

impl Transformation for TextSpace {
    type Element = i32;

    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] {
        let v = v.into();
        [(v[0] + v[1]) / 2, v[1]]
    }

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] {
        let v = v.into();
        [2 * v[0] - v[1], v[1]]
    }
}

impl TextSpace {
    /// Which of the two possible map lattices is this vector in?
    pub fn in_even_lattice(v: TextVector) -> bool { (v.x + v.y) % 2 == 0 }
}

/// The on-screen minimap pixel coordinate space.
struct MinimapSpace;

// |  2  1 |
// | -2  1 |
//
// | 1/4  -1/4 |
// | 1/2   1/2 |

impl Transformation for MinimapSpace {
    type Element = i32;

    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] {
        let v = v.into();
        [v[0] / 4 + v[1] / 2, v[1] / 2 - v[0] / 4]
    }

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] {
        let v = v.into();
        [2 * v[0] - 2 * v[1], v[0] + v[1]]
    }
}

// Idea was there'd be both 'Floating' and 'Anchored' prefabs, but I don't think I actually have an
// use case for the 'Floating' one. So just using the 'Anchored' one and calling it a generic
// Prefab.

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum PrefabError {
    InvalidInput,
    MissingAnchor,
}

impl fmt::Display for PrefabError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
}

impl Error for PrefabError {
    fn description(&self) -> &str {
        match self {
            &PrefabError::InvalidInput => "Invalid input",
            &PrefabError::MissingAnchor => "Anchor not found in input",
        }
    }
}

// TODO: Once `std::convert::TryFrom` becomes stable, use that instead of this custom trait.

/// A trait for types that can be parsed into a map `Prefab`.
pub trait IntoPrefab<T> {
    fn try_into(self) -> Result<Prefab<T>, PrefabError>;
}

/// A piece of 2D map data with a fixed origin position.
///
/// # Examples
///
/// ```
/// # extern crate euclid;
/// # extern crate calx;
/// # fn main() {
///
/// use euclid::vec2;
/// use calx::{Prefab, IntoPrefab};
///
/// let string_map = Prefab::parse(r#"
///   1 2
///  3[4]5
///   6 7"#).expect("Failed to parse string map");
///
/// for &(c, p) in &[
///   ('1', (-1, -1)),
///   ('2', ( 0, -1)),
///   ('3', (-1,  0)),
///   ('4', ( 0,  0)),
///   ('5', ( 1,  0)),
///   ('6', ( 0,  1)),
///   ('7', ( 1,  1))] {
///     assert_eq!(Some(&c), string_map.get(vec2(p.0, p.1)));
/// }
///
/// # }
/// ```
#[derive(Clone)]
pub struct Prefab<T> {
    points: HashMap<CellVector, T>,
}

impl<T> Prefab<T> {
    pub fn parse<U: IntoPrefab<T>>(value: U) -> Result<Prefab<T>, PrefabError> { value.try_into() }

    pub fn get(&self, pos: CellVector) -> Option<&T> { self.points.get(&pos) }

    pub fn iter(&self) -> hash_map::Iter<CellVector, T> { self.points.iter() }
}

// Not using FromStr even when the input type is String, since there's a conversion family here
// where the sources can be strings or images and I want the interface to be uniform for both.

impl<S: Into<String>> IntoPrefab<char> for S {
    fn try_into(self) -> Result<Prefab<char>, PrefabError> {
        /// Recognize the point set that only contains the origin markup and return the
        /// corresponding origin value.
        fn is_origin(points: &HashMap<TextVector, char>) -> Option<TextVector> {
            if points.len() != 2 {
                return None;
            }
            let mut it = points.iter();
            let (mut p1, mut p2) = (it.next().unwrap(), it.next().unwrap());

            if p1.0.x > p2.0.x {
                ::std::mem::swap(&mut p1, &mut p2);
            }

            if *p2.0 - *p1.0 == vec2(2, 0) && *p1.1 == '[' && *p2.1 == ']' {
                Some(*p1.0 + vec2(1, 0))
            } else {
                None
            }
        }

        let value: String = self.into();

        let mut odds = HashMap::new();
        let mut evens = HashMap::new();
        for (y, line) in value.lines().enumerate() {
            for (x, c) in line.chars().enumerate() {
                if !c.is_whitespace() {
                    let vec = vec2(x as i32, y as i32);
                    if TextSpace::in_even_lattice(vec) {
                        evens.insert(vec, c);
                    } else {
                        odds.insert(vec, c);
                    }
                }
            }
        }

        let (offset, data) = if let Some(offset) = is_origin(&odds) {
            (offset, &evens)
        } else if let Some(offset) = is_origin(&evens) {
            (offset, &odds)
        } else {
            return Err(PrefabError::InvalidInput);
        };

        let mut points = HashMap::new();
        for text_data in data.iter() {
            // Set origin
            let text_pos = *text_data.0 - offset;
            // Store into cell space.
            points.insert(text_pos.to_cell_space(), *text_data.1);
        }

        Ok(Prefab { points })
    }
}

impl fmt::Display for Prefab<char> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let dummy_origin: (&CellVector, &char) = (&vec2(0, 0), &' ');
        // Ensure that the origin cell shows up in the printout even if it's blank in the prefab.
        let append = if self.iter().find(|&(&pos, _)| pos == vec2(0, 0)).is_none() {
            Some(dummy_origin)
        } else {
            None
        };

        // How far left does the textmap go?
        //
        // Subtract 1 from the result so that we can always fit the origin brackets on the leftmost
        // char if need be.
        let min_x = self.iter()
            .chain(append)
            .map(|(&pos, _)| TextVector::from_cell_space(pos).x)
            .min()
            .unwrap_or(0) - 1;

        // Arrange cells in print order.
        let mut sorted: Vec<(TextVector, char)> = self.iter()
            .chain(append)
            .map(|(&pos, &c)| (TextVector::from_cell_space(pos), c))
            .collect();

        sorted.sort_by(|a, b| (a.0.y, a.0.x).cmp(&(b.0.y, b.0.x)));

        if sorted.len() == 0 {
            return Ok(());
        }

        // Printing position.
        let mut print_y = sorted[0].0.y;
        let mut print_x = min_x;

        for &(pos, c) in &sorted {
            while print_y < pos.y {
                writeln!(f, "")?;
                print_x = min_x;
                print_y += 1;
            }

            while print_x < pos.x - 1 {
                write!(f, " ")?;
                print_x += 1;
            }

            debug_assert_eq!(print_y, pos.y);
            if pos == vec2(0, 0) {
                // Write origin markers around origin cell.
                debug_assert_eq!(print_x, pos.x - 1);
                write!(f, "[{}]", c)?;
                print_x += 3;
            } else {
                // Print x should be in pos.x - 1, except in the case right after the origin marker
                // when it's in pos.x.
                debug_assert!(print_x == pos.x - 1 || print_x == pos.x);
                if print_x < pos.x {
                    write!(f, " ")?;
                    print_x += 1;
                }
                write!(f, "{}", c)?;
                print_x += 1;
            }
        }

        Ok(())
    }
}

impl From<Prefab<char>> for String {
    fn from(prefab: Prefab<char>) -> String { format!("{}", prefab) }
}
<|endoftext|>


<commit_before>/// experimental module to measure buffer pool usage
///
/// this allows us to track how many buffers are used through the
/// buffers.count metric.
///
/// Right now, we wrap the `pool` crate, but we might write a different
/// buffer pool in the future, so this module will still be useful to
/// test the differences

use pool_crate;
use std::ops;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

static BUFFER_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;

pub type Reset = pool_crate::Reset;

pub struct Pool<T:pool_crate::Reset> {
  pub inner: pool_crate::Pool<T>,
}

impl<T: pool_crate::Reset> Pool<T> {
  pub fn with_capacity<F>(count: usize, mut extra: usize, init: F) -> Pool<T>
    where F: Fn() -> T {
    Pool {
      inner: pool_crate::Pool::with_capacity(count, extra, init),
    }
  }

  pub fn checkout(&mut self) -> Option<Checkout<T>> {
    self.inner.checkout().map(|c| {
      let old_buffer_count = BUFFER_COUNT.fetch_add(1, Ordering::SeqCst);
      gauge!("buffer.count", old_buffer_count + 1);
      Checkout {
        inner: c
      }
    })
  }
}

impl<T: pool_crate::Reset> ops::Deref for Pool<T> {
  type Target = pool_crate::Pool<T>;

  fn deref(&self) -> &Self::Target {
    &self.inner
  }
}

impl<T: pool_crate::Reset> ops::DerefMut for Pool<T> {
  fn deref_mut(&mut self) -> &mut pool_crate::Pool<T> {
    &mut self.inner
  }
}

pub struct Checkout<T> {
  pub inner: pool_crate::Checkout<T>,
}

impl<T> ops::Deref for Checkout<T> {
    type Target = pool_crate::Checkout<T>;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T> ops::DerefMut for Checkout<T> {
    fn deref_mut(&mut self) -> &mut pool_crate::Checkout<T> {
        &mut self.inner
    }
}

impl<T> Drop for Checkout<T> {
    fn drop(&mut self) {
      let old_buffer_count = BUFFER_COUNT.fetch_sub(1, Ordering::SeqCst);
      gauge!("buffer.count", old_buffer_count - 1);
    }
}

unsafe impl<T: Send> Send for Checkout<T> { }
unsafe impl<T: Sync> Sync for Checkout<T> { }
<commit_msg>remove warning<commit_after>/// experimental module to measure buffer pool usage
///
/// this allows us to track how many buffers are used through the
/// buffers.count metric.
///
/// Right now, we wrap the `pool` crate, but we might write a different
/// buffer pool in the future, so this module will still be useful to
/// test the differences

use pool_crate;
use std::ops;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

static BUFFER_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;

pub type Reset = pool_crate::Reset;

pub struct Pool<T:pool_crate::Reset> {
  pub inner: pool_crate::Pool<T>,
}

impl<T: pool_crate::Reset> Pool<T> {
  pub fn with_capacity<F>(count: usize, extra: usize, init: F) -> Pool<T>
    where F: Fn() -> T {
    Pool {
      inner: pool_crate::Pool::with_capacity(count, extra, init),
    }
  }

  pub fn checkout(&mut self) -> Option<Checkout<T>> {
    self.inner.checkout().map(|c| {
      let old_buffer_count = BUFFER_COUNT.fetch_add(1, Ordering::SeqCst);
      gauge!("buffer.count", old_buffer_count + 1);
      Checkout {
        inner: c
      }
    })
  }
}

impl<T: pool_crate::Reset> ops::Deref for Pool<T> {
  type Target = pool_crate::Pool<T>;

  fn deref(&self) -> &Self::Target {
    &self.inner
  }
}

impl<T: pool_crate::Reset> ops::DerefMut for Pool<T> {
  fn deref_mut(&mut self) -> &mut pool_crate::Pool<T> {
    &mut self.inner
  }
}

pub struct Checkout<T> {
  pub inner: pool_crate::Checkout<T>,
}

impl<T> ops::Deref for Checkout<T> {
    type Target = pool_crate::Checkout<T>;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T> ops::DerefMut for Checkout<T> {
    fn deref_mut(&mut self) -> &mut pool_crate::Checkout<T> {
        &mut self.inner
    }
}

impl<T> Drop for Checkout<T> {
    fn drop(&mut self) {
      let old_buffer_count = BUFFER_COUNT.fetch_sub(1, Ordering::SeqCst);
      gauge!("buffer.count", old_buffer_count - 1);
    }
}

unsafe impl<T: Send> Send for Checkout<T> { }
unsafe impl<T: Sync> Sync for Checkout<T> { }
<|endoftext|>


<commit_before>//! Read points from las files.

use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::path::Path;

use byteorder::LittleEndian;
use byteorder::ReadBytesExt;

use num::FromPrimitive;

use Result;
use header::Header;
use point::Classification;
use point::Point;
use point::ScanDirection;

pub struct Reader<R: Read + Seek> {
    header: Header,
    reader: R,
}

impl<R: Read + Seek> Reader<R> {
    /// Creates a reader for a `Read` object.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// let stream = std::fs::File::open("data/1.2_0.las").unwrap();
    /// let reader = Reader::new(stream);
    /// ```
    pub fn new(mut reader: R) -> Result<Reader<R>> {
        Ok(Reader {
            header: try!(Header::new(&mut reader)),
            reader: reader,
        })
    }

    /// Returns the `Header`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// let reader = Reader::open("data/1.2_0.las").unwrap();
    /// let header = reader.header();
    /// assert_eq!(*b"LASF", header.file_signature);
    /// ```
    pub fn header(&self) -> &Header { &self.header }

    /// Returns a vector of all the points in the lasfile.
    ///
    /// Only use this method if you really do want to load all the points into memory at once.
    /// Otherwise, use the provided iterator methods to scan through the points in a more efficient
    /// manner.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// let mut reader = Reader::open("data/1.2_0.las").unwrap();
    /// let points = reader.points().unwrap();
    /// assert_eq!(1, points.len());
    /// ```
    pub fn points(&mut self) -> Result<Vec<Point>> {
        Ok(try!(self.points_iter()).collect())
    }

    /// Creates an interator over this reader's points.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// # use las::point::Point;
    /// let mut reader = Reader::open("data/1.2_0.las").unwrap();
    /// let points: Vec<Point> = reader.points_iter().unwrap().collect();
    /// assert_eq!(1, points.len());
    /// ```
    pub fn points_iter(&mut self) -> Result<PointsIterator<R>> {
        try!(self.reader.seek(SeekFrom::Start(self.header.offset_to_point_data as u64)));
        Ok(PointsIterator {
            reader: self,
        })
    }

    /// Reads and returns the next point from the reader.
    fn next_point(&mut self) -> Result<Point> {
        let mut point: Point = Default::default();
        point.x = try!(self.reader.read_u32::<LittleEndian>()) as f64 * self.header.scale.x +
            self.header.offset.x;
        point.y = try!(self.reader.read_u32::<LittleEndian>()) as f64 * self.header.scale.y +
            self.header.offset.y;
        point.z = try!(self.reader.read_u32::<LittleEndian>()) as f64 * self.header.scale.z +
            self.header.offset.z;
        point.intensity = try!(self.reader.read_u16::<LittleEndian>());
        let byte = try!(self.reader.read_u8());
        point.return_number = byte & 0b00000111;
        point.number_of_returns = byte >> 3 & 0b00000111;
        point.scan_direction = match ScanDirection::from_u8(byte >> 6 & 0b00000001) {
            Some(scan_direction) => scan_direction,
            None => unreachable!(),
        };
        point.edge_of_flight_line = (byte >> 7 & 0b00000001) == 1;
        point.classification = match Classification::from_u8(try!(self.reader.read_u8())) {
            Some(classification) => classification,
            None => Default::default(),
        };
        point.scan_angle_rank = try!(self.reader.read_i8());
        point.user_data = try!(self.reader.read_u8());
        point.point_source_id = try!(self.reader.read_u16::<LittleEndian>());

        match self.header.point_data_format_id {
            1 | 3 => point.gps_time = Some(try!(self.reader.read_f64::<LittleEndian>())),
            _ => (),
        }

        match self.header.point_data_format_id {
            2 | 3 => {
                point.red = Some(try!(self.reader.read_u16::<LittleEndian>()));
                point.green = Some(try!(self.reader.read_u16::<LittleEndian>()));
                point.blue = Some(try!(self.reader.read_u16::<LittleEndian>()));
            },
            _ => (),
        }

        Ok(point)
    }
}

impl Reader<File> {
    /// Opens a reader for a given file path.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// let reader = Reader::open("data/1.2_0.las");
    /// ```
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Reader<File>> {
        // TODO wrap in BufRead
        let reader = try!(File::open(path));
        Ok(try!(Reader::new(reader)))
    }
}

/// Iterator over the points of a reader.
///
/// The iterator starts at the first point and reads through to the end.
pub struct PointsIterator<'a, R: 'a + Read + Seek> {
    reader: &'a mut Reader<R>,
}

impl<'a, R: Read + Seek> Iterator for PointsIterator<'a, R> {
    type Item = Point;

    fn next(&mut self) -> Option<Point> {
        self.reader.next_point().ok()
    }
}

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

    use point::Classification;
    use point::Point;
    use point::ScanDirection;

    const POINT0: Point = Point {
        x: 470692.44,
        y: 4602888.90,
        z: 16.0,
        intensity: 0,
        return_number: 2,
        number_of_returns: 0,
        scan_direction: ScanDirection::Backward,
        edge_of_flight_line: false,
        classification: Classification::Ground,
        scan_angle_rank: -13,
        user_data: 0,
        point_source_id: 0,
        gps_time: None,
        red: None,
        green: None,
        blue: None,
    };

    const POINT1: Point = Point {
        x: 470692.44,
        y: 4602888.90,
        z: 16.0,
        intensity: 0,
        return_number: 2,
        number_of_returns: 0,
        scan_direction: ScanDirection::Backward,
        edge_of_flight_line: false,
        classification: Classification::Ground,
        scan_angle_rank: -13,
        user_data: 0,
        point_source_id: 0,
        gps_time: Some(1205902800.0),
        red: None,
        green: None,
        blue: None,
    };

    const POINT2: Point = Point {
        x: 470692.44,
        y: 4602888.90,
        z: 16.0,
        intensity: 0,
        return_number: 2,
        number_of_returns: 0,
        scan_direction: ScanDirection::Backward,
        edge_of_flight_line: false,
        classification: Classification::Ground,
        scan_angle_rank: -13,
        user_data: 0,
        point_source_id: 0,
        gps_time: None,
        red: Some(255),
        green: Some(12),
        blue: Some(234),
    };

    const POINT3: Point = Point {
        x: 470692.44,
        y: 4602888.90,
        z: 16.0,
        intensity: 0,
        return_number: 2,
        number_of_returns: 0,
        scan_direction: ScanDirection::Backward,
        edge_of_flight_line: false,
        classification: Classification::Ground,
        scan_angle_rank: -13,
        user_data: 0,
        point_source_id: 0,
        gps_time: Some(1205902800.0),
        red: Some(255),
        green: Some(12),
        blue: Some(234),
    };

    fn check_file(filename: &str, reference_point: &Point) {
        let mut reader = Reader::open(filename).unwrap();
        let point = &reader.points().unwrap()[0];
        assert_eq!(reference_point, point);
    }

    #[test]
    fn header() {
        let reader = Reader::open("data/1.2_0.las").unwrap();
        let header = reader.header();
        assert_eq!(*b"LASF", header.file_signature);
        assert_eq!(0, header.file_source_id);
        assert_eq!(0, header.global_encoding);
        assert_eq!("b8f18883-1baa-0841-bca3-6bc68e7b062e", header.project_id.as_string());
        assert_eq!(1, header.version_major);
        assert_eq!(2, header.version_minor);
        assert_eq!("libLAS", header.system_identifier);
        assert_eq!("libLAS 1.2", header.generating_software);
        assert_eq!(78, header.file_creation_day_of_year);
        assert_eq!(2008, header.file_creation_year);
        assert_eq!(227, header.header_size);
        assert_eq!(438, header.offset_to_point_data);
        assert_eq!(2, header.number_of_variable_length_records);
        assert_eq!(0, header.point_data_format_id);
        assert_eq!(20, header.point_data_record_length);
        assert_eq!(1, header.number_of_point_records);
        assert_eq!([0, 1, 0, 0, 0], header.number_of_points_by_return);
        assert_eq!(0.01, header.scale.x);
        assert_eq!(0.01, header.scale.y);
        assert_eq!(0.01, header.scale.z);
        assert_eq!(0.0, header.offset.x);
        assert_eq!(0.0, header.offset.y);
        assert_eq!(0.0, header.offset.z);
        assert_eq!(470692.447538, header.min.x);
        assert_eq!(4602888.904642, header.min.y);
        assert_eq!(16.0, header.min.z);
        assert_eq!(470692.447538, header.max.x);
        assert_eq!(4602888.904642, header.max.y);
        assert_eq!(16.0, header.max.z);
    }

    #[test]
    fn points() {
        let mut reader = Reader::open("data/1.2_0.las").unwrap();
        let points: Vec<Point> = reader.points_iter().unwrap().collect();
        assert_eq!(1, points.len());
    }

    #[test]
    fn formats_and_versions() {
        check_file("data/1.0_0.las", &POINT0);
        check_file("data/1.1_0.las", &POINT0);
        check_file("data/1.2_0.las", &POINT0);

        check_file("data/1.0_1.las", &POINT1);
        check_file("data/1.1_1.las", &POINT1);
        check_file("data/1.2_1.las", &POINT1);

        check_file("data/1.2_2.las", &POINT2);

        check_file("data/1.2_3.las", &POINT3);
    }
}
<commit_msg>Specialize open for buf reader<commit_after>//! Read points from las files.

use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::path::Path;

use byteorder::LittleEndian;
use byteorder::ReadBytesExt;

use num::FromPrimitive;

use Result;
use header::Header;
use point::Classification;
use point::Point;
use point::ScanDirection;

pub struct Reader<R: Read + Seek> {
    header: Header,
    reader: R,
}

impl<R: Read + Seek> Reader<R> {
    /// Creates a reader for a `Read` object.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// let stream = std::fs::File::open("data/1.2_0.las").unwrap();
    /// let reader = Reader::new(stream);
    /// ```
    pub fn new(mut reader: R) -> Result<Reader<R>> {
        Ok(Reader {
            header: try!(Header::new(&mut reader)),
            reader: reader,
        })
    }

    /// Returns the `Header`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// let reader = Reader::open("data/1.2_0.las").unwrap();
    /// let header = reader.header();
    /// assert_eq!(*b"LASF", header.file_signature);
    /// ```
    pub fn header(&self) -> &Header { &self.header }

    /// Returns a vector of all the points in the lasfile.
    ///
    /// Only use this method if you really do want to load all the points into memory at once.
    /// Otherwise, use the provided iterator methods to scan through the points in a more efficient
    /// manner.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// let mut reader = Reader::open("data/1.2_0.las").unwrap();
    /// let points = reader.points().unwrap();
    /// assert_eq!(1, points.len());
    /// ```
    pub fn points(&mut self) -> Result<Vec<Point>> {
        Ok(try!(self.points_iter()).collect())
    }

    /// Creates an interator over this reader's points.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// # use las::point::Point;
    /// let mut reader = Reader::open("data/1.2_0.las").unwrap();
    /// let points: Vec<Point> = reader.points_iter().unwrap().collect();
    /// assert_eq!(1, points.len());
    /// ```
    pub fn points_iter(&mut self) -> Result<PointsIterator<R>> {
        try!(self.reader.seek(SeekFrom::Start(self.header.offset_to_point_data as u64)));
        Ok(PointsIterator {
            reader: self,
        })
    }

    /// Reads and returns the next point from the reader.
    fn next_point(&mut self) -> Result<Point> {
        let mut point: Point = Default::default();
        point.x = try!(self.reader.read_u32::<LittleEndian>()) as f64 * self.header.scale.x +
            self.header.offset.x;
        point.y = try!(self.reader.read_u32::<LittleEndian>()) as f64 * self.header.scale.y +
            self.header.offset.y;
        point.z = try!(self.reader.read_u32::<LittleEndian>()) as f64 * self.header.scale.z +
            self.header.offset.z;
        point.intensity = try!(self.reader.read_u16::<LittleEndian>());
        let byte = try!(self.reader.read_u8());
        point.return_number = byte & 0b00000111;
        point.number_of_returns = byte >> 3 & 0b00000111;
        point.scan_direction = match ScanDirection::from_u8(byte >> 6 & 0b00000001) {
            Some(scan_direction) => scan_direction,
            None => unreachable!(),
        };
        point.edge_of_flight_line = (byte >> 7 & 0b00000001) == 1;
        point.classification = match Classification::from_u8(try!(self.reader.read_u8())) {
            Some(classification) => classification,
            None => Default::default(),
        };
        point.scan_angle_rank = try!(self.reader.read_i8());
        point.user_data = try!(self.reader.read_u8());
        point.point_source_id = try!(self.reader.read_u16::<LittleEndian>());

        match self.header.point_data_format_id {
            1 | 3 => point.gps_time = Some(try!(self.reader.read_f64::<LittleEndian>())),
            _ => (),
        }

        match self.header.point_data_format_id {
            2 | 3 => {
                point.red = Some(try!(self.reader.read_u16::<LittleEndian>()));
                point.green = Some(try!(self.reader.read_u16::<LittleEndian>()));
                point.blue = Some(try!(self.reader.read_u16::<LittleEndian>()));
            },
            _ => (),
        }

        Ok(point)
    }
}

impl Reader<BufReader<File>> {
    /// Opens a reader for a given file path.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::reader::Reader;
    /// let reader = Reader::open("data/1.2_0.las");
    /// ```
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Reader<BufReader<File>>> {
        let reader = try!(File::open(path));
        Ok(try!(Reader::new(BufReader::new(reader))))
    }
}

/// Iterator over the points of a reader.
///
/// The iterator starts at the first point and reads through to the end.
pub struct PointsIterator<'a, R: 'a + Read + Seek> {
    reader: &'a mut Reader<R>,
}

impl<'a, R: Read + Seek> Iterator for PointsIterator<'a, R> {
    type Item = Point;

    fn next(&mut self) -> Option<Point> {
        self.reader.next_point().ok()
    }
}

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

    use point::Classification;
    use point::Point;
    use point::ScanDirection;

    const POINT0: Point = Point {
        x: 470692.44,
        y: 4602888.90,
        z: 16.0,
        intensity: 0,
        return_number: 2,
        number_of_returns: 0,
        scan_direction: ScanDirection::Backward,
        edge_of_flight_line: false,
        classification: Classification::Ground,
        scan_angle_rank: -13,
        user_data: 0,
        point_source_id: 0,
        gps_time: None,
        red: None,
        green: None,
        blue: None,
    };

    const POINT1: Point = Point {
        x: 470692.44,
        y: 4602888.90,
        z: 16.0,
        intensity: 0,
        return_number: 2,
        number_of_returns: 0,
        scan_direction: ScanDirection::Backward,
        edge_of_flight_line: false,
        classification: Classification::Ground,
        scan_angle_rank: -13,
        user_data: 0,
        point_source_id: 0,
        gps_time: Some(1205902800.0),
        red: None,
        green: None,
        blue: None,
    };

    const POINT2: Point = Point {
        x: 470692.44,
        y: 4602888.90,
        z: 16.0,
        intensity: 0,
        return_number: 2,
        number_of_returns: 0,
        scan_direction: ScanDirection::Backward,
        edge_of_flight_line: false,
        classification: Classification::Ground,
        scan_angle_rank: -13,
        user_data: 0,
        point_source_id: 0,
        gps_time: None,
        red: Some(255),
        green: Some(12),
        blue: Some(234),
    };

    const POINT3: Point = Point {
        x: 470692.44,
        y: 4602888.90,
        z: 16.0,
        intensity: 0,
        return_number: 2,
        number_of_returns: 0,
        scan_direction: ScanDirection::Backward,
        edge_of_flight_line: false,
        classification: Classification::Ground,
        scan_angle_rank: -13,
        user_data: 0,
        point_source_id: 0,
        gps_time: Some(1205902800.0),
        red: Some(255),
        green: Some(12),
        blue: Some(234),
    };

    fn check_file(filename: &str, reference_point: &Point) {
        let mut reader = Reader::open(filename).unwrap();
        let point = &reader.points().unwrap()[0];
        assert_eq!(reference_point, point);
    }

    #[test]
    fn header() {
        let reader = Reader::open("data/1.2_0.las").unwrap();
        let header = reader.header();
        assert_eq!(*b"LASF", header.file_signature);
        assert_eq!(0, header.file_source_id);
        assert_eq!(0, header.global_encoding);
        assert_eq!("b8f18883-1baa-0841-bca3-6bc68e7b062e", header.project_id.as_string());
        assert_eq!(1, header.version_major);
        assert_eq!(2, header.version_minor);
        assert_eq!("libLAS", header.system_identifier);
        assert_eq!("libLAS 1.2", header.generating_software);
        assert_eq!(78, header.file_creation_day_of_year);
        assert_eq!(2008, header.file_creation_year);
        assert_eq!(227, header.header_size);
        assert_eq!(438, header.offset_to_point_data);
        assert_eq!(2, header.number_of_variable_length_records);
        assert_eq!(0, header.point_data_format_id);
        assert_eq!(20, header.point_data_record_length);
        assert_eq!(1, header.number_of_point_records);
        assert_eq!([0, 1, 0, 0, 0], header.number_of_points_by_return);
        assert_eq!(0.01, header.scale.x);
        assert_eq!(0.01, header.scale.y);
        assert_eq!(0.01, header.scale.z);
        assert_eq!(0.0, header.offset.x);
        assert_eq!(0.0, header.offset.y);
        assert_eq!(0.0, header.offset.z);
        assert_eq!(470692.447538, header.min.x);
        assert_eq!(4602888.904642, header.min.y);
        assert_eq!(16.0, header.min.z);
        assert_eq!(470692.447538, header.max.x);
        assert_eq!(4602888.904642, header.max.y);
        assert_eq!(16.0, header.max.z);
    }

    #[test]
    fn points() {
        let mut reader = Reader::open("data/1.2_0.las").unwrap();
        let points: Vec<Point> = reader.points_iter().unwrap().collect();
        assert_eq!(1, points.len());
    }

    #[test]
    fn formats_and_versions() {
        check_file("data/1.0_0.las", &POINT0);
        check_file("data/1.1_0.las", &POINT0);
        check_file("data/1.2_0.las", &POINT0);

        check_file("data/1.0_1.las", &POINT1);
        check_file("data/1.1_1.las", &POINT1);
        check_file("data/1.2_1.las", &POINT1);

        check_file("data/1.2_2.las", &POINT2);

        check_file("data/1.2_3.las", &POINT3);
    }
}
<|endoftext|>


<commit_before>use regex::{Regex};
use std::str::{FromStr};

use super::metrics::*;

pub trait LogLineReader: Send + Sync {
    fn read(&self, &str) -> Vec<Metric>;
}

pub struct StandardLogLineReader;

lazy_static! {
    static ref LOG_MEASURE_REGEX: Regex =
        Regex::new(r"measure#([a-zA-Z0-9._]+)=(\d+)").unwrap();

    static ref LOG_COUNT_REGEX: Regex =
        Regex::new(r"count#([a-zA-Z0-9._]+)=(\d+)").unwrap();
}

impl LogLineReader for StandardLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        let mut metrics = vec![];

        // Look for measures
        for cap in LOG_MEASURE_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = f64::from_str(cap.at(2).unwrap()) {
                metrics.push(Measure(name.to_owned(), value))
            }
        }

        // Look for counts
        for cap in LOG_COUNT_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = u64::from_str(cap.at(2).unwrap()) {
                metrics.push(Count(name.to_owned(), value))
            }
        }

        metrics
    } // fn read
}
<commit_msg>Improve regex in log line reader<commit_after>use regex::{Regex};
use std::str::{FromStr};

use super::metrics::*;

pub trait LogLineReader: Send + Sync {
    fn read(&self, &str) -> Vec<Metric>;
}

pub struct StandardLogLineReader;

lazy_static! {
    static ref LOG_MEASURE_REGEX: Regex =
        Regex::new(r"measure#([a-zA-Z0-9._]+)=(\d+(?:\.\d+))").unwrap();

    static ref LOG_COUNT_REGEX: Regex =
        Regex::new(r"count#([a-zA-Z0-9._]+)=(\d+)").unwrap();
}

impl LogLineReader for StandardLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        let mut metrics = vec![];

        // Look for measures
        for cap in LOG_MEASURE_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = f64::from_str(cap.at(2).unwrap()) {
                metrics.push(Measure(name.to_owned(), value))
            }
        }

        // Look for counts
        for cap in LOG_COUNT_REGEX.captures_iter(line) {
            let name = cap.at(1).unwrap();

            if let Ok(value) = u64::from_str(cap.at(2).unwrap()) {
                metrics.push(Count(name.to_owned(), value))
            }
        }

        metrics
    } // fn read
}
<|endoftext|>


<commit_before>//!Bindings and rewrites of include/linux/time.h and include/linux/time64.h

extern crate linux_api;

use linux_api::*;

///Checks to see if two timespecs are equal
pub fn timespec_equal(a: &timespec, b: &timespec) -> c_int {
    return ((a.tv_sec == b.tv_sec) && (a.tv_nsec == b.tv_nsec)) as c_int;
}

///Compares two timespecs
///
///If lhs < rhs, return < 0
///If lhs == rhs, return 0
///If lhs > rhs, return > 0
pub fn timespec_compare(lhs: &timespec, rhs: &timespec) -> c_int {
    if lhs.tv_sec < rhs.tv_sec { return -1; }
    if lhs.tv_sec > rhs.tv_sec { return 1; }
    return (lhs.tv_nsec - rhs.tv_nsec) as c_int;
}

///Compares two timevals
///
///If lhs < rhs, return < 0
///If lhs == rhs, return 0
///If lhs > rhs, return > 0
pub fn timeval_compare(lhs: &timeval, rhs: &timeval) -> c_int {
    if lhs.tv_sec < rhs.tv_sec { return -1; }
    if lhs.tv_sec > rhs.tv_sec { return 1; }
    return (lhs.tv_usec - rhs.tv_usec) as c_int;
}

//TODO: mktime64
//TODO: mktime
//TODO: set_normalized_timespec

//TODO: timespec_add_safe, timespec_add, timespec_sub

pub fn timespec_valid(ts: &timespec) -> bool {
    //Dates before 1970 are bogus
    if ts.tv_sec < 0 {
        return false;
    }

    //Can't have more nanoseconds than a second
    if ts.tv_nsec >= NSEC_PER_SEC {
        return false;
    }

    //Okay, all looks good
    return true;
}

pub fn timespec_valid_strict(ts: &timespec) -> bool {
    if !timespec_valid(ts) {
        return false;
    }

    //Okay, we've validated it quickly
    //Disallow values that could overflow ktime_t
    if ts.tv_sec >= KTIME_SEC_MAX {
        return false;
    }

    return true;
}

pub fn timeval_valid(tv: &timeval) -> bool {
    //Dates before 1970 are bogus
    if tv.tv_sec < 0 {
        return false;
    }

    //Can't have more microseconds than a second
    if tv.tv_usec >= USEC_PER_SEC {
        return false;
    }

    //Okay, all looks good
    return true;
}

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

    #[test]
    fn test_timespec_valid() {
        let atime: timespec = timespec { tv_sec: 0, tv_nsec: 4262 };

        assert!(timespec_valid(&atime));
    }

    #[test]
    fn test_timespec_too_many_seconds_valid() {
        let atime: timespec = timespec { tv_sec: KTIME_SEC_MAX + 365, tv_nsec: 4262 };

        assert!(!timespec_valid_strict(&atime));
    }

    #[test]
    fn test_timespec_before_1970_invalid() {
        let atime: timespec = timespec { tv_sec: -63, tv_nsec: 4262 };

        assert!(!timespec_valid(&atime));
    }

    #[test]
    fn test_timespec_too_many_nanoseconds_invalid() {
        let atime: timespec = timespec { tv_sec: 0, tv_nsec: NSEC_PER_SEC + 2 };

        assert!(!timespec_valid(&atime));
    }

    #[test]
    fn test_timeval_valid() {
        let atime: timeval = timeval { tv_sec: 0, tv_usec: 4262 };

        assert!(timeval_valid(&atime));
    }

    #[test]
    fn test_timeval_before_1970_invalid() {
        let atime: timeval = timeval { tv_sec: -63, tv_usec: 4262 };

        assert!(!timeval_valid(&atime));
    }

    #[test]
    fn test_timeval_too_many_nanoseconds_invalid() {
        let atime: timeval = timeval { tv_sec: 0, tv_usec: USEC_PER_SEC + 2 };

        assert!(!timeval_valid(&atime));
    }

    #[test]
    fn test_timespec_equal_when_equal() {
        let one: timespec = timespec { tv_sec: 77, tv_nsec: 77 };
        let two: timespec = timespec { tv_sec: 77, tv_nsec: 77 };

        assert_eq!(1, timespec_equal(&one, &two));
    }

    #[test]
    fn test_timespec_equal_when_inequal() {
        let one: timespec = timespec { tv_sec: 0, tv_nsec: 266 };
        let two: timespec = timespec { tv_sec: 4, tv_nsec: 0 };

        assert_eq!(0, timespec_equal(&one, &two));
    }

    #[test]
    fn test_timespec_compare_same() {
        let one = timespec { tv_sec: 10, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(0, timespec_compare(&one, &two));
    }

    #[test]
    fn test_timespec_compare_larger() {
        let one = timespec { tv_sec: 11, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(1, timespec_compare(&one, &two));
    }

    #[test]
    fn test_timespec_compare_smaller() {
        let one = timespec { tv_sec: 9, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(-1, timespec_compare(&one, &two));
    }

    #[test]
    fn test_timeval_compare_same() {
        let one = timeval { tv_sec: 10, tv_usec: 0 };
        let two = timeval { tv_sec: 10, tv_usec: 0 };

        assert_eq!(0, timeval_compare(&one, &two));
    }

    #[test]
    fn test_timeval_compare_larger() {
        let one = timeval { tv_sec: 11, tv_usec: 0 };
        let two = timeval { tv_sec: 10, tv_usec: 0 };

        assert_eq!(1, timeval_compare(&one, &two));
    }

    #[test]
    fn test_timeval_compare_smaller() {
        let one = timeval { tv_sec: 9, tv_usec: 0 };
        let two = timeval { tv_sec: 10, tv_usec: 0 };

        assert_eq!(-1, timeval_compare(&one, &two));
    }

}<commit_msg>Implement set_normalized_timespec<commit_after>//!Bindings and rewrites of include/linux/time.h and include/linux/time64.h

extern crate linux_api;

use linux_api::*;

///Checks to see if two timespecs are equal
pub fn timespec_equal(a: &timespec, b: &timespec) -> c_int {
    return ((a.tv_sec == b.tv_sec) && (a.tv_nsec == b.tv_nsec)) as c_int;
}

///Compares two timespecs
///
///If lhs < rhs, return < 0
///If lhs == rhs, return 0
///If lhs > rhs, return > 0
pub fn timespec_compare(lhs: &timespec, rhs: &timespec) -> c_int {
    if lhs.tv_sec < rhs.tv_sec { return -1; }
    if lhs.tv_sec > rhs.tv_sec { return 1; }
    return (lhs.tv_nsec - rhs.tv_nsec) as c_int;
}

///Compares two timevals
///
///If lhs < rhs, return < 0
///If lhs == rhs, return 0
///If lhs > rhs, return > 0
pub fn timeval_compare(lhs: &timeval, rhs: &timeval) -> c_int {
    if lhs.tv_sec < rhs.tv_sec { return -1; }
    if lhs.tv_sec > rhs.tv_sec { return 1; }
    return (lhs.tv_usec - rhs.tv_usec) as c_int;
}

//TODO: mktime64
//TODO: mktime

///Sets the data contained in the borrowed `timespec` ts to normalized values
pub fn set_normalized_timespec(ts: &mut timespec, sec: time_t, nsec: i64) {
    let mut realsec = sec;
    let mut realnsec = nsec;

    while realnsec >= NSEC_PER_SEC {
        //TODO: Check to see if this gets optimized and, if so, force it not to as per kernel definition
        realnsec -= NSEC_PER_SEC;
        realsec += 1;
    }

    while realnsec < 0 {
        //Ditto
        realnsec += NSEC_PER_SEC;
        realsec -= 1;
    }

    ts.tv_sec = realsec;
    ts.tv_nsec = realnsec;
}

//TODO: timespec_add_safe, timespec_add, timespec_sub

pub fn timespec_valid(ts: &timespec) -> bool {
    //Dates before 1970 are bogus
    if ts.tv_sec < 0 {
        return false;
    }

    //Can't have more nanoseconds than a second
    if ts.tv_nsec >= NSEC_PER_SEC {
        return false;
    }

    //Okay, all looks good
    return true;
}

pub fn timespec_valid_strict(ts: &timespec) -> bool {
    if !timespec_valid(ts) {
        return false;
    }

    //Okay, we've validated it quickly
    //Disallow values that could overflow ktime_t
    if ts.tv_sec >= KTIME_SEC_MAX {
        return false;
    }

    return true;
}

pub fn timeval_valid(tv: &timeval) -> bool {
    //Dates before 1970 are bogus
    if tv.tv_sec < 0 {
        return false;
    }

    //Can't have more microseconds than a second
    if tv.tv_usec >= USEC_PER_SEC {
        return false;
    }

    //Okay, all looks good
    return true;
}

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

    #[test]
    fn test_timespec_valid() {
        let atime: timespec = timespec { tv_sec: 0, tv_nsec: 4262 };

        assert!(timespec_valid(&atime));
    }

    #[test]
    fn test_timespec_too_many_seconds_valid() {
        let atime: timespec = timespec { tv_sec: KTIME_SEC_MAX + 365, tv_nsec: 4262 };

        assert!(!timespec_valid_strict(&atime));
    }

    #[test]
    fn test_timespec_before_1970_invalid() {
        let atime: timespec = timespec { tv_sec: -63, tv_nsec: 4262 };

        assert!(!timespec_valid(&atime));
    }

    #[test]
    fn test_timespec_too_many_nanoseconds_invalid() {
        let atime: timespec = timespec { tv_sec: 0, tv_nsec: NSEC_PER_SEC + 2 };

        assert!(!timespec_valid(&atime));
    }

    #[test]
    fn test_timeval_valid() {
        let atime: timeval = timeval { tv_sec: 0, tv_usec: 4262 };

        assert!(timeval_valid(&atime));
    }

    #[test]
    fn test_timeval_before_1970_invalid() {
        let atime: timeval = timeval { tv_sec: -63, tv_usec: 4262 };

        assert!(!timeval_valid(&atime));
    }

    #[test]
    fn test_timeval_too_many_nanoseconds_invalid() {
        let atime: timeval = timeval { tv_sec: 0, tv_usec: USEC_PER_SEC + 2 };

        assert!(!timeval_valid(&atime));
    }

    #[test]
    fn test_timespec_equal_when_equal() {
        let one: timespec = timespec { tv_sec: 77, tv_nsec: 77 };
        let two: timespec = timespec { tv_sec: 77, tv_nsec: 77 };

        assert_eq!(1, timespec_equal(&one, &two));
    }

    #[test]
    fn test_timespec_equal_when_inequal() {
        let one: timespec = timespec { tv_sec: 0, tv_nsec: 266 };
        let two: timespec = timespec { tv_sec: 4, tv_nsec: 0 };

        assert_eq!(0, timespec_equal(&one, &two));
    }

    #[test]
    fn test_timespec_compare_same() {
        let one = timespec { tv_sec: 10, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(0, timespec_compare(&one, &two));
    }

    #[test]
    fn test_timespec_compare_larger() {
        let one = timespec { tv_sec: 11, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(1, timespec_compare(&one, &two));
    }

    #[test]
    fn test_timespec_compare_smaller() {
        let one = timespec { tv_sec: 9, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(-1, timespec_compare(&one, &two));
    }

    #[test]
    fn test_timeval_compare_same() {
        let one = timeval { tv_sec: 10, tv_usec: 0 };
        let two = timeval { tv_sec: 10, tv_usec: 0 };

        assert_eq!(0, timeval_compare(&one, &two));
    }

    #[test]
    fn test_timeval_compare_larger() {
        let one = timeval { tv_sec: 11, tv_usec: 0 };
        let two = timeval { tv_sec: 10, tv_usec: 0 };

        assert_eq!(1, timeval_compare(&one, &two));
    }

    #[test]
    fn test_timeval_compare_smaller() {
        let one = timeval { tv_sec: 9, tv_usec: 0 };
        let two = timeval { tv_sec: 10, tv_usec: 0 };

        assert_eq!(-1, timeval_compare(&one, &two));
    }

    #[test]
    fn test_normalized_timespec_many_nanos() {
        let mut spec: timespec = timespec { tv_sec: 0, tv_nsec: 0 };

        set_normalized_timespec(&mut spec, 10, NSEC_PER_SEC * 20);

        assert_eq!(30, spec.tv_sec);
        assert_eq!(0, spec.tv_nsec);
    }

    #[test]
    fn test_normalized_timespec_negative_nanos() {
        let mut spec: timespec = timespec { tv_sec: 0, tv_nsec: 0 };

        set_normalized_timespec(&mut spec, 5, -800);

        assert_eq!(4, spec.tv_sec);
        assert_eq!(NSEC_PER_SEC - 800, spec.tv_nsec);
    }

}<|endoftext|>


<commit_before>use gfx;
use rand;
use std::convert::AsRef;

use cube::{CubeRef, Spatial};
use math::{IntoSpace, FPoint3, FScalar, FVector3, FVector4};
use mesh::{DecomposePolygon, DecomposePrimitive, Triangle, UCube};
use super::OptionExt;

pub type Index = u32;

gfx_pipeline!{
    pipeline {
        vertex_buffer: gfx::VertexBuffer<RawVertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_transform",
        output: gfx::RenderTarget<gfx::format::Rgba8> = "f_target0",
    }
}

gfx_vertex_struct!{
    RawVertex {
        position: [f32; 3] = "a_position",
        color: [f32; 4] = "a_color",
    }
}

impl From<Vertex> for RawVertex {
    fn from(vertex: Vertex) -> Self {
        RawVertex {
            position: *vertex.position.as_ref(),
            color: *vertex.color.as_ref(),
        }
    }
}

pub struct Vertex {
    pub position: FPoint3,
    pub color: FVector4,
}

impl Vertex {
    pub fn new(position: FPoint3, color: FVector4) -> Self {
        Vertex {
            position: position,
            color: color,
        }
    }
}

pub struct MeshBuffer {
    pub vertices: Vec<RawVertex>,
    pub indeces: Vec<Index>,
}

impl MeshBuffer {
    pub fn new() -> Self {
        MeshBuffer {
            vertices: vec![],
            indeces: vec![],
        }
    }

    pub fn extend(&mut self, buffer: &Self) {
        let offset = self.vertices.len();
        self.vertices.extend(buffer.vertices.iter());
        self.indeces.extend(buffer.indeces
            .iter()
            .map(|index| index + offset as Index));
    }
}

pub trait Mesh {
    fn mesh_buffer(&self) -> MeshBuffer;
}

impl<'a> Mesh for CubeRef<'a> {
    fn mesh_buffer(&self) -> MeshBuffer {
        let mut buffer = MeshBuffer::new();
        if let Some(leaf) = self.try_as_leaf().and_if(|leaf| !leaf.geometry.is_empty()) {
            let origin: FVector3 = self.partition().origin().to_vector().into_space();
            let width = self.partition().width().exp() as FScalar;

            buffer.vertices.extend(UCube::with_unit_width()
                .triangulate()
                .map(|triangle| {
                    let color = random_color();
                    Triangle::new((triangle.a, color), (triangle.b, color), (triangle.c, color))
                })
                .points()
                .map(|(point, color)| {
                    (leaf.geometry.map_unit_cube_point(&point), color)
                })
                .map(|(point, color)| ((point * width) + origin, color))
                .map(|(point, color)| RawVertex::from(Vertex::new(point, color))));
            buffer.indeces.extend(UCube::with_unit_width()
                .triangulate()
                .points()
                .enumerate()
                .map(|(index, _)| index as Index));
        }
        buffer
    }
}

pub fn vertex_buffer_from_cube<R, F>(cube: &CubeRef,
                                     factory: &mut F)
                                     -> (gfx::handle::Buffer<R, RawVertex>, gfx::Slice<R>)
    where R: gfx::Resources,
          F: gfx::traits::FactoryExt<R>
{
    let mut buffer = MeshBuffer::new();
    for cube in cube.iter().filter(|cube| cube.is_leaf()) {
        buffer.extend(&cube.mesh_buffer());
    }
    factory.create_vertex_buffer_with_slice(buffer.vertices.as_slice(), buffer.indeces.as_slice())
}

fn random_color() -> FVector4 {
    FVector4::new(rand::random::<FScalar>(),
                  rand::random::<FScalar>(),
                  rand::random::<FScalar>(),
                  1.0)
}

#[cfg(test)]
mod tests {
    use super::*;
}
<commit_msg>Use `map_points` to build cube meshes.<commit_after>use gfx;
use rand;
use std::convert::AsRef;

use cube::{CubeRef, Spatial};
use math::{IntoSpace, FPoint3, FScalar, FVector3, FVector4};
use mesh::{DecomposePolygon, DecomposePrimitive, MapPrimitive, Triangle, UCube};
use super::OptionExt;

pub type Index = u32;

gfx_pipeline!{
    pipeline {
        vertex_buffer: gfx::VertexBuffer<RawVertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_transform",
        output: gfx::RenderTarget<gfx::format::Rgba8> = "f_target0",
    }
}

gfx_vertex_struct!{
    RawVertex {
        position: [f32; 3] = "a_position",
        color: [f32; 4] = "a_color",
    }
}

impl RawVertex {
    fn new(position: &FPoint3, color: &FVector4) -> Self {
        RawVertex {
            position: *position.as_ref(),
            color: *color.as_ref(),
        }
    }
}

impl From<Vertex> for RawVertex {
    fn from(vertex: Vertex) -> Self {
        RawVertex::new(&vertex.position, &vertex.color)
    }
}

pub struct Vertex {
    pub position: FPoint3,
    pub color: FVector4,
}

impl Vertex {
    pub fn new(position: FPoint3, color: FVector4) -> Self {
        Vertex {
            position: position,
            color: color,
        }
    }
}

pub struct MeshBuffer {
    pub vertices: Vec<RawVertex>,
    pub indeces: Vec<Index>,
}

impl MeshBuffer {
    pub fn new() -> Self {
        MeshBuffer {
            vertices: vec![],
            indeces: vec![],
        }
    }

    pub fn extend(&mut self, buffer: &Self) {
        let offset = self.vertices.len();
        self.vertices.extend(buffer.vertices.iter());
        self.indeces.extend(buffer.indeces
            .iter()
            .map(|index| index + offset as Index));
    }
}

pub trait Mesh {
    fn mesh_buffer(&self) -> MeshBuffer;
}

impl<'a> Mesh for CubeRef<'a> {
    fn mesh_buffer(&self) -> MeshBuffer {
        let mut buffer = MeshBuffer::new();
        if let Some(leaf) = self.try_as_leaf().and_if(|leaf| !leaf.geometry.is_empty()) {
            let origin: FVector3 = self.partition().origin().to_vector().into_space();
            let width = self.partition().width().exp() as FScalar;

            buffer.vertices.extend(UCube::with_unit_width()
                .map_points(|point| leaf.geometry.map_unit_cube_point(&point))
                .map_points(|point| (point * width) + origin)
                .triangulate()
                .map(|triangle| {
                    let color = random_color();
                    Triangle::new(RawVertex::new(&triangle.a, &color),
                                  RawVertex::new(&triangle.b, &color),
                                  RawVertex::new(&triangle.c, &color))
                })
                .points());
            buffer.indeces.extend(UCube::with_unit_width()
                .triangulate()
                .points()
                .enumerate()
                .map(|(index, _)| index as Index));
        }
        buffer
    }
}

pub fn vertex_buffer_from_cube<R, F>(cube: &CubeRef,
                                     factory: &mut F)
                                     -> (gfx::handle::Buffer<R, RawVertex>, gfx::Slice<R>)
    where R: gfx::Resources,
          F: gfx::traits::FactoryExt<R>
{
    let mut buffer = MeshBuffer::new();
    for cube in cube.iter().filter(|cube| cube.is_leaf()) {
        buffer.extend(&cube.mesh_buffer());
    }
    factory.create_vertex_buffer_with_slice(buffer.vertices.as_slice(), buffer.indeces.as_slice())
}

fn random_color() -> FVector4 {
    FVector4::new(rand::random::<FScalar>(),
                  rand::random::<FScalar>(),
                  rand::random::<FScalar>(),
                  1.0)
}

#[cfg(test)]
mod tests {
    use super::*;
}
<|endoftext|>