fatui 0.1.0

a silly little framework for terminal user interfaces
Documentation
use owo_colors::Style;

use crate::{
	grid::Grid,
	input::Nop,
	output::StyledChar,
	pos::{Size, X, Y},
};

use super::Frame;

const BLANK_CHAR: StyledChar = StyledChar::from_owo(Style::new().red().on_green().style('_'));
const WRITTEN_CHAR: StyledChar = StyledChar::from_owo(Style::new().yellow().on_black().style('#'));

macro_rules! draw_test {
	(
		$name:ident: |$frame:ident, ($x:expr, $y:expr)|
		$proc:expr => [$([$($ch:literal),*]),* $(,)?]
	) => {
		#[test]
		fn $name() {
			let mut cg = Grid::<StyledChar>::new(Size::new($x.into(), $y.into()));
			cg.all_cells().fill(BLANK_CHAR);
			{ // end the frame's lifetime earlier
				#[forbid(unused_variables)]
				#[allow(unused_mut)]
				let mut $frame = Frame::new(Nop, &mut cg);
				$proc
			}
			println!("wanted grid:");
			$(println!("  {}", [$($ch),*].iter().collect::<String>());)*
			println!("got grid:");
			for row in cg.rows() {
				print!("  ");
				for ch in row.iter() {
					print!("{}", ch.char);
				}
				println!();
			}
			let mut rows = cg.rows();
			let i = 0;
			$(
				let row = rows.next().expect("result too short");
				let chars = row.iter().map(|sc| sc.char).collect::<Vec<_>>();
				if &chars != &[$($ch),*] {
					panic!("row {i} is the first wrong");
				}
				#[allow(unused_variables)]
				let i = i + 1;
			)*
			assert!(rows.next().is_none(), "expected fewer rows");
		}
	}
}

draw_test! {
	fill: |f, (2, 2)| {
		f.fill(WRITTEN_CHAR);
	} => [
		['#', '#'],
		['#', '#'],
	]
}
draw_test! {
	h1_fill_l: |f, (2, 2)| {
		let (mut l, _) = f.split_h(X(1)).expect("couldn't split");
		l.fill(WRITTEN_CHAR);
	} => [
		['#', '_'],
		['#', '_'],
	]
}
draw_test! {
	h1_fill_r: |f, (2, 2)| {
		let (_, mut r) = f.split_h(X(1)).expect("couldn't split");
		r.fill(WRITTEN_CHAR);
	} => [
		['_', '#'],
		['_', '#'],
	]
}
draw_test! {
	v1_fill_t: |f, (2, 2)| {
		let (mut t, _) = f.split_v(Y(1)).expect("couldn't split");
		t.fill(WRITTEN_CHAR);
	} => [
		['#', '#'],
		['_', '_'],
	]
}
draw_test! {
	v1_fill_b: |f, (2, 2)| {
		let (_, mut b) = f.split_v(Y(1)).expect("couldn't split");
		println!("b={b:?}");
		b.fill(WRITTEN_CHAR);
	} => [
		['_', '_'],
		['#', '#'],
	]
}
draw_test! {
	hv_fill_tr: |f, (2, 2)| {
		let (_, r) = f.split_h(X(1)).expect("couldn't split_h");
		let (mut tr, _) = r.split_v(Y(1)).expect("couldn't split_v");
		tr.fill(WRITTEN_CHAR);
	} => [
		['_', '#'],
		['_', '_'],
	]
}
draw_test! {
	vh_fill_bl: |f, (2, 2)| {
		let (_, b) = f.split_v(Y(1)).expect("couldn't split_v");
		let (mut bl, _) = b.split_h(X(1)).expect("couldn't split_h");
		bl.fill(WRITTEN_CHAR);
	} => [
		['_', '_'],
		['#', '_'],
	]
}
draw_test! {
	gbr_fill: |f, (2, 2)| {
		f.vgrow(Size::ZERO, Size::rnew(2, 2));
		f.fill(WRITTEN_CHAR);
	} => [
		['#', '#'],
		['#', '#'],
	]
}
draw_test! {
	gbr_v1_fill_b: |f, (2, 2)| {
		f.vgrow(Size::ZERO, Size::rnew(2, 2));
		let (_, mut b) = f.split_v(Y(1)).expect("couldn't split");
		b.fill(WRITTEN_CHAR);
	} => [
		['_', '_'],
		['#', '#'],
	]
}
draw_test! {
	gtl_v1_fill_b: |f, (2, 2)| {
		f.vgrow(Size::rnew(2, 2), Size::ZERO);
		let (_, mut b) = f.split_v(Y(3)).expect("couldn't split");
		b.fill(WRITTEN_CHAR);
	} => [
		['_', '_'],
		['#', '#'],
	]
}