use crate::style::{Color, Modifier, Style};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Cell {
pub symbol: String,
pub fg: Color,
pub bg: Color,
pub modifiers: Modifier,
}
impl Default for Cell {
fn default() -> Self {
Self {
symbol: " ".to_string(),
fg: Color::Reset,
bg: Color::Reset,
modifiers: Modifier::empty(),
}
}
}
impl Cell {
pub fn new(symbol: &str) -> Self {
Self {
symbol: symbol.to_string(),
..Default::default()
}
}
#[must_use]
pub fn fg(mut self, color: Color) -> Self {
self.fg = color;
self
}
#[must_use]
pub fn bg(mut self, color: Color) -> Self {
self.bg = color;
self
}
pub fn set_style(&mut self, style: Style) {
self.fg = style.fg;
self.bg = style.bg;
self.modifiers = style.modifiers;
}
pub fn set_symbol(&mut self, symbol: &str) {
self.symbol = symbol.to_string();
}
pub fn reset(&mut self) {
*self = Self::default();
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Buffer {
cells: Vec<Cell>,
width: u16,
height: u16,
}
impl Buffer {
pub fn new(width: u16, height: u16) -> Self {
let size = (width as usize) * (height as usize);
Self {
cells: vec![Cell::default(); size],
width,
height,
}
}
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
fn index_of(&self, x: u16, y: u16) -> usize {
(y as usize) * (self.width as usize) + (x as usize)
}
pub fn get(&self, x: u16, y: u16) -> &Cell {
let idx = self.index_of(x, y);
&self.cells[idx]
}
pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
let idx = self.index_of(x, y);
&mut self.cells[idx]
}
pub fn set(&mut self, x: u16, y: u16, cell: Cell) {
let idx = self.index_of(x, y);
self.cells[idx] = cell;
}
pub fn set_string(&mut self, x: u16, y: u16, text: &str, style: Style) {
let mut current_x = x;
for ch in text.chars() {
if current_x >= self.width {
break;
}
let cell = self.get_mut(current_x, y);
cell.set_symbol(&ch.to_string());
cell.set_style(style);
current_x += 1;
}
}
pub fn diff(old: &Buffer, new: &Buffer) -> Vec<(u16, u16, Cell)> {
let mut changes = Vec::new();
assert_eq!(old.width, new.width);
assert_eq!(old.height, new.height);
for y in 0..new.height {
for x in 0..new.width {
let old_cell = old.get(x, y);
let new_cell = new.get(x, y);
if old_cell != new_cell {
changes.push((x, y, new_cell.clone()));
}
}
}
changes
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cell_default() {
let cell = Cell::default();
assert_eq!(cell.symbol, " ");
assert_eq!(cell.fg, Color::Reset);
assert_eq!(cell.bg, Color::Reset);
}
#[test]
fn test_cell_new() {
let cell = Cell::new("X");
assert_eq!(cell.symbol, "X");
}
#[test]
fn test_cell_styled() {
let cell = Cell::new("A").fg(Color::Red).bg(Color::Blue);
assert_eq!(cell.symbol, "A");
assert_eq!(cell.fg, Color::Red);
assert_eq!(cell.bg, Color::Blue);
}
#[test]
fn test_buffer_new() {
let buf = Buffer::new(80, 24);
assert_eq!(buf.width(), 80);
assert_eq!(buf.height(), 24);
}
#[test]
fn test_buffer_filled_with_default() {
let buf = Buffer::new(10, 5);
for y in 0..5 {
for x in 0..10 {
assert_eq!(buf.get(x, y).symbol, " ");
}
}
}
#[test]
fn test_buffer_set_get() {
let mut buf = Buffer::new(10, 5);
buf.set(3, 2, Cell::new("X"));
assert_eq!(buf.get(3, 2).symbol, "X");
assert_eq!(buf.get(0, 0).symbol, " "); }
#[test]
fn test_buffer_set_string() {
let mut buf = Buffer::new(20, 5);
buf.set_string(2, 1, "Hello", Style::default());
assert_eq!(buf.get(2, 1).symbol, "H");
assert_eq!(buf.get(3, 1).symbol, "e");
assert_eq!(buf.get(4, 1).symbol, "l");
assert_eq!(buf.get(5, 1).symbol, "l");
assert_eq!(buf.get(6, 1).symbol, "o");
}
#[test]
fn test_buffer_diff_empty() {
let a = Buffer::new(10, 5);
let b = Buffer::new(10, 5);
let diff = Buffer::diff(&a, &b);
assert!(diff.is_empty());
}
#[test]
fn test_buffer_diff_single_change() {
let old = Buffer::new(10, 5);
let mut new = Buffer::new(10, 5);
new.set(3, 2, Cell::new("X"));
let diff = Buffer::diff(&old, &new);
assert_eq!(diff.len(), 1);
assert_eq!(diff[0].0, 3); assert_eq!(diff[0].1, 2); assert_eq!(diff[0].2.symbol, "X");
}
#[test]
fn test_buffer_diff_multiple_changes() {
let old = Buffer::new(10, 5);
let mut new = Buffer::new(10, 5);
new.set(0, 0, Cell::new("A"));
new.set(5, 3, Cell::new("B"));
new.set(9, 4, Cell::new("C"));
let diff = Buffer::diff(&old, &new);
assert_eq!(diff.len(), 3);
}
}