fatui 0.1.0

a silly little framework for terminal user interfaces
Documentation
//! a generic grid of things
//!
//! this acts a bit like a 2d `Vec`:
//! - it has a width and height, instead of just a length
//! - if resized, it fills in any added rows/columns with the default
//! -

use std::{
	fmt,
	ops::{Deref, DerefMut, Index, IndexMut},
};

use crate::pos::{Size, X, Y};

/// a 2d grid of something
///
/// this is meant for `Default + Copy` types.
/// if you *really* need a grid of big, uncopyable types --
/// consider using Grid<Option<&T>>, with a separate backing storage!
/// or send me an email with your very convincing use-case: badgrid@genderphas.ing
pub struct Grid<T: Default + Copy> {
	size: Size,
	cells: Vec<T>,
}

impl<T: Default + Copy> Grid<T> {
	/// create a new grid, with the function determining what cells have what values
	pub fn new_with(size: Size, mut cell: impl FnMut(X, Y) -> T) -> Self {
		// slightly weird construction here to avoid lifetime issues; tldr:
		// - first we assemble the iterator of (X, Y) in cellvec order
		// - then we map the cell-making function over that
		// - otherwise we can't move `y` without moving `cell`, oops, lol
		let cells = (0..size.height.0)
			.flat_map(|y| (0..size.width.0).map(move |x| (X(x), Y(y))))
			.map(|(x, y)| cell(x, y))
			.collect();
		Self { size, cells }
	}

	/// create a new grid of the default value with the given dimensions
	pub fn new(size: Size) -> Self {
		Self { size, cells: vec![T::default(); size.area()] }
	}

	/// get the size of this grid
	#[inline]
	pub fn size(&self) -> Size {
		self.size
	}

	/// get the width of this grid
	#[inline]
	pub fn width(&self) -> X {
		self.size.width
	}
	/// get the height of this grid
	#[inline]
	pub fn height(&self) -> Y {
		self.size.height
	}
	/// whether this grid is empty
	///
	/// see [`Size::is_empty`].
	#[inline]
	pub fn is_empty(&self) -> bool {
		self.size.is_empty()
	}

	/// resize this grid to the given size
	///
	/// this will intelligently handle rows and columns, i.e.
	/// it's not just a `self.cells.resize` --
	/// instead it will rearrange the backing buffer so that
	/// additional width leads to columns of `T::default()` on the right
	/// and additional height leads to blank rows on the bottom,
	/// while diminishing those directions neatly trims things.
	pub fn resize(&mut self, new: Size) {
		let minheight = self.height().min(new.height).0;
		if new.width < self.width() {
			// for each row, in ascending order:
			for row in 0..minheight {
				// copy its shortened data...
				let os = row * self.width().0;
				let oe = os + new.width.0;
				// ...to its new home.
				let ns = row * new.width.0;
				self.cells.copy_within(os..oe, ns);
			}
		}
		self.cells.resize(new.width.0 * new.height.0, T::default());
		if new.width > self.width() {
			// for each row, in DEscending order:
			for row in 0..minheight {
				let row = minheight - 1 - row;
				// copy its full data...
				let os = row * self.width().0;
				let oe = os + self.width().0;
				// ...to its new home...
				let ns = row * new.width.0;
				let bs = ns + self.width().0;
				let be = ns + new.width.0;
				self.cells.copy_within(os..oe, ns);
				// ...and then fill the rest with blanks
				self.cells[bs..be].fill(T::default());
			}
		} else if self.height() < new.height {
			// narrow + tallen can lead to the last cells
			// being copied earlier, but still in the length of the vec
			let bs = new.width.0 * self.height().0;
			self.cells[bs..].fill(T::default());
		}
		self.size = new;
	}

	/// a slice of every character in this array
	///
	/// you should use this fairly rarely.
	/// it mostly makes sense in tests,
	/// or when you want to fill the entire grid.
	/// if you want to target a specific location
	/// it's much more advisable to use indexing by [`X`]/[`Y`].
	pub fn all_cells(&mut self) -> &mut [T] {
		&mut self.cells
	}
}

impl<T: Default + Copy + Into<char>> fmt::Debug for Grid<T> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		let mut rvs = Vec::with_capacity(self.height().0);
		for row in self.rows() {
			let rv: String = row.iter().copied().map(|c| c.into()).collect();
			rvs.push(rv);
		}
		f.debug_struct("Grid")
			.field("size", &Size::new(self.width(), self.height()))
			.field("rows", &rvs)
			.finish()
	}
}

macro_rules! stripe {
	(
		$fn:ident($pos:ident: $ptype:ident) -> $type:ident:
		$xtype:ident, $dim:ident, |$self:ident, $index:ident| $get:expr
	) => {
		paste::paste! {
			impl<T: Default + Copy> Grid<T> {
				#[doc = concat!("get the nth ", stringify!($fn), " in the grid")]
				pub fn $fn<'g>(&'g self, $pos: $ptype) -> Option<$type<'g, T>> {
					if $pos >= self.$dim() {
						return None;
					}
					Some($type {
						grid: self,
						$pos: $pos,
					})
				}
				#[doc = concat!("get the nth ", stringify!($fn), " in the grid for writing")]
				pub fn [<$fn _mut>]<'g>(&'g mut self, $pos: $ptype) -> Option<[<$type Mut>]<'g, T>> {
					if $pos >= self.$dim() {
						return None;
					}
					Some([<$type Mut>] {
						grid: self,
						$pos: $pos,
					})
				}
				#[doc = concat!("iterate over all ", stringify!($fn), "s in the grid")]
				///
				/// there's no equivalent `_mut` because of how [`Iterator`] works --
				/// there's no way to enforce that you got rid of the previous [`RowMut`],
				/// so the implementation can't know it's safe to yield one.
				pub fn [<$fn s>]<'g>(&'g self) -> impl Iterator<Item = $type<'g, T>> {
					[<$type sIter>] {
						grid: self,
						$pos: $ptype(0),
					}
				}
			}

			#[doc = concat!("the nth ", stringify!($fn), " in the grid")]
			pub struct $type<'g, T: Default + Copy> {
				grid: &'g Grid<T>,
				$pos: $ptype,
			}
			impl<'g, T: Default + Copy> Index<$xtype> for $type<'g, T> {
				type Output = T;
				fn index(&self, index: $xtype) -> &Self::Output {
					let $self = self;
					let $index = index;
					&self.grid.cells[$get]
				}
			}
			#[doc = concat!("the nth ", stringify!($fn), " in the grid, mutably referenced")]
			pub struct [<$type Mut>]<'g, T: Default + Copy> {
				grid: &'g mut Grid<T>,
				$pos: $ptype,
			}
			impl<'g, T: Default + Copy> Index<$xtype> for [<$type Mut>]<'g, T> {
				type Output = T;
				fn index(&self, index: $xtype) -> &Self::Output {
					let $self = self;
					let $index = index;
					&self.grid.cells[$get]
				}
			}
			impl<'g, T: Default + Copy> IndexMut<$xtype> for [<$type Mut>]<'g, T> {
				fn index_mut(&mut self, index: $xtype) -> &mut Self::Output {
					let $self = &mut *self;
					let $index = index;
					&mut $self.grid.cells[$get]
				}
			}
			struct [<$type sIter>]<'g, T: Default + Copy> {
				grid: &'g Grid<T>,
				$pos: $ptype,
			}
			impl<'g, T: Default + Copy> Iterator for [<$type sIter>]<'g, T> {
				type Item = $type<'g, T>;
				fn next(&mut self) -> Option<Self::Item> {
					let stripe = self.grid.$fn(self.$pos)?;
					self.$pos += $ptype(1);
					Some(stripe)
				}
			}
		}
	};
}

stripe!(row(y: Y) -> Row: X, height, |s, i| s.y.0 * s.grid.size.width.0 + i.0);
stripe!(col(x: X) -> Col: Y, width, |s, i| i.0 * s.grid.size.width.0 + s.x.0);

impl<'g, T: Default + Copy> Deref for Row<'g, T> {
	type Target = [T];
	fn deref(&self) -> &Self::Target {
		let start = self.y.0 * self.grid.width().0;
		let end = start + self.grid.width().0;
		&self.grid.cells[start..end]
	}
}
impl<'g, T: Default + Copy> Deref for RowMut<'g, T> {
	type Target = [T];
	fn deref(&self) -> &Self::Target {
		let start = self.y.0 * self.grid.width().0;
		let end = start + self.grid.width().0;
		&self.grid.cells[start..end]
	}
}
impl<'g, T: Default + Copy> DerefMut for RowMut<'g, T> {
	fn deref_mut(&mut self) -> &mut Self::Target {
		let start = self.y.0 * self.grid.width().0;
		let end = start + self.grid.width().0;
		&mut self.grid.cells[start..end]
	}
}