use super::border::Border;
use super::cell::Cell;
use crate::content::{CellWidth};
pub struct CellIterator<'a> {
cells: &'a Vec<Cell>,
current_cell_ix: usize
}
impl<'a> Iterator for CellIterator<'a> {
type Item = &'a Cell;
fn next(&mut self) -> Option<&'a Cell> {
if self.current_cell_ix < self.cells.len() {
let cell: &Cell = &self.cells[self.current_cell_ix];
self.current_cell_ix += 1;
Some(cell)
} else {
None
}
}
}
#[allow(unused_macros)]
#[macro_export]
macro_rules! row {
( $($style:expr => $content:expr),* ) => {
{
let mut r: Row = Row::new();
$( r.add_cell(crate::cell!($style, $content)); )*
r
}
};
( $style:expr, $($content:expr),* ) => {
{
let mut r: Row = Row::new();
$( r.add_cell(crate::cell!($style, $content)); )*
r
}
};
}
#[derive(Debug)]
pub struct Row {
cells: Vec<Cell>
}
impl Default for Row {
fn default() -> Self {
Self::new()
}
}
impl Row {
#[must_use]
pub fn new() -> Row {
Row {
cells: Vec::new()
}
}
#[must_use]
pub fn from(
cells: Vec<Cell>
) -> Row {
Row { cells }
}
pub fn add_cell(&mut self, cell: Cell) {
self.cells.push(cell);
}
#[must_use]
pub fn iter(
self: &Row,
) -> CellIterator {
CellIterator {
cells: &self.cells,
current_cell_ix: 0
}
}
#[must_use]
pub fn len(self: &Row) -> usize {
self.cells.len()
}
#[must_use]
pub fn is_empty(self: &Row) -> bool {
self.len() == 0
}
#[must_use]
#[allow(clippy::option_if_let_else)]
pub fn format(
self: &Row,
border: &Border,
column_breaks: &[CellWidth]
) -> String {
let mut result: String = String::from("");
let row_height = self.measure_height(column_breaks);
let mut content_iterators = Vec::new();
for (cell_ix, cell) in self.cells.iter().enumerate() {
let column_break = &column_breaks[cell_ix];
content_iterators.push(cell.get_iterator(&column_break));
}
let content_break = CellWidth::default();
for _line_ix in 0..row_height {
result.push_str(&border.format_left());
for cell_ix in 0..self.cells.len() {
let cell = &self.cells[cell_ix];
let column_break: &CellWidth =
if cell_ix < column_breaks.len() {
&column_breaks[cell_ix]
} else {
&content_break
};
result.push_str(
&if let Some(content) = content_iterators[cell_ix].next() {
content.to_string()
} else {
let cell_width = cell.measure_width(column_break);
(0..cell_width)
.map(|_| " ")
.collect::<String>()
}
);
if cell_ix < column_breaks.len() - 1 {
result.push_str(&border.format_vertical_split());
}
}
result.push_str(&border.format_right());
result.push('\n');
}
result
}
#[must_use]
pub fn measure_height(
self: &Row,
column_breaks: &[CellWidth],
) -> usize {
let mut tallest_height = 0;
let column_break_ix = 0;
let content_break = CellWidth::Content;
for cell in &self.cells {
let column_break: &CellWidth =
if column_break_ix < column_breaks.len() {
&column_breaks[column_break_ix]
} else {
&content_break
};
let cell_height = cell.measure_height(column_break);
if cell_height > tallest_height {
tallest_height = cell_height;
}
}
tallest_height
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_row_macro_style_per_cell() {
assert_eq!(
format!("{:?}", row!("{c^}" => "Head 1", "{G-r>}" => "Head 2")),
format!("{:?}", Row::from(
vec!(
crate::cell!("{c^}", "Head 1"),
crate::cell!("{G-r>}", "Head 2")
)
))
);
}
#[test]
fn test_row_macro_common_style() {
assert_eq!(
format!("{:?}", row!("{c^}", "Text 1", "Text 2", "Text 3")),
format!("{:?}", Row::from(
vec!(
crate::cell!("{c^}", "Text 1"),
crate::cell!("{c^}", "Text 2"),
crate::cell!("{c^}", "Text 3")
)
))
);
}
}