use crate::table_formatting::grid_formatting::GridSizes;
use crate::table_formatting::StringTable;
use std::collections::{BTreeMap, HashMap};
use std::fmt::Display;
impl<K: Display, V: Display> StringTable for HashMap<K, V> {
fn to_table(&self) -> Vec<String> {
let mut values = vec![[String::from("Keys:"), String::from("Values:")]];
for (key, value) in self {
values.push([format!("{}", key), format!("{}", value)]);
}
generate_string_grid(&values)
}
}
impl<K: Display, V: Display> StringTable for BTreeMap<K, V> {
fn to_table(&self) -> Vec<String> {
let mut values = vec![[String::from("Keys:"), String::from("Values:")]];
for (key, value) in self {
values.push([format!("{}", key), format!("{}", value)]);
}
generate_string_grid(&values)
}
}
impl<T: Display> StringTable for Vec<T> {
fn to_table(&self) -> Vec<String> {
let mut values = Vec::new();
for current_value in self {
values.push([format!("{}", current_value)]);
}
generate_string_grid(&values)
}
}
impl StringTable for String {
fn to_table(&self) -> Vec<String> {
generate_string_grid(&vec![[self.to_string()]])
}
}
struct GridCoord {
x: usize,
y: usize,
}
pub fn generate_string_grid<const U: usize>(contents: &Vec<[String; U]>) -> Vec<String> {
if contents.is_empty() {
return vec![String::from("┏┓"), String::from("┗┛")];
}
let grid = GridSizes {
widths: get_max_widths(contents),
heights: get_max_heights(contents),
};
let mut result = grid.to_table();
for (row_index, row) in contents.iter().enumerate() {
for (column_index, column) in row.iter().enumerate().take(U) {
result = insert_text(
column,
&mut result,
&grid,
GridCoord {
x: column_index,
y: row_index,
},
)
}
}
result
}
fn get_max_widths<const U: usize>(contents: &Vec<[String; U]>) -> Vec<usize> {
let mut widths = Vec::new();
for column_index in 0..U {
let mut max_width = 0usize;
for row in contents.iter() {
let current_width = get_string_width(&row[column_index]) + 2;
max_width = std::cmp::max(max_width, current_width);
}
widths.push(max_width);
}
widths
}
fn get_max_heights<const U: usize>(contents: &Vec<[String; U]>) -> Vec<usize> {
let mut heights = Vec::new();
for row in contents.iter() {
let mut max_height = 0usize;
for column in row.iter().take(U) {
let current_height = get_string_height(column) + 2;
max_height = std::cmp::max(max_height, current_height);
}
heights.push(max_height);
}
heights
}
fn insert_text(
inserted_text: &str,
original_lines: &mut [String],
grid: &GridSizes,
replacement_coords: GridCoord,
) -> Vec<String> {
let string_y_start = get_replaced_dimension_index(&grid.heights, replacement_coords.y);
let string_y_end = string_y_start + get_string_height(inserted_text);
let string_x_start = get_replaced_dimension_index(&grid.widths, replacement_coords.x);
let changed_lines = &mut original_lines[string_y_start..string_y_end];
for (line_index, current_line) in changed_lines.iter_mut().enumerate() {
let inserted_line = inserted_text.split('\n').nth(line_index).unwrap();
let string_x_end = map_string_index(
current_line,
string_x_start + get_string_width(&String::from(inserted_line)),
);
let string_x_start = map_string_index(current_line, string_x_start);
current_line.replace_range(string_x_start..string_x_end, inserted_line);
}
original_lines.to_vec()
}
fn map_string_index(controlling_string: &str, index: usize) -> usize {
controlling_string.char_indices().nth(index).unwrap().0
}
fn get_replaced_dimension_index(dimension: &[usize], coord: usize) -> usize {
let mut start_index = 1usize;
for (current_coord, current_dimension) in dimension.iter().enumerate() {
if current_coord == coord {
return start_index;
}
start_index += current_dimension - 1; }
panic!("coord value of {} could not be reached", coord)
}
fn get_string_width(s: &str) -> usize {
let mut max_width = 0usize;
for current_line in s.split('\n') {
let current_width = current_line.chars().count();
if current_width > max_width {
max_width = current_width
}
}
max_width
}
fn get_string_height(s: &str) -> usize {
s.matches('\n').count() + 1usize
}