intuitive 0.6.3

a library for building declarative text-based user interfaces
Documentation
use tui::{
  buffer::Buffer,
  layout::{Constraint, Rect},
  style::{Modifier, Style},
  text::{Span, Spans},
  widgets::{Cell as TuiCell, Row, Table as TuiTable, Widget},
};

use super::alignment::Alignment;

type Rows<'a, const N: usize> = Vec<[Spans<'a>; N]>;

pub struct Table<'a, const N: usize> {
  pub rows: Rows<'a, N>,
  pub alignments: [Alignment; N],
}

impl<'a, const N: usize> Table<'a, N> {
  pub fn new(rows: Rows<'a, N>, alignments: [Alignment; N]) -> Self {
    Self { rows, alignments }
  }

  fn lengths(&self) -> Vec<[usize; N]> {
    self
      .rows
      .iter()
      .map(|row| {
        let mut lengths = [0; N];
        for (i, cell) in row.iter().enumerate() {
          lengths[i] = Spans::width(cell);
        }

        lengths
      })
      .collect()
  }

  #[allow(clippy::cast_possible_truncation)]
  fn constraints(&self) -> [Constraint; N] {
    let lengths: Vec<[usize; N]> = self.lengths();
    let mut constraints = [0; N];
    for i in 0..N {
      constraints[i] = lengths.iter().map(|l| l[i]).max().unwrap_or_default() as u16;
    }

    constraints.map(Constraint::Length)
  }

  fn aligned_rows(rows: Rows<'a, N>, alignments: &[Alignment; N], constraints: &[Constraint; N]) -> Vec<Row<'a>> {
    rows
      .into_iter()
      .map(|mut row| {
        for i in 0..N {
          let (spans, alignment, constraint) = (&mut row[i], &alignments[i], constraints[i]);
          if *alignment == Alignment::Right {
            if let Constraint::Length(width) = constraint {
              spans.0.insert(0, Span::raw(" ".repeat((width as usize) - spans.width())));
            }
          }
        }

        Row::new(row.into_iter().map(TuiCell::from).collect::<Vec<_>>())
      })
      .collect()
  }

  fn widget(self, constraints: &'a [Constraint; N]) -> TuiTable<'a> {
    TuiTable::new(Self::aligned_rows(self.rows, &self.alignments, constraints))
      .highlight_style(Style::default().add_modifier(Modifier::BOLD))
      .widths(constraints)
  }
}

impl<'a, const N: usize> Widget for Table<'a, N> {
  fn render(self, area: Rect, buf: &mut Buffer) {
    let constraints = self.constraints();

    Widget::render(self.widget(&constraints), area, buf);
  }
}