use crossterm::style::style;
use crate::cell::Cell;
use crate::row::Row;
use crate::style::CellAlignment;
use crate::table::Table;
use crate::utils::arrangement::ColumnDisplayInfo;
use crate::utils::split::split_line;
pub fn format_content(table: &Table, display_info: &[ColumnDisplayInfo]) -> Vec<Vec<Vec<String>>> {
let mut table_content = Vec::new();
if let Some(header) = table.get_header() {
table_content.push(format_row(header, display_info, table));
}
for row in table.rows.iter() {
table_content.push(format_row(row, display_info, table));
}
table_content
}
pub fn format_row(
row: &Row,
display_infos: &[ColumnDisplayInfo],
table: &Table,
) -> Vec<Vec<String>> {
let mut temp_row_content = Vec::new();
let mut cell_iter = row.cells.iter();
for info in display_infos.iter() {
if info.is_hidden() {
cell_iter.next();
continue;
}
let mut cell_lines = Vec::new();
let cell = if let Some(cell) = cell_iter.next() {
cell
} else {
cell_lines.push(" ".repeat(info.width() as usize));
temp_row_content.push(cell_lines);
continue;
};
let delimiter = if let Some(delimiter) = cell.delimiter {
delimiter
} else if let Some(delimiter) = info.delimiter {
delimiter
} else if let Some(delimiter) = table.delimiter {
delimiter
} else {
' '
};
for line in cell.content.iter() {
if (line.chars().count() as u16) > info.content_width() {
let mut splitted = split_line(&line, &info, delimiter);
cell_lines.append(&mut splitted);
} else {
cell_lines.push(line.into());
}
}
if let Some(lines) = row.max_height {
if cell_lines.len() > lines {
let _ = cell_lines.split_off(lines);
let last_line = cell_lines.get_mut(lines - 1).unwrap();
let width = info.content_width() as usize;
if width >= 6 {
if last_line.len() >= width - 3 {
let surplus = (last_line.len() + 3) - width;
last_line.truncate(last_line.len() - surplus);
}
last_line.push_str("...");
}
}
}
let cell_lines = cell_lines
.iter()
.map(|line| align_line(line.to_string(), info, cell));
if table.should_style() {
let cell_lines = cell_lines.map(|line| style_line(line, cell));
temp_row_content.push(cell_lines.collect());
} else {
temp_row_content.push(cell_lines.collect());
}
}
let max_lines = temp_row_content
.iter()
.map(|cell| cell.len())
.max()
.unwrap_or(0);
let mut row_content = Vec::new();
for index in 0..max_lines {
let mut line = Vec::new();
let mut cell_iter = temp_row_content.iter();
for info in display_infos.iter() {
if info.is_hidden() {
continue;
}
let cell = cell_iter.next().unwrap();
match cell.get(index) {
Some(content) => line.push(content.clone()),
None => line.push(" ".repeat(info.width() as usize)),
}
}
row_content.push(line);
}
row_content
}
fn align_line(mut line: String, info: &ColumnDisplayInfo, cell: &Cell) -> String {
let content_width = info.content_width();
let remaining = content_width - line.chars().count() as u16;
let alignment = if let Some(alignment) = cell.alignment {
alignment
} else if let Some(alignment) = info.cell_alignment {
alignment
} else {
CellAlignment::Left
};
match alignment {
CellAlignment::Left => {
line += &" ".repeat(remaining as usize);
}
CellAlignment::Right => {
line = " ".repeat(remaining as usize) + &line;
}
CellAlignment::Center => {
let left_padding = (remaining as f32 / 2f32).ceil() as usize;
let right_padding = (remaining as f32 / 2f32).floor() as usize;
line = " ".repeat(left_padding) + &line + &" ".repeat(right_padding);
}
}
pad_line(line, info)
}
fn pad_line(line: String, info: &ColumnDisplayInfo) -> String {
let mut padded_line = String::new();
padded_line += &" ".repeat(info.padding.0 as usize);
padded_line += &line;
padded_line += &" ".repeat(info.padding.1 as usize);
padded_line
}
fn style_line(line: String, cell: &Cell) -> String {
let mut content = style(line);
if let Some(color) = cell.fg {
content = content.with(color);
}
if let Some(color) = cell.bg {
content = content.on(color);
}
for attribute in cell.attributes.iter() {
content = content.attribute(*attribute);
}
content.to_string()
}