use std::fmt::Display;
use crate::{format::Format, utils::terminal_size};
#[derive(Debug)]
pub struct Cell {
content: String,
format: Format,
}
impl Cell {
pub fn new(content: impl Into<String>) -> Self {
Self {
content: content.into(),
format: Format::None,
}
}
pub fn format(mut self, format: Format) -> Self {
self.format = format;
self
}
}
impl Display for Cell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.format)?;
f.pad(&self.content)?;
write!(f, "{}", Format::Reset)
}
}
#[derive(Debug)]
pub struct Table {
padding: usize,
cells: Vec<Vec<Cell>>,
center: bool,
}
impl Default for Table {
fn default() -> Self {
Self {
padding: 2,
cells: Vec::new(),
center: false,
}
}
}
impl Table {
pub fn padding(mut self, padding: usize) -> Self {
self.padding = padding;
self
}
pub fn center(mut self) -> Self {
self.center = true;
self
}
pub fn row(&mut self, row: Vec<Cell>) {
self.cells.push(row);
}
pub fn len(&self) -> usize {
self.cells.len()
}
pub fn is_empty(&self) -> bool {
self.cells.is_empty()
}
}
impl Display for Table {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let n_cols = match self.cells.first() {
Some(row) => row.len(),
None => return Ok(()),
};
let widths: Vec<usize> = (0..n_cols)
.map(|i| {
self.cells
.iter()
.filter_map(|row| row.get(i))
.map(|cell| cell.content.chars().count())
.max()
.unwrap_or(0)
+ self.padding
})
.collect();
let cols = terminal_size().map(|(cols, _)| cols).unwrap_or(u16::MAX);
for row in self.cells.iter() {
let mut cols_left = cols as usize;
for (cell, width) in row.iter().zip(widths.iter()) {
match cols_left.checked_sub(cell.content.len()) {
Some(_) => match cols_left.checked_sub(*width) {
Some(new) => {
cols_left = new;
if self.center {
write!(f, "{:^width$}", cell)?;
} else {
write!(f, "{:<width$}", cell)?;
}
}
None => write!(f, "{cell}")?,
},
None => {
cell.content
.chars()
.take(cols_left - 1)
.try_for_each(|c| write!(f, "{c}"))?;
write!(f, "…")?;
break;
}
}
}
writeln!(f)?;
}
Ok(())
}
}