textris 0.2.0

Tetris on terminal!
Documentation
use block::Block;
use coord::Coord;
use std::iter;
use std::ops::{Index, IndexMut};
use std::slice::Iter;

type Line = Vec<Option<Block>>;
type Cells = Vec<Line>;

pub struct Field {
    cells: Cells,
    width: usize,
    height: usize,
}

fn make_line(width: usize) -> Line {
    iter::repeat(None).take(width).collect()
}

impl Field {
    pub fn new(width: usize, height: usize) -> Self {
        let cells = (0..height).map(|_| make_line(width)).collect();
        Field {
            cells,
            width,
            height,
        }
    }

    pub fn width(&self) -> usize {
        self.width
    }

    pub fn height(&self) -> usize {
        self.height
    }

    pub fn is_in_range(&self, pos: Coord) -> bool {
        let w = self.width as i8;
        let h = self.height as i8;
        0 <= pos.x() && pos.x() < w && 0 <= pos.y() && pos.y() < h
    }

    fn is_above_ceil(&self, pos: Coord) -> bool {
        pos.y() < 0 && 0 <= pos.x() && pos.x() < self.width as i8
    }

    pub fn is_movable(&self, coords: &[Coord]) -> bool {
        coords
            .iter()
            .all(|&c| self.is_above_ceil(c) || self.is_in_range(c) && self[c].is_none())
    }

    pub fn is_reached(&self) -> bool {
        self.cells[0].iter().any(|c| c.is_some())
    }

    pub fn clear_blocks(&mut self, coords: &[Coord]) {
        for &pos in coords {
            if self.is_in_range(pos) {
                self[pos] = None;
            }
        }
    }

    pub fn render_blocks(&mut self, block: Block, coords: &[Coord]) {
        for &pos in coords {
            if self.is_in_range(pos) {
                self[pos] = Some(block);
            }
        }
    }

    pub fn lines_iter(&self) -> Iter<Line> {
        self.cells.iter()
    }

    pub fn get_line(&self, y: usize) -> &Line {
        &self.cells[y]
    }

    pub fn set_line(&mut self, y: usize, line: Line) {
        if y < self.height {
            self.cells[y] = line;
        }
    }

    pub fn delete_line(&mut self, idx: usize) {
        self.cells.remove(idx);
        self.cells.insert(0, make_line(self.width));
    }
}

impl Index<Coord> for Field {
    type Output = Option<Block>;

    fn index(&self, index: Coord) -> &Self::Output {
        if !self.is_in_range(index) {
            return &None;
        }
        let Coord(x, y) = index;
        &self.cells[y as usize][x as usize]
    }
}

impl IndexMut<Coord> for Field {
    fn index_mut(&mut self, index: Coord) -> &mut Self::Output {
        let Coord(x, y) = index;
        &mut self.cells[y as usize][x as usize]
    }
}