<commit_before>use std;
use std::ops::Range;
use std::collections::{HashSet, VecDeque};

use rand;
use rand::Rng;

use tcod;
use tcod::console::Console;
use tcod::Color;

use traits::{Position, Renderable};
use unit_type::UnitTypeLists;
use units::Unit;
use item::Item;

use point::Point;
use rectangle::Rectangle;

use SCREEN_WIDTH;
use SCREEN_HEIGHT;

const MAP_MIN_WIDTH: u8 = SCREEN_WIDTH;
const MAP_MIN_HEIGHT: u8 = SCREEN_HEIGHT;
pub const MAP_MAX_WIDTH: u8 = std::u8::MAX;
pub const MAP_MAX_HEIGHT: u8 = std::u8::MAX;

const ROOM_MAX_MONSTERS: u8 = 3;
// Values for I/N chance of generating monsters for a room.
const ROOM_CHANCE_OF_MONSTERS_I: u32 = 2;
const ROOM_CHANCE_OF_MONSTERS_N: u32 = 5;
const ROOM_MAX_SIZE: u8 = 10;
const ROOM_MIN_SIZE: u8 = 6;
// This value determines the maximum number of rooms for a given map size.
// This was calculated by the previous map size (80 x 45) divided by the
// previous max room count of 30.
// This should provide a similar room densiter for each map.
const ROOM_PER_TILE: u8 = 120;

const ERR_MSG_TUNNEL: &str = "Failed to create tunnel.";
const ERR_MSG_ROOM: &str = "Failed to create room.";
const ERR_MSG_ROOM_CMP: &str = "Error comparing rooms.";

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum CanMoveResponse {
    Open,
    Scenery,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TileType {
    Floor,
    Wall,
}

impl TileType {
    fn get_color_not_visible(self) -> Color {
        match self {
            TileType::Floor => Color::new(50, 50, 150),
            TileType::Wall => Color::new(0, 0, 100),
        }
    }

    fn get_color_visible(self) -> Color {
        match self {
            TileType::Floor => Color::new(200, 180, 50),
            TileType::Wall => Color::new(130, 110, 50),
        }
    }

    fn get_glyph(self) -> char {
        match self {
            TileType::Floor => '.',
            TileType::Wall => '#',
        }
    }

    pub fn blocks_move(self) -> bool {
        match self {
            TileType::Floor => false,
            TileType::Wall => true,
        }
    }

    pub fn blocks_sight(self) -> bool {
        match self {
            TileType::Floor => false,
            TileType::Wall => true,
        }
    }
}

#[derive(Debug)]
pub struct Tile {
    position: Point<i16>,
    tile_type: TileType,
    is_explored: bool,
    is_visible: bool,
}

impl Position for Tile {
    fn get_x(&self) -> i16 {
        self.position.x
    }

    fn get_y(&self) -> i16 {
        self.position.y
    }
}

impl Renderable for Tile {
    fn get_color(&self) -> Color {
        if !self.is_explored {
            tcod::colors::BLACK
        } else {
            match self.is_visible {
                true => self.tile_type.get_color_visible(),
                false => self.tile_type.get_color_not_visible(),
            }
        }
    }
    fn get_glyph(&self) -> char {
        self.tile_type.get_glyph()
    }
}

impl Tile {
    pub fn new(pos: Point<i16>, tile_type: TileType) -> Tile {
        Tile{
            position: pos,
            tile_type: tile_type,
            is_explored: false,
            is_visible: false,
        }
    }
}

pub struct Map {
    width: u8,
    height: u8,
    tile_map: Vec<Tile>,
    items: Vec<Item>,
    fov_map: tcod::map::Map,
}

// Init and building.
impl Map {
    pub fn init<'a>(unit_types: &'a UnitTypeLists) -> (Map, VecDeque<Unit<'a>>, Point<i16>) {
        let mut rng = rand::thread_rng();

        let map_width = rng.gen_range(MAP_MIN_WIDTH, MAP_MAX_WIDTH);
        let map_height = rng.gen_range(MAP_MIN_HEIGHT, MAP_MAX_HEIGHT);

        let mut map = vec![];
        for y in 0..map_height {
            for x in 0..map_width {
                map.push( Tile::new(Point{x: x as i16, y: y as i16}, TileType::Wall) );
            }
        }

        let mut map = Map {
            width: map_width,
            height: map_height,
            tile_map: map,
            items: vec![],
            fov_map: tcod::map::Map::new(map_width as i32, map_height as i32),
        };

        let mut npcs = VecDeque::new();

        let (rooms, player_start) = map.build_rooms(&mut rng);
        map.build_coridoors(&rooms, &mut rng);

        for room in rooms {
            if rng.gen_range(0, ROOM_CHANCE_OF_MONSTERS_N) < ROOM_CHANCE_OF_MONSTERS_I {
                map.place_npcs(&room, &unit_types, &mut npcs, &mut rng);
            }
        }

        Map::build_fov_map(&map.tile_map, &mut map.fov_map);

        (map, npcs, player_start)
    }

    fn place_npcs<'a>(&mut self, room: &Rectangle, units: &'a UnitTypeLists, npc_list: &mut VecDeque<Unit<'a>>, rng: &mut rand::ThreadRng) {
        let max_monsters = rng.gen_range(0, ROOM_MAX_MONSTERS);

        for _ in 0..max_monsters {
            let position = room.get_random_position(rng);
            let monster_type = units.get_random_type(rng);

            let monster = Unit::new(position, monster_type);
            npc_list.push_back(monster);
        }
    }

    pub fn place_item(&mut self, item: Item) {
        self.items.push(item);
    }

    fn build_rooms(&mut self, rng: &mut rand::ThreadRng) -> (Vec<Rectangle>, Point<i16>) {
        let mut rooms = vec![];
        let mut player_start = Point{x:0, y:0};

        // Casts are to avoid overflow.
        let max_rooms = self.width as u16 * self.height as u16 / ROOM_PER_TILE as u16;

        for _ in 0..max_rooms {
            let width = rng.gen_range(ROOM_MIN_SIZE, ROOM_MAX_SIZE);
            let height = rng.gen_range(ROOM_MIN_SIZE, ROOM_MAX_SIZE);

            let room = Rectangle::new(
                Point {
                    x: rng.gen_range(0, self.width as i16 - width as i16 - 1),
                    y: rng.gen_range(0, self.height as i16 - height as i16 - 1)
                },
                (width, height)
            );

            if !rooms.iter().any(|r: &Rectangle| r.is_intersecting(&room)) {
                self.create_room(&room).expect(ERR_MSG_ROOM);

                if rooms.len() == 0 {
                    player_start = room.centre();
                }

                rooms.push(room);
            }
        }

        (rooms, player_start)
    }

    fn build_coridoors(&mut self, rooms: &Vec<Rectangle>, rng: &mut rand::ThreadRng) {
        let mut sorted_rooms: Vec<_> = rooms.iter().map(|r| r).collect();
        let mut connected_rooms = HashSet::new();

        for room in rooms {
            sorted_rooms.sort_by(|a,b| (room.centre() - a.centre()).sqr_radius().partial_cmp(&(room.centre() - b.centre()).sqr_radius()).expect(ERR_MSG_ROOM_CMP) );

            for next in sorted_rooms.iter().skip(1).take(3) {
                if connected_rooms.contains(&(*next, room)) {
                    continue;
                }

                connected_rooms.insert((room, *next));

                let r1_centre = room.centre();
                let r2_centre = next.centre();

                // Decide whether to first tunnel horizontally or vertically.
                if rng.gen_weighted_bool(2) {
                    // Horizontal
                    self.create_h_tunnel(r1_centre.x..r2_centre.x, r1_centre.y).expect(ERR_MSG_TUNNEL);
                    self.create_v_tunnel(r2_centre.x, r1_centre.y..r2_centre.y).expect(ERR_MSG_TUNNEL);
                } else {
                    // Vertical
                    self.create_v_tunnel(r1_centre.x, r1_centre.y..r2_centre.y).expect(ERR_MSG_TUNNEL);
                    self.create_h_tunnel(r1_centre.x..r2_centre.x, r2_centre.y).expect(ERR_MSG_TUNNEL);
                }
            }
        }
    }

    fn create_v_tunnel(&mut self, x: i16, y_r: Range<i16>) -> Result<(),()> {
        // Need to handle the case where start > end.
        // I decided to do it here to make it easier to call the function.
        // Because this is a half-open range, we need to adjust the start
        // and end to account for that.
        let y_r = if y_r.start > y_r.end {
            y_r.end+1 .. y_r.start+1
        } else {
            y_r
        };

        for y in y_r {
            let pos = Point{x:x, y:y};
            self.set_tile_type(pos, TileType::Floor)?;
        }

        Ok(())
    }

    fn create_h_tunnel(&mut self, x_r: Range<i16>, y: i16) -> Result<(),()> {
        // Need to handle the case where start > end.
        // I decided to do it here to make it easier to call the function.
        // Because this is a half-open range, we need to adjust the start
        // and end to account for that.
        let x_r = if x_r.start > x_r.end {
            x_r.end+1 .. x_r.start+1
        } else {
            x_r
        };

        for x in x_r {
            let pos = Point{x:x, y:y};
            self.set_tile_type(pos, TileType::Floor)?;
        }

        Ok(())
    }

    fn create_room(&mut self, rect: &Rectangle) -> Result<(),()> {
        for y in (rect.top_left.y+1)..rect.bottom_right.y {
            for x in (rect.top_left.x+1)..rect.bottom_right.x {
                let pos = Point{x:x, y:y};
                self.set_tile_type(pos, TileType::Floor)?;
            }
        }

        Ok(())
    }
}

// Rendering
impl Map {
    pub fn render_map<T: Console>(&self, cons: &mut T) {
        for tile in self.tile_map.iter() {
            tile.render(cons);
        }

        for item in self.items.iter() {
            item.render(cons);
        }
    }
}

// Queries
impl Map {
    pub fn point_in_map(&self, Point{x,y}: Point<i16>) -> bool {
        x >= 0 && x < self.width as i16 && y >= 0 && y < self.height as i16
    }

    pub fn get_tile_type(&self, pos: Point<i16>) -> Result<TileType,()> {
        if !self.point_in_map(pos) {
            Err(())
        } else {
            let Point{x, y} = pos;
            Ok(self.tile_map[y as usize * self.width as usize + x as usize].tile_type)
        }
    }

    pub fn can_move_to(&self, pos: Point<i16>) -> CanMoveResponse {
        match self.get_tile_type(pos) {
            Ok(tile) if tile.blocks_move() => CanMoveResponse::Scenery,
            _ => CanMoveResponse::Open
        }
    }

    pub fn get_map_size(&self) -> (u8, u8) {
        (self.width, self.height)
    }

    pub fn point_in_fov(&self, Point{x,y}: Point<i16>) -> bool {
        self.fov_map.is_in_fov(x as i32, y as i32)
    }

    fn build_fov_map(tiles: &Vec<Tile>, fov_map: &mut tcod::map::Map) {
        for tile in tiles.iter() {
            let pos = tile.get_position();
            fov_map.set(pos.x as i32, pos.y as i32, tile.tile_type.blocks_sight(), tile.tile_type.blocks_move());
        }
    }

    pub fn get_pathfinding_map(&self) -> tcod::Map {
        let mut path_map = tcod::Map::new(self.width as i32, self.height as i32);
        Map::build_fov_map(&self.tile_map, &mut path_map);
        path_map
    }
}

// Updating
impl Map {
    pub fn update_fov(&mut self, Point{x, y}: Point<i16>, light_radius: u8) {
        self.fov_map.compute_fov(x as i32, y as i32, light_radius as i32, true, tcod::map::FovAlgorithm::Permissive0);

        // I've opted to update the tile map here, because it doesn't make sense that
        // a function for rendering should need to mutate the object.
        // It does make this function more expensive to run, but it won't be run
        // too frequently.
        for tile in self.tile_map.iter_mut() {
            tile.is_visible = self.fov_map.is_in_fov(tile.get_x() as i32, tile.get_y() as i32);

            if tile.is_visible {
                tile.is_explored = true;
            }
        }
    }

    fn set_tile_type(&mut self, pos: Point<i16>, new_tile: TileType) -> Result<(),()> {
        if !self.point_in_map(pos) {
            Err(())
        } else {
            let Point{x,y} = pos;
            self.tile_map[y as usize * self.width as usize + x as usize].tile_type = new_tile;

            Ok(())
        }
    }
}<commit_msg>Fixed inverted value from blocks_sight().<commit_after>use std;
use std::ops::Range;
use std::collections::{HashSet, VecDeque};

use rand;
use rand::Rng;

use tcod;
use tcod::console::Console;
use tcod::Color;

use traits::{Position, Renderable};
use unit_type::UnitTypeLists;
use units::Unit;
use item::Item;

use point::Point;
use rectangle::Rectangle;

use SCREEN_WIDTH;
use SCREEN_HEIGHT;

const MAP_MIN_WIDTH: u8 = SCREEN_WIDTH;
const MAP_MIN_HEIGHT: u8 = SCREEN_HEIGHT;
pub const MAP_MAX_WIDTH: u8 = std::u8::MAX;
pub const MAP_MAX_HEIGHT: u8 = std::u8::MAX;

const ROOM_MAX_MONSTERS: u8 = 3;
// Values for I/N chance of generating monsters for a room.
const ROOM_CHANCE_OF_MONSTERS_I: u32 = 2;
const ROOM_CHANCE_OF_MONSTERS_N: u32 = 5;
const ROOM_MAX_SIZE: u8 = 10;
const ROOM_MIN_SIZE: u8 = 6;
// This value determines the maximum number of rooms for a given map size.
// This was calculated by the previous map size (80 x 45) divided by the
// previous max room count of 30.
// This should provide a similar room densiter for each map.
const ROOM_PER_TILE: u8 = 120;

const ERR_MSG_TUNNEL: &str = "Failed to create tunnel.";
const ERR_MSG_ROOM: &str = "Failed to create room.";
const ERR_MSG_ROOM_CMP: &str = "Error comparing rooms.";

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum CanMoveResponse {
    Open,
    Scenery,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TileType {
    Floor,
    Wall,
}

impl TileType {
    fn get_color_not_visible(self) -> Color {
        match self {
            TileType::Floor => Color::new(50, 50, 150),
            TileType::Wall => Color::new(0, 0, 100),
        }
    }

    fn get_color_visible(self) -> Color {
        match self {
            TileType::Floor => Color::new(200, 180, 50),
            TileType::Wall => Color::new(130, 110, 50),
        }
    }

    fn get_glyph(self) -> char {
        match self {
            TileType::Floor => '.',
            TileType::Wall => '#',
        }
    }

    pub fn blocks_move(self) -> bool {
        match self {
            TileType::Floor => false,
            TileType::Wall => true,
        }
    }

    pub fn blocks_sight(self) -> bool {
        // NOTE: TCod library input is whether it's transparent, not whether it blocks.
        !match self {
            TileType::Floor => false,
            TileType::Wall => true,
        }
    }
}

#[derive(Debug)]
pub struct Tile {
    position: Point<i16>,
    tile_type: TileType,
    is_explored: bool,
    is_visible: bool,
}

impl Position for Tile {
    fn get_x(&self) -> i16 {
        self.position.x
    }

    fn get_y(&self) -> i16 {
        self.position.y
    }
}

impl Renderable for Tile {
    fn get_color(&self) -> Color {
        if !self.is_explored {
            tcod::colors::BLACK
        } else {
            match self.is_visible {
                true => self.tile_type.get_color_visible(),
                false => self.tile_type.get_color_not_visible(),
            }
        }
    }
    fn get_glyph(&self) -> char {
        self.tile_type.get_glyph()
    }
}

impl Tile {
    pub fn new(pos: Point<i16>, tile_type: TileType) -> Tile {
        Tile{
            position: pos,
            tile_type: tile_type,
            is_explored: false,
            is_visible: false,
        }
    }
}

pub struct Map {
    width: u8,
    height: u8,
    tile_map: Vec<Tile>,
    items: Vec<Item>,
    fov_map: tcod::map::Map,
}

// Init and building.
impl Map {
    pub fn init<'a>(unit_types: &'a UnitTypeLists) -> (Map, VecDeque<Unit<'a>>, Point<i16>) {
        let mut rng = rand::thread_rng();

        let map_width = rng.gen_range(MAP_MIN_WIDTH, MAP_MAX_WIDTH);
        let map_height = rng.gen_range(MAP_MIN_HEIGHT, MAP_MAX_HEIGHT);

        let mut map = vec![];
        for y in 0..map_height {
            for x in 0..map_width {
                map.push( Tile::new(Point{x: x as i16, y: y as i16}, TileType::Wall) );
            }
        }

        let mut map = Map {
            width: map_width,
            height: map_height,
            tile_map: map,
            items: vec![],
            fov_map: tcod::map::Map::new(map_width as i32, map_height as i32),
        };

        let mut npcs = VecDeque::new();

        let (rooms, player_start) = map.build_rooms(&mut rng);
        map.build_coridoors(&rooms, &mut rng);

        for room in rooms {
            if rng.gen_range(0, ROOM_CHANCE_OF_MONSTERS_N) < ROOM_CHANCE_OF_MONSTERS_I {
                map.place_npcs(&room, &unit_types, &mut npcs, &mut rng);
            }
        }

        Map::build_fov_map(&map.tile_map, &mut map.fov_map);

        (map, npcs, player_start)
    }

    fn place_npcs<'a>(&mut self, room: &Rectangle, units: &'a UnitTypeLists, npc_list: &mut VecDeque<Unit<'a>>, rng: &mut rand::ThreadRng) {
        let max_monsters = rng.gen_range(0, ROOM_MAX_MONSTERS);

        for _ in 0..max_monsters {
            let position = room.get_random_position(rng);
            let monster_type = units.get_random_type(rng);

            let monster = Unit::new(position, monster_type);
            npc_list.push_back(monster);
        }
    }

    pub fn place_item(&mut self, item: Item) {
        self.items.push(item);
    }

    fn build_rooms(&mut self, rng: &mut rand::ThreadRng) -> (Vec<Rectangle>, Point<i16>) {
        let mut rooms = vec![];
        let mut player_start = Point{x:0, y:0};

        // Casts are to avoid overflow.
        let max_rooms = self.width as u16 * self.height as u16 / ROOM_PER_TILE as u16;

        for _ in 0..max_rooms {
            let width = rng.gen_range(ROOM_MIN_SIZE, ROOM_MAX_SIZE);
            let height = rng.gen_range(ROOM_MIN_SIZE, ROOM_MAX_SIZE);

            let room = Rectangle::new(
                Point {
                    x: rng.gen_range(0, self.width as i16 - width as i16 - 1),
                    y: rng.gen_range(0, self.height as i16 - height as i16 - 1)
                },
                (width, height)
            );

            if !rooms.iter().any(|r: &Rectangle| r.is_intersecting(&room)) {
                self.create_room(&room).expect(ERR_MSG_ROOM);

                if rooms.len() == 0 {
                    player_start = room.centre();
                }

                rooms.push(room);
            }
        }

        (rooms, player_start)
    }

    fn build_coridoors(&mut self, rooms: &Vec<Rectangle>, rng: &mut rand::ThreadRng) {
        let mut sorted_rooms: Vec<_> = rooms.iter().map(|r| r).collect();
        let mut connected_rooms = HashSet::new();

        for room in rooms {
            sorted_rooms.sort_by(|a,b| (room.centre() - a.centre()).sqr_radius().partial_cmp(&(room.centre() - b.centre()).sqr_radius()).expect(ERR_MSG_ROOM_CMP) );

            for next in sorted_rooms.iter().skip(1).take(3) {
                if connected_rooms.contains(&(*next, room)) {
                    continue;
                }

                connected_rooms.insert((room, *next));

                let r1_centre = room.centre();
                let r2_centre = next.centre();

                // Decide whether to first tunnel horizontally or vertically.
                if rng.gen_weighted_bool(2) {
                    // Horizontal
                    self.create_h_tunnel(r1_centre.x..r2_centre.x, r1_centre.y).expect(ERR_MSG_TUNNEL);
                    self.create_v_tunnel(r2_centre.x, r1_centre.y..r2_centre.y).expect(ERR_MSG_TUNNEL);
                } else {
                    // Vertical
                    self.create_v_tunnel(r1_centre.x, r1_centre.y..r2_centre.y).expect(ERR_MSG_TUNNEL);
                    self.create_h_tunnel(r1_centre.x..r2_centre.x, r2_centre.y).expect(ERR_MSG_TUNNEL);
                }
            }
        }
    }

    fn create_v_tunnel(&mut self, x: i16, y_r: Range<i16>) -> Result<(),()> {
        // Need to handle the case where start > end.
        // I decided to do it here to make it easier to call the function.
        // Because this is a half-open range, we need to adjust the start
        // and end to account for that.
        let y_r = if y_r.start > y_r.end {
            y_r.end+1 .. y_r.start+1
        } else {
            y_r
        };

        for y in y_r {
            let pos = Point{x:x, y:y};
            self.set_tile_type(pos, TileType::Floor)?;
        }

        Ok(())
    }

    fn create_h_tunnel(&mut self, x_r: Range<i16>, y: i16) -> Result<(),()> {
        // Need to handle the case where start > end.
        // I decided to do it here to make it easier to call the function.
        // Because this is a half-open range, we need to adjust the start
        // and end to account for that.
        let x_r = if x_r.start > x_r.end {
            x_r.end+1 .. x_r.start+1
        } else {
            x_r
        };

        for x in x_r {
            let pos = Point{x:x, y:y};
            self.set_tile_type(pos, TileType::Floor)?;
        }

        Ok(())
    }

    fn create_room(&mut self, rect: &Rectangle) -> Result<(),()> {
        for y in (rect.top_left.y+1)..rect.bottom_right.y {
            for x in (rect.top_left.x+1)..rect.bottom_right.x {
                let pos = Point{x:x, y:y};
                self.set_tile_type(pos, TileType::Floor)?;
            }
        }

        Ok(())
    }
}

// Rendering
impl Map {
    pub fn render_map<T: Console>(&self, cons: &mut T) {
        for tile in self.tile_map.iter() {
            tile.render(cons);
        }

        for item in self.items.iter() {
            item.render(cons);
        }
    }
}

// Queries
impl Map {
    pub fn point_in_map(&self, Point{x,y}: Point<i16>) -> bool {
        x >= 0 && x < self.width as i16 && y >= 0 && y < self.height as i16
    }

    pub fn get_tile_type(&self, pos: Point<i16>) -> Result<TileType,()> {
        if !self.point_in_map(pos) {
            Err(())
        } else {
            let Point{x, y} = pos;
            Ok(self.tile_map[y as usize * self.width as usize + x as usize].tile_type)
        }
    }

    pub fn can_move_to(&self, pos: Point<i16>) -> CanMoveResponse {
        match self.get_tile_type(pos) {
            Ok(tile) if tile.blocks_move() => CanMoveResponse::Scenery,
            _ => CanMoveResponse::Open
        }
    }

    pub fn get_map_size(&self) -> (u8, u8) {
        (self.width, self.height)
    }

    pub fn point_in_fov(&self, Point{x,y}: Point<i16>) -> bool {
        self.fov_map.is_in_fov(x as i32, y as i32)
    }

    fn build_fov_map(tiles: &Vec<Tile>, fov_map: &mut tcod::map::Map) {
        for tile in tiles.iter() {
            let pos = tile.get_position();
            fov_map.set(pos.x as i32, pos.y as i32, tile.tile_type.blocks_sight(), tile.tile_type.blocks_move());
        }
    }

    pub fn get_pathfinding_map(&self) -> tcod::Map {
        let mut path_map = tcod::Map::new(self.width as i32, self.height as i32);
        Map::build_fov_map(&self.tile_map, &mut path_map);
        path_map
    }
}

// Updating
impl Map {
    pub fn update_fov(&mut self, Point{x, y}: Point<i16>, light_radius: u8) {
        self.fov_map.compute_fov(x as i32, y as i32, light_radius as i32, true, tcod::map::FovAlgorithm::Permissive0);

        // I've opted to update the tile map here, because it doesn't make sense that
        // a function for rendering should need to mutate the object.
        // It does make this function more expensive to run, but it won't be run
        // too frequently.
        for tile in self.tile_map.iter_mut() {
            tile.is_visible = self.fov_map.is_in_fov(tile.get_x() as i32, tile.get_y() as i32);

            if tile.is_visible {
                tile.is_explored = true;
            }
        }
    }

    fn set_tile_type(&mut self, pos: Point<i16>, new_tile: TileType) -> Result<(),()> {
        if !self.point_in_map(pos) {
            Err(())
        } else {
            let Point{x,y} = pos;
            self.tile_map[y as usize * self.width as usize + x as usize].tile_type = new_tile;

            Ok(())
        }
    }
}<|endoftext|>


<commit_before>//! Installs rust-analyzer language server and/or editor plugin.

use std::{env, path::PathBuf, str};

use anyhow::{bail, format_err, Context, Result};
use xshell::{cmd, pushd};

// Latest stable, feel free to send a PR if this lags behind.
const REQUIRED_RUST_VERSION: u32 = 47;

pub struct InstallCmd {
    pub client: Option<ClientOpt>,
    pub server: Option<ServerOpt>,
}

#[derive(Clone, Copy)]
pub enum ClientOpt {
    VsCode,
    VsCodeInsiders,
    VsCodium,
    VsCodeOss,
    Any,
}

impl ClientOpt {
    pub const fn as_cmds(&self) -> &'static [&'static str] {
        match self {
            ClientOpt::VsCode => &["code"],
            ClientOpt::VsCodeInsiders => &["code-insiders"],
            ClientOpt::VsCodium => &["codium"],
            ClientOpt::VsCodeOss => &["code-oss"],
            ClientOpt::Any => &["code", "code-insiders", "codium", "code-oss"],
        }
    }
}

impl Default for ClientOpt {
    fn default() -> Self {
        ClientOpt::Any
    }
}

impl std::str::FromStr for ClientOpt {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        [ClientOpt::VsCode, ClientOpt::VsCodeInsiders, ClientOpt::VsCodium, ClientOpt::VsCodeOss]
            .iter()
            .copied()
            .find(|c| [s] == c.as_cmds())
            .ok_or_else(|| anyhow::format_err!("no such client"))
    }
}

pub struct ServerOpt {
    pub malloc: Malloc,
}

pub enum Malloc {
    System,
    Mimalloc,
}

impl InstallCmd {
    pub fn run(self) -> Result<()> {
        if cfg!(target_os = "macos") {
            fix_path_for_mac().context("Fix path for mac")?
        }
        if let Some(server) = self.server {
            install_server(server).context("install server")?;
        }
        if let Some(client) = self.client {
            install_client(client).context("install client")?;
        }
        Ok(())
    }
}

fn fix_path_for_mac() -> Result<()> {
    let mut vscode_path: Vec<PathBuf> = {
        const COMMON_APP_PATH: &str =
            r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
        const ROOT_DIR: &str = "";
        let home_dir = match env::var("HOME") {
            Ok(home) => home,
            Err(e) => bail!("Failed getting HOME from environment with error: {}.", e),
        };

        [ROOT_DIR, &home_dir]
            .iter()
            .map(|dir| String::from(*dir) + COMMON_APP_PATH)
            .map(PathBuf::from)
            .filter(|path| path.exists())
            .collect()
    };

    if !vscode_path.is_empty() {
        let vars = match env::var_os("PATH") {
            Some(path) => path,
            None => bail!("Could not get PATH variable from env."),
        };

        let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
        paths.append(&mut vscode_path);
        let new_paths = env::join_paths(paths).context("build env PATH")?;
        env::set_var("PATH", &new_paths);
    }

    Ok(())
}

fn install_client(client_opt: ClientOpt) -> Result<()> {
    let _dir = pushd("./editors/code");

    let find_code = |f: fn(&str) -> bool| -> Result<&'static str> {
        client_opt.as_cmds().iter().copied().find(|bin| f(bin)).ok_or_else(|| {
            format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?")
        })
    };

    let installed_extensions = if cfg!(unix) {
        cmd!("npm --version").run().context("`npm` is required to build the VS Code plugin")?;
        cmd!("npm install").run()?;

        cmd!("npm run package --scripts-prepend-node-path").run()?;

        let code = find_code(|bin| cmd!("{bin} --version").read().is_ok())?;
        cmd!("{code} --install-extension rust-analyzer.vsix --force").run()?;
        cmd!("{code} --list-extensions").read()?
    } else {
        cmd!("cmd.exe /c npm --version")
            .run()
            .context("`npm` is required to build the VS Code plugin")?;
        cmd!("cmd.exe /c npm install").run()?;

        cmd!("cmd.exe /c npm run package").run()?;

        let code = find_code(|bin| cmd!("cmd.exe /c {bin}.cmd --version").read().is_ok())?;
        cmd!("cmd.exe /c {code}.cmd --install-extension rust-analyzer.vsix --force").run()?;
        cmd!("cmd.exe /c {code}.cmd --list-extensions").read()?
    };

    if !installed_extensions.contains("rust-analyzer") {
        bail!(
            "Could not install the Visual Studio Code extension. \
            Please make sure you have at least NodeJS 12.x together with the latest version of VS Code installed and try again. \
            Note that installing via xtask install does not work for VS Code Remote, instead you’ll need to install the .vsix manually."
        );
    }

    Ok(())
}

fn install_server(opts: ServerOpt) -> Result<()> {
    let mut old_rust = false;
    if let Ok(stdout) = cmd!("cargo --version").read() {
        if !check_version(&stdout, REQUIRED_RUST_VERSION) {
            old_rust = true;
        }
    }

    if old_rust {
        eprintln!(
            "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
            REQUIRED_RUST_VERSION,
        )
    }
    let features = match opts.malloc {
        Malloc::System => &[][..],
        Malloc::Mimalloc => &["--features", "mimalloc"],
    };

    let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force {features...}");
    let res = cmd.run();

    if res.is_err() && old_rust {
        eprintln!(
            "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
            REQUIRED_RUST_VERSION,
        );
    }

    res?;
    Ok(())
}

fn check_version(version_output: &str, min_minor_version: u32) -> bool {
    // Parse second the number out of
    //      cargo 1.39.0-beta (1c6ec66d5 2019-09-30)
    let minor: Option<u32> = version_output.split('.').nth(1).and_then(|it| it.parse().ok());
    match minor {
        None => true,
        Some(minor) => minor >= min_minor_version,
    }
}
<commit_msg>Add client install support for `code-exploration` build.<commit_after>//! Installs rust-analyzer language server and/or editor plugin.

use std::{env, path::PathBuf, str};

use anyhow::{bail, format_err, Context, Result};
use xshell::{cmd, pushd};

// Latest stable, feel free to send a PR if this lags behind.
const REQUIRED_RUST_VERSION: u32 = 47;

pub struct InstallCmd {
    pub client: Option<ClientOpt>,
    pub server: Option<ServerOpt>,
}

#[derive(Clone, Copy)]
pub enum ClientOpt {
    VsCode,
    VsCodeExploration,
    VsCodeInsiders,
    VsCodium,
    VsCodeOss,
    Any,
}

impl ClientOpt {
    pub const fn as_cmds(&self) -> &'static [&'static str] {
        match self {
            ClientOpt::VsCode => &["code"],
            ClientOpt::VsCodeExploration => &["code-exploration"],
            ClientOpt::VsCodeInsiders => &["code-insiders"],
            ClientOpt::VsCodium => &["codium"],
            ClientOpt::VsCodeOss => &["code-oss"],
            ClientOpt::Any => &["code", "code-exploration", "code-insiders", "codium", "code-oss"],
        }
    }
}

impl Default for ClientOpt {
    fn default() -> Self {
        ClientOpt::Any
    }
}

impl std::str::FromStr for ClientOpt {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        [
            ClientOpt::VsCode,
            ClientOpt::VsCodeExploration,
            ClientOpt::VsCodeInsiders,
            ClientOpt::VsCodium,
            ClientOpt::VsCodeOss,
        ]
        .iter()
        .copied()
        .find(|c| [s] == c.as_cmds())
        .ok_or_else(|| anyhow::format_err!("no such client"))
    }
}

pub struct ServerOpt {
    pub malloc: Malloc,
}

pub enum Malloc {
    System,
    Mimalloc,
}

impl InstallCmd {
    pub fn run(self) -> Result<()> {
        if cfg!(target_os = "macos") {
            fix_path_for_mac().context("Fix path for mac")?
        }
        if let Some(server) = self.server {
            install_server(server).context("install server")?;
        }
        if let Some(client) = self.client {
            install_client(client).context("install client")?;
        }
        Ok(())
    }
}

fn fix_path_for_mac() -> Result<()> {
    let mut vscode_path: Vec<PathBuf> = {
        const COMMON_APP_PATH: &str =
            r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
        const ROOT_DIR: &str = "";
        let home_dir = match env::var("HOME") {
            Ok(home) => home,
            Err(e) => bail!("Failed getting HOME from environment with error: {}.", e),
        };

        [ROOT_DIR, &home_dir]
            .iter()
            .map(|dir| String::from(*dir) + COMMON_APP_PATH)
            .map(PathBuf::from)
            .filter(|path| path.exists())
            .collect()
    };

    if !vscode_path.is_empty() {
        let vars = match env::var_os("PATH") {
            Some(path) => path,
            None => bail!("Could not get PATH variable from env."),
        };

        let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
        paths.append(&mut vscode_path);
        let new_paths = env::join_paths(paths).context("build env PATH")?;
        env::set_var("PATH", &new_paths);
    }

    Ok(())
}

fn install_client(client_opt: ClientOpt) -> Result<()> {
    let _dir = pushd("./editors/code");

    let find_code = |f: fn(&str) -> bool| -> Result<&'static str> {
        client_opt.as_cmds().iter().copied().find(|bin| f(bin)).ok_or_else(|| {
            format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?")
        })
    };

    let installed_extensions = if cfg!(unix) {
        cmd!("npm --version").run().context("`npm` is required to build the VS Code plugin")?;
        cmd!("npm install").run()?;

        cmd!("npm run package --scripts-prepend-node-path").run()?;

        let code = find_code(|bin| cmd!("{bin} --version").read().is_ok())?;
        cmd!("{code} --install-extension rust-analyzer.vsix --force").run()?;
        cmd!("{code} --list-extensions").read()?
    } else {
        cmd!("cmd.exe /c npm --version")
            .run()
            .context("`npm` is required to build the VS Code plugin")?;
        cmd!("cmd.exe /c npm install").run()?;

        cmd!("cmd.exe /c npm run package").run()?;

        let code = find_code(|bin| cmd!("cmd.exe /c {bin}.cmd --version").read().is_ok())?;
        cmd!("cmd.exe /c {code}.cmd --install-extension rust-analyzer.vsix --force").run()?;
        cmd!("cmd.exe /c {code}.cmd --list-extensions").read()?
    };

    if !installed_extensions.contains("rust-analyzer") {
        bail!(
            "Could not install the Visual Studio Code extension. \
            Please make sure you have at least NodeJS 12.x together with the latest version of VS Code installed and try again. \
            Note that installing via xtask install does not work for VS Code Remote, instead you’ll need to install the .vsix manually."
        );
    }

    Ok(())
}

fn install_server(opts: ServerOpt) -> Result<()> {
    let mut old_rust = false;
    if let Ok(stdout) = cmd!("cargo --version").read() {
        if !check_version(&stdout, REQUIRED_RUST_VERSION) {
            old_rust = true;
        }
    }

    if old_rust {
        eprintln!(
            "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
            REQUIRED_RUST_VERSION,
        )
    }
    let features = match opts.malloc {
        Malloc::System => &[][..],
        Malloc::Mimalloc => &["--features", "mimalloc"],
    };

    let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force {features...}");
    let res = cmd.run();

    if res.is_err() && old_rust {
        eprintln!(
            "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
            REQUIRED_RUST_VERSION,
        );
    }

    res?;
    Ok(())
}

fn check_version(version_output: &str, min_minor_version: u32) -> bool {
    // Parse second the number out of
    //      cargo 1.39.0-beta (1c6ec66d5 2019-09-30)
    let minor: Option<u32> = version_output.split('.').nth(1).and_then(|it| it.parse().ok());
    match minor {
        None => true,
        Some(minor) => minor >= min_minor_version,
    }
}
<|endoftext|>


<commit_before>use std::{
    path::{Path, PathBuf},
    thread::sleep,
    time::Duration,
};

use isahc::{
    auth::{Authentication, Credentials},
    config::Configurable,
    http::StatusCode,
    HttpClient, ReadResponseExt, Request,
};
use itertools::Itertools;
use semver::Version;
use serde::{de::IgnoredAny, Deserialize};
use serde_json::json;
use toml::from_str as from_toml_str;
use xshell::{pushd, read_file};

use crate::{cmd, config, util::ask_yes_no, Result};

const CRATESIO_API: &str = "https://crates.io/api/v1/crates";
const GITHUB_API_RUMA: &str = "https://api.github.com/repos/ruma/ruma";

/// Task to create a new release of the given crate.
#[derive(Debug)]
pub struct ReleaseTask {
    /// The crate to release.
    local_crate: LocalCrate,

    /// The root of the workspace.
    project_root: PathBuf,

    /// The http client to use for requests.
    client: HttpClient,
}

impl ReleaseTask {
    /// Create a new `ReleaseTask` with the given `name` and `project_root`.
    pub(crate) fn new(name: String, project_root: PathBuf) -> Result<Self> {
        let local_crate = LocalCrate::new(name, &project_root)?;

        Ok(Self { local_crate, project_root, client: HttpClient::new()? })
    }

    /// Run the task to effectively create a release.
    pub(crate) fn run(self) -> Result<()> {
        let title = &self.title();
        let prerelease = self.local_crate.version.is_prerelease();
        println!(
            "Starting {} for {}…",
            match prerelease {
                true => "pre-release",
                false => "release",
            },
            title
        );

        if self.is_released()? {
            return Err("This crate version is already released".into());
        }

        let remote = Self::git_remote()?;

        println!("Checking status of git repository…");
        if !cmd!("git status -s -uno").read()?.is_empty()
            && !ask_yes_no("This git repository contains untracked files. Continue?")?
        {
            return Ok(());
        }

        if let Some(macros) = self.macros() {
            print!("Found macros crate. ");
            let _dir = pushd(&macros.path)?;
            macros.publish(&self.client)?;

            println!("Waiting 10 seconds for the release to make it into the crates.io index…");
            sleep(Duration::from_secs(10));

            println!("Resuming release of {}…", self.title());
        }

        let _dir = pushd(&self.local_crate.path)?;

        self.local_crate.publish(&self.client)?;

        if prerelease {
            println!("Pre-release created successfully!");
            return Ok(());
        }

        let changes = &self.local_crate.changes()?;

        let tag = &self.tag_name();

        println!("Creating git tag…");
        if cmd!("git tag -l {tag}").read()?.is_empty() {
            cmd!("git tag -s {tag} -m {title} -m {changes}").read()?;
        } else if !ask_yes_no("This tag already exists. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Pushing tag to remote repository…");
        if cmd!("git ls-remote --tags {remote} {tag}").read()?.is_empty() {
            cmd!("git push {remote} {tag}").run()?;
        } else if !ask_yes_no("This tag has already been pushed. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Creating release on GitHub…");
        let request_body = &json!({
            "tag_name": tag,
            "name": title,
            "body": changes.trim_softbreaks(),
        })
        .to_string();

        self.release(request_body)?;

        println!("Release created successfully!");

        Ok(())
    }

    /// Get the associated `-macros` crate of the current crate, if any.
    fn macros(&self) -> Option<LocalCrate> {
        LocalCrate::new(format!("{}-macros", self.local_crate.name), &self.project_root).ok()
    }

    /// Get the title of this release.
    fn title(&self) -> String {
        format!("{} {}", self.local_crate.name, self.local_crate.version)
    }

    /// Load the GitHub config from the config file.
    fn git_remote() -> Result<String> {
        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        let remote = cmd!("git config branch.{branch}.remote").read()?;

        if remote.is_empty() {
            return Err("Could not get current git remote".into());
        }

        Ok(remote)
    }

    /// Get the tag name for this release.
    fn tag_name(&self) -> String {
        format!("{}-{}", self.local_crate.name, self.local_crate.version)
    }

    /// Check if the tag for the current version of the crate has been pushed on GitHub.
    fn is_released(&self) -> Result<bool> {
        let response =
            self.client.get(format!("{}/releases/tags/{}", GITHUB_API_RUMA, self.tag_name()))?;

        Ok(response.status() == StatusCode::OK)
    }

    /// Create the release on GitHub with the given `config` and `credentials`.
    fn release(&self, body: &str) -> Result<()> {
        let config = config()?.github;

        let request = Request::post(format!("{}/releases", GITHUB_API_RUMA))
            .authentication(Authentication::basic())
            .credentials(Credentials::new(config.user, config.token))
            .header("Accept", "application/vnd.github.v3+json")
            .body(body)?;

        let mut response = self.client.send(request)?;

        if response.status() == StatusCode::CREATED {
            Ok(())
        } else {
            Err(format!("{}: {}", response.status(), response.text()?).into())
        }
    }
}

/// A local Rust crate.
#[derive(Debug)]
struct LocalCrate {
    /// The name of the crate.
    name: String,

    /// The version of the crate.
    version: Version,

    /// The local path of the crate.
    path: PathBuf,
}

impl LocalCrate {
    /// Creates a new `Crate` with the given name and project root.
    pub fn new(name: String, project_root: &Path) -> Result<Self> {
        let path = project_root.join(&name);

        let version = Self::version(&path)?;

        Ok(Self { name, version, path })
    }

    /// The current version of the crate at `path` from its manifest.
    fn version(path: &Path) -> Result<Version> {
        let manifest_toml = read_file(path.join("Cargo.toml"))?;
        let manifest: CargoManifest = from_toml_str(&manifest_toml)?;

        Ok(manifest.package.version)
    }

    /// The changes of the given version from the changelog.
    fn changes(&self) -> Result<String> {
        let changelog = read_file(self.path.join("CHANGELOG.md"))?;
        let lines_nb = changelog.lines().count();
        let mut lines = changelog.lines();

        let start = match lines.position(|l| l.starts_with(&format!("# {}", self.version))) {
            Some(p) => p + 1,
            None => {
                return Err("Could not find version title in changelog".into());
            }
        };

        let length = match lines.position(|l| l.starts_with("# ")) {
            Some(p) => p,
            None => lines_nb,
        };

        let changes = changelog.lines().skip(start).take(length).join("\n");

        Ok(changes.trim().to_owned())
    }

    /// Check if the current version of the crate is published on crates.io.
    fn is_published(&self, client: &HttpClient) -> Result<bool> {
        let response: CratesIoCrate =
            client.get(format!("{}/{}/{}", CRATESIO_API, self.name, self.version))?.json()?;

        Ok(response.version.is_some())
    }

    /// Publish this package on crates.io.
    fn publish(&self, client: &HttpClient) -> Result<()> {
        println!("Publishing {} {} on crates.io…", self.name, self.version);
        if self.is_published(client)? {
            if ask_yes_no("This version is already published. Skip this step and continue?")? {
                Ok(())
            } else {
                Err("Release interrupted by user.".into())
            }
        } else {
            Ok(cmd!("cargo publish").run()?)
        }
    }
}

/// The required cargo manifest data of a crate.
#[derive(Debug, Deserialize)]
struct CargoManifest {
    /// The package information.
    package: CargoPackage,
}

/// The required package information from a crate's cargo manifest.
#[derive(Debug, Deserialize)]
struct CargoPackage {
    /// The package version.
    version: Version,
}

/// A crate from the `GET /crates/{crate}` endpoint of crates.io.
#[derive(Deserialize)]
struct CratesIoCrate {
    version: Option<IgnoredAny>,
}

/// A tag from the `GET /repos/{owner}/{repo}/tags` endpoint of GitHub REST API.
#[derive(Debug, Deserialize)]
struct GithubTag {
    /// The name of the tag.
    name: String,
}

/// String manipulations for crate release.
trait StrExt {
    /// Remove soft line breaks as defined in CommonMark spec.
    fn trim_softbreaks(&self) -> String;
}

impl StrExt for str {
    fn trim_softbreaks(&self) -> String {
        let mut string = String::new();
        let mut s = self;

        while let Some(pos) = s.find('\n') {
            string.push_str(&s[..pos]);
            let pos_s = &s[pos..];

            if pos_s.starts_with("\n\n") {
                // Keep new paragraphs (multiple `\n`s).
                let next = pos_s.find(|c: char| c != '\n').unwrap_or(0);
                let (push, next_s) = pos_s.split_at(next);

                string.push_str(push);
                s = next_s;
            } else if s[..pos].ends_with("  ") || s[..pos].ends_with('\\') {
                // Keep hard line breaks (two spaces or a backslash before the line break).
                string.push('\n');
                s = &pos_s[1..];
            } else if let Some(p) = pos_s.find(|c: char| !c.is_ascii_whitespace()) {
                // Keep line break before list items (`\n` + whitespaces + `*` + whitespaces).
                // Remove line break and keep one space otherwise.
                let mut chars = pos_s.char_indices();
                let (_, char) = chars.find(|(i, _)| *i == p).unwrap();

                if char == '*' || char == '-' {
                    match chars.next() {
                        Some((_, next_char)) if next_char.is_ascii_whitespace() => {
                            string.push('\n');
                            s = &pos_s[1..];
                            continue;
                        }
                        _ => {}
                    }
                }

                string.push(' ');
                s = &pos_s[p..];
            }
        }

        string + s
    }
}
<commit_msg>xtask: Fail early in release if config.toml does not exist<commit_after>use std::{
    path::{Path, PathBuf},
    thread::sleep,
    time::Duration,
};

use isahc::{
    auth::{Authentication, Credentials},
    config::Configurable,
    http::StatusCode,
    HttpClient, ReadResponseExt, Request,
};
use itertools::Itertools;
use semver::Version;
use serde::{de::IgnoredAny, Deserialize};
use serde_json::json;
use toml::from_str as from_toml_str;
use xshell::{pushd, read_file};

use crate::{cmd, util::ask_yes_no, GithubConfig, Result};

const CRATESIO_API: &str = "https://crates.io/api/v1/crates";
const GITHUB_API_RUMA: &str = "https://api.github.com/repos/ruma/ruma";

/// Task to create a new release of the given crate.
#[derive(Debug)]
pub struct ReleaseTask {
    /// The crate to release.
    local_crate: LocalCrate,

    /// The root of the workspace.
    project_root: PathBuf,

    /// The http client to use for requests.
    http_client: HttpClient,

    /// The github configuration required to publish a release.
    config: GithubConfig,
}

impl ReleaseTask {
    /// Create a new `ReleaseTask` with the given `name` and `project_root`.
    pub(crate) fn new(name: String, project_root: PathBuf) -> Result<Self> {
        let local_crate = LocalCrate::new(name, &project_root)?;
        let config = crate::config()?.github;
        let http_client = HttpClient::new()?;

        Ok(Self { local_crate, project_root, http_client, config })
    }

    /// Run the task to effectively create a release.
    pub(crate) fn run(self) -> Result<()> {
        let title = &self.title();
        let prerelease = self.local_crate.version.is_prerelease();
        println!(
            "Starting {} for {}…",
            match prerelease {
                true => "pre-release",
                false => "release",
            },
            title
        );

        if self.is_released()? {
            return Err("This crate version is already released".into());
        }

        let remote = Self::git_remote()?;

        println!("Checking status of git repository…");
        if !cmd!("git status -s -uno").read()?.is_empty()
            && !ask_yes_no("This git repository contains untracked files. Continue?")?
        {
            return Ok(());
        }

        if let Some(macros) = self.macros() {
            print!("Found macros crate. ");
            let _dir = pushd(&macros.path)?;
            macros.publish(&self.http_client)?;

            println!("Waiting 10 seconds for the release to make it into the crates.io index…");
            sleep(Duration::from_secs(10));

            println!("Resuming release of {}…", self.title());
        }

        let _dir = pushd(&self.local_crate.path)?;

        self.local_crate.publish(&self.http_client)?;

        if prerelease {
            println!("Pre-release created successfully!");
            return Ok(());
        }

        let changes = &self.local_crate.changes()?;

        let tag = &self.tag_name();

        println!("Creating git tag…");
        if cmd!("git tag -l {tag}").read()?.is_empty() {
            cmd!("git tag -s {tag} -m {title} -m {changes}").read()?;
        } else if !ask_yes_no("This tag already exists. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Pushing tag to remote repository…");
        if cmd!("git ls-remote --tags {remote} {tag}").read()?.is_empty() {
            cmd!("git push {remote} {tag}").run()?;
        } else if !ask_yes_no("This tag has already been pushed. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Creating release on GitHub…");
        let request_body = &json!({
            "tag_name": tag,
            "name": title,
            "body": changes.trim_softbreaks(),
        })
        .to_string();

        self.release(request_body)?;

        println!("Release created successfully!");

        Ok(())
    }

    /// Get the associated `-macros` crate of the current crate, if any.
    fn macros(&self) -> Option<LocalCrate> {
        LocalCrate::new(format!("{}-macros", self.local_crate.name), &self.project_root).ok()
    }

    /// Get the title of this release.
    fn title(&self) -> String {
        format!("{} {}", self.local_crate.name, self.local_crate.version)
    }

    /// Load the GitHub config from the config file.
    fn git_remote() -> Result<String> {
        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        let remote = cmd!("git config branch.{branch}.remote").read()?;

        if remote.is_empty() {
            return Err("Could not get current git remote".into());
        }

        Ok(remote)
    }

    /// Get the tag name for this release.
    fn tag_name(&self) -> String {
        format!("{}-{}", self.local_crate.name, self.local_crate.version)
    }

    /// Check if the tag for the current version of the crate has been pushed on GitHub.
    fn is_released(&self) -> Result<bool> {
        let response = self.http_client.get(format!(
            "{}/releases/tags/{}",
            GITHUB_API_RUMA,
            self.tag_name()
        ))?;

        Ok(response.status() == StatusCode::OK)
    }

    /// Create the release on GitHub with the given `config` and `credentials`.
    fn release(&self, body: &str) -> Result<()> {
        let request = Request::post(format!("{}/releases", GITHUB_API_RUMA))
            .authentication(Authentication::basic())
            .credentials(Credentials::new(&self.config.user, &self.config.token))
            .header("Accept", "application/vnd.github.v3+json")
            .body(body)?;

        let mut response = self.http_client.send(request)?;

        if response.status() == StatusCode::CREATED {
            Ok(())
        } else {
            Err(format!("{}: {}", response.status(), response.text()?).into())
        }
    }
}

/// A local Rust crate.
#[derive(Debug)]
struct LocalCrate {
    /// The name of the crate.
    name: String,

    /// The version of the crate.
    version: Version,

    /// The local path of the crate.
    path: PathBuf,
}

impl LocalCrate {
    /// Creates a new `Crate` with the given name and project root.
    pub fn new(name: String, project_root: &Path) -> Result<Self> {
        let path = project_root.join(&name);

        let version = Self::version(&path)?;

        Ok(Self { name, version, path })
    }

    /// The current version of the crate at `path` from its manifest.
    fn version(path: &Path) -> Result<Version> {
        let manifest_toml = read_file(path.join("Cargo.toml"))?;
        let manifest: CargoManifest = from_toml_str(&manifest_toml)?;

        Ok(manifest.package.version)
    }

    /// The changes of the given version from the changelog.
    fn changes(&self) -> Result<String> {
        let changelog = read_file(self.path.join("CHANGELOG.md"))?;
        let lines_nb = changelog.lines().count();
        let mut lines = changelog.lines();

        let start = match lines.position(|l| l.starts_with(&format!("# {}", self.version))) {
            Some(p) => p + 1,
            None => {
                return Err("Could not find version title in changelog".into());
            }
        };

        let length = match lines.position(|l| l.starts_with("# ")) {
            Some(p) => p,
            None => lines_nb,
        };

        let changes = changelog.lines().skip(start).take(length).join("\n");

        Ok(changes.trim().to_owned())
    }

    /// Check if the current version of the crate is published on crates.io.
    fn is_published(&self, client: &HttpClient) -> Result<bool> {
        let response: CratesIoCrate =
            client.get(format!("{}/{}/{}", CRATESIO_API, self.name, self.version))?.json()?;

        Ok(response.version.is_some())
    }

    /// Publish this package on crates.io.
    fn publish(&self, client: &HttpClient) -> Result<()> {
        println!("Publishing {} {} on crates.io…", self.name, self.version);
        if self.is_published(client)? {
            if ask_yes_no("This version is already published. Skip this step and continue?")? {
                Ok(())
            } else {
                Err("Release interrupted by user.".into())
            }
        } else {
            Ok(cmd!("cargo publish").run()?)
        }
    }
}

/// The required cargo manifest data of a crate.
#[derive(Debug, Deserialize)]
struct CargoManifest {
    /// The package information.
    package: CargoPackage,
}

/// The required package information from a crate's cargo manifest.
#[derive(Debug, Deserialize)]
struct CargoPackage {
    /// The package version.
    version: Version,
}

/// A crate from the `GET /crates/{crate}` endpoint of crates.io.
#[derive(Deserialize)]
struct CratesIoCrate {
    version: Option<IgnoredAny>,
}

/// A tag from the `GET /repos/{owner}/{repo}/tags` endpoint of GitHub REST API.
#[derive(Debug, Deserialize)]
struct GithubTag {
    /// The name of the tag.
    name: String,
}

/// String manipulations for crate release.
trait StrExt {
    /// Remove soft line breaks as defined in CommonMark spec.
    fn trim_softbreaks(&self) -> String;
}

impl StrExt for str {
    fn trim_softbreaks(&self) -> String {
        let mut string = String::new();
        let mut s = self;

        while let Some(pos) = s.find('\n') {
            string.push_str(&s[..pos]);
            let pos_s = &s[pos..];

            if pos_s.starts_with("\n\n") {
                // Keep new paragraphs (multiple `\n`s).
                let next = pos_s.find(|c: char| c != '\n').unwrap_or(0);
                let (push, next_s) = pos_s.split_at(next);

                string.push_str(push);
                s = next_s;
            } else if s[..pos].ends_with("  ") || s[..pos].ends_with('\\') {
                // Keep hard line breaks (two spaces or a backslash before the line break).
                string.push('\n');
                s = &pos_s[1..];
            } else if let Some(p) = pos_s.find(|c: char| !c.is_ascii_whitespace()) {
                // Keep line break before list items (`\n` + whitespaces + `*` + whitespaces).
                // Remove line break and keep one space otherwise.
                let mut chars = pos_s.char_indices();
                let (_, char) = chars.find(|(i, _)| *i == p).unwrap();

                if char == '*' || char == '-' {
                    match chars.next() {
                        Some((_, next_char)) if next_char.is_ascii_whitespace() => {
                            string.push('\n');
                            s = &pos_s[1..];
                            continue;
                        }
                        _ => {}
                    }
                }

                string.push(' ');
                s = &pos_s[p..];
            }
        }

        string + s
    }
}
<|endoftext|>


<commit_before>use crate::cmp;
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen};
use crate::ops::{ControlFlow, Try};

/// An iterator that only iterates over the first `n` iterations of `iter`.
///
/// This `struct` is created by the [`take`] method on [`Iterator`]. See its
/// documentation for more.
///
/// [`take`]: Iterator::take
/// [`Iterator`]: trait.Iterator.html
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Take<I> {
    iter: I,
    n: usize,
}

impl<I> Take<I> {
    pub(in crate::iter) fn new(iter: I, n: usize) -> Take<I> {
        Take { iter, n }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> Iterator for Take<I>
where
    I: Iterator,
{
    type Item = <I as Iterator>::Item;

    #[inline]
    fn next(&mut self) -> Option<<I as Iterator>::Item> {
        if self.n != 0 {
            self.n -= 1;
            self.iter.next()
        } else {
            None
        }
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<I::Item> {
        if self.n > n {
            self.n -= n + 1;
            self.iter.nth(n)
        } else {
            if self.n > 0 {
                self.iter.nth(self.n - 1);
                self.n = 0;
            }
            None
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.n == 0 {
            return (0, Some(0));
        }

        let (lower, upper) = self.iter.size_hint();

        let lower = cmp::min(lower, self.n);

        let upper = match upper {
            Some(x) if x < self.n => Some(x),
            _ => Some(self.n),
        };

        (lower, upper)
    }

    #[inline]
    fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Output = Acc>,
    {
        fn check<'a, T, Acc, R: Try<Output = Acc>>(
            n: &'a mut usize,
            mut fold: impl FnMut(Acc, T) -> R + 'a,
        ) -> impl FnMut(Acc, T) -> ControlFlow<R, Acc> + 'a {
            move |acc, x| {
                *n -= 1;
                let r = fold(acc, x);
                if *n == 0 { ControlFlow::Break(r) } else { ControlFlow::from_try(r) }
            }
        }

        if self.n == 0 {
            try { init }
        } else {
            let n = &mut self.n;
            self.iter.try_fold(init, check(n, fold)).into_try()
        }
    }

    #[inline]
    fn fold<Acc, Fold>(mut self, init: Acc, fold: Fold) -> Acc
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        #[inline]
        fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
            move |acc, x| Ok(f(acc, x))
        }

        self.try_fold(init, ok(fold)).unwrap()
    }

    #[inline]
    #[rustc_inherit_overflow_checks]
    fn advance_by(&mut self, n: usize) -> Result<(), usize> {
        let min = crate::cmp::min(self.n, n);
        return match self.iter.advance_by(min) {
            Ok(_) => {
                self.n -= min;
                if min < n { Err(min) } else { Ok(()) }
            }
            ret @ Err(advanced) => {
                self.n -= advanced;
                ret
            }
        };
    }
}

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<S: Iterator, I: Iterator> SourceIter for Take<I>
where
    I: SourceIter<Source = S>,
{
    type Source = S;

    #[inline]
    unsafe fn as_inner(&mut self) -> &mut S {
        // SAFETY: unsafe function forwarding to unsafe function with the same requirements
        unsafe { SourceIter::as_inner(&mut self.iter) }
    }
}

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable> InPlaceIterable for Take<I> {}

#[stable(feature = "double_ended_take_iterator", since = "1.38.0")]
impl<I> DoubleEndedIterator for Take<I>
where
    I: DoubleEndedIterator + ExactSizeIterator,
{
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.n == 0 {
            None
        } else {
            let n = self.n;
            self.n -= 1;
            self.iter.nth_back(self.iter.len().saturating_sub(n))
        }
    }

    #[inline]
    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
        let len = self.iter.len();
        if self.n > n {
            let m = len.saturating_sub(self.n) + n;
            self.n -= n + 1;
            self.iter.nth_back(m)
        } else {
            if len > 0 {
                self.iter.nth_back(len - 1);
            }
            None
        }
    }

    #[inline]
    fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Output = Acc>,
    {
        if self.n == 0 {
            try { init }
        } else {
            let len = self.iter.len();
            if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() {
                try { init }
            } else {
                self.iter.try_rfold(init, fold)
            }
        }
    }

    #[inline]
    fn rfold<Acc, Fold>(mut self, init: Acc, fold: Fold) -> Acc
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        if self.n == 0 {
            init
        } else {
            let len = self.iter.len();
            if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() {
                init
            } else {
                self.iter.rfold(init, fold)
            }
        }
    }

    #[inline]
    fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
        let inner_len = self.iter.len();
        let len = self.n;
        let remainder = len.saturating_sub(n);
        let to_advance = inner_len - remainder;
        match self.iter.advance_back_by(to_advance) {
            Ok(_) => {
                self.n = remainder;
                if n > len {
                    return Err(len);
                }
                return Ok(());
            }
            _ => panic!("ExactSizeIterator contract violation"),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> ExactSizeIterator for Take<I> where I: ExactSizeIterator {}

#[stable(feature = "fused", since = "1.26.0")]
impl<I> FusedIterator for Take<I> where I: FusedIterator {}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<I: TrustedLen> TrustedLen for Take<I> {}
<commit_msg>from review: code style<commit_after>use crate::cmp;
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen};
use crate::ops::{ControlFlow, Try};

/// An iterator that only iterates over the first `n` iterations of `iter`.
///
/// This `struct` is created by the [`take`] method on [`Iterator`]. See its
/// documentation for more.
///
/// [`take`]: Iterator::take
/// [`Iterator`]: trait.Iterator.html
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Take<I> {
    iter: I,
    n: usize,
}

impl<I> Take<I> {
    pub(in crate::iter) fn new(iter: I, n: usize) -> Take<I> {
        Take { iter, n }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> Iterator for Take<I>
where
    I: Iterator,
{
    type Item = <I as Iterator>::Item;

    #[inline]
    fn next(&mut self) -> Option<<I as Iterator>::Item> {
        if self.n != 0 {
            self.n -= 1;
            self.iter.next()
        } else {
            None
        }
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<I::Item> {
        if self.n > n {
            self.n -= n + 1;
            self.iter.nth(n)
        } else {
            if self.n > 0 {
                self.iter.nth(self.n - 1);
                self.n = 0;
            }
            None
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.n == 0 {
            return (0, Some(0));
        }

        let (lower, upper) = self.iter.size_hint();

        let lower = cmp::min(lower, self.n);

        let upper = match upper {
            Some(x) if x < self.n => Some(x),
            _ => Some(self.n),
        };

        (lower, upper)
    }

    #[inline]
    fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Output = Acc>,
    {
        fn check<'a, T, Acc, R: Try<Output = Acc>>(
            n: &'a mut usize,
            mut fold: impl FnMut(Acc, T) -> R + 'a,
        ) -> impl FnMut(Acc, T) -> ControlFlow<R, Acc> + 'a {
            move |acc, x| {
                *n -= 1;
                let r = fold(acc, x);
                if *n == 0 { ControlFlow::Break(r) } else { ControlFlow::from_try(r) }
            }
        }

        if self.n == 0 {
            try { init }
        } else {
            let n = &mut self.n;
            self.iter.try_fold(init, check(n, fold)).into_try()
        }
    }

    #[inline]
    fn fold<Acc, Fold>(mut self, init: Acc, fold: Fold) -> Acc
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        #[inline]
        fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
            move |acc, x| Ok(f(acc, x))
        }

        self.try_fold(init, ok(fold)).unwrap()
    }

    #[inline]
    #[rustc_inherit_overflow_checks]
    fn advance_by(&mut self, n: usize) -> Result<(), usize> {
        let min = self.n.min(n);
        match self.iter.advance_by(min) {
            Ok(_) => {
                self.n -= min;
                if min < n { Err(min) } else { Ok(()) }
            }
            ret @ Err(advanced) => {
                self.n -= advanced;
                ret
            }
        }
    }
}

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<S: Iterator, I: Iterator> SourceIter for Take<I>
where
    I: SourceIter<Source = S>,
{
    type Source = S;

    #[inline]
    unsafe fn as_inner(&mut self) -> &mut S {
        // SAFETY: unsafe function forwarding to unsafe function with the same requirements
        unsafe { SourceIter::as_inner(&mut self.iter) }
    }
}

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable> InPlaceIterable for Take<I> {}

#[stable(feature = "double_ended_take_iterator", since = "1.38.0")]
impl<I> DoubleEndedIterator for Take<I>
where
    I: DoubleEndedIterator + ExactSizeIterator,
{
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.n == 0 {
            None
        } else {
            let n = self.n;
            self.n -= 1;
            self.iter.nth_back(self.iter.len().saturating_sub(n))
        }
    }

    #[inline]
    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
        let len = self.iter.len();
        if self.n > n {
            let m = len.saturating_sub(self.n) + n;
            self.n -= n + 1;
            self.iter.nth_back(m)
        } else {
            if len > 0 {
                self.iter.nth_back(len - 1);
            }
            None
        }
    }

    #[inline]
    fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> R,
        R: Try<Output = Acc>,
    {
        if self.n == 0 {
            try { init }
        } else {
            let len = self.iter.len();
            if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() {
                try { init }
            } else {
                self.iter.try_rfold(init, fold)
            }
        }
    }

    #[inline]
    fn rfold<Acc, Fold>(mut self, init: Acc, fold: Fold) -> Acc
    where
        Self: Sized,
        Fold: FnMut(Acc, Self::Item) -> Acc,
    {
        if self.n == 0 {
            init
        } else {
            let len = self.iter.len();
            if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() {
                init
            } else {
                self.iter.rfold(init, fold)
            }
        }
    }

    #[inline]
    fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
        let inner_len = self.iter.len();
        let len = self.n;
        let remainder = len.saturating_sub(n);
        let to_advance = inner_len - remainder;
        match self.iter.advance_back_by(to_advance) {
            Ok(_) => {
                self.n = remainder;
                if n > len {
                    return Err(len);
                }
                return Ok(());
            }
            _ => panic!("ExactSizeIterator contract violation"),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I> ExactSizeIterator for Take<I> where I: ExactSizeIterator {}

#[stable(feature = "fused", since = "1.26.0")]
impl<I> FusedIterator for Take<I> where I: FusedIterator {}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<I: TrustedLen> TrustedLen for Take<I> {}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(unused)]

use kernel::common::cells::VolatileCell;
use kernel::common::registers::ReadWrite;

// The hardware flash controller. Cannot be used in userspace (accessing will
// trigger a fault), and should only be manipulated by the flash hardware.
pub static mut H1B_HW: *const H1bHw = 0x40720000 as *const H1bHw;

const H1B_FLASH_START: usize     = 0x40000;
const H1B_FLASH_BANK_SIZE: usize = 0x40000;
const H1B_FLASH_SIZE: usize      = 0x80000; // Two banks

const H1B_INFO_0_START: usize    = 0x20000;
const H1B_INFO_1_START: usize    = 0x28000;
const H1B_INFO_SIZE: usize       = 0x00800;

register_bitfields![u32,
        TransactionParameters [
                Offset OFFSET(0) NUMBITS(16) [],
                Bank   OFFSET(16) NUMBITS(1) [],
                Size   OFFSET(17) NUMBITS(5) []
        ]
];

#[repr(C)]
pub struct H1bHw {
        /// Read/Program/Erase control for flash macro 0.
        _pe_control_0: VolatileCell<u32>,

        /// Read/Program/Erase control for flash macro 1.
        pe_control_1: VolatileCell<u32>,

        /// Write size and offset.
        transaction_parameters: ReadWrite<u32, TransactionParameters::Register>,

        /// Read/erase/program lockdown modes for the various flash regions.
        _lockdown_triggers: VolatileCell<u32>,

        /// Triggers a read of info 0 data to check for secure data write functionality. Only available
        /// in test mode.
        _enable_info0_shadow_read: VolatileCell<u32>,

        /// Operation-completion interrupt controls.
        _interrupt_control: VolatileCell<u32>,

        /// Operation-completion state. Cleared on read.
        _interrupt_state: VolatileCell<u32>,

        /// Macro 0 override signal unlock.
        _override_0_unlock: VolatileCell<u32>,

        /// Macro 1 override signal unlock.
        _override_1_unlock: VolatileCell<u32>,

        /// DIN override.
        _din_override: VolatileCell<u32>,

        /// Offset override.
        _offset_override: VolatileCell<u32>,

        /// Signal override values.
        _override_signal_values: VolatileCell<u32>,

        /// Signal override controls.
        _override_signal_control: VolatileCell<u32>,

        /// Controls whether the system may powerdown without waiting for graceful brownout completion.
        disable_brownout_wait: VolatileCell<u32>,

        /// The most recent value of Macro 0's DOUT.
        dout_value_0: VolatileCell<u32>,

        /// TC override read value.
        _tc_override_value_0: VolatileCell<u32>,

        /// The most recent value of Macro 1's DOUT.
        dout_value_1: VolatileCell<u32>,

        /// TC override read value.
        _tc_override_value_1: VolatileCell<u32>,

        /// Write data buffer.
        write_data: [VolatileCell<u32>; 32],

        /// Program and erase enable.
        program_erase_enable: VolatileCell<u32>,

        /// Redundancy remapping enablement and control for macro 0.
        redundancy_remapping_0: VolatileCell<u32>,

        /// Redundancy remapping enablement and control for macro 1.
        redundancy_remapping_1: VolatileCell<u32>,

        /// Error signalling.
        error_code: VolatileCell<u32>,

        /// Read operation duration.
        read_cycles: VolatileCell<u32>,

        /// The first cycle XE should be asserted during reads.
        read_xe_first_cycle: VolatileCell<u32>,

        /// The last cycle XE should be asserted during reads.
        read_xe_last_cycle: VolatileCell<u32>,

        /// The first cycle YE should be asserted during reads.
        read_ye_first_cycle: VolatileCell<u32>,

        /// The last cycle YE should be asserted during reads.
        read_ye_last_cycle: VolatileCell<u32>,

        /// The first cycle SE should be asserted during reads.
        read_se_first_cycle: VolatileCell<u32>,

        /// The last cycle SE should be asserted during reads.
        read_se_last_cycle: VolatileCell<u32>,

        /// The first cycle PV should be asserted during program verify reads.
        read_pv_first_cycle: VolatileCell<u32>,

        /// The last cycle PV should be asserted during program verify reads.
        read_pv_last_cycle: VolatileCell<u32>,

        /// The first cycle EV should be asserted during erase verify reads.
        read_ev_first_cycle: VolatileCell<u32>,

        /// The last cycle EV should be asserted during erase verify reads.
        read_ev_last_cycle: VolatileCell<u32>,

        /// Smart program algorithm enablement.
        enable_smart_program: VolatileCell<u32>,

        /// Single-word program timing.
        program_cycles: VolatileCell<u32>,

        /// The first cycle XE should be asserted during program.
        program_xe_first_cycle: VolatileCell<u32>,

        /// The last cycle XE should be asserted during program.
        program_xe_last_cycle: VolatileCell<u32>,

        /// The first cycle YE should be asserted during program.
        program_ye_first_cycle: VolatileCell<u32>,

        /// The last cycle YE should be asserted during program.
        program_ye_last_cycle: VolatileCell<u32>,

        /// The first cycle DIN and YADR are ready during program.
        program_din_yadr_first_cycle: VolatileCell<u32>,

        /// The last cycle DIN and YADR are ready during program.
        program_din_yadr_last_cycle: VolatileCell<u32>,

        /// The first cycle PROG should be asserted during program.
        program_prog_first_cycle: VolatileCell<u32>,

        /// The last cycle PROG should be asserted during program.
        program_prog_last_cycle: VolatileCell<u32>,

        /// The first cycle NVSTR should be asserted during program.
        program_nvstr_first_cycle: VolatileCell<u32>,

        /// The last cycle NVSTR should be asserted during program.
        program_nvstr_last_cycle: VolatileCell<u32>,

        /// Smart erase algorithm enablement.
        enable_smart_erase: VolatileCell<u32>,

        /// Flash erase cycle count.
        erase_cycles: VolatileCell<u32>,

        /// The first cycle XE should be asserted during erase.
        erase_xe_first_cycle: VolatileCell<u32>,

        /// The last cycle XE should be asserted during erase.
        erase_xe_last_cycle: VolatileCell<u32>,

        /// The first cycle ERASE should be asserted during erase.
        erase_erase_first_cycle: VolatileCell<u32>,

        /// The last cycle ERASE should be asserted during erase.
        erase_erase_last_cycle: VolatileCell<u32>,

        /// The first cycle NVSTR should be asserted during erase.
        erase_nvstr_first_cycle: VolatileCell<u32>,

        /// The last cycle NVSTR should be asserted during erase.
        erase_nvstr_last_cycle: VolatileCell<u32>,

        /// Enable smart erase for bulk erases.
        enable_bulk_smart_erase: VolatileCell<u32>,

        /// Duration of bulk erase operations.
        bulk_erase_cycles: VolatileCell<u32>,

        /// The first cycle XE should be asserted during bulkerase.
        bulkerase_xe_first_cycle: VolatileCell<u32>,

        /// The last cycle XE should be asserted during bulkerase.
        bulkerase_xe_last_cycle: VolatileCell<u32>,

        /// The first cycle BULKERASE should be asserted during bulkerase.
        bulkerase_bulkerase_first_cycle: VolatileCell<u32>,

        /// The last cycle BULKERASE should be asserted during bulkerase.
        bulkerase_bulkerase_last_cycle: VolatileCell<u32>,

        /// The first cycle MAS1 should be asserted during bulkerase.
        bulkerase_mas1_first_cycle: VolatileCell<u32>,

        /// The last cycle MAS1 should be asserted during bulkerase.
        bulkerase_mas1_last_cycle: VolatileCell<u32>,

        /// The first cycle NVSTR should be asserted during bulkerase.
        bulkerase_nvstr_first_cycle: VolatileCell<u32>,

        /// The last cycle NVSTR should be asserted during bulkerase.
        bulkerase_nvstr_last_cycle: VolatileCell<u32>,

        /// Controller debug signals.
        debug_signals: VolatileCell<u32>,

        // The integration test controls are omitted because they are unnecessary
        // and are after a large address space gap.
}

impl super::hardware::Hardware for H1bHw {
        fn is_programming(&self) -> bool {
                // TODO(jrvanwhy): Only checks the second flash bank.
                self.pe_control_1.get() != 0
        }

        fn read(&self, offset: usize) -> u32 {
            // The two flash macros are in consecutive memory locations, so they can
            // be addressed as one.
            if offset > H1B_FLASH_SIZE {
                0
            } else {
                unsafe { ::core::ptr::read_volatile((H1B_FLASH_START as *const u32).add(offset)) }
            }
        }

        fn read_error(&self) -> u16 {
                self.error_code.get() as u16
        }

        fn set_transaction(&self, offset: usize, size: usize) {
                use self::TransactionParameters::{Offset,Size};
                // The offset is relative to the beginning of the flash module. There
                // are 128 pages per flash module.
                // TODO(jrvanwhy): Assumes the read is from the second flash bank.
                if offset > H1B_FLASH_SIZE {
                   return; // TODO(pal): Fails silently!
                }

                let offset = offset - 128 * super::WORDS_PER_PAGE;
                self.transaction_parameters.write(Offset.val(offset as u32) + Size.val(size as u32));
        }

        fn set_write_data(&self, data: &[u32]) {
                for (i, &v) in data.iter().enumerate() { self.write_data[i].set(v); }
        }

        fn trigger(&self, opcode: u32) {
                self.program_erase_enable.set(0xb11924e1);
                // TODO(jrvanwhy): Assumes the write is to the second flash bank (where
                // the nvmem counter is).
                self.pe_control_1.set(opcode);
        }
}
<commit_msg>Make constants pub so other users of flash can refer to them.<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(unused)]

use kernel::common::cells::VolatileCell;
use kernel::common::registers::ReadWrite;

// The hardware flash controller. Cannot be used in userspace (accessing will
// trigger a fault), and should only be manipulated by the flash hardware.
pub static mut H1B_HW: *const H1bHw = 0x40720000 as *const H1bHw;

pub const H1B_FLASH_START: usize     = 0x40000;
pub const H1B_FLASH_BANK_SIZE: usize = 0x40000;
pub const H1B_FLASH_SIZE: usize      = 0x80000; // Two banks

pub const H1B_INFO_0_START: usize    = 0x20000;
pub const H1B_INFO_1_START: usize    = 0x28000;
pub const H1B_INFO_SIZE: usize       = 0x00800;

register_bitfields![u32,
        TransactionParameters [
                Offset OFFSET(0) NUMBITS(16) [],
                Bank   OFFSET(16) NUMBITS(1) [],
                Size   OFFSET(17) NUMBITS(5) []
        ]
];

#[repr(C)]
pub struct H1bHw {
        /// Read/Program/Erase control for flash macro 0.
        _pe_control_0: VolatileCell<u32>,

        /// Read/Program/Erase control for flash macro 1.
        pe_control_1: VolatileCell<u32>,

        /// Write size and offset.
        transaction_parameters: ReadWrite<u32, TransactionParameters::Register>,

        /// Read/erase/program lockdown modes for the various flash regions.
        _lockdown_triggers: VolatileCell<u32>,

        /// Triggers a read of info 0 data to check for secure data write functionality. Only available
        /// in test mode.
        _enable_info0_shadow_read: VolatileCell<u32>,

        /// Operation-completion interrupt controls.
        _interrupt_control: VolatileCell<u32>,

        /// Operation-completion state. Cleared on read.
        _interrupt_state: VolatileCell<u32>,

        /// Macro 0 override signal unlock.
        _override_0_unlock: VolatileCell<u32>,

        /// Macro 1 override signal unlock.
        _override_1_unlock: VolatileCell<u32>,

        /// DIN override.
        _din_override: VolatileCell<u32>,

        /// Offset override.
        _offset_override: VolatileCell<u32>,

        /// Signal override values.
        _override_signal_values: VolatileCell<u32>,

        /// Signal override controls.
        _override_signal_control: VolatileCell<u32>,

        /// Controls whether the system may powerdown without waiting for graceful brownout completion.
        disable_brownout_wait: VolatileCell<u32>,

        /// The most recent value of Macro 0's DOUT.
        dout_value_0: VolatileCell<u32>,

        /// TC override read value.
        _tc_override_value_0: VolatileCell<u32>,

        /// The most recent value of Macro 1's DOUT.
        dout_value_1: VolatileCell<u32>,

        /// TC override read value.
        _tc_override_value_1: VolatileCell<u32>,

        /// Write data buffer.
        write_data: [VolatileCell<u32>; 32],

        /// Program and erase enable.
        program_erase_enable: VolatileCell<u32>,

        /// Redundancy remapping enablement and control for macro 0.
        redundancy_remapping_0: VolatileCell<u32>,

        /// Redundancy remapping enablement and control for macro 1.
        redundancy_remapping_1: VolatileCell<u32>,

        /// Error signalling.
        error_code: VolatileCell<u32>,

        /// Read operation duration.
        read_cycles: VolatileCell<u32>,

        /// The first cycle XE should be asserted during reads.
        read_xe_first_cycle: VolatileCell<u32>,

        /// The last cycle XE should be asserted during reads.
        read_xe_last_cycle: VolatileCell<u32>,

        /// The first cycle YE should be asserted during reads.
        read_ye_first_cycle: VolatileCell<u32>,

        /// The last cycle YE should be asserted during reads.
        read_ye_last_cycle: VolatileCell<u32>,

        /// The first cycle SE should be asserted during reads.
        read_se_first_cycle: VolatileCell<u32>,

        /// The last cycle SE should be asserted during reads.
        read_se_last_cycle: VolatileCell<u32>,

        /// The first cycle PV should be asserted during program verify reads.
        read_pv_first_cycle: VolatileCell<u32>,

        /// The last cycle PV should be asserted during program verify reads.
        read_pv_last_cycle: VolatileCell<u32>,

        /// The first cycle EV should be asserted during erase verify reads.
        read_ev_first_cycle: VolatileCell<u32>,

        /// The last cycle EV should be asserted during erase verify reads.
        read_ev_last_cycle: VolatileCell<u32>,

        /// Smart program algorithm enablement.
        enable_smart_program: VolatileCell<u32>,

        /// Single-word program timing.
        program_cycles: VolatileCell<u32>,

        /// The first cycle XE should be asserted during program.
        program_xe_first_cycle: VolatileCell<u32>,

        /// The last cycle XE should be asserted during program.
        program_xe_last_cycle: VolatileCell<u32>,

        /// The first cycle YE should be asserted during program.
        program_ye_first_cycle: VolatileCell<u32>,

        /// The last cycle YE should be asserted during program.
        program_ye_last_cycle: VolatileCell<u32>,

        /// The first cycle DIN and YADR are ready during program.
        program_din_yadr_first_cycle: VolatileCell<u32>,

        /// The last cycle DIN and YADR are ready during program.
        program_din_yadr_last_cycle: VolatileCell<u32>,

        /// The first cycle PROG should be asserted during program.
        program_prog_first_cycle: VolatileCell<u32>,

        /// The last cycle PROG should be asserted during program.
        program_prog_last_cycle: VolatileCell<u32>,

        /// The first cycle NVSTR should be asserted during program.
        program_nvstr_first_cycle: VolatileCell<u32>,

        /// The last cycle NVSTR should be asserted during program.
        program_nvstr_last_cycle: VolatileCell<u32>,

        /// Smart erase algorithm enablement.
        enable_smart_erase: VolatileCell<u32>,

        /// Flash erase cycle count.
        erase_cycles: VolatileCell<u32>,

        /// The first cycle XE should be asserted during erase.
        erase_xe_first_cycle: VolatileCell<u32>,

        /// The last cycle XE should be asserted during erase.
        erase_xe_last_cycle: VolatileCell<u32>,

        /// The first cycle ERASE should be asserted during erase.
        erase_erase_first_cycle: VolatileCell<u32>,

        /// The last cycle ERASE should be asserted during erase.
        erase_erase_last_cycle: VolatileCell<u32>,

        /// The first cycle NVSTR should be asserted during erase.
        erase_nvstr_first_cycle: VolatileCell<u32>,

        /// The last cycle NVSTR should be asserted during erase.
        erase_nvstr_last_cycle: VolatileCell<u32>,

        /// Enable smart erase for bulk erases.
        enable_bulk_smart_erase: VolatileCell<u32>,

        /// Duration of bulk erase operations.
        bulk_erase_cycles: VolatileCell<u32>,

        /// The first cycle XE should be asserted during bulkerase.
        bulkerase_xe_first_cycle: VolatileCell<u32>,

        /// The last cycle XE should be asserted during bulkerase.
        bulkerase_xe_last_cycle: VolatileCell<u32>,

        /// The first cycle BULKERASE should be asserted during bulkerase.
        bulkerase_bulkerase_first_cycle: VolatileCell<u32>,

        /// The last cycle BULKERASE should be asserted during bulkerase.
        bulkerase_bulkerase_last_cycle: VolatileCell<u32>,

        /// The first cycle MAS1 should be asserted during bulkerase.
        bulkerase_mas1_first_cycle: VolatileCell<u32>,

        /// The last cycle MAS1 should be asserted during bulkerase.
        bulkerase_mas1_last_cycle: VolatileCell<u32>,

        /// The first cycle NVSTR should be asserted during bulkerase.
        bulkerase_nvstr_first_cycle: VolatileCell<u32>,

        /// The last cycle NVSTR should be asserted during bulkerase.
        bulkerase_nvstr_last_cycle: VolatileCell<u32>,

        /// Controller debug signals.
        debug_signals: VolatileCell<u32>,

        // The integration test controls are omitted because they are unnecessary
        // and are after a large address space gap.
}

impl super::hardware::Hardware for H1bHw {
        fn is_programming(&self) -> bool {
                // TODO(jrvanwhy): Only checks the second flash bank.
                self.pe_control_1.get() != 0
        }

        fn read(&self, offset: usize) -> u32 {
            // The two flash macros are in consecutive memory locations, so they can
            // be addressed as one.
            if offset > H1B_FLASH_SIZE {
                0
            } else {
                unsafe { ::core::ptr::read_volatile((H1B_FLASH_START as *const u32).add(offset)) }
            }
        }

        fn read_error(&self) -> u16 {
                self.error_code.get() as u16
        }

        fn set_transaction(&self, offset: usize, size: usize) {
                use self::TransactionParameters::{Offset,Size};
                // The offset is relative to the beginning of the flash module. There
                // are 128 pages per flash module.
                // TODO(jrvanwhy): Assumes the read is from the second flash bank.
                if offset > H1B_FLASH_SIZE {
                   return; // TODO(pal): Fails silently!
                }

                let offset = offset - 128 * super::WORDS_PER_PAGE;
                self.transaction_parameters.write(Offset.val(offset as u32) + Size.val(size as u32));
        }

        fn set_write_data(&self, data: &[u32]) {
                for (i, &v) in data.iter().enumerate() { self.write_data[i].set(v); }
        }

        fn trigger(&self, opcode: u32) {
                self.program_erase_enable.set(0xb11924e1);
                // TODO(jrvanwhy): Assumes the write is to the second flash bank (where
                // the nvmem counter is).
                self.pe_control_1.set(opcode);
        }
}
<|endoftext|>