use crate::error::ImageError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Rgb {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Rgb {
pub const BLACK: Self = Self { r: 0, g: 0, b: 0 };
pub const WHITE: Self = Self {
r: 255,
g: 255,
b: 255,
};
#[inline]
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Cell {
pub ch: char,
pub color: Rgb,
}
impl Default for Cell {
fn default() -> Self {
Self {
ch: ' ',
color: Rgb::BLACK,
}
}
}
impl Cell {
#[inline]
pub const fn new(ch: char, color: Rgb) -> Self {
Self { ch, color }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Grid {
width: u32,
height: u32,
cells: Vec<Cell>,
}
impl Grid {
pub(crate) fn new(width: u32, height: u32) -> Self {
let len = (width as usize) * (height as usize);
Self {
width,
height,
cells: vec![Cell::default(); len],
}
}
#[allow(dead_code)] pub(crate) fn from_cells(
width: u32,
height: u32,
cells: Vec<Cell>,
) -> Result<Self, ImageError> {
let expected_len = (width as usize) * (height as usize);
if cells.len() != expected_len {
return Err(ImageError::BufferSizeMismatch(expected_len, cells.len()));
}
Ok(Self {
width,
height,
cells,
})
}
#[inline]
pub fn width(&self) -> u32 {
self.width
}
#[inline]
pub fn height(&self) -> u32 {
self.height
}
#[inline]
pub fn get(&self, x: u32, y: u32) -> Option<&Cell> {
if x >= self.width || y >= self.height {
return None;
}
let idx = (y * self.width + x) as usize;
self.cells.get(idx)
}
#[inline]
pub fn row(&self, y: u32) -> Option<&[Cell]> {
if y >= self.height {
return None;
}
let start = (y * self.width) as usize;
let end = start + (self.width as usize);
Some(&self.cells[start..end])
}
#[inline]
pub fn rows(&self) -> std::slice::Chunks<'_, Cell> {
self.cells.chunks((self.width as usize).max(1))
}
#[inline]
pub fn as_slice(&self) -> &[Cell] {
&self.cells
}
#[inline]
#[allow(dead_code)]
pub(crate) fn get_mut(&mut self, x: u32, y: u32) -> Option<&mut Cell> {
if x >= self.width || y >= self.height {
return None;
}
let idx = (y * self.width + x) as usize;
self.cells.get_mut(idx)
}
#[inline]
pub(crate) fn row_mut(&mut self, y: u32) -> Option<&mut [Cell]> {
if y >= self.height {
return None;
}
let start = (y * self.width) as usize;
let end = start + (self.width as usize);
Some(&mut self.cells[start..end])
}
#[inline]
#[allow(dead_code)]
pub(crate) fn rows_mut(&mut self) -> std::slice::ChunksMut<'_, Cell> {
self.cells.chunks_mut((self.width as usize).max(1))
}
#[cfg_attr(not(feature = "parallel"), allow(dead_code))]
#[inline]
pub(crate) fn as_mut_slice(&mut self) -> &mut [Cell] {
&mut self.cells
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::ImageError;
#[test]
fn test_grid_indexing_and_accessors() {
let mut cells = vec![Cell::default(); 6];
cells[0] = Cell::new('A', Rgb::new(255, 0, 0));
cells[5] = Cell::new('Z', Rgb::new(0, 255, 0));
let grid = Grid::from_cells(3, 2, cells).unwrap();
assert_eq!(grid.width(), 3);
assert_eq!(grid.height(), 2);
assert_eq!(grid.get(0, 0).unwrap().ch, 'A');
assert_eq!(grid.get(2, 1).unwrap().ch, 'Z');
assert!(grid.get(3, 0).is_none());
let row1 = grid.row(1).unwrap();
assert_eq!(row1.len(), 3);
assert_eq!(row1[2].ch, 'Z');
}
#[test]
fn test_grid_buffer_mismatch() {
let cells = vec![Cell::default(); 5]; let res = Grid::from_cells(3, 2, cells);
assert!(matches!(res, Err(ImageError::BufferSizeMismatch(6, 5))));
}
}