blob: b4f317bf0eeb287f3b3b914949d2540f85bfb610 [file] [log] [blame]
use crate::{grid::records::vec_records::CellInfo, Table};
use super::Builder;
/// [`IndexBuilder`] helps to add an index to the table.
///
/// Index is a column on the left of the table.
///
/// It also can be used to transpose the table.
///
/// Creates a new [`IndexBuilder`] instance.
///
/// It creates a default index a range from 0 to N. (N - count rows)
/// It also sets a default columns to the range 0 .. N (N - count columns).
///nfo<'a>
/// # Example
///
/// ```
/// use tabled::builder::Builder;
///
/// let mut builder = Builder::default();
/// builder.push_record(["i", "col-1", "col-2"]);
/// builder.push_record(["0", "value-1", "value-2"]);
///
/// let table = builder.index().build().to_string();
///
/// assert_eq!(
/// table,
/// "+---+---+---------+---------+\n\
/// | | i | col-1 | col-2 |\n\
/// +---+---+---------+---------+\n\
/// | 0 | 0 | value-1 | value-2 |\n\
/// +---+---+---------+---------+"
/// )
/// ```
///
/// # Example
///
/// ```
/// use tabled::builder::Builder;
///
/// let table = Builder::default()
/// .index()
/// .build();
/// ```
#[derive(Debug, Clone)]
pub struct IndexBuilder {
/// Index is an index data.
/// It's always set.
index: Vec<CellInfo<String>>,
/// Name of an index
name: Option<CellInfo<String>>,
/// A flag which checks if we need to actually use index.
///
/// It might happen when it's only necessary to [`Self::transpose`] table.
print_index: bool,
/// A flag which checks if table was transposed.
transposed: bool,
/// Data originated in [`Builder`].
data: Vec<Vec<CellInfo<String>>>,
/// A size of columns
count_columns: usize,
}
impl IndexBuilder {
/// No flag makes builder to not use an index.
///
/// It may be useful when only [`Self::transpose`] need to be used.
///
/// ```
/// use tabled::builder::Builder;
///
/// let mut builder = Builder::default();
/// builder.push_record(["i", "col-1", "col-2"]);
/// builder.push_record(["0", "value-1", "value-2"]);
/// builder.push_record(["2", "value-3", "value-4"]);
///
/// let table = builder.index().hide().build().to_string();
///
/// assert_eq!(
/// table,
/// "+---+---------+---------+\n\
/// | i | col-1 | col-2 |\n\
/// +---+---------+---------+\n\
/// | 0 | value-1 | value-2 |\n\
/// +---+---------+---------+\n\
/// | 2 | value-3 | value-4 |\n\
/// +---+---------+---------+"
/// )
/// ```
pub fn hide(mut self) -> Self {
self.print_index = false;
self
}
/// Set an index name.
///
/// When [`None`] the name won't be used.
///
/// # Example
///
/// ```
/// use tabled::builder::Builder;
///
/// let mut builder = Builder::default();
/// builder.push_record(["i", "column1", "column2"]);
/// builder.push_record(["0", "value1", "value2"]);
///
/// let table = builder.index()
/// .column(1)
/// .name(Some(String::from("index")))
/// .build();
///
/// assert_eq!(
/// table.to_string(),
/// "+--------+---+---------+\n\
/// | | i | column2 |\n\
/// +--------+---+---------+\n\
/// | index | | |\n\
/// +--------+---+---------+\n\
/// | value1 | 0 | value2 |\n\
/// +--------+---+---------+"
/// )
/// ```
pub fn name(mut self, name: Option<String>) -> Self {
self.name = name.map(CellInfo::new);
self
}
/// Sets a index to the chosen column.
///
/// Also sets a name of the index to the column name.
///
/// # Example
///
/// ```
/// use tabled::builder::Builder;
///
/// let mut builder = Builder::default();
/// builder.push_record(["i", "column1", "column2"]);
/// builder.push_record(["0", "value1", "value2"]);
///
/// let table = builder.index().column(1).build();
///
/// assert_eq!(
/// table.to_string(),
/// "+---------+---+---------+\n\
/// | | i | column2 |\n\
/// +---------+---+---------+\n\
/// | column1 | | |\n\
/// +---------+---+---------+\n\
/// | value1 | 0 | value2 |\n\
/// +---------+---+---------+"
/// )
/// ```
pub fn column(mut self, column: usize) -> Self {
if column >= self.count_columns {
return self;
}
self.index = get_column(&mut self.data, column);
let name = self.index.remove(0);
self.name = Some(name);
self
}
/// Transpose index and columns.
///
/// # Example
///
/// ```
/// use tabled::builder::Builder;
///
/// let mut builder = Builder::default();
/// builder.push_record(["i", "column-1", "column-2", "column-3"]);
/// builder.push_record(["0", "value-1", "value-2", "value-3"]);
/// builder.push_record(["1", "value-4", "value-5", "value-6"]);
/// builder.push_record(["2", "value-7", "value-8", "value-9"]);
///
/// let table = builder.index().column(1).transpose().build();
///
/// assert_eq!(
/// table.to_string(),
/// "+----------+---------+---------+---------+\n\
/// | column-1 | value-1 | value-4 | value-7 |\n\
/// +----------+---------+---------+---------+\n\
/// | i | 0 | 1 | 2 |\n\
/// +----------+---------+---------+---------+\n\
/// | column-2 | value-2 | value-5 | value-8 |\n\
/// +----------+---------+---------+---------+\n\
/// | column-3 | value-3 | value-6 | value-9 |\n\
/// +----------+---------+---------+---------+"
/// )
/// ```
pub fn transpose(mut self) -> Self {
if self.data.is_empty() {
return self;
}
let mut columns = self.data.remove(0);
std::mem::swap(&mut self.index, &mut columns);
let count_columns = columns.len();
make_rows_columns(&mut self.data, self.index.len());
self.data.insert(0, columns);
self.transposed = !self.transposed;
self.count_columns = count_columns;
self
}
/// Builds a table.
pub fn build(self) -> Table {
let builder: Builder = self.into();
builder.build()
}
}
impl From<Builder> for IndexBuilder {
fn from(builder: Builder) -> Self {
let count_columns = builder.count_columns();
let data: Vec<Vec<_>> = builder.into();
let mut index = Vec::new();
if !data.is_empty() {
// we exclude first row which contains a header
let count_rows = data.len() - 1;
index = build_range_index(count_rows);
}
Self {
index,
data,
count_columns,
name: None,
print_index: true,
transposed: false,
}
}
}
impl From<IndexBuilder> for Builder {
fn from(b: IndexBuilder) -> Self {
build_index(b)
}
}
fn build_index(mut b: IndexBuilder) -> Builder {
// we can skip the conversion if this builder has neither data rows nor header row
if b.index.is_empty() && b.count_columns == 0 {
return Builder::default();
}
// add index column
if b.print_index {
b.index.insert(0, CellInfo::default());
insert_column(&mut b.data, b.index, 0);
}
if let Some(name) = b.name {
if b.transposed && b.print_index {
b.data[0][0] = name;
} else {
let count_columns = b.data[0].len();
let mut name_row = vec![CellInfo::default(); count_columns];
name_row[0] = name;
b.data.insert(1, name_row);
}
}
Builder::from_vec(b.data)
}
fn build_range_index(n: usize) -> Vec<CellInfo<String>> {
(0..n).map(|i| i.to_string()).map(CellInfo::new).collect()
}
fn get_column<T>(v: &mut [Vec<T>], col: usize) -> Vec<T>
where
T: Default,
{
let mut column = Vec::with_capacity(v.len());
for row in v.iter_mut() {
let value = row.remove(col);
column.push(value);
}
column
}
// todo: Seems like can be hugely simplified.
fn make_rows_columns<T>(v: &mut Vec<Vec<T>>, count_columns: usize)
where
T: Default,
{
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 insert_column<T: Default>(v: &mut [Vec<T>], mut column: Vec<T>, col: usize) {
for row in v.iter_mut() {
let value = column.remove(col);
row.insert(col, value);
}
}