diff options
Diffstat (limited to 'vendor/tabled/src/grid')
20 files changed, 2020 insertions, 0 deletions
diff --git a/vendor/tabled/src/grid/colored_config.rs b/vendor/tabled/src/grid/colored_config.rs new file mode 100644 index 000000000..51a00fbf4 --- /dev/null +++ b/vendor/tabled/src/grid/colored_config.rs @@ -0,0 +1,112 @@ +use std::ops::{Deref, DerefMut}; + +use crate::grid::{ + color::AnsiColor, + config::{Entity, EntityMap, SpannedConfig}, +}; + +/// A spanned configuration plus colors for cells. +#[derive(Debug, Default, PartialEq, Eq, Clone)] +pub struct ColoredConfig { + config: SpannedConfig, + colors: ColorMap, +} + +impl ColoredConfig { + /// Create a new colored config. + pub fn new(config: SpannedConfig) -> Self { + Self { + config, + colors: ColorMap::default(), + } + } + + /// Set a color for a given cell. + /// + /// The outcome is the same as if you'd use [`Format`] and added a color but it'd work only with `color` feature on. + /// While this method works in all contexts. + /// + /// [`Format`]: crate::settings::Format + pub fn set_color(&mut self, pos: Entity, color: AnsiColor<'static>) -> &mut Self { + match self.colors.0.as_mut() { + Some(map) => map.insert(pos, color), + None => { + let mut colors = EntityMap::default(); + colors.insert(pos, color); + self.colors = ColorMap(Some(colors)); + } + } + + self + } + + /// Set a list of colors. + pub fn set_colors(&mut self, colors: EntityMap<AnsiColor<'static>>) -> &mut Self { + self.colors = ColorMap(Some(colors)); + self + } + + /// Remove a color for a given cell. + pub fn remove_color(&mut self, pos: Entity) -> &mut Self { + if let Some(colors) = self.colors.0.as_mut() { + colors.remove(pos); + } + + self + } + + /// Returns a list of colors. + pub fn get_colors(&self) -> &ColorMap { + &self.colors + } + + /// Returns an inner config. + pub fn into_inner(self) -> SpannedConfig { + self.config + } +} + +impl Deref for ColoredConfig { + type Target = SpannedConfig; + + fn deref(&self) -> &Self::Target { + &self.config + } +} + +impl DerefMut for ColoredConfig { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.config + } +} + +impl From<SpannedConfig> for ColoredConfig { + fn from(value: SpannedConfig) -> Self { + Self::new(value) + } +} + +impl AsRef<SpannedConfig> for ColoredConfig { + fn as_ref(&self) -> &SpannedConfig { + &self.config + } +} + +/// A colors structure for [`ColoredConfig`]. +#[derive(Debug, Default, PartialEq, Eq, Clone)] +pub struct ColorMap(Option<EntityMap<AnsiColor<'static>>>); + +impl ColorMap { + /// Checks if any colors is set on. + pub fn is_empty(&self) -> bool { + self.0.is_none() + } +} + +impl crate::grid::colors::Colors for ColorMap { + type Color = AnsiColor<'static>; + + fn get_color(&self, (row, col): (usize, usize)) -> Option<&Self::Color> { + self.0.as_ref().map(|map| map.get(Entity::Cell(row, col))) + } +} diff --git a/vendor/tabled/src/grid/compact_multiline_config.rs b/vendor/tabled/src/grid/compact_multiline_config.rs new file mode 100644 index 000000000..c9056c911 --- /dev/null +++ b/vendor/tabled/src/grid/compact_multiline_config.rs @@ -0,0 +1,211 @@ +use crate::grid::color::StaticColor; +use crate::grid::config::{ + AlignmentHorizontal, AlignmentVertical, Borders, CompactConfig, Indent, Line, Sides, +}; + +/// A [`CompactConfig`] configuration plus vertical alignment. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct CompactMultilineConfig { + config: CompactConfig, + alignment_vertical: AlignmentVertical, + formatting: Formatting, +} + +impl CompactMultilineConfig { + /// Create a new colored config. + pub fn new(config: CompactConfig) -> Self { + Self::from(config) + } + + /// Set a horizontal alignment. + pub const fn set_alignment_vertical(mut self, alignment: AlignmentVertical) -> Self { + self.alignment_vertical = alignment; + self + } + + /// Get a alignment horizontal. + pub const fn get_alignment_vertical(&self) -> AlignmentVertical { + self.alignment_vertical + } + + /// Set grid margin. + pub const fn set_margin(mut self, margin: Sides<Indent>) -> Self { + self.config = self.config.set_margin(margin); + self + } + + /// Returns a grid margin. + pub const fn get_margin(&self) -> &Sides<Indent> { + self.config.get_margin() + } + + /// Set the [`Borders`] value as correct one. + pub const fn set_borders(mut self, borders: Borders<char>) -> Self { + self.config = self.config.set_borders(borders); + self + } + + /// Set the first horizontal line. + /// + /// It ignores the [`Borders`] horizontal value if set for 1st row. + pub const fn set_first_horizontal_line(mut self, line: Line<char>) -> Self { + self.config = self.config.set_first_horizontal_line(line); + self + } + + /// Set the first horizontal line. + /// + /// It ignores the [`Borders`] horizontal value if set for 1st row. + pub const fn get_first_horizontal_line(&self) -> Option<Line<char>> { + self.config.get_first_horizontal_line() + } + + /// Returns a current [`Borders`] structure. + pub const fn get_borders(&self) -> &Borders<char> { + self.config.get_borders() + } + + /// Returns a current [`Borders`] structure. + pub const fn get_borders_color(&self) -> &Borders<StaticColor> { + self.config.get_borders_color() + } + + /// Set a padding to a given cells. + pub const fn set_padding(mut self, padding: Sides<Indent>) -> Self { + self.config = self.config.set_padding(padding); + self + } + + /// Get a padding for a given. + pub const fn get_padding(&self) -> &Sides<Indent> { + self.config.get_padding() + } + + /// Set a horizontal alignment. + pub const fn set_alignment_horizontal(mut self, alignment: AlignmentHorizontal) -> Self { + self.config = self.config.set_alignment_horizontal(alignment); + self + } + + /// Get a alignment horizontal. + pub const fn get_alignment_horizontal(&self) -> AlignmentHorizontal { + self.config.get_alignment_horizontal() + } + + /// Sets colors of border carcass on the grid. + pub const fn set_borders_color(mut self, borders: Borders<StaticColor>) -> Self { + self.config = self.config.set_borders_color(borders); + self + } + + /// Set colors for a margin. + pub const fn set_margin_color(mut self, color: Sides<StaticColor>) -> Self { + self.config = self.config.set_margin_color(color); + self + } + + /// Returns a margin color. + pub const fn get_margin_color(&self) -> Sides<StaticColor> { + self.config.get_margin_color() + } + + /// Set a padding color to all cells. + pub const fn set_padding_color(mut self, color: Sides<StaticColor>) -> Self { + self.config = self.config.set_padding_color(color); + self + } + + /// get a padding color. + pub const fn get_padding_color(&self) -> Sides<StaticColor> { + self.config.get_padding_color() + } + + /// Set formatting. + pub const fn set_formatting(mut self, formatting: Formatting) -> Self { + self.formatting = formatting; + self + } + + /// Get formatting. + pub const fn get_formatting(&self) -> Formatting { + self.formatting + } +} + +impl Default for CompactMultilineConfig { + fn default() -> Self { + Self { + config: Default::default(), + alignment_vertical: AlignmentVertical::Top, + formatting: Formatting::default(), + } + } +} + +impl From<CompactConfig> for CompactMultilineConfig { + fn from(config: CompactConfig) -> Self { + Self { + config, + alignment_vertical: AlignmentVertical::Top, + formatting: Formatting::default(), + } + } +} + +impl AsRef<CompactConfig> for CompactMultilineConfig { + fn as_ref(&self) -> &CompactConfig { + &self.config + } +} + +impl AsMut<CompactConfig> for CompactMultilineConfig { + fn as_mut(&mut self) -> &mut CompactConfig { + &mut self.config + } +} + +#[cfg(feature = "std")] +impl From<CompactMultilineConfig> for crate::grid::config::SpannedConfig { + fn from(compact: CompactMultilineConfig) -> Self { + use crate::grid::config::Entity; + + let mut cfg = crate::grid::config::SpannedConfig::from(compact.config); + cfg.set_alignment_vertical(Entity::Global, compact.alignment_vertical); + cfg.set_formatting(Entity::Global, compact.formatting.into()); + + cfg + } +} + +/// Formatting represent a logic of formatting of a cell. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Formatting { + /// An setting to allow horizontal trim. + pub horizontal_trim: bool, + /// An setting to allow vertical trim. + pub vertical_trim: bool, + /// An setting to allow alignment per line. + pub allow_lines_alignment: bool, +} + +impl Formatting { + /// Creates a new [`Formatting`] structure. + pub fn new(horizontal_trim: bool, vertical_trim: bool, allow_lines_alignment: bool) -> Self { + Self { + horizontal_trim, + vertical_trim, + allow_lines_alignment, + } + } +} + +#[cfg(feature = "std")] +impl From<Formatting> for crate::grid::config::Formatting { + fn from(val: Formatting) -> Self { + crate::grid::config::Formatting { + allow_lines_alignment: val.allow_lines_alignment, + horizontal_trim: val.horizontal_trim, + vertical_trim: val.vertical_trim, + } + } +} diff --git a/vendor/tabled/src/grid/dimension/complete_dimension.rs b/vendor/tabled/src/grid/dimension/complete_dimension.rs new file mode 100644 index 000000000..3147cb27a --- /dev/null +++ b/vendor/tabled/src/grid/dimension/complete_dimension.rs @@ -0,0 +1,136 @@ +use std::borrow::Cow; + +use crate::grid::{ + config::{ColoredConfig, SpannedConfig}, + dimension::{Dimension, Estimate, SpannedGridDimension}, + records::Records, +}; + +/// CompleteDimension is a [`Dimension`] implementation for a [`Table`] +/// +/// [`Table`]: crate::Table +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct CompleteDimension<'a> { + width: Option<Cow<'a, [usize]>>, + height: Option<Cow<'a, [usize]>>, +} + +impl CompleteDimension<'_> { + /// Checks whether is the dimensions is set. + pub fn is_complete(&self) -> bool { + self.width.is_some() && self.height.is_some() + } + + /// Checks whether is nothing was set. + pub fn is_empty(&self) -> bool { + self.width.is_none() && self.height.is_none() + } + + /// Set column widths. + /// + /// In general the method is only considered to be useful to a [`TableOption`]. + /// + /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided widths. + /// + /// [`TableOption`]: crate::settings::TableOption + pub fn set_widths(&mut self, columns: Vec<usize>) -> bool { + self.width = Some(Cow::Owned(columns)); + + true + } + + /// Set rows heights. + /// + /// In general the method is only considered to be useful to a [`TableOption`]. + /// + /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided heights. + /// + /// [`TableOption`]: crate::settings::TableOption + pub fn set_heights(&mut self, rows: Vec<usize>) -> bool { + self.height = Some(Cow::Owned(rows)); + + true + } + + /// Force width estimation. + pub fn clear_width(&mut self) { + self.width = None; + } + + /// Force height estimation. + pub fn clear_height(&mut self) { + self.height = None; + } + + /// Copies a reference from self. + pub fn from_origin(&self) -> CompleteDimension<'_> { + let width = self.width.as_deref().map(Cow::Borrowed); + let height = self.height.as_deref().map(Cow::Borrowed); + + CompleteDimension { width, height } + } +} + +impl Dimension for CompleteDimension<'_> { + fn get_width(&self, column: usize) -> usize { + let width = self + .width + .as_ref() + .expect("It must always be Some at this point"); + + width[column] + } + + fn get_height(&self, row: usize) -> usize { + let height = self + .height + .as_ref() + .expect("It must always be Some at this point"); + + height[row] + } +} + +impl<R: Records> Estimate<R, SpannedConfig> for CompleteDimension<'_> { + fn estimate(&mut self, records: R, cfg: &SpannedConfig) { + match (self.width.is_some(), self.height.is_some()) { + (true, true) => {} + (true, false) => { + self.height = Some(Cow::Owned(SpannedGridDimension::height(records, cfg))); + } + (false, true) => { + self.width = Some(Cow::Owned(SpannedGridDimension::width(records, cfg))); + } + (false, false) => { + let mut dims = SpannedGridDimension::default(); + dims.estimate(records, cfg); + + let (width, height) = dims.get_values(); + self.width = Some(Cow::Owned(width)); + self.height = Some(Cow::Owned(height)); + } + } + } +} + +impl<R: Records> Estimate<R, ColoredConfig> for CompleteDimension<'_> { + fn estimate(&mut self, records: R, cfg: &ColoredConfig) { + match (self.width.is_some(), self.height.is_some()) { + (true, true) => {} + (true, false) => { + self.height = Some(Cow::Owned(SpannedGridDimension::height(records, cfg))); + } + (false, true) => { + self.width = Some(Cow::Owned(SpannedGridDimension::width(records, cfg))); + } + (false, false) => { + let mut dims = SpannedGridDimension::default(); + dims.estimate(records, cfg); + + let (width, height) = dims.get_values(); + self.width = Some(Cow::Owned(width)); + self.height = Some(Cow::Owned(height)); + } + } + } +} diff --git a/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs b/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs new file mode 100644 index 000000000..ddc806a45 --- /dev/null +++ b/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs @@ -0,0 +1,128 @@ +use std::borrow::Cow; + +use papergrid::{ + dimension::spanned_vec_records::SpannedVecRecordsDimension, records::vec_records::VecRecords, +}; + +use crate::grid::{ + config::{ColoredConfig, SpannedConfig}, + dimension::{Dimension, Estimate}, + records::vec_records::Cell, +}; + +/// CompleteDimension is a [`Dimension`] implementation for a [`Table`] +/// +/// [`Table`]: crate::Table +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct CompleteDimensionVecRecords<'a> { + width: Option<Cow<'a, [usize]>>, + height: Option<Cow<'a, [usize]>>, +} + +impl CompleteDimensionVecRecords<'_> { + /// Checks whether is the dimensions is set. + pub fn is_complete(&self) -> bool { + self.width.is_some() && self.height.is_some() + } + + /// Checks whether is nothing was set. + pub fn is_empty(&self) -> bool { + self.width.is_none() && self.height.is_none() + } + + /// Set column widths. + /// + /// In general the method is only considered to be useful to a [`TableOption`]. + /// + /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided widths. + /// + /// [`TableOption`]: crate::settings::TableOption + pub fn set_widths(&mut self, columns: Vec<usize>) -> bool { + self.width = Some(Cow::Owned(columns)); + + true + } + + /// Set rows heights. + /// + /// In general the method is only considered to be useful to a [`TableOption`]. + /// + /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided heights. + /// + /// [`TableOption`]: crate::settings::TableOption + pub fn set_heights(&mut self, rows: Vec<usize>) -> bool { + self.height = Some(Cow::Owned(rows)); + + true + } + + /// Force width estimation. + pub fn clear_width(&mut self) { + self.width = None; + } + + /// Force height estimation. + pub fn clear_height(&mut self) { + self.height = None; + } + + /// Copies a reference from self. + pub fn from_origin(&self) -> CompleteDimensionVecRecords<'_> { + let width = self.width.as_deref().map(Cow::Borrowed); + let height = self.height.as_deref().map(Cow::Borrowed); + + CompleteDimensionVecRecords { width, height } + } +} + +impl Dimension for CompleteDimensionVecRecords<'_> { + fn get_width(&self, column: usize) -> usize { + let width = self + .width + .as_ref() + .expect("It must always be Some at this point"); + + width[column] + } + + fn get_height(&self, row: usize) -> usize { + let height = self + .height + .as_ref() + .expect("It must always be Some at this point"); + + height[row] + } +} + +impl<T: AsRef<str> + Cell> Estimate<&VecRecords<T>, SpannedConfig> + for CompleteDimensionVecRecords<'_> +{ + fn estimate(&mut self, records: &VecRecords<T>, cfg: &SpannedConfig) { + match (self.width.is_some(), self.height.is_some()) { + (true, true) => {} + (true, false) => { + self.height = Some(Cow::Owned(SpannedVecRecordsDimension::height(records, cfg))); + } + (false, true) => { + self.width = Some(Cow::Owned(SpannedVecRecordsDimension::width(records, cfg))); + } + (false, false) => { + let mut dims = SpannedVecRecordsDimension::default(); + dims.estimate(records, cfg); + + let (width, height) = dims.get_values(); + self.width = Some(Cow::Owned(width)); + self.height = Some(Cow::Owned(height)); + } + } + } +} + +impl<T: AsRef<str> + Cell> Estimate<&VecRecords<T>, ColoredConfig> + for CompleteDimensionVecRecords<'_> +{ + fn estimate(&mut self, records: &VecRecords<T>, cfg: &ColoredConfig) { + self.estimate(records, cfg.as_ref()) + } +} diff --git a/vendor/tabled/src/grid/dimension/const_dimension.rs b/vendor/tabled/src/grid/dimension/const_dimension.rs new file mode 100644 index 000000000..450b1abfe --- /dev/null +++ b/vendor/tabled/src/grid/dimension/const_dimension.rs @@ -0,0 +1,70 @@ +//! Module contains a dimension estimator for [`CompactTable`] +//! +//! [`CompactTable`]: crate::tables::CompactTable + +use crate::grid::dimension::{Dimension, Estimate}; + +/// A constant size dimension or a value dimension. +#[derive(Debug, Clone, Copy)] +pub struct ConstDimension<const COLUMNS: usize, const ROWS: usize> { + height: ConstSize<ROWS>, + width: ConstSize<COLUMNS>, +} + +impl<const COLUMNS: usize, const ROWS: usize> ConstDimension<COLUMNS, ROWS> { + /// Returns a new dimension object with a given estimates. + pub const fn new(width: ConstSize<COLUMNS>, height: ConstSize<ROWS>) -> Self { + Self { width, height } + } +} + +impl<const COLUMNS: usize, const ROWS: usize> Dimension for ConstDimension<COLUMNS, ROWS> { + fn get_width(&self, column: usize) -> usize { + match self.width { + ConstSize::List(list) => list[column], + ConstSize::Value(val) => val, + } + } + + fn get_height(&self, row: usize) -> usize { + match self.height { + ConstSize::List(list) => list[row], + ConstSize::Value(val) => val, + } + } +} + +impl<const COLUMNS: usize, const ROWS: usize> From<ConstDimension<COLUMNS, ROWS>> + for (ConstSize<COLUMNS>, ConstSize<ROWS>) +{ + fn from(value: ConstDimension<COLUMNS, ROWS>) -> Self { + (value.width, value.height) + } +} + +impl<R, D, const COLUMNS: usize, const ROWS: usize> Estimate<R, D> + for ConstDimension<COLUMNS, ROWS> +{ + fn estimate(&mut self, _: R, _: &D) {} +} + +/// Const size represents either a const array values or a single value which responsible for the whole list. +#[derive(Debug, Clone, Copy)] +pub enum ConstSize<const N: usize> { + /// A constant array of estimates. + List([usize; N]), + /// A value which act as a single estimate for all entries. + Value(usize), +} + +impl From<usize> for ConstSize<0> { + fn from(value: usize) -> Self { + ConstSize::Value(value) + } +} + +impl<const N: usize> From<[usize; N]> for ConstSize<N> { + fn from(value: [usize; N]) -> Self { + ConstSize::List(value) + } +} diff --git a/vendor/tabled/src/grid/dimension/mod.rs b/vendor/tabled/src/grid/dimension/mod.rs new file mode 100644 index 000000000..2f67b402f --- /dev/null +++ b/vendor/tabled/src/grid/dimension/mod.rs @@ -0,0 +1,32 @@ +//! Module contains a list of implementations of [`Estimate`] and [`Dimension`]. + +mod const_dimension; +mod pool_table_dimension; + +#[cfg(feature = "std")] +mod complete_dimension; +#[cfg(feature = "std")] +mod complete_dimension_vec_records; +#[cfg(feature = "std")] +mod peekable_dimension; +#[cfg(feature = "std")] +mod static_dimension; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub use self::{ + complete_dimension::CompleteDimension, + complete_dimension_vec_records::CompleteDimensionVecRecords, + peekable_dimension::PeekableDimension, + static_dimension::{DimensionValue, StaticDimension}, +}; +pub use const_dimension::{ConstDimension, ConstSize}; +pub use papergrid::dimension::{Dimension, Estimate}; +pub use pool_table_dimension::{DimensionPriority, PoolTableDimension}; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub use papergrid::dimension::{ + compact::CompactGridDimension, spanned::SpannedGridDimension, + spanned_vec_records::SpannedVecRecordsDimension, +}; diff --git a/vendor/tabled/src/grid/dimension/peekable_dimension.rs b/vendor/tabled/src/grid/dimension/peekable_dimension.rs new file mode 100644 index 000000000..1ba6fda21 --- /dev/null +++ b/vendor/tabled/src/grid/dimension/peekable_dimension.rs @@ -0,0 +1,335 @@ +use papergrid::records::vec_records::{CellInfo, VecRecords}; + +use crate::grid::{ + config::SpannedConfig, + dimension::{Dimension, Estimate}, + records::Records, +}; + +/// PeekableDimension is a [`Dimension`] implementation for a [`Table`] +/// +/// [`Table`]: crate::Table +#[derive(Debug, Default, Clone)] +pub struct PeekableDimension { + width: Vec<usize>, + height: Vec<usize>, +} + +impl PeekableDimension { + /// Calculates height of rows. + pub fn height<T: AsRef<str>>( + records: &VecRecords<CellInfo<T>>, + cfg: &SpannedConfig, + ) -> Vec<usize> { + estimation::build_height(records, cfg) + } + + /// Calculates width of columns. + pub fn width<T: AsRef<str>>( + records: &VecRecords<CellInfo<T>>, + cfg: &SpannedConfig, + ) -> Vec<usize> { + estimation::build_width(records, cfg) + } + + /// Return width and height lists. + pub fn get_values(self) -> (Vec<usize>, Vec<usize>) { + (self.width, self.height) + } +} + +impl Dimension for PeekableDimension { + fn get_width(&self, column: usize) -> usize { + self.width[column] + } + + fn get_height(&self, row: usize) -> usize { + self.height[row] + } +} + +impl<T> Estimate<&VecRecords<CellInfo<T>>, SpannedConfig> for PeekableDimension +where + T: AsRef<str>, +{ + fn estimate(&mut self, records: &VecRecords<CellInfo<T>>, cfg: &SpannedConfig) { + let (width, height) = estimation::build_dimensions(records, cfg); + self.width = width; + self.height = height; + } +} + +mod estimation { + use core::cmp::{max, Ordering}; + use std::collections::HashMap; + + use papergrid::{ + config::Position, + records::vec_records::{Cell, CellInfo, VecRecords}, + }; + + use super::*; + + pub(super) fn build_dimensions<T: AsRef<str>>( + records: &VecRecords<CellInfo<T>>, + cfg: &SpannedConfig, + ) -> (Vec<usize>, Vec<usize>) { + let count_columns = records.count_columns(); + + let mut widths = vec![0; count_columns]; + let mut heights = vec![]; + + let mut vspans = HashMap::new(); + let mut hspans = HashMap::new(); + + for (row, columns) in records.iter_rows().enumerate() { + let mut row_height = 0; + for (col, cell) in columns.iter().enumerate() { + let pos = (row, col); + if !cfg.is_cell_visible(pos) { + continue; + } + + let height = cell.count_lines(); + let width = cell.width(); + + let pad = cfg.get_padding(pos.into()); + let width = width + pad.left.size + pad.right.size; + let height = height + pad.top.size + pad.bottom.size; + + match cfg.get_column_span(pos) { + Some(n) if n > 1 => { + let _ = vspans.insert(pos, (n, width)); + } + _ => widths[col] = max(widths[col], width), + } + + match cfg.get_row_span(pos) { + Some(n) if n > 1 => { + let _ = hspans.insert(pos, (n, height)); + } + _ => row_height = max(row_height, height), + } + } + + heights.push(row_height); + } + + let count_rows = heights.len(); + + adjust_vspans(cfg, count_columns, &vspans, &mut widths); + adjust_hspans(cfg, count_rows, &hspans, &mut heights); + + (widths, heights) + } + + fn adjust_hspans( + cfg: &SpannedConfig, + len: usize, + spans: &HashMap<Position, (usize, usize)>, + heights: &mut [usize], + ) { + if spans.is_empty() { + return; + } + + let mut spans_ordered = spans + .iter() + .map(|(k, v)| ((k.0, k.1), *v)) + .collect::<Vec<_>>(); + spans_ordered.sort_unstable_by(|(arow, acol), (brow, bcol)| match arow.cmp(brow) { + Ordering::Equal => acol.cmp(bcol), + ord => ord, + }); + + for ((row, _), (span, height)) in spans_ordered { + adjust_row_range(cfg, height, len, row, row + span, heights); + } + } + + fn adjust_row_range( + cfg: &SpannedConfig, + max_span_height: usize, + len: usize, + start: usize, + end: usize, + heights: &mut [usize], + ) { + let range_height = range_height(cfg, len, start, end, heights); + if range_height >= max_span_height { + return; + } + + inc_range(heights, max_span_height - range_height, start, end); + } + + fn range_height( + cfg: &SpannedConfig, + len: usize, + start: usize, + end: usize, + heights: &[usize], + ) -> usize { + let count_borders = count_horizontal_borders(cfg, len, start, end); + let range_height = heights[start..end].iter().sum::<usize>(); + count_borders + range_height + } + + fn count_horizontal_borders( + cfg: &SpannedConfig, + len: usize, + start: usize, + end: usize, + ) -> usize { + (start..end) + .skip(1) + .filter(|&i| cfg.has_horizontal(i, len)) + .count() + } + + fn inc_range(list: &mut [usize], size: usize, start: usize, end: usize) { + if list.is_empty() { + return; + } + + let span = end - start; + let one = size / span; + let rest = size - span * one; + + let mut i = start; + while i < end { + if i == start { + list[i] += one + rest; + } else { + list[i] += one; + } + + i += 1; + } + } + + fn adjust_vspans( + cfg: &SpannedConfig, + len: usize, + spans: &HashMap<Position, (usize, usize)>, + widths: &mut [usize], + ) { + if spans.is_empty() { + return; + } + + // The overall width distribution will be different depend on the order. + // + // We sort spans in order to prioritize the smaller spans first. + let mut spans_ordered = spans + .iter() + .map(|(k, v)| ((k.0, k.1), *v)) + .collect::<Vec<_>>(); + spans_ordered.sort_unstable_by(|a, b| match a.1 .0.cmp(&b.1 .0) { + Ordering::Equal => a.0.cmp(&b.0), + o => o, + }); + + for ((_, col), (span, width)) in spans_ordered { + adjust_column_range(cfg, width, len, col, col + span, widths); + } + } + + fn adjust_column_range( + cfg: &SpannedConfig, + max_span_width: usize, + len: usize, + start: usize, + end: usize, + widths: &mut [usize], + ) { + let range_width = range_width(cfg, len, start, end, widths); + if range_width >= max_span_width { + return; + } + + inc_range(widths, max_span_width - range_width, start, end); + } + + fn range_width( + cfg: &SpannedConfig, + len: usize, + start: usize, + end: usize, + widths: &[usize], + ) -> usize { + let count_borders = count_vertical_borders(cfg, len, start, end); + let range_width = widths[start..end].iter().sum::<usize>(); + count_borders + range_width + } + + fn count_vertical_borders(cfg: &SpannedConfig, len: usize, start: usize, end: usize) -> usize { + (start..end) + .skip(1) + .filter(|&i| cfg.has_vertical(i, len)) + .count() + } + + pub(super) fn build_height<T: AsRef<str>>( + records: &VecRecords<CellInfo<T>>, + cfg: &SpannedConfig, + ) -> Vec<usize> { + let mut heights = vec![]; + let mut hspans = HashMap::new(); + + for (row, columns) in records.iter_rows().enumerate() { + let mut row_height = 0; + for (col, cell) in columns.iter().enumerate() { + let pos = (row, col); + if !cfg.is_cell_visible(pos) { + continue; + } + + let height = cell.count_lines(); + match cfg.get_row_span(pos) { + Some(n) if n > 1 => { + let _ = hspans.insert(pos, (n, height)); + } + _ => row_height = max(row_height, height), + } + } + + heights.push(row_height); + } + + adjust_hspans(cfg, heights.len(), &hspans, &mut heights); + + heights + } + + pub(super) fn build_width<T: AsRef<str>>( + records: &VecRecords<CellInfo<T>>, + cfg: &SpannedConfig, + ) -> Vec<usize> { + let count_columns = records.count_columns(); + + let mut widths = vec![0; count_columns]; + let mut vspans = HashMap::new(); + + for (row, columns) in records.iter_rows().enumerate() { + for (col, cell) in columns.iter().enumerate() { + let pos = (row, col); + if !cfg.is_cell_visible(pos) { + continue; + } + + let width = cell.width(); + match cfg.get_column_span(pos) { + Some(n) if n > 1 => { + let _ = vspans.insert(pos, (n, width)); + } + _ => widths[col] = max(widths[col], width), + } + } + } + + adjust_vspans(cfg, count_columns, &vspans, &mut widths); + + widths + } +} diff --git a/vendor/tabled/src/grid/dimension/pool_table_dimension.rs b/vendor/tabled/src/grid/dimension/pool_table_dimension.rs new file mode 100644 index 000000000..9909c661d --- /dev/null +++ b/vendor/tabled/src/grid/dimension/pool_table_dimension.rs @@ -0,0 +1,36 @@ +/// PoolTableDimension is a dimension resolve strategy for [`PoolTable`] +/// +/// [`PoolTable`]: crate::tables::PoolTable +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +pub struct PoolTableDimension { + width: DimensionPriority, + height: DimensionPriority, +} + +impl PoolTableDimension { + /// Creates a new object. + pub fn new(width: DimensionPriority, height: DimensionPriority) -> Self { + Self { width, height } + } + + /// Return a width priority. + pub fn width(&self) -> DimensionPriority { + self.width + } + + /// Return a height priority. + pub fn height(&self) -> DimensionPriority { + self.height + } +} + +/// A control of width/height logic for situations where we must increase some cell to align columns/row. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum DimensionPriority { + /// Increase first cell width/height in a row/column. + First, + /// Increase last cell width/height in a row/column. + Last, + /// Increase cells width/height 1 by 1 in a row/column. + List, +} diff --git a/vendor/tabled/src/grid/dimension/static_dimension.rs b/vendor/tabled/src/grid/dimension/static_dimension.rs new file mode 100644 index 000000000..f9474f212 --- /dev/null +++ b/vendor/tabled/src/grid/dimension/static_dimension.rs @@ -0,0 +1,63 @@ +use crate::grid::dimension::{Dimension, Estimate}; + +/// A constant dimension. +#[derive(Debug, Clone)] +pub struct StaticDimension { + width: DimensionValue, + height: DimensionValue, +} + +impl StaticDimension { + /// Creates a constant dimension. + pub fn new(width: DimensionValue, height: DimensionValue) -> Self { + Self { width, height } + } +} + +impl From<StaticDimension> for (DimensionValue, DimensionValue) { + fn from(value: StaticDimension) -> Self { + (value.width, value.height) + } +} + +impl Dimension for StaticDimension { + fn get_width(&self, column: usize) -> usize { + self.width.get(column) + } + + fn get_height(&self, row: usize) -> usize { + self.height.get(row) + } +} + +impl<R, C> Estimate<R, C> for StaticDimension { + fn estimate(&mut self, _: R, _: &C) {} +} + +/// A dimension value. +#[derive(Debug, Clone)] +pub enum DimensionValue { + /// Const width value. + Exact(usize), + /// A list of width values for columns. + List(Vec<usize>), + /// A list of width values for columns and a value for the rest. + Partial(Vec<usize>, usize), +} + +impl DimensionValue { + /// Get a width by column. + pub fn get(&self, col: usize) -> usize { + match self { + DimensionValue::Exact(val) => *val, + DimensionValue::List(cols) => cols[col], + DimensionValue::Partial(cols, val) => { + if cols.len() > col { + cols[col] + } else { + *val + } + } + } + } +} diff --git a/vendor/tabled/src/grid/mod.rs b/vendor/tabled/src/grid/mod.rs new file mode 100644 index 000000000..cdd8c55c0 --- /dev/null +++ b/vendor/tabled/src/grid/mod.rs @@ -0,0 +1,48 @@ +//! Module is responsible for tables underlyign grid. +//! +//! It might be used when implementing your own [`TableOption`] and [`CellOption`]. +//! +//! [`TableOption`]: crate::settings::TableOption +//! [`CellOption`]: crate::settings::CellOption +#[cfg(feature = "std")] +mod colored_config; + +mod compact_multiline_config; + +pub mod dimension; +pub mod records; + +pub use papergrid::color; +pub use papergrid::colors; +pub use papergrid::util; + +pub mod config { + //! Module contains a list of configs for varios tables/grids. + + pub use papergrid::config::{ + compact::CompactConfig, AlignmentHorizontal, AlignmentVertical, Border, Borders, Entity, + EntityIterator, Indent, Line, Position, Sides, + }; + + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + pub use papergrid::config::spanned::{ + EntityMap, Formatting, HorizontalLine, Offset, SpannedConfig, VerticalLine, + }; + + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + pub use super::colored_config::{ColorMap, ColoredConfig}; + + pub use super::compact_multiline_config::CompactMultilineConfig; +} + +pub use papergrid::grid::compact::CompactGrid; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub use papergrid::grid::iterable::Grid; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub use papergrid::grid::peekable::PeekableGrid; diff --git a/vendor/tabled/src/grid/records/empty_records.rs b/vendor/tabled/src/grid/records/empty_records.rs new file mode 100644 index 000000000..77ebc812d --- /dev/null +++ b/vendor/tabled/src/grid/records/empty_records.rs @@ -0,0 +1,41 @@ +//! An empty [`Records`] implementation. + +use core::iter::{repeat, Repeat, Take}; + +use super::Records; + +/// Empty representation of [`Records`]. +#[derive(Debug, Default, Clone)] +pub struct EmptyRecords { + rows: usize, + cols: usize, +} + +impl EmptyRecords { + /// Constructs an empty representation of [`Records`] with a given shape. + pub fn new(rows: usize, cols: usize) -> Self { + Self { rows, cols } + } +} + +impl From<(usize, usize)> for EmptyRecords { + fn from((count_rows, count_columns): (usize, usize)) -> Self { + Self::new(count_rows, count_columns) + } +} + +impl Records for EmptyRecords { + type Iter = Take<Repeat<Take<Repeat<&'static str>>>>; + + fn iter_rows(self) -> Self::Iter { + repeat(repeat("").take(self.cols)).take(self.rows) + } + + fn count_columns(&self) -> usize { + self.cols + } + + fn hint_count_rows(&self) -> Option<usize> { + Some(self.rows) + } +} diff --git a/vendor/tabled/src/grid/records/into_records/buf_records.rs b/vendor/tabled/src/grid/records/into_records/buf_records.rs new file mode 100644 index 000000000..2db3e45bf --- /dev/null +++ b/vendor/tabled/src/grid/records/into_records/buf_records.rs @@ -0,0 +1,213 @@ +//! A module contains [`BufRows`] and [`BufColumns`] iterators. +//! +//! Almoust always they both can be used interchangeably but [`BufRows`] is supposed to be lighter cause it +//! does not reads columns. + +use crate::grid::records::IntoRecords; + +use super::either_string::EitherString; + +/// BufRecords inspects [`IntoRecords`] iterator and keeps read data buffered. +/// So it can be checking before hand. +#[derive(Debug)] +pub struct BufRows<I, T> { + iter: I, + buf: Vec<T>, +} + +impl BufRows<(), ()> { + /// Creates a new [`BufRows`] structure, filling the buffer. + pub fn new<I: IntoRecords>( + records: I, + sniff: usize, + ) -> BufRows<<I::IterRows as IntoIterator>::IntoIter, I::IterColumns> { + let mut buf = vec![]; + + let mut iter = records.iter_rows().into_iter(); + for _ in 0..sniff { + match iter.next() { + Some(row) => buf.push(row), + None => break, + } + } + + BufRows { iter, buf } + } +} + +impl<I, T> BufRows<I, T> { + /// Returns a slice of a record buffer. + pub fn as_slice(&self) -> &[T] { + &self.buf + } +} + +impl<I, T> From<BufRows<I, T>> for BufColumns<I> +where + T: IntoIterator, + T::Item: AsRef<str>, +{ + fn from(value: BufRows<I, T>) -> Self { + let buf = value + .buf + .into_iter() + .map(|row| row.into_iter().map(|s| s.as_ref().to_string()).collect()) + .collect(); + + BufColumns { + iter: value.iter, + buf, + } + } +} + +impl<I, T> IntoRecords for BufRows<I, T> +where + I: Iterator<Item = T>, + T: IntoIterator, + T::Item: AsRef<str>, +{ + type Cell = T::Item; + type IterColumns = T; + type IterRows = BufRowIter<I, T>; + + fn iter_rows(self) -> Self::IterRows { + BufRowIter { + buf: self.buf.into_iter(), + iter: self.iter, + } + } +} + +/// Buffered [`Iterator`]. +#[derive(Debug)] +pub struct BufRowIter<I, T> { + buf: std::vec::IntoIter<T>, + iter: I, +} + +impl<I, T> Iterator for BufRowIter<I, T> +where + I: Iterator<Item = T>, + T: IntoIterator, + T::Item: AsRef<str>, +{ + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + match self.buf.next() { + Some(i) => Some(i), + None => self.iter.next(), + } + } +} + +/// BufRecords inspects [`IntoRecords`] iterator and keeps read data buffered. +/// So it can be checking before hand. +/// +/// In contrast to [`BufRows`] it keeps records by columns. +#[derive(Debug)] +pub struct BufColumns<I> { + iter: I, + buf: Vec<Vec<String>>, +} + +impl BufColumns<()> { + /// Creates new [`BufColumns`] structure, filling the buffer. + pub fn new<I: IntoRecords>( + records: I, + sniff: usize, + ) -> BufColumns<<I::IterRows as IntoIterator>::IntoIter> { + let mut buf = vec![]; + + let mut iter = records.iter_rows().into_iter(); + for _ in 0..sniff { + match iter.next() { + Some(row) => { + let row = row + .into_iter() + .map(|cell| cell.as_ref().to_string()) + .collect::<Vec<_>>(); + buf.push(row) + } + None => break, + } + } + + BufColumns { iter, buf } + } +} + +impl<I> BufColumns<I> { + /// Returns a slice of a keeping buffer. + pub fn as_slice(&self) -> &[Vec<String>] { + &self.buf + } +} + +impl<I> IntoRecords for BufColumns<I> +where + I: Iterator, + I::Item: IntoIterator, + <I::Item as IntoIterator>::Item: AsRef<str>, +{ + type Cell = EitherString<<I::Item as IntoIterator>::Item>; + type IterColumns = EitherRowIterator<<I::Item as IntoIterator>::IntoIter>; + type IterRows = BufColumnIter<I>; + + fn iter_rows(self) -> Self::IterRows { + BufColumnIter { + buf: self.buf.into_iter(), + iter: self.iter, + } + } +} + +/// A row iterator for [`BufColumns`] +#[derive(Debug)] +pub struct BufColumnIter<I> { + buf: std::vec::IntoIter<Vec<String>>, + iter: I, +} + +impl<I> Iterator for BufColumnIter<I> +where + I: Iterator, + I::Item: IntoIterator, + <I::Item as IntoIterator>::Item: AsRef<str>, +{ + type Item = EitherRowIterator<<I::Item as IntoIterator>::IntoIter>; + + fn next(&mut self) -> Option<Self::Item> { + match self.buf.next() { + Some(i) => Some(EitherRowIterator::Owned(i.into_iter())), + None => self + .iter + .next() + .map(|i| EitherRowIterator::Some(i.into_iter())), + } + } +} + +/// An iterator over some iterator or allocated buffer. +#[derive(Debug)] +pub enum EitherRowIterator<I> { + /// Allocated iterator. + Owned(std::vec::IntoIter<String>), + /// Given iterator. + Some(I), +} + +impl<I> Iterator for EitherRowIterator<I> +where + I: Iterator, +{ + type Item = EitherString<I::Item>; + + fn next(&mut self) -> Option<Self::Item> { + match self { + EitherRowIterator::Owned(iter) => iter.next().map(EitherString::Owned), + EitherRowIterator::Some(iter) => iter.next().map(EitherString::Some), + } + } +} diff --git a/vendor/tabled/src/grid/records/into_records/either_string.rs b/vendor/tabled/src/grid/records/into_records/either_string.rs new file mode 100644 index 000000000..f4d97290d --- /dev/null +++ b/vendor/tabled/src/grid/records/into_records/either_string.rs @@ -0,0 +1,22 @@ +//! A module with a utility enum [`EitherString`]. + +/// Either allocated string or some type which can be used as a string. +#[derive(Debug)] +pub enum EitherString<T> { + /// Allocated string. + Owned(String), + /// Something which can be used as a string. + Some(T), +} + +impl<T> AsRef<str> for EitherString<T> +where + T: AsRef<str>, +{ + fn as_ref(&self) -> &str { + match self { + EitherString::Owned(s) => s.as_ref(), + EitherString::Some(s) => s.as_ref(), + } + } +} diff --git a/vendor/tabled/src/grid/records/into_records/limit_column_records.rs b/vendor/tabled/src/grid/records/into_records/limit_column_records.rs new file mode 100644 index 000000000..89b2b89ed --- /dev/null +++ b/vendor/tabled/src/grid/records/into_records/limit_column_records.rs @@ -0,0 +1,82 @@ +//! The module contains [`LimitColumns`] records iterator. + +use crate::grid::records::IntoRecords; + +/// An iterator which limits amount of columns. +#[derive(Debug)] +pub struct LimitColumns<I> { + records: I, + limit: usize, +} + +impl LimitColumns<()> { + /// Creates new [`LimitColumns`]. + pub fn new<I: IntoRecords>(records: I, limit: usize) -> LimitColumns<I> { + LimitColumns { records, limit } + } +} + +impl<I> IntoRecords for LimitColumns<I> +where + I: IntoRecords, +{ + type Cell = I::Cell; + type IterColumns = LimitColumnsColumnsIter<<I::IterColumns as IntoIterator>::IntoIter>; + type IterRows = LimitColumnsIter<<I::IterRows as IntoIterator>::IntoIter>; + + fn iter_rows(self) -> Self::IterRows { + LimitColumnsIter { + iter: self.records.iter_rows().into_iter(), + limit: self.limit, + } + } +} + +/// An iterator over rows for [`LimitColumns`]. +#[derive(Debug)] +pub struct LimitColumnsIter<I> { + iter: I, + limit: usize, +} + +impl<I> Iterator for LimitColumnsIter<I> +where + I: Iterator, + I::Item: IntoIterator, + <I::Item as IntoIterator>::Item: AsRef<str>, +{ + type Item = LimitColumnsColumnsIter<<I::Item as IntoIterator>::IntoIter>; + + fn next(&mut self) -> Option<Self::Item> { + let iter = self.iter.next()?; + Some(LimitColumnsColumnsIter { + iter: iter.into_iter(), + limit: self.limit, + }) + } +} + +/// An iterator over columns for [`LimitColumns`]. +#[derive(Debug)] +pub struct LimitColumnsColumnsIter<I> { + iter: I, + limit: usize, +} + +impl<I> Iterator for LimitColumnsColumnsIter<I> +where + I: Iterator, + I::Item: AsRef<str>, +{ + type Item = I::Item; + + fn next(&mut self) -> Option<Self::Item> { + if self.limit == 0 { + return None; + } + + self.limit -= 1; + + self.iter.next() + } +} diff --git a/vendor/tabled/src/grid/records/into_records/limit_row_records.rs b/vendor/tabled/src/grid/records/into_records/limit_row_records.rs new file mode 100644 index 000000000..a461c6682 --- /dev/null +++ b/vendor/tabled/src/grid/records/into_records/limit_row_records.rs @@ -0,0 +1,59 @@ +//! The module contains [`LimitRows`] records iterator. + +use crate::grid::records::IntoRecords; + +/// [`LimitRows`] is an records iterator which limits amount of rows. +#[derive(Debug)] +pub struct LimitRows<I> { + records: I, + limit: usize, +} + +impl LimitRows<()> { + /// Creates new [`LimitRows`] iterator. + pub fn new<I: IntoRecords>(records: I, limit: usize) -> LimitRows<I> { + LimitRows { records, limit } + } +} + +impl<I> IntoRecords for LimitRows<I> +where + I: IntoRecords, +{ + type Cell = I::Cell; + type IterColumns = I::IterColumns; + type IterRows = LimitRowsIter<<I::IterRows as IntoIterator>::IntoIter>; + + fn iter_rows(self) -> Self::IterRows { + LimitRowsIter { + iter: self.records.iter_rows().into_iter(), + limit: self.limit, + } + } +} + +/// A rows iterator for [`LimitRows`] +#[derive(Debug)] +pub struct LimitRowsIter<I> { + iter: I, + limit: usize, +} + +impl<I> Iterator for LimitRowsIter<I> +where + I: Iterator, + I::Item: IntoIterator, + <I::Item as IntoIterator>::Item: AsRef<str>, +{ + type Item = I::Item; + + fn next(&mut self) -> Option<Self::Item> { + if self.limit == 0 { + return None; + } + + self.limit -= 1; + + self.iter.next() + } +} diff --git a/vendor/tabled/src/grid/records/into_records/mod.rs b/vendor/tabled/src/grid/records/into_records/mod.rs new file mode 100644 index 000000000..0a52c41c1 --- /dev/null +++ b/vendor/tabled/src/grid/records/into_records/mod.rs @@ -0,0 +1,26 @@ +//! The module contains a list of helpers for [`IntoRecords`] +//! +//! [`IntoRecords`]: crate::grid::records::IntoRecords + +pub mod limit_column_records; +pub mod limit_row_records; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub mod buf_records; +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub mod either_string; +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub mod truncate_records; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub use buf_records::{BufColumns, BufRows}; +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub use truncate_records::TruncateContent; + +pub use limit_column_records::LimitColumns; +pub use limit_row_records::LimitRows; diff --git a/vendor/tabled/src/grid/records/into_records/truncate_records.rs b/vendor/tabled/src/grid/records/into_records/truncate_records.rs new file mode 100644 index 000000000..17e7e533e --- /dev/null +++ b/vendor/tabled/src/grid/records/into_records/truncate_records.rs @@ -0,0 +1,136 @@ +//! The module contains [`TruncateContent`] records iterator. + +use std::borrow::Cow; + +use crate::{ + grid::records::IntoRecords, grid::util::string::string_width_multiline, + settings::width::Truncate, +}; + +use super::either_string::EitherString; + +/// A records iterator which truncates all cells to a given width. +#[derive(Debug)] +pub struct TruncateContent<'a, I> { + records: I, + width: ExactValue<'a>, +} + +impl TruncateContent<'_, ()> { + /// Creates new [`TruncateContent`] object. + pub fn new<I: IntoRecords>(records: I, width: ExactValue<'_>) -> TruncateContent<'_, I> { + TruncateContent { records, width } + } +} + +impl<'a, I> IntoRecords for TruncateContent<'a, I> +where + I: IntoRecords, +{ + type Cell = EitherString<I::Cell>; + type IterColumns = TruncateContentColumnsIter<'a, <I::IterColumns as IntoIterator>::IntoIter>; + type IterRows = TruncateContentIter<'a, <I::IterRows as IntoIterator>::IntoIter>; + + fn iter_rows(self) -> Self::IterRows { + TruncateContentIter { + iter: self.records.iter_rows().into_iter(), + width: self.width.clone(), + } + } +} + +/// A row iterator for [`TruncateContent`]. +#[derive(Debug)] +pub struct TruncateContentIter<'a, I> { + iter: I, + width: ExactValue<'a>, +} + +impl<'a, I> Iterator for TruncateContentIter<'a, I> +where + I: Iterator, + I::Item: IntoIterator, + <I::Item as IntoIterator>::Item: AsRef<str>, +{ + type Item = TruncateContentColumnsIter<'a, <I::Item as IntoIterator>::IntoIter>; + + fn next(&mut self) -> Option<Self::Item> { + let iter = self.iter.next()?; + Some(TruncateContentColumnsIter { + iter: iter.into_iter(), + current: 0, + width: self.width.clone(), + }) + } +} + +/// A column iterator for [`TruncateContent`]. +#[derive(Debug)] +pub struct TruncateContentColumnsIter<'a, I> { + iter: I, + width: ExactValue<'a>, + current: usize, +} + +impl<I> Iterator for TruncateContentColumnsIter<'_, I> +where + I: Iterator, + I::Item: AsRef<str>, +{ + type Item = EitherString<I::Item>; + + fn next(&mut self) -> Option<Self::Item> { + let s = self.iter.next()?; + + let width = self.width.get(self.current); + self.current += 1; + + let text_width = string_width_multiline(s.as_ref()); + if text_width <= width { + return Some(EitherString::Some(s)); + } + + let text = Truncate::truncate_text(s.as_ref(), width); + let text = text.into_owned(); + let text = EitherString::Owned(text); + + Some(text) + } +} + +/// A width value. +#[derive(Debug, Clone)] +pub enum ExactValue<'a> { + /// Const width value. + Exact(usize), + /// A list of width values for columns. + List(Cow<'a, [usize]>), +} + +impl<'a> From<&'a [usize]> for ExactValue<'a> { + fn from(value: &'a [usize]) -> Self { + Self::List(value.into()) + } +} + +impl From<Vec<usize>> for ExactValue<'_> { + fn from(value: Vec<usize>) -> Self { + Self::List(value.into()) + } +} + +impl From<usize> for ExactValue<'_> { + fn from(value: usize) -> Self { + Self::Exact(value) + } +} + +impl ExactValue<'_> { + /// Get a width by column. + pub fn get(&self, col: usize) -> usize { + match self { + ExactValue::Exact(val) => *val, + ExactValue::List(cols) => cols[col], + } + } +} diff --git a/vendor/tabled/src/grid/records/mod.rs b/vendor/tabled/src/grid/records/mod.rs new file mode 100644 index 000000000..494002346 --- /dev/null +++ b/vendor/tabled/src/grid/records/mod.rs @@ -0,0 +1,19 @@ +//! The module contains [`Records`], [`ExactRecords`], [`RecordsMut`], [`Resizable`] traits +//! and its implementations. +//! +//! Also it provies a list of helpers for a user built [`Records`] via [`into_records`]. + +mod empty_records; +mod records_mut; +mod resizable; + +pub mod into_records; + +pub use empty_records::EmptyRecords; +pub use papergrid::records::{ExactRecords, IntoRecords, IterRecords, PeekableRecords, Records}; +pub use records_mut::RecordsMut; +pub use resizable::Resizable; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub use papergrid::records::vec_records; diff --git a/vendor/tabled/src/grid/records/records_mut.rs b/vendor/tabled/src/grid/records/records_mut.rs new file mode 100644 index 000000000..38c42d2c7 --- /dev/null +++ b/vendor/tabled/src/grid/records/records_mut.rs @@ -0,0 +1,34 @@ +use crate::grid::config::Position; +#[cfg(feature = "std")] +use crate::grid::records::vec_records::{CellInfo, VecRecords}; + +/// A [`Records`] representation which can modify cell by (row, column) index. +/// +/// [`Records`]: crate::grid::records::Records +pub trait RecordsMut<Text> { + /// Sets a text to a given cell by index. + fn set(&mut self, pos: Position, text: Text); +} + +impl<T, Text> RecordsMut<Text> for &'_ mut T +where + T: RecordsMut<Text>, +{ + fn set(&mut self, pos: Position, text: Text) { + T::set(self, pos, text) + } +} + +#[cfg(feature = "std")] +impl RecordsMut<String> for VecRecords<CellInfo<String>> { + fn set(&mut self, (row, col): Position, text: String) { + self[row][col] = CellInfo::new(text); + } +} + +#[cfg(feature = "std")] +impl RecordsMut<&str> for VecRecords<CellInfo<String>> { + fn set(&mut self, (row, col): Position, text: &str) { + self[row][col] = CellInfo::new(text.to_string()); + } +} diff --git a/vendor/tabled/src/grid/records/resizable.rs b/vendor/tabled/src/grid/records/resizable.rs new file mode 100644 index 000000000..29ab38038 --- /dev/null +++ b/vendor/tabled/src/grid/records/resizable.rs @@ -0,0 +1,217 @@ +use papergrid::config::Position; + +#[cfg(feature = "std")] +use crate::grid::records::vec_records::VecRecords; + +/// A records representation which can be modified by moving rows/columns around. +pub trait Resizable { + /// Swap cells with one another. + fn swap(&mut self, lhs: Position, rhs: Position); + /// Swap rows with one another. + fn swap_row(&mut self, lhs: usize, rhs: usize); + /// Swap columns with one another. + fn swap_column(&mut self, lhs: usize, rhs: usize); + /// Adds a new row to a data set. + fn push_row(&mut self); + /// Adds a new column to a data set. + fn push_column(&mut self); + /// Removes a row from a data set by index. + fn remove_row(&mut self, row: usize); + /// Removes a column from a data set by index. + fn remove_column(&mut self, column: usize); + /// Inserts a row at index. + fn insert_row(&mut self, row: usize); + /// Inserts column at index. + fn insert_column(&mut self, column: usize); +} + +impl<T> Resizable for &'_ mut T +where + T: Resizable, +{ + fn swap(&mut self, lhs: Position, rhs: Position) { + T::swap(self, lhs, rhs) + } + + fn swap_row(&mut self, lhs: usize, rhs: usize) { + T::swap_row(self, lhs, rhs) + } + + fn swap_column(&mut self, lhs: usize, rhs: usize) { + T::swap_column(self, lhs, rhs) + } + + fn push_row(&mut self) { + T::push_row(self) + } + + fn push_column(&mut self) { + T::push_column(self) + } + + fn remove_row(&mut self, row: usize) { + T::remove_row(self, row) + } + + fn remove_column(&mut self, column: usize) { + T::remove_column(self, column) + } + + fn insert_row(&mut self, row: usize) { + T::insert_row(self, row) + } + + fn insert_column(&mut self, column: usize) { + T::insert_column(self, column) + } +} + +#[cfg(feature = "std")] +impl<T> Resizable for Vec<Vec<T>> +where + T: Default + Clone, +{ + fn swap(&mut self, lhs: Position, rhs: Position) { + if lhs == rhs { + return; + } + + let t = std::mem::take(&mut self[lhs.0][lhs.1]); + let t = std::mem::replace(&mut self[rhs.0][rhs.1], t); + let _ = std::mem::replace(&mut self[lhs.0][lhs.1], t); + } + + fn swap_row(&mut self, lhs: usize, rhs: usize) { + let t = std::mem::take(&mut self[lhs]); + let t = std::mem::replace(&mut self[rhs], t); + let _ = std::mem::replace(&mut self[lhs], t); + } + + fn swap_column(&mut self, lhs: usize, rhs: usize) { + for row in self.iter_mut() { + row.swap(lhs, rhs); + } + } + + fn push_row(&mut self) { + let count_columns = self.get(0).map(|l| l.len()).unwrap_or(0); + self.push(vec![T::default(); count_columns]); + } + + fn push_column(&mut self) { + for row in self.iter_mut() { + row.push(T::default()); + } + } + + fn remove_row(&mut self, row: usize) { + let _ = self.remove(row); + } + + fn remove_column(&mut self, column: usize) { + for row in self.iter_mut() { + let _ = row.remove(column); + } + } + + fn insert_row(&mut self, row: usize) { + let count_columns = self.get(0).map(|l| l.len()).unwrap_or(0); + self.insert(row, vec![T::default(); count_columns]); + } + + fn insert_column(&mut self, column: usize) { + for row in self { + row.insert(column, T::default()); + } + } +} + +#[cfg(feature = "std")] +impl<T> Resizable for VecRecords<T> +where + T: Default + Clone, +{ + fn swap(&mut self, lhs: Position, rhs: Position) { + if lhs == rhs { + return; + } + + let t = std::mem::take(&mut self[lhs.0][lhs.1]); + let t = std::mem::replace(&mut self[rhs.0][rhs.1], t); + let _ = std::mem::replace(&mut self[lhs.0][lhs.1], t); + } + + fn swap_row(&mut self, lhs: usize, rhs: usize) { + let t = std::mem::take(&mut self[lhs]); + let t = std::mem::replace(&mut self[rhs], t); + let _ = std::mem::replace(&mut self[lhs], t); + } + + fn swap_column(&mut self, lhs: usize, rhs: usize) { + for row in self.iter_mut() { + row.swap(lhs, rhs); + } + } + + fn push_row(&mut self) { + let records = std::mem::replace(self, VecRecords::new(vec![])); + let mut data: Vec<Vec<_>> = records.into(); + + let count_columns = data.get(0).map(|l| l.len()).unwrap_or(0); + data.push(vec![T::default(); count_columns]); + + *self = VecRecords::new(data); + } + + fn push_column(&mut self) { + let records = std::mem::replace(self, VecRecords::new(vec![])); + let mut data: Vec<Vec<_>> = records.into(); + + for row in &mut data { + row.push(T::default()); + } + + *self = VecRecords::new(data); + } + + fn remove_row(&mut self, row: usize) { + let records = std::mem::replace(self, VecRecords::new(vec![])); + let mut data: Vec<Vec<_>> = records.into(); + + let _ = data.remove(row); + + *self = VecRecords::new(data); + } + + fn remove_column(&mut self, column: usize) { + let records = std::mem::replace(self, VecRecords::new(vec![])); + let mut data: Vec<Vec<_>> = records.into(); + + for row in &mut data { + let _ = row.remove(column); + } + + *self = VecRecords::new(data); + } + + fn insert_row(&mut self, row: usize) { + let records = std::mem::replace(self, VecRecords::new(vec![])); + let mut data: Vec<Vec<_>> = records.into(); + + let count_columns = data.get(0).map(|l| l.len()).unwrap_or(0); + data.insert(row, vec![T::default(); count_columns]); + + *self = VecRecords::new(data); + } + + fn insert_column(&mut self, column: usize) { + let records = std::mem::replace(self, VecRecords::new(vec![])); + let mut data: Vec<Vec<_>> = records.into(); + + for row in &mut data { + row.insert(column, T::default()); + } + + *self = VecRecords::new(data); + } +} |