use crate::style::{Separator, Styled, Styles}; use alloc::boxed::Box; use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; use core::ops::Deref; #[derive(Default)] pub struct Table { styles: Styles, cols: Vec, rows: Vec, } impl Styled for Table { fn styles(&self) -> &Styles { &self.styles } } impl Table { pub fn new(styles: Styles, cols: Vec, rows: Vec) -> Self { Self { styles, cols, rows } } pub fn with_styles(styles: Styles) -> Self { Self::new(styles, Vec::default(), Vec::default()) } #[must_use] pub fn with_cols(mut self, cols: Vec) -> Self { self.set_cols(cols); self } #[must_use] pub fn with_row>(mut self, row: R) -> Self { self.push_row(row); self } #[must_use] pub fn with_rows(self, rows: impl IntoIterator) -> Self { let mut s = self; for row in rows { s = s.with_row(row); } s } /// Assigns columns to the table. The number of columns cannot be less (but may exceed) /// the number of cells in the widest row. /// /// # Panics /// If the number of columns is fewer than the number of cells in the widest row. pub fn set_cols(&mut self, cols: Vec) { let widest_row = self.compute_widest_row(); assert!( cols.len() >= widest_row, "cannot assign fewer than {widest_row} columns" ); self.cols = cols; } pub fn push_row>(&mut self, row: R) { let row = row.into(); while self.cols.len() < row.1.len() { self.cols.push(Col::new(Styles::default())); } self.rows.push(row); } pub fn push_rows(&mut self, it: impl IntoIterator) { for row in it { self.push_row(row); } } pub fn num_rows(&self) -> usize { self.rows.len() } pub fn num_cols(&self) -> usize { self.cols.len() } fn compute_widest_row(&self) -> usize { self.rows.iter().map(|row| row.1.len()).max().unwrap_or(0) } pub fn col(&self, col: usize) -> Element { let parent_styles = vec![&self.styles]; let col = self.cols.get(col); Element { parent_styles, element: col, } } pub fn row(&self, row_idx: usize) -> Element { let parent_styles = vec![&self.styles]; let row = self.rows.get(row_idx); Element { parent_styles, element: row, } } pub fn cell(&self, col_idx: usize, row_idx: usize) -> Element { let col = self.cols.get(col_idx); let row = self.rows.get(row_idx); let mut parent_styles = vec![&self.styles]; if let Some(col) = col { parent_styles.push(col.styles()); } let cell = match row { None => None, Some(row) => { parent_styles.push(row.styles()); row.1.get(col_idx) } }; Element { parent_styles, element: cell, } } pub fn is_empty(&self) -> bool { self.num_rows() == 0 || self.num_cols() == 0 } } #[derive(Default)] pub struct Col(Styles); impl Col { pub fn new(styles: Styles) -> Self { styles.assert_assignability::(|assignability| assignability.at_col()); Self(styles) } pub fn separator() -> Self { Self::new(Styles::default().with(Separator(true))) } } impl Styled for Col { fn styles(&self) -> &Styles { &self.0 } } #[derive(Default)] pub struct Row(Styles, Vec); impl Row { pub fn new(styles: Styles, cells: Vec) -> Self { styles.assert_assignability::(|assignability| assignability.at_row()); Self(styles, cells) } #[must_use] pub fn with_styles(mut self, styles: Styles) -> Self { styles.assert_assignability::(|assignability| assignability.at_row()); self.0 = styles; self } pub fn separator() -> Self { Self::new(Styles::default().with(Separator(true)), vec![]) } pub fn cells(&self) -> &[Cell] { &self.1 } } impl Styled for Row { fn styles(&self) -> &Styles { &self.0 } } impl From for Row where I: IntoIterator, I::Item: ToString { fn from(it: I) -> Self { Self( Styles::default(), it.into_iter().map(Cell::from).collect(), ) } } pub struct Cell { styles: Styles, data: Content, } impl Styled for Cell { fn styles(&self) -> &Styles { &self.styles } } impl Cell { pub fn new(styles: Styles, data: Content) -> Self { styles.assert_assignability::(|assignability| assignability.at_cell()); Self { styles, data } } pub fn data(&self) -> &Content { &self.data } } impl From for Cell { fn from(data: Content) -> Self { Self::new(Styles::default(), data) } } impl From for Cell { fn from(table: Table) -> Self { Self::new(Styles::default(), table.into()) } } impl From for Cell { fn from(data: S) -> Self { Content::from(data).into() } } pub enum Content { Label(String), Computed(Box String>), Nested(Table), Composite(Vec), } impl From for Content { fn from(data: S) -> Self { Self::Label(data.to_string()) } } impl From
for Content { fn from(table: Table) -> Self { Self::Nested(table) } } pub struct Element<'a, T: Styled> { parent_styles: Vec<&'a Styles>, element: Option<&'a T>, } impl<'a, T: Styled> Element<'a, T> { pub fn parent_styles(&self) -> &[&'a Styles] { &self.parent_styles } pub fn blended_styles(&self) -> Styles { let mut styles = Styles::default(); for &s in &self.parent_styles { styles.insert_all(s); } if let Some(element) = self.element { styles.insert_all(element.styles()); } styles } } impl<'a, T: Styled> Deref for Element<'a, T> { type Target = Option<&'a T>; fn deref(&self) -> &Self::Target { &self.element } } #[cfg(test)] mod tests;