#![cfg_attr(feature = "derive", doc = "```")]
#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
#![cfg_attr(feature = "derive", doc = "```")]
#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
use std::{borrow::Cow, iter::FromIterator};
use papergrid::{
records::{
cell_info::CellInfo,
vec_records::{CellMut, VecRecords},
Records,
},
width::{CfgWidthFunction, WidthFunc},
AlignmentHorizontal, Entity, Formatting, GridConfig, Indent, Padding,
};
use crate::{Style, Table};
#[derive(Debug, Default, Clone)]
pub struct Builder<'a> {
records: Vec<Vec<CellInfo<'a>>>,
columns: Option<Vec<CellInfo<'a>>>,
size: usize,
different_column_sizes_used: bool,
empty_cell_text: Option<String>,
}
impl<'a> Builder<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn hint_column_size(&mut self, size: usize) {
self.size = size;
}
pub fn set_columns<H, T>(&mut self, columns: H) -> &mut Self
where
H: IntoIterator<Item = T>,
T: Into<Cow<'a, str>>,
{
let ctrl = CfgWidthFunction::new(4);
let list = create_row(columns, self.size, &ctrl);
self.update_size(list.len());
self.columns = Some(list);
self
}
pub fn remove_columns(&mut self) -> &mut Self {
self.columns = None;
let size = self.get_size();
self.size = size;
self
}
pub fn add_record<R, T>(&mut self, row: R) -> &mut Self
where
R: IntoIterator<Item = T>,
T: Into<Cow<'a, str>>,
{
let ctrl = CfgWidthFunction::new(4);
let list = create_row(row, self.size, &ctrl);
self.update_size(list.len());
self.records.push(list);
self
}
pub fn set_default_text<T>(&mut self, text: T) -> &mut Self
where
T: Into<String>,
{
self.empty_cell_text = Some(text.into());
self
}
pub fn build(mut self) -> Table<VecRecords<CellInfo<'a>>> {
if self.different_column_sizes_used {
self.fix_rows();
}
let records = build_grid(self.records, self.columns, self.size);
build_table(records)
}
pub fn index(self) -> IndexBuilder<'a> {
IndexBuilder::new(self)
}
pub fn clean(&mut self) -> &mut Self {
self.clean_columns();
self.clean_rows();
self
}
pub fn custom<R>(records: R) -> CustomRecords<R> {
CustomRecords::new(records)
}
fn clean_columns(&mut self) {
let mut i = 0;
for col in 0..self.size {
let col = col - i;
let mut is_empty = true;
for row in 0..self.records.len() {
if !self.records[row][col].is_empty() {
is_empty = false;
break;
}
}
if is_empty {
for row in 0..self.records.len() {
self.records[row].remove(col);
}
if let Some(columns) = self.columns.as_mut() {
if columns.len() > col {
columns.remove(col);
}
}
i += 1;
}
}
self.size -= i;
}
fn clean_rows(&mut self) {
for row in (0..self.records.len()).rev() {
let mut is_empty = true;
for col in 0..self.size {
if !self.records[row][col].is_empty() {
is_empty = false;
break;
}
}
if is_empty {
self.records.remove(row);
}
if row == 0 {
break;
}
}
}
fn update_size(&mut self, size: usize) {
match size.cmp(&self.size) {
std::cmp::Ordering::Less => {
if !self.records.is_empty() {
self.different_column_sizes_used = true;
}
}
std::cmp::Ordering::Greater => {
self.size = size;
if !self.records.is_empty() || self.columns.is_some() {
self.different_column_sizes_used = true;
}
}
std::cmp::Ordering::Equal => (),
}
}
fn get_size(&mut self) -> usize {
let mut max = self.columns.as_ref().map_or(0, Vec::len);
let max_records = self.records.iter().map(Vec::len).max().unwrap_or(0);
max = std::cmp::max(max_records, max);
max
}
fn fix_rows(&mut self) {
let ctrl = CfgWidthFunction::new(4);
let text = self.empty_cell_text.clone().unwrap_or_default();
let empty_cell_text = CellInfo::new(text, &ctrl);
if let Some(header) = self.columns.as_mut() {
if self.size > header.len() {
append_vec(header, self.size - header.len(), &empty_cell_text);
}
}
for row in &mut self.records {
if self.size > row.len() {
append_vec(row, self.size - row.len(), &empty_cell_text);
}
}
}
}
impl<'a, R, V> FromIterator<R> for Builder<'a>
where
R: IntoIterator<Item = V>,
V: Into<Cow<'a, str>>,
{
fn from_iter<T: IntoIterator<Item = R>>(iter: T) -> Self {
let mut builder = Self::default();
for row in iter {
builder.add_record(row);
}
builder
}
}
impl<'a, D> Extend<D> for Builder<'a>
where
D: Into<Cow<'a, str>>,
{
fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) {
self.add_record(iter);
}
}
impl From<Vec<Vec<String>>> for Builder<'_> {
fn from(strings: Vec<Vec<String>>) -> Self {
let size = strings.iter().map(|r| r.len()).max().unwrap_or(0);
if size == 0 {
return Self::default();
}
let ctrl = CfgWidthFunction::new(4);
let mut records = vec![vec![CellInfo::default(); size]; strings.len()];
for (row, list) in strings.into_iter().zip(records.iter_mut()) {
create_row_exact(list, row, &ctrl);
}
Self {
records,
size,
..Default::default()
}
}
}
impl<'a> From<Vec<Vec<CellInfo<'a>>>> for Builder<'a> {
fn from(records: Vec<Vec<CellInfo<'a>>>) -> Self {
Self {
records,
..Default::default()
}
}
}
#[derive(Debug, Clone)]
pub struct IndexBuilder<'a> {
index: Vec<CellInfo<'a>>,
name: Option<CellInfo<'a>>,
print_index: bool,
transposed: bool,
b: Builder<'a>,
}
impl<'a> IndexBuilder<'a> {
fn new(mut b: Builder<'a>) -> Self {
let index = build_range_index(b.records.len());
if b.columns.is_none() {
b.columns = Some(build_range_index(b.size));
}
Self {
index,
name: None,
print_index: true,
transposed: false,
b,
}
}
pub fn hide_index(&mut self) -> &mut Self {
self.print_index = false;
self
}
pub fn set_name(&mut self, name: Option<String>) -> &mut Self {
self.name = name.map(|s| {
let ctrl = CfgWidthFunction::new(4);
CellInfo::new(s, ctrl)
});
self
}
pub fn set_index(&mut self, column: usize) -> &mut Self {
if self.b.columns.is_none() {
return self;
}
if column >= self.b.size {
return self;
}
let name = self
.b
.columns
.as_mut()
.map(|v| remove_or_default(v, column))
.unwrap_or_default();
self.name = Some(name);
self.index = get_column(&mut self.b.records, column);
self.b.size -= 1;
self
}
pub fn transpose(&mut self) -> &mut Self {
let columns = self.b.columns.take().unwrap_or_default();
let index = std::mem::replace(&mut self.index, columns);
self.b.columns = Some(index);
let new_count_columns = self.b.records.len();
make_rows_columns(&mut self.b.records, self.b.size);
self.b.size = new_count_columns;
self.transposed = !self.transposed;
self
}
pub fn build(self) -> Table<VecRecords<CellInfo<'a>>> {
Builder::from(self).build()
}
}
impl<'a> From<IndexBuilder<'a>> for Builder<'a> {
fn from(index_builder: IndexBuilder<'a>) -> Self {
let mut b = build_index(index_builder);
b.fix_rows();
b.different_column_sizes_used = false;
b
}
}
#[derive(Debug, Clone)]
pub struct CustomRecords<R> {
records: R,
}
impl<R> CustomRecords<R> {
fn new(records: R) -> Self {
Self { records }
}
}
impl<R> CustomRecords<R>
where
R: Records,
{
pub fn build(self) -> Table<R> {
build_table(self.records)
}
}
fn make_rows_columns(v: &mut Vec<Vec<CellInfo<'_>>>, count_columns: usize) {
let mut columns = Vec::with_capacity(count_columns);
for _ in 0..count_columns {
let column = get_column(v, 0);
columns.push(column);
}
v.clear();
for column in columns {
v.push(column);
}
}
fn build_index(mut b: IndexBuilder<'_>) -> Builder<'_> {
if b.index.is_empty() {
return b.b;
}
let records = &mut b.b.records;
let columns = b.b.columns.take().unwrap();
records.insert(0, columns);
if b.print_index {
b.b.size += 1;
b.index.insert(0, CellInfo::default());
insert_column(records, b.index, 0);
}
if let Some(name) = b.name {
if b.transposed && b.print_index {
records[0][0] = name;
} else {
records.insert(1, vec![name]);
}
}
b.b
}
fn insert_column<T: Default>(v: &mut [Vec<T>], mut column: Vec<T>, col: usize) {
for row in v.iter_mut() {
let value = remove_or_default(&mut column, col);
row.insert(col, value);
}
}
fn get_column<'a>(v: &mut [Vec<CellInfo<'a>>], col: usize) -> Vec<CellInfo<'a>> {
let mut column = Vec::with_capacity(v.len());
for row in v.iter_mut() {
let value = remove_or_default(row, col);
column.push(value);
}
column
}
fn remove_or_default<T: Default>(v: &mut Vec<T>, i: usize) -> T {
if v.len() > i {
v.remove(i)
} else {
T::default()
}
}
fn build_range_index(n: usize) -> Vec<CellInfo<'static>> {
let ctrl = CfgWidthFunction::new(4);
(0..n)
.map(|i| CellInfo::new(i.to_string(), &ctrl))
.collect()
}
fn create_row<'a, R, T, W>(row: R, size: usize, ctrl: &W) -> Vec<CellInfo<'a>>
where
R: IntoIterator<Item = T>,
T: Into<Cow<'a, str>>,
W: WidthFunc,
{
let mut list = Vec::with_capacity(size);
for text in row {
list.push(CellInfo::new(text, ctrl))
}
list
}
fn create_row_exact<'a, R, T, W>(list: &mut [CellInfo<'a>], row: R, ctrl: &W)
where
R: IntoIterator<Item = T>,
T: Into<Cow<'a, str>>,
W: WidthFunc,
{
for (text, cell) in row.into_iter().zip(list.iter_mut()) {
CellMut::set(cell, text, ctrl);
}
}
fn build_table<R>(records: R) -> Table<R>
where
R: Records,
{
let mut table = Table::from(records);
table.with(Style::ascii());
configure_grid(table.get_config_mut());
table
}
fn build_grid<'a>(
mut records: Vec<Vec<CellInfo<'a>>>,
columns: Option<Vec<CellInfo<'a>>>,
count_columns: usize,
) -> VecRecords<CellInfo<'a>> {
if let Some(columns) = columns {
records.insert(0, columns);
}
VecRecords::with_hint(records, count_columns)
}
fn configure_grid(cfg: &mut GridConfig) {
cfg.set_tab_width(4);
cfg.set_padding(
Entity::Global,
Padding {
left: Indent::spaced(1),
right: Indent::spaced(1),
top: Indent::default(),
bottom: Indent::default(),
},
);
cfg.set_alignment_horizontal(Entity::Global, AlignmentHorizontal::Left);
cfg.set_formatting(
Entity::Global,
Formatting {
horizontal_trim: false,
allow_lines_alignement: false,
vertical_trim: false,
},
);
}
fn append_vec<'a>(v: &mut Vec<CellInfo<'a>>, n: usize, value: &CellInfo<'a>) {
v.extend((0..n).map(|_| value.clone()));
}