use core::{cmp::max, fmt};
use crate::{
grid::{
colors::NoColors,
config::{AlignmentHorizontal, CompactConfig, Indent, Sides},
dimension::{ConstDimension, ConstSize, Dimension},
records::{
into_records::{LimitColumns, LimitRows},
IntoRecords, IterRecords,
},
util::string::get_line_width,
CompactGrid,
},
settings::{style::Style, TableOption},
};
#[cfg_attr(feature = "std", doc = "```")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
#[cfg_attr(feature = "std", doc = "```")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
#[derive(Debug, Clone)]
pub struct CompactTable<I, D> {
records: I,
cfg: CompactConfig,
dims: D,
count_columns: usize,
count_rows: Option<usize>,
}
impl<I> CompactTable<I, ConstDimension<0, 0>> {
pub const fn new(iter: I) -> Self
where
I: IntoRecords,
{
Self {
records: iter,
cfg: create_config(),
count_columns: 0,
count_rows: None,
dims: ConstDimension::new(ConstSize::Value(2), ConstSize::Value(1)),
}
}
}
impl<I, const ROWS: usize, const COLS: usize> CompactTable<I, ConstDimension<COLS, ROWS>> {
pub fn height<S: Into<ConstSize<COUNT_ROWS>>, const COUNT_ROWS: usize>(
self,
size: S,
) -> CompactTable<I, ConstDimension<COLS, COUNT_ROWS>> {
let (width, _) = self.dims.into();
CompactTable {
dims: ConstDimension::new(width, size.into()),
records: self.records,
cfg: self.cfg,
count_columns: self.count_columns,
count_rows: self.count_rows,
}
}
pub fn width<S: Into<ConstSize<COUNT_COLUMNS>>, const COUNT_COLUMNS: usize>(
self,
size: S,
) -> CompactTable<I, ConstDimension<COUNT_COLUMNS, ROWS>> {
let (_, height) = self.dims.into();
CompactTable {
dims: ConstDimension::new(size.into(), height),
records: self.records,
cfg: self.cfg,
count_columns: self.count_columns,
count_rows: self.count_rows,
}
}
}
impl<I, D> CompactTable<I, D> {
pub fn with_dimension(iter: I, dimension: D) -> Self
where
I: IntoRecords,
{
Self {
records: iter,
dims: dimension,
cfg: create_config(),
count_columns: 0,
count_rows: None,
}
}
pub fn with<O>(mut self, option: O) -> Self
where
for<'a> O: TableOption<IterRecords<&'a I>, CompactConfig, D>,
{
let mut records = IterRecords::new(&self.records, self.count_columns, self.count_rows);
option.change(&mut records, &mut self.cfg, &mut self.dims);
self
}
pub const fn rows(mut self, count_rows: usize) -> Self {
self.count_rows = Some(count_rows);
self
}
pub const fn columns(mut self, count: usize) -> Self {
self.count_columns = count;
self
}
pub fn get_config(&self) -> &CompactConfig {
&self.cfg
}
pub fn get_config_mut(&mut self) -> &mut CompactConfig {
&mut self.cfg
}
pub fn fmt<W>(self, writer: W) -> fmt::Result
where
I: IntoRecords,
I::Cell: AsRef<str>,
D: Dimension,
W: fmt::Write,
{
build_grid(
writer,
self.records,
self.dims,
self.cfg,
self.count_columns,
self.count_rows,
)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn build<W>(self, writer: W) -> std::io::Result<()>
where
I: IntoRecords,
I::Cell: AsRef<str>,
D: Dimension,
W: std::io::Write,
{
let writer = crate::util::utf8_writer::UTF8Writer::new(writer);
self.fmt(writer).map_err(std::io::Error::other)
}
#[allow(clippy::inherent_to_string)]
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn to_string(self) -> String
where
I: IntoRecords,
I::Cell: AsRef<str>,
D: Dimension,
{
let mut buf = String::new();
self.fmt(&mut buf)
.expect("it's expected to be ok according to doc");
buf
}
}
impl<T, const ROWS: usize, const COLS: usize> From<[[T; COLS]; ROWS]>
for CompactTable<[[T; COLS]; ROWS], ConstDimension<COLS, ROWS>>
where
T: AsRef<str>,
{
fn from(mat: [[T; COLS]; ROWS]) -> Self {
let mut width = [0; COLS];
for row in mat.iter() {
for (col, text) in row.iter().enumerate() {
let text = text.as_ref();
let text_width = get_line_width(text);
width[col] = max(width[col], text_width);
}
}
for w in &mut width {
*w += 2;
}
let dims = ConstDimension::new(ConstSize::List(width), ConstSize::Value(1));
Self::with_dimension(mat, dims).columns(COLS).rows(ROWS)
}
}
fn build_grid<W, I, D>(
writer: W,
records: I,
dims: D,
config: CompactConfig,
cols: usize,
rows: Option<usize>,
) -> fmt::Result
where
W: fmt::Write,
I: IntoRecords,
I::Cell: AsRef<str>,
D: Dimension,
{
match rows {
Some(limit) => {
let records = LimitRows::new(records, limit);
let records = LimitColumns::new(records, cols);
let records = IterRecords::new(records, cols, rows);
CompactGrid::new(records, config, dims, NoColors).build(writer)
}
None => {
let records = LimitColumns::new(records, cols);
let records = IterRecords::new(records, cols, rows);
CompactGrid::new(records, config, dims, NoColors).build(writer)
}
}
}
const fn create_config() -> CompactConfig {
CompactConfig::new()
.set_padding(Sides::new(
Indent::spaced(1),
Indent::spaced(1),
Indent::zero(),
Indent::zero(),
))
.set_alignment_horizontal(AlignmentHorizontal::Left)
.set_borders(Style::ascii().get_borders())
}
impl<R, D> TableOption<R, CompactConfig, D> for CompactConfig {
fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
*cfg = self;
}
}