use std::iter::FromIterator;
use crate::{grid::records::vec_records::Text, Table};
use super::IndexBuilder;
#[derive(Debug, Default, Clone)]
pub struct Builder {
data: Vec<Vec<Text<String>>>,
count_columns: usize,
empty_text: Text<String>,
}
impl Builder {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(count_records: usize, count_columns: usize) -> Self {
let mut builder = Self::new();
builder.data = Vec::with_capacity(count_records);
builder.count_columns = count_columns;
builder
}
pub fn from_vec(data: Vec<Vec<Text<String>>>) -> Self {
let count_columns = if data.is_empty() { 0 } else { data[0].len() };
Self {
data,
count_columns,
empty_text: Text::default(),
}
}
pub fn set_empty<T>(&mut self, text: T)
where
T: Into<String>,
{
self.empty_text = Text::new(text.into());
}
pub fn build(self) -> Table {
Table::from(self)
}
pub fn index(self) -> IndexBuilder {
IndexBuilder::from(self)
}
pub fn push_record<R>(&mut self, record: R)
where
R: IntoIterator,
R::Item: Into<String>,
{
let list = create_row(record, self.count_columns, &self.empty_text);
let list_length = list.len();
if !is_size_eq(self.count_columns, list_length) {
let size = list_length - self.count_columns;
resize_rows(&mut self.data, size, &self.empty_text)
}
self.count_columns = list_length;
self.data.push(list);
}
pub fn insert_record<R>(&mut self, index: usize, record: R)
where
R: IntoIterator,
R::Item: Into<String>,
{
let list = create_row(record, self.count_columns, &self.empty_text);
let list_length = list.len();
if !is_size_eq(self.count_columns, list_length) {
let size = list_length - self.count_columns;
resize_rows(&mut self.data, size, &self.empty_text)
}
self.count_columns = list_length;
self.data.insert(index, list);
}
pub fn clean(&mut self) {
self.count_columns -= remove_empty_columns(&mut self.data, self.count_columns);
remove_empty_rows(&mut self.data, self.count_columns);
}
pub fn remove_record(&mut self, index: usize) {
let _ = self.data.remove(index);
}
pub fn remove_column(&mut self, index: usize) {
for row in &mut self.data {
let _ = row.remove(index);
}
self.count_columns -= 1;
}
pub fn push_column<I>(&mut self, column: I)
where
I: IntoIterator,
I::Item: Into<String>,
{
let mut iter = column.into_iter();
for row in self.data.iter_mut() {
let text = iter
.next()
.map(Into::into)
.map(Text::new)
.unwrap_or(self.empty_text.clone());
row.push(text);
}
for text in iter {
let text = Text::new(text.into());
let mut row = Vec::with_capacity(self.count_columns + 1);
for _ in 0..self.count_columns {
row.push(self.empty_text.clone());
}
row.push(text);
self.data.push(row);
}
self.count_columns += 1;
}
pub fn insert_column<I>(&mut self, index: usize, column: I)
where
I: IntoIterator,
I::Item: Into<String>,
{
let mut iter = column.into_iter();
for row in self.data.iter_mut() {
let text = iter
.next()
.map(Into::into)
.map(Text::new)
.unwrap_or(self.empty_text.clone());
row.insert(index, text);
}
for text in iter {
let text = Text::new(text.into());
let mut row = Vec::with_capacity(self.count_columns + 1);
for _ in 0..index {
row.push(self.empty_text.clone());
}
row.push(text);
for _ in index..self.count_columns {
row.push(self.empty_text.clone());
}
}
self.count_columns += 1;
}
pub fn clear(&mut self) {
self.data.clear();
self.count_columns = 0;
}
pub fn count_columns(&self) -> usize {
self.count_columns
}
pub fn count_records(&self) -> usize {
self.data.len()
}
}
impl From<Builder> for Vec<Vec<String>> {
fn from(builder: Builder) -> Self {
builder
.data
.into_iter()
.map(|row| row.into_iter().map(Text::into_inner).collect())
.collect()
}
}
impl From<Builder> for Vec<Vec<Text<String>>> {
fn from(builder: Builder) -> Self {
builder.data
}
}
impl<K, V, S> From<std::collections::HashMap<K, V, S>> for Builder
where
K: ToString,
V: ToString,
{
fn from(m: std::collections::HashMap<K, V, S>) -> Self {
let mut b = Self::with_capacity(m.len(), 2);
for (k, v) in m {
b.push_record([k.to_string(), v.to_string()]);
}
b
}
}
impl<K, V> From<std::collections::BTreeMap<K, V>> for Builder
where
K: ToString,
V: ToString,
{
fn from(m: std::collections::BTreeMap<K, V>) -> Self {
let mut b = Self::with_capacity(m.len(), 2);
for (k, v) in m {
b.push_record([k.to_string(), v.to_string()]);
}
b
}
}
impl<V> From<std::collections::HashSet<V>> for Builder
where
V: ToString,
{
fn from(m: std::collections::HashSet<V>) -> Self {
let mut b = Self::with_capacity(m.len(), 1);
for v in m {
b.push_record([v.to_string()]);
}
b
}
}
impl<V> From<std::collections::BTreeSet<V>> for Builder
where
V: ToString,
{
fn from(m: std::collections::BTreeSet<V>) -> Self {
let mut b = Self::with_capacity(m.len(), 1);
for v in m {
b.push_record([v.to_string()]);
}
b
}
}
impl<R> FromIterator<R> for Builder
where
R: IntoIterator,
R::Item: Into<String>,
{
fn from_iter<T: IntoIterator<Item = R>>(iter: T) -> Self {
let mut builder = Self::new();
for row in iter {
builder.push_record(row);
}
builder
}
}
impl<D> Extend<D> for Builder
where
D: Into<String>,
{
fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) {
self.push_record(iter);
}
}
impl From<Vec<Vec<String>>> for Builder {
fn from(data: Vec<Vec<String>>) -> Self {
let mut data = data
.into_iter()
.map(|row| row.into_iter().map(Text::new).collect())
.collect();
let count_columns = equalize_row_length(&mut data);
Self {
data,
count_columns,
empty_text: Text::default(),
}
}
}
impl From<Vec<Vec<Text<String>>>> for Builder {
fn from(mut data: Vec<Vec<Text<String>>>) -> Self {
let count_columns = equalize_row_length(&mut data);
Self {
data,
count_columns,
empty_text: Text::default(),
}
}
}
fn create_row<R>(row: R, size: usize, default: &Text<String>) -> Vec<Text<String>>
where
R: IntoIterator,
R::Item: Into<String>,
{
let mut list = Vec::with_capacity(size);
for text in row {
let text = text.into();
let text = Text::new(text);
list.push(text);
}
if list.len() < size {
for _ in 0..size - list.len() {
let text = default.clone();
list.push(text);
}
}
list
}
fn remove_empty_columns(data: &mut [Vec<Text<String>>], count_columns: usize) -> usize {
let mut deleted = 0;
for col in 0..count_columns {
let col = col - deleted;
let mut is_empty_column = true;
for row in data.iter() {
let text = &row[col];
if !text.as_ref().is_empty() {
is_empty_column = false;
break;
}
}
if is_empty_column {
for row in data.iter_mut() {
let _ = row.remove(col);
}
deleted += 1;
}
}
deleted
}
fn remove_empty_rows(data: &mut Vec<Vec<Text<String>>>, count_columns: usize) {
let mut deleted = 0;
for row in 0..data.len() {
let row = row - deleted;
let mut is_empty_row = true;
for col in 0..count_columns {
let cell = &data[row][col];
if !cell.as_ref().is_empty() {
is_empty_row = false;
break;
}
}
if is_empty_row {
let _ = data.remove(row);
deleted += 1;
}
}
}
fn resize_rows(data: &mut Vec<Vec<Text<String>>>, size: usize, empty_text: &Text<String>) {
for row in data {
append_vec(row, empty_text.clone(), size);
}
}
fn append_vec<T>(v: &mut Vec<T>, value: T, n: usize)
where
T: Clone,
{
for _ in 0..n {
v.push(value.clone());
}
}
fn is_size_eq(expected: usize, new: usize) -> bool {
use std::cmp::Ordering;
match new.cmp(&expected) {
Ordering::Less => {
unreachable!("must be impossible due to the assumptions/checks we do");
}
Ordering::Greater => false,
Ordering::Equal => true,
}
}
fn equalize_row_length(data: &mut Vec<Vec<Text<String>>>) -> usize {
if data.is_empty() {
return 0;
}
let first_row_length = data[0].len();
let init = (first_row_length, true);
let (count_columns, is_consistent) = data.iter().fold(init, |mut acc, cur| {
let length = cur.len();
acc.1 = acc.1 && acc.0 == length;
acc.0 = std::cmp::max(acc.0, length);
acc
});
if !is_consistent {
let empty_text = Text::default();
for row in data {
let size = count_columns - row.len();
append_vec(row, empty_text.clone(), size);
}
}
count_columns
}