use nu_ansi_term::Style;
use unicode_width::UnicodeWidthStr;
pub struct Column {
pub header: String,
pub style: Option<Style>,
}
impl Column {
pub fn new(header: impl Into<String>) -> Self {
Self { header: header.into(), style: None }
}
pub fn with_style(mut self, style: Style) -> Self {
self.style = Some(style);
self
}
}
pub struct Row {
pub cells: Vec<String>,
pub style: Option<Style>,
}
impl Row {
pub fn new(cells: Vec<impl Into<String>>) -> Self {
Self {
cells: cells.into_iter().map(|c| c.into()).collect(),
style: None,
}
}
pub fn with_style(mut self, style: Style) -> Self {
self.style = Some(style);
self
}
}
pub fn print_table(columns: &[Column], rows: &[Row]) {
if rows.is_empty() {
return;
}
let num_cols = columns.len();
let mut widths: Vec<usize> = columns.iter().map(|c| c.header.width()).collect();
for row in rows {
for (i, cell) in row.cells.iter().enumerate() {
if i < num_cols {
widths[i] = widths[i].max(cell.width());
}
}
}
let header_line: String = columns
.iter()
.enumerate()
.map(|(i, col)| {
let padded = format!("{:<width$}", col.header, width = widths[i]);
let style = col.style.unwrap_or_default();
format!("{}", style.paint(&padded))
})
.collect::<Vec<_>>()
.join(" ");
println!("{}", header_line);
let sep: String = widths.iter().map(|w| "─".repeat(*w)).collect::<Vec<_>>().join(" ");
println!("{}", sep);
for row in rows {
let line: String = row
.cells
.iter()
.enumerate()
.map(|(i, cell)| {
let w = widths.get(i).copied().unwrap_or(0);
let padded = format!("{:<width$}", cell, width = w);
if let Some(style) = row.style {
format!("{}", style.paint(&padded))
} else {
padded
}
})
.collect::<Vec<_>>()
.join(" ");
println!("{}", line);
}
}