use ::crossterm::style::style;
use ::std::iter::FromIterator;
use crate::cell::Cell;
use crate::row::Row;
use crate::style::CellAlignment;
use crate::table::Table;
use crate::utils::arrangement::ColumnDisplayInfo;
pub fn format_content(
table: &Table,
display_info: &Vec<ColumnDisplayInfo>,
) -> Vec<Vec<Vec<String>>> {
let mut table_content = Vec::new();
match table.get_header() {
Some(header) => {
table_content.push(format_row(header, display_info, table));
}
None => (),
}
for row in table.rows.iter() {
table_content.push(format_row(row, display_info, table));
}
table_content
}
pub fn format_row(
row: &Row,
display_info: &Vec<ColumnDisplayInfo>,
table: &Table,
) -> Vec<Vec<String>> {
let mut temp_row_content = Vec::new();
let mut cell_iter = row.cells.iter();
for info in display_info.iter() {
let mut cell_content = Vec::new();
let cell = if let Some(cell) = cell_iter.next() {
cell
} else {
cell_content.push(" ".repeat(info.width() as usize));
temp_row_content.push(cell_content);
continue;
};
for line in cell.content.iter() {
if (line.chars().count() as u16) > info.content_width() {
let mut splitted = split_line(line.clone(), &info, cell, table);
cell_content.append(&mut splitted);
} else {
let mut line = align_line(line.clone(), info, cell);
if table.should_style() {
line = style_line(line, cell);
}
cell_content.push(line);
}
}
temp_row_content.push(cell_content);
}
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_info.iter() {
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
}
pub fn split_line(
line: String,
info: &ColumnDisplayInfo,
cell: &Cell,
table: &Table,
) -> Vec<String> {
let mut lines = Vec::new();
let content_width = info.content_width();
let mut splitted = line
.split(' ')
.map(|part| part.to_string())
.collect::<Vec<String>>();
splitted.reverse();
let mut current_line = String::new();
while let Some(next) = splitted.pop() {
let current_length = current_line.chars().count();
let next_length = next.chars().count();
let added_length = next_length + current_length + 1;
if current_line.len() == 0 {
if next_length as u16 <= content_width {
current_line += &next;
continue;
} else {
let mut next: Vec<char> = next.chars().collect();
let remaining = next.split_off(content_width as usize);
splitted.push(String::from_iter(remaining));
lines.push(String::from_iter(next));
}
}
else if added_length as u16 <= content_width {
current_line += " ";
current_line += &next;
if current_line.chars().count() as i32 >= content_width as i32 - 2 {
lines.push(current_line);
current_line = String::new();
}
} else {
let remaining_width = content_width as i32 - current_line.chars().count() as i32 - 1;
if remaining_width <= 2 {
splitted.push(next);
lines.push(current_line);
current_line = String::new();
}
else if next_length as u16 > content_width {
let mut next: Vec<char> = next.chars().collect();
let remaining = next.split_off((remaining_width) as usize);
current_line += " ";
current_line += &String::from_iter(next);
splitted.push(String::from_iter(remaining));
lines.push(current_line);
current_line = String::new();
} else {
lines.push(current_line);
current_line = next.to_string();
}
}
}
if current_line.len() != 0 {
lines.push(current_line);
}
lines = lines
.iter()
.map(|line| align_line(line.to_string(), info, cell))
.map(|line| {
if table.should_style() {
return style_line(line, cell);
}
line
})
.collect();
lines
}
pub 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)
}
pub 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
}
pub 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()
}