use ratatui::layout::Alignment;
use ratatui::style::{Color, Style};
use ratatui::text::{Line, Span};
pub fn render_frame(
frame_rows: &[&str],
palette_fn: impl Fn(char) -> Option<Color>,
cols: usize,
) -> Vec<Line<'static>> {
frame_rows
.iter()
.map(|row| {
let chars: Vec<char> = row.chars().collect();
let spans: Vec<Span> = (0..cols)
.map(|col| {
let key = chars.get(col).copied().unwrap_or(' ');
match palette_fn(key) {
Some(color) => Span::styled("██", Style::default().fg(color)),
None => Span::raw(" "),
}
})
.collect();
Line::from(spans).alignment(Alignment::Center)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::{render_frame, Color};
#[test]
fn renders_double_cell_pixels_per_glyph() {
let frame = &["#.#", ".#."];
let palette = |c: char| -> Option<Color> {
if c == '#' {
Some(Color::Rgb(255, 0, 0))
} else {
None
}
};
let lines = render_frame(frame, palette, 3);
assert_eq!(lines.len(), 2, "one line per row");
for line in &lines {
assert_eq!(line.spans.len(), 3, "one span per column");
for span in &line.spans {
assert_eq!(
span.content.chars().count(),
2,
"every cell renders to exactly two terminal cells",
);
}
}
assert_eq!(lines[0].spans[0].content.as_ref(), "██");
assert_eq!(lines[0].spans[0].style.fg, Some(Color::Rgb(255, 0, 0)));
assert_eq!(lines[0].spans[1].content.as_ref(), " ");
assert_eq!(lines[0].spans[1].style.fg, None);
}
#[test]
fn pads_short_rows_via_palette_default() {
let frame = &["##"];
let palette = |c: char| -> Option<Color> {
if c == '#' {
Some(Color::Rgb(0, 255, 0))
} else {
None
}
};
let lines = render_frame(frame, palette, 5);
assert_eq!(lines[0].spans.len(), 5);
for (i, span) in lines[0].spans.iter().enumerate() {
if i < 2 {
assert_eq!(span.content.as_ref(), "██");
} else {
assert_eq!(
span.content.as_ref(),
" ",
"col {i} should be transparent pad"
);
}
}
}
#[test]
fn empty_input_yields_empty_output() {
let lines = render_frame(&[], |_| Some(Color::Black), 10);
assert!(lines.is_empty());
}
}