use std::cmp; use std::fmt; #[derive(Debug, Clone, Copy)] pub enum Align { Left, Right, } #[derive(Debug, Clone)] pub struct Table { header: Vec<(Align, String)>, rows: Vec>, } impl Table { pub(crate) fn with_header(header: Vec<(Align, String)>) -> Table { assert!(!header.is_empty()); Table { header, rows: vec![], } } pub(crate) fn add_row(&mut self, row: Vec) { assert_eq!(self.header.len(), row.len()); self.rows.push(row); } } impl fmt::Display for Table { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut maxs: Vec<_> = self.header.iter().map(|h| h.1.len()).collect(); for row in &self.rows { for (i, x) in row.iter().enumerate() { maxs[i] = cmp::max(maxs[i], x.len()); } } let last = self.header.len() - 1; for (i, h) in self.header.iter().map(|h| &h.1).enumerate() { if i == 0 { write!(f, " ")?; } else { write!(f, " │ ")?; } write!(f, "{}", h)?; if i != last { for _ in 0..maxs[i] - h.len() { write!(f, " ")?; } } } writeln!(f)?; for (i, max_len) in maxs.iter().enumerate().take(self.header.len()) { if i == 0 { write!(f, "─")?; } else { write!(f, "─┼─")?; } for _ in 0..*max_len { write!(f, "─")?; } } writeln!(f)?; for row in &self.rows { for (i, (x, align)) in row.iter().zip(self.header.iter().map(|h| h.0)).enumerate() { if i == 0 { write!(f, " ")?; } else { write!(f, " ┊ ")?; } match align { Align::Left => { write!(f, "{}", x)?; if i != last { for _ in 0..maxs[i] - x.len() { write!(f, " ")?; } } } Align::Right => { for _ in 0..maxs[i] - x.len() { write!(f, " ")?; } write!(f, "{}", x)?; } } } writeln!(f)?; } Ok(()) } }