summaryrefslogtreecommitdiffstats
path: root/vendor/papergrid/src/config
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/papergrid/src/config')
-rw-r--r--vendor/papergrid/src/config/alignment.rs21
-rw-r--r--vendor/papergrid/src/config/border.rs142
-rw-r--r--vendor/papergrid/src/config/borders.rs152
-rw-r--r--vendor/papergrid/src/config/compact/mod.rs141
-rw-r--r--vendor/papergrid/src/config/entity.rs120
-rw-r--r--vendor/papergrid/src/config/indent.rs31
-rw-r--r--vendor/papergrid/src/config/line.rs42
-rw-r--r--vendor/papergrid/src/config/mod.rs23
-rw-r--r--vendor/papergrid/src/config/position.rs13
-rw-r--r--vendor/papergrid/src/config/sides.rs37
-rw-r--r--vendor/papergrid/src/config/spanned/borders_config.rs486
-rw-r--r--vendor/papergrid/src/config/spanned/entity_map.rs108
-rw-r--r--vendor/papergrid/src/config/spanned/formatting.rs21
-rw-r--r--vendor/papergrid/src/config/spanned/mod.rs887
-rw-r--r--vendor/papergrid/src/config/spanned/offset.rs8
15 files changed, 2232 insertions, 0 deletions
diff --git a/vendor/papergrid/src/config/alignment.rs b/vendor/papergrid/src/config/alignment.rs
new file mode 100644
index 000000000..2b3b5e9db
--- /dev/null
+++ b/vendor/papergrid/src/config/alignment.rs
@@ -0,0 +1,21 @@
+/// [`AlignmentHorizontal`] represents an horizontal alignment of a cell content.
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub enum AlignmentHorizontal {
+ /// Align to the center.
+ Center,
+ /// Align on the left.
+ Left,
+ /// Align on the right.
+ Right,
+}
+
+/// [`AlignmentVertical`] represents an vertical alignment of a cell content.
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub enum AlignmentVertical {
+ /// Align to the center.
+ Center,
+ /// Align to the top.
+ Top,
+ /// Align to the bottom.
+ Bottom,
+}
diff --git a/vendor/papergrid/src/config/border.rs b/vendor/papergrid/src/config/border.rs
new file mode 100644
index 000000000..e803900f7
--- /dev/null
+++ b/vendor/papergrid/src/config/border.rs
@@ -0,0 +1,142 @@
+/// Border is a representation of a cells's borders (left, right, top, bottom, and the corners)
+///
+///
+/// ```text
+/// top border
+/// |
+/// V
+/// corner top left ------> +_______+ <---- corner top left
+/// | |
+/// left border ----------> | cell | <---- right border
+/// | |
+/// corner bottom right --> +_______+ <---- corner bottom right
+/// ^
+/// |
+/// bottom border
+/// ```
+#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord)]
+pub struct Border<T> {
+ /// A character for a top.
+ pub top: Option<T>,
+ /// A character for a bottom.
+ pub bottom: Option<T>,
+ /// A character for a left.
+ pub left: Option<T>,
+ /// A character for a right.
+ pub right: Option<T>,
+ /// A character for a left top corner.
+ pub left_top_corner: Option<T>,
+ /// A character for a left bottom corner.
+ pub left_bottom_corner: Option<T>,
+ /// A character for a right top corner.
+ pub right_top_corner: Option<T>,
+ /// A character for a right bottom corner.
+ pub right_bottom_corner: Option<T>,
+}
+
+impl<T> Border<T> {
+ /// This function constructs a cell borders with all sides set.
+ #[allow(clippy::too_many_arguments)]
+ pub const fn full(
+ top: T,
+ bottom: T,
+ left: T,
+ right: T,
+ top_left: T,
+ top_right: T,
+ bottom_left: T,
+ bottom_right: T,
+ ) -> Self {
+ Self {
+ top: Some(top),
+ bottom: Some(bottom),
+ right: Some(right),
+ right_top_corner: Some(top_right),
+ right_bottom_corner: Some(bottom_right),
+ left: Some(left),
+ left_bottom_corner: Some(bottom_left),
+ left_top_corner: Some(top_left),
+ }
+ }
+
+ /// Checks whether any side is set.
+ pub const fn is_empty(&self) -> bool {
+ self.top.is_none()
+ && self.left_top_corner.is_none()
+ && self.right_top_corner.is_none()
+ && self.bottom.is_none()
+ && self.left_bottom_corner.is_none()
+ && self.left_top_corner.is_none()
+ && self.left.is_none()
+ && self.right.is_none()
+ }
+
+ /// Verifies whether anything is set on the top.
+ pub const fn has_top(&self) -> bool {
+ self.top.is_some() || self.left_top_corner.is_some() || self.right_top_corner.is_some()
+ }
+
+ /// Verifies whether anything is set on the bottom.
+ pub const fn has_bottom(&self) -> bool {
+ self.bottom.is_some()
+ || self.left_bottom_corner.is_some()
+ || self.right_bottom_corner.is_some()
+ }
+
+ /// Verifies whether anything is set on the left.
+ pub const fn has_left(&self) -> bool {
+ self.left.is_some() || self.left_top_corner.is_some() || self.left_bottom_corner.is_some()
+ }
+
+ /// Verifies whether anything is set on the right.
+ pub const fn has_right(&self) -> bool {
+ self.right.is_some()
+ || self.right_top_corner.is_some()
+ || self.right_bottom_corner.is_some()
+ }
+}
+
+impl<T: Copy> Border<T> {
+ /// This function constructs a cell borders with all sides's char set to a given character.
+ ///
+ /// It behaves like [`Border::full`] with the same character set to each side.
+ pub fn filled(c: T) -> Self {
+ Self::full(c, c, c, c, c, c, c, c)
+ }
+}
+
+impl<T: Copy> Border<&T> {
+ /// This function constructs a cell borders with all sides's char set to a given character.
+ ///
+ /// It behaves like [`Border::full`] with the same character set to each side.
+ pub fn copied(&self) -> Border<T> {
+ Border {
+ top: self.top.copied(),
+ bottom: self.bottom.copied(),
+ left: self.left.copied(),
+ right: self.right.copied(),
+ left_bottom_corner: self.left_bottom_corner.copied(),
+ left_top_corner: self.left_top_corner.copied(),
+ right_bottom_corner: self.right_bottom_corner.copied(),
+ right_top_corner: self.right_top_corner.copied(),
+ }
+ }
+}
+
+impl<T: Clone> Border<&T> {
+ /// This function constructs a cell borders with all sides's char set to a given character.
+ ///
+ /// It behaves like [`Border::full`] with the same character set to each side.
+ pub fn cloned(&self) -> Border<T> {
+ Border {
+ top: self.top.cloned(),
+ bottom: self.bottom.cloned(),
+ left: self.left.cloned(),
+ right: self.right.cloned(),
+ left_bottom_corner: self.left_bottom_corner.cloned(),
+ left_top_corner: self.left_top_corner.cloned(),
+ right_bottom_corner: self.right_bottom_corner.cloned(),
+ right_top_corner: self.right_top_corner.cloned(),
+ }
+ }
+}
diff --git a/vendor/papergrid/src/config/borders.rs b/vendor/papergrid/src/config/borders.rs
new file mode 100644
index 000000000..d242e1f63
--- /dev/null
+++ b/vendor/papergrid/src/config/borders.rs
@@ -0,0 +1,152 @@
+/// Borders represents a Table frame with horizontal and vertical split lines.
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Borders<T> {
+ /// A top horizontal on the frame.
+ pub top: Option<T>,
+ /// A top left on the frame.
+ pub top_left: Option<T>,
+ /// A top right on the frame.
+ pub top_right: Option<T>,
+ /// A top horizontal intersection on the frame.
+ pub top_intersection: Option<T>,
+
+ /// A bottom horizontal on the frame.
+ pub bottom: Option<T>,
+ /// A bottom left on the frame.
+ pub bottom_left: Option<T>,
+ /// A bottom right on the frame.
+ pub bottom_right: Option<T>,
+ /// A bottom horizontal intersection on the frame.
+ pub bottom_intersection: Option<T>,
+
+ /// A horizontal split.
+ pub horizontal: Option<T>,
+ /// A vertical split.
+ pub vertical: Option<T>,
+ /// A top left character on the frame.
+ pub intersection: Option<T>,
+
+ /// A vertical split on the left frame line.
+ pub left: Option<T>,
+ /// A horizontal split on the left frame line.
+ pub left_intersection: Option<T>,
+
+ /// A vertical split on the right frame line.
+ pub right: Option<T>,
+ /// A horizontal split on the right frame line.
+ pub right_intersection: Option<T>,
+}
+
+impl<T> Borders<T> {
+ /// Returns empty borders.
+ pub const fn empty() -> Self {
+ Self {
+ top: None,
+ top_left: None,
+ top_right: None,
+ top_intersection: None,
+ bottom: None,
+ bottom_left: None,
+ bottom_right: None,
+ bottom_intersection: None,
+ horizontal: None,
+ left: None,
+ right: None,
+ vertical: None,
+ left_intersection: None,
+ right_intersection: None,
+ intersection: None,
+ }
+ }
+
+ /// Returns Borders filled in with a supplied value.
+ pub const fn filled(val: T) -> Self
+ where
+ T: Copy,
+ {
+ Self {
+ top: Some(val),
+ top_left: Some(val),
+ top_right: Some(val),
+ top_intersection: Some(val),
+ bottom: Some(val),
+ bottom_left: Some(val),
+ bottom_right: Some(val),
+ bottom_intersection: Some(val),
+ horizontal: Some(val),
+ left: Some(val),
+ right: Some(val),
+ vertical: Some(val),
+ left_intersection: Some(val),
+ right_intersection: Some(val),
+ intersection: Some(val),
+ }
+ }
+
+ /// A verification whether any border was set.
+ pub const fn is_empty(&self) -> bool {
+ !(self.top.is_some()
+ || self.top_left.is_some()
+ || self.top_right.is_some()
+ || self.top_intersection.is_some()
+ || self.bottom.is_some()
+ || self.bottom_left.is_some()
+ || self.bottom_right.is_some()
+ || self.bottom_intersection.is_some()
+ || self.horizontal.is_some()
+ || self.left.is_some()
+ || self.right.is_some()
+ || self.vertical.is_some()
+ || self.left_intersection.is_some()
+ || self.right_intersection.is_some()
+ || self.intersection.is_some())
+ }
+
+ /// Verifies if borders has left line set on the frame.
+ pub const fn has_left(&self) -> bool {
+ self.left.is_some()
+ || self.left_intersection.is_some()
+ || self.top_left.is_some()
+ || self.bottom_left.is_some()
+ }
+
+ /// Verifies if borders has right line set on the frame.
+ pub const fn has_right(&self) -> bool {
+ self.right.is_some()
+ || self.right_intersection.is_some()
+ || self.top_right.is_some()
+ || self.bottom_right.is_some()
+ }
+
+ /// Verifies if borders has top line set on the frame.
+ pub const fn has_top(&self) -> bool {
+ self.top.is_some()
+ || self.top_intersection.is_some()
+ || self.top_left.is_some()
+ || self.top_right.is_some()
+ }
+
+ /// Verifies if borders has bottom line set on the frame.
+ pub const fn has_bottom(&self) -> bool {
+ self.bottom.is_some()
+ || self.bottom_intersection.is_some()
+ || self.bottom_left.is_some()
+ || self.bottom_right.is_some()
+ }
+
+ /// Verifies if borders has horizontal lines set.
+ pub const fn has_horizontal(&self) -> bool {
+ self.horizontal.is_some()
+ || self.left_intersection.is_some()
+ || self.right_intersection.is_some()
+ || self.intersection.is_some()
+ }
+
+ /// Verifies if borders has vertical lines set.
+ pub const fn has_vertical(&self) -> bool {
+ self.intersection.is_some()
+ || self.vertical.is_some()
+ || self.top_intersection.is_some()
+ || self.bottom_intersection.is_some()
+ }
+}
diff --git a/vendor/papergrid/src/config/compact/mod.rs b/vendor/papergrid/src/config/compact/mod.rs
new file mode 100644
index 000000000..099fa44d9
--- /dev/null
+++ b/vendor/papergrid/src/config/compact/mod.rs
@@ -0,0 +1,141 @@
+//! A module which contains configuration of a [`CompactGrid`] which is responsible for grid configuration.
+//!
+//! [`CompactGrid`]: crate::grid::compact::CompactGrid
+
+use crate::color::StaticColor;
+
+use crate::config::{AlignmentHorizontal, Borders, Indent, Line, Sides};
+
+/// This structure represents a settings of a grid.
+///
+/// grid: crate::Grid.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CompactConfig {
+ borders: Borders<char>,
+ horizontal_line1: Option<Line<char>>,
+ border_colors: Borders<StaticColor>,
+ margin: Sides<Indent>,
+ margin_color: Sides<StaticColor>,
+ padding: Sides<Indent>,
+ padding_color: Sides<StaticColor>,
+ halignment: AlignmentHorizontal,
+}
+
+impl Default for CompactConfig {
+ fn default() -> Self {
+ Self::empty()
+ }
+}
+
+impl CompactConfig {
+ /// Returns an standard config.
+ pub const fn empty() -> Self {
+ Self {
+ halignment: AlignmentHorizontal::Left,
+ horizontal_line1: None,
+ borders: Borders::empty(),
+ border_colors: Borders::empty(),
+ margin: Sides::filled(Indent::zero()),
+ margin_color: Sides::filled(StaticColor::new("", "")),
+ padding: Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::zero(),
+ Indent::zero(),
+ ),
+ padding_color: Sides::filled(StaticColor::new("", "")),
+ }
+ }
+
+ /// Set grid margin.
+ pub const fn set_margin(mut self, margin: Sides<Indent>) -> Self {
+ self.margin = margin;
+ self
+ }
+
+ /// Returns a grid margin.
+ pub const fn get_margin(&self) -> &Sides<Indent> {
+ &self.margin
+ }
+
+ /// Set the [`Borders`] value as correct one.
+ pub const fn set_borders(mut self, borders: Borders<char>) -> Self {
+ self.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.horizontal_line1 = Some(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.horizontal_line1
+ }
+
+ /// Returns a current [`Borders`] structure.
+ pub const fn get_borders(&self) -> &Borders<char> {
+ &self.borders
+ }
+
+ /// Returns a current [`Borders`] structure.
+ pub const fn get_borders_color(&self) -> &Borders<StaticColor> {
+ &self.border_colors
+ }
+
+ /// Set a padding to a given cells.
+ pub const fn set_padding(mut self, padding: Sides<Indent>) -> Self {
+ self.padding = padding;
+ self
+ }
+
+ /// Get a padding for a given.
+ pub const fn get_padding(&self) -> &Sides<Indent> {
+ &self.padding
+ }
+
+ /// Set a horizontal alignment.
+ pub const fn set_alignment_horizontal(mut self, alignment: AlignmentHorizontal) -> Self {
+ self.halignment = alignment;
+ self
+ }
+
+ /// Get a alignment horizontal.
+ pub const fn get_alignment_horizontal(&self) -> AlignmentHorizontal {
+ self.halignment
+ }
+
+ /// Sets colors of border carcass on the grid.
+ pub const fn set_borders_color(mut self, borders: Borders<StaticColor>) -> Self {
+ self.border_colors = borders;
+ self
+ }
+
+ /// Set colors for a margin.
+ pub const fn set_margin_color(mut self, color: Sides<StaticColor>) -> Self {
+ self.margin_color = color;
+ self
+ }
+
+ /// Returns a margin color.
+ pub const fn get_margin_color(&self) -> Sides<StaticColor> {
+ self.margin_color
+ }
+
+ /// Set a padding to a given cells.
+ pub const fn set_padding_color(mut self, color: Sides<StaticColor>) -> Self {
+ self.padding_color = color;
+ self
+ }
+
+ /// Set a padding to a given cells.
+ pub const fn get_padding_color(&self) -> Sides<StaticColor> {
+ self.padding_color
+ }
+}
diff --git a/vendor/papergrid/src/config/entity.rs b/vendor/papergrid/src/config/entity.rs
new file mode 100644
index 000000000..0a2d3ba9b
--- /dev/null
+++ b/vendor/papergrid/src/config/entity.rs
@@ -0,0 +1,120 @@
+use super::Position;
+
+/// Entity a structure which represent a set of cells.
+///
+/// For example such table:
+///
+/// ```text
+/// ┌───┬───┐
+/// │ 0 │ 1 │
+/// ├───┼───┤
+/// │ 1 │ 2 │
+/// └───┴───┘
+/// ```
+///
+/// - has 4 cells.
+/// Which indexes are (0, 0), (0, 1), (1, 0), (1, 1).
+///
+/// - has 2 rows.
+/// Which indexes are 0, 1.
+///
+/// - has 2 column.
+/// Which indexes are 0, 1.
+///
+/// In [`Entity`] terms, all cells on the grid we call `Global`.
+#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
+pub enum Entity {
+ /// All cells on the grid.
+ Global,
+ /// All cells in a column on the grid.
+ Column(usize),
+ /// All cells in a row on the grid.
+ Row(usize),
+ /// A particular cell (row, column) on the grid.
+ Cell(usize, usize),
+}
+
+impl Entity {
+ /// Iterate over cells which are covered via the [`Entity`].
+ pub fn iter(&self, count_rows: usize, count_cols: usize) -> EntityIterator {
+ EntityIterator {
+ entity: *self,
+ count_rows,
+ count_cols,
+ i: 0,
+ j: 0,
+ }
+ }
+}
+
+impl From<Position> for Entity {
+ fn from((row, col): Position) -> Self {
+ Self::Cell(row, col)
+ }
+}
+
+/// An iterator over cells.
+///
+/// Produced from [`Entity::iter`].
+#[derive(Debug, Clone)]
+pub struct EntityIterator {
+ entity: Entity,
+ count_rows: usize,
+ count_cols: usize,
+ i: usize,
+ j: usize,
+}
+
+impl Iterator for EntityIterator {
+ type Item = Position;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.count_rows == 0 || self.count_cols == 0 {
+ return None;
+ }
+
+ match self.entity {
+ Entity::Cell(row, col) => {
+ self.count_cols = 0;
+ self.count_rows = 0;
+
+ Some((row, col))
+ }
+ Entity::Column(col) => {
+ if self.i >= self.count_rows {
+ return None;
+ }
+
+ let i = self.i;
+ self.i += 1;
+
+ Some((i, col))
+ }
+ Entity::Row(row) => {
+ if self.j >= self.count_cols {
+ return None;
+ }
+
+ let j = self.j;
+ self.j += 1;
+
+ Some((row, j))
+ }
+ Entity::Global => {
+ if self.j >= self.count_cols {
+ self.j = 0;
+ self.i += 1;
+
+ if self.i >= self.count_rows {
+ return None;
+ }
+ }
+
+ let j = self.j;
+ self.j += 1;
+
+ Some((self.i, j))
+ }
+ }
+ }
+}
diff --git a/vendor/papergrid/src/config/indent.rs b/vendor/papergrid/src/config/indent.rs
new file mode 100644
index 000000000..4d987724c
--- /dev/null
+++ b/vendor/papergrid/src/config/indent.rs
@@ -0,0 +1,31 @@
+/// Indent represent a filled space.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Indent {
+ /// A fill character.
+ pub fill: char,
+ /// A number of repeats of a fill character.
+ pub size: usize,
+}
+
+impl Indent {
+ /// Creates a new Indent structure.
+ pub const fn new(size: usize, fill: char) -> Self {
+ Self { fill, size }
+ }
+
+ /// Creates a new Indent structure with space (`' '`) as a fill character.
+ pub const fn spaced(size: usize) -> Self {
+ Self { size, fill: ' ' }
+ }
+
+ /// Creates a new Indent structure with space (`' '`) as a fill character.
+ pub const fn zero() -> Self {
+ Self::new(0, ' ')
+ }
+}
+
+impl Default for Indent {
+ fn default() -> Self {
+ Self { size: 0, fill: ' ' }
+ }
+}
diff --git a/vendor/papergrid/src/config/line.rs b/vendor/papergrid/src/config/line.rs
new file mode 100644
index 000000000..c3089e1c4
--- /dev/null
+++ b/vendor/papergrid/src/config/line.rs
@@ -0,0 +1,42 @@
+/// A line data structure.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
+pub struct Line<T> {
+ /// A horizontal/vertical character.
+ pub main: T,
+ /// A horizontal/vertical intersection.
+ pub intersection: Option<T>,
+ /// A horizontal left / vertical top intersection.
+ pub connect1: Option<T>,
+ /// A horizontal right / vertical bottom intersection.
+ pub connect2: Option<T>,
+}
+
+impl<T> Line<T> {
+ /// Creates a new line.
+ pub const fn new(
+ main: T,
+ intersection: Option<T>,
+ connect1: Option<T>,
+ connect2: Option<T>,
+ ) -> Self {
+ Self {
+ main,
+ intersection,
+ connect1,
+ connect2,
+ }
+ }
+
+ /// Creates a new line.
+ pub const fn filled(val: T) -> Self
+ where
+ T: Copy,
+ {
+ Self {
+ main: val,
+ intersection: Some(val),
+ connect1: Some(val),
+ connect2: Some(val),
+ }
+ }
+}
diff --git a/vendor/papergrid/src/config/mod.rs b/vendor/papergrid/src/config/mod.rs
new file mode 100644
index 000000000..bba0b458e
--- /dev/null
+++ b/vendor/papergrid/src/config/mod.rs
@@ -0,0 +1,23 @@
+//! A module which contains a general settings which might be used in other grid implementations.
+
+mod alignment;
+mod border;
+mod borders;
+mod entity;
+mod indent;
+mod line;
+mod position;
+mod sides;
+
+pub mod compact;
+#[cfg(feature = "std")]
+pub mod spanned;
+
+pub use alignment::{AlignmentHorizontal, AlignmentVertical};
+pub use border::Border;
+pub use borders::Borders;
+pub use entity::{Entity, EntityIterator};
+pub use indent::Indent;
+pub use line::Line;
+pub use position::Position;
+pub use sides::Sides;
diff --git a/vendor/papergrid/src/config/position.rs b/vendor/papergrid/src/config/position.rs
new file mode 100644
index 000000000..3996453cc
--- /dev/null
+++ b/vendor/papergrid/src/config/position.rs
@@ -0,0 +1,13 @@
+/// Position is a (row, col) position on a Grid.
+///
+/// For example such table has 4 cells.
+/// Which indexes are (0, 0), (0, 1), (1, 0), (1, 1).
+///
+/// ```text
+/// ┌───┬───┐
+/// │ 0 │ 1 │
+/// ├───┼───┤
+/// │ 1 │ 2 │
+/// └───┴───┘
+/// ```
+pub type Position = (usize, usize);
diff --git a/vendor/papergrid/src/config/sides.rs b/vendor/papergrid/src/config/sides.rs
new file mode 100644
index 000000000..7aa0fb498
--- /dev/null
+++ b/vendor/papergrid/src/config/sides.rs
@@ -0,0 +1,37 @@
+/// A structure which represents 4 box sides.
+#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Sides<T> {
+ /// Top side.
+ pub top: T,
+ /// Bottom side.
+ pub bottom: T,
+ /// Left side.
+ pub left: T,
+ /// Right side.
+ pub right: T,
+}
+
+impl<T> Sides<T> {
+ /// Creates a new object.
+ pub const fn new(left: T, right: T, top: T, bottom: T) -> Self {
+ Self {
+ top,
+ bottom,
+ left,
+ right,
+ }
+ }
+
+ /// Creates a new object.
+ pub const fn filled(value: T) -> Self
+ where
+ T: Copy,
+ {
+ Self {
+ top: value,
+ bottom: value,
+ left: value,
+ right: value,
+ }
+ }
+}
diff --git a/vendor/papergrid/src/config/spanned/borders_config.rs b/vendor/papergrid/src/config/spanned/borders_config.rs
new file mode 100644
index 000000000..fe7729806
--- /dev/null
+++ b/vendor/papergrid/src/config/spanned/borders_config.rs
@@ -0,0 +1,486 @@
+use std::collections::{HashMap, HashSet};
+
+use crate::config::{Border, Borders, Position};
+
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub(crate) struct BordersConfig<T> {
+ global: Option<T>,
+ borders: Borders<T>,
+ cells: BordersMap<T>,
+ horizontals: HashMap<usize, HorizontalLine<T>>,
+ verticals: HashMap<usize, VerticalLine<T>>,
+ layout: BordersLayout,
+}
+
+impl<T: std::fmt::Debug> BordersConfig<T> {
+ pub(crate) fn insert_border(&mut self, pos: Position, border: Border<T>) {
+ if let Some(c) = border.top {
+ self.cells.horizontal.insert(pos, c);
+ self.layout.horizontals.insert(pos.0);
+ }
+
+ if let Some(c) = border.bottom {
+ self.cells.horizontal.insert((pos.0 + 1, pos.1), c);
+ self.layout.horizontals.insert(pos.0 + 1);
+ }
+
+ if let Some(c) = border.left {
+ self.cells.vertical.insert(pos, c);
+ self.layout.verticals.insert(pos.1);
+ }
+
+ if let Some(c) = border.right {
+ self.cells.vertical.insert((pos.0, pos.1 + 1), c);
+ self.layout.verticals.insert(pos.1 + 1);
+ }
+
+ if let Some(c) = border.left_top_corner {
+ self.cells.intersection.insert((pos.0, pos.1), c);
+ self.layout.horizontals.insert(pos.0);
+ self.layout.verticals.insert(pos.1);
+ }
+
+ if let Some(c) = border.right_top_corner {
+ self.cells.intersection.insert((pos.0, pos.1 + 1), c);
+ self.layout.horizontals.insert(pos.0);
+ self.layout.verticals.insert(pos.1 + 1);
+ }
+
+ if let Some(c) = border.left_bottom_corner {
+ self.cells.intersection.insert((pos.0 + 1, pos.1), c);
+ self.layout.horizontals.insert(pos.0 + 1);
+ self.layout.verticals.insert(pos.1);
+ }
+
+ if let Some(c) = border.right_bottom_corner {
+ self.cells.intersection.insert((pos.0 + 1, pos.1 + 1), c);
+ self.layout.horizontals.insert(pos.0 + 1);
+ self.layout.verticals.insert(pos.1 + 1);
+ }
+ }
+
+ pub(crate) fn remove_border(&mut self, pos: Position, shape: (usize, usize)) {
+ let (count_rows, count_cols) = shape;
+
+ self.cells.horizontal.remove(&pos);
+ self.cells.horizontal.remove(&(pos.0 + 1, pos.1));
+ self.cells.vertical.remove(&pos);
+ self.cells.vertical.remove(&(pos.0, pos.1 + 1));
+ self.cells.intersection.remove(&pos);
+ self.cells.intersection.remove(&(pos.0 + 1, pos.1));
+ self.cells.intersection.remove(&(pos.0, pos.1 + 1));
+ self.cells.intersection.remove(&(pos.0 + 1, pos.1 + 1));
+
+ // clean up the layout.
+
+ if !self.check_is_horizontal_set(pos.0, count_rows) {
+ self.layout.horizontals.remove(&pos.0);
+ }
+
+ if !self.check_is_horizontal_set(pos.0 + 1, count_rows) {
+ self.layout.horizontals.remove(&(pos.0 + 1));
+ }
+
+ if !self.check_is_vertical_set(pos.1, count_cols) {
+ self.layout.verticals.remove(&pos.1);
+ }
+
+ if !self.check_is_vertical_set(pos.1 + 1, count_cols) {
+ self.layout.verticals.remove(&(pos.1 + 1));
+ }
+ }
+
+ pub(crate) fn get_border(&self, pos: Position, shape: (usize, usize)) -> Border<&T> {
+ Border {
+ top: self.get_horizontal(pos, shape.0),
+ bottom: self.get_horizontal((pos.0 + 1, pos.1), shape.0),
+ left: self.get_vertical(pos, shape.1),
+ left_top_corner: self.get_intersection(pos, shape),
+ left_bottom_corner: self.get_intersection((pos.0 + 1, pos.1), shape),
+ right: self.get_vertical((pos.0, pos.1 + 1), shape.1),
+ right_top_corner: self.get_intersection((pos.0, pos.1 + 1), shape),
+ right_bottom_corner: self.get_intersection((pos.0 + 1, pos.1 + 1), shape),
+ }
+ }
+
+ pub(crate) fn insert_horizontal_line(&mut self, row: usize, line: HorizontalLine<T>) {
+ if line.left.is_some() {
+ self.layout.left = true;
+ }
+
+ // todo: when we delete lines these are still left set; so has_horizontal/vertical return true in some cases;
+ // it shall be fixed, but maybe we can improve the logic as it got a bit complicated.
+ if line.right.is_some() {
+ self.layout.right = true;
+ }
+
+ if line.intersection.is_some() {
+ self.layout.inner_verticals = true;
+ }
+
+ self.horizontals.insert(row, line);
+ self.layout.horizontals.insert(row);
+ }
+
+ pub(crate) fn get_horizontal_line(&self, row: usize) -> Option<&HorizontalLine<T>> {
+ self.horizontals.get(&row)
+ }
+
+ pub(crate) fn remove_horizontal_line(&mut self, row: usize, count_rows: usize) {
+ self.horizontals.remove(&row);
+ self.layout.horizontals.remove(&row);
+
+ if self.has_horizontal(row, count_rows) {
+ self.layout.horizontals.insert(row);
+ }
+ }
+
+ pub(crate) fn insert_vertical_line(&mut self, row: usize, line: VerticalLine<T>) {
+ if line.top.is_some() {
+ self.layout.top = true;
+ }
+
+ if line.bottom.is_some() {
+ self.layout.bottom = true;
+ }
+
+ self.verticals.insert(row, line);
+ self.layout.verticals.insert(row);
+ }
+
+ pub(crate) fn get_vertical_line(&self, row: usize) -> Option<&VerticalLine<T>> {
+ self.verticals.get(&row)
+ }
+
+ pub(crate) fn remove_vertical_line(&mut self, col: usize, count_columns: usize) {
+ self.verticals.remove(&col);
+ self.layout.verticals.remove(&col);
+
+ if self.has_vertical(col, count_columns) {
+ self.layout.verticals.insert(col);
+ }
+ }
+
+ pub(crate) fn set_borders(&mut self, borders: Borders<T>) {
+ self.borders = borders;
+ }
+
+ pub(crate) fn get_borders(&self) -> &Borders<T> {
+ &self.borders
+ }
+
+ pub(crate) fn get_global(&self) -> Option<&T> {
+ self.global.as_ref()
+ }
+
+ pub(crate) fn set_global(&mut self, value: T) {
+ self.global = Some(value);
+ }
+
+ pub(crate) fn get_vertical(&self, pos: Position, count_cols: usize) -> Option<&T> {
+ self.cells
+ .vertical
+ .get(&pos)
+ .or_else(|| self.verticals.get(&pos.1).and_then(|l| l.main.as_ref()))
+ .or({
+ if pos.1 == count_cols {
+ self.borders.right.as_ref()
+ } else if pos.1 == 0 {
+ self.borders.left.as_ref()
+ } else {
+ self.borders.vertical.as_ref()
+ }
+ })
+ .or(self.global.as_ref())
+ }
+
+ pub(crate) fn get_horizontal(&self, pos: Position, count_rows: usize) -> Option<&T> {
+ self.cells
+ .horizontal
+ .get(&pos)
+ .or_else(|| self.horizontals.get(&pos.0).and_then(|l| l.main.as_ref()))
+ .or({
+ if pos.0 == 0 {
+ self.borders.top.as_ref()
+ } else if pos.0 == count_rows {
+ self.borders.bottom.as_ref()
+ } else {
+ self.borders.horizontal.as_ref()
+ }
+ })
+ .or(self.global.as_ref())
+ }
+
+ pub(crate) fn get_intersection(
+ &self,
+ pos: Position,
+ (count_rows, count_cols): (usize, usize),
+ ) -> Option<&T> {
+ let use_top = pos.0 == 0;
+ let use_bottom = pos.0 == count_rows;
+ let use_left = pos.1 == 0;
+ let use_right = pos.1 == count_cols;
+
+ if let Some(c) = self.cells.intersection.get(&pos) {
+ return Some(c);
+ }
+
+ let hl_c = self.horizontals.get(&pos.0).and_then(|l| {
+ if use_left && l.left.is_some() {
+ l.left.as_ref()
+ } else if use_right && l.right.is_some() {
+ l.right.as_ref()
+ } else if !use_right && !use_left && l.intersection.is_some() {
+ l.intersection.as_ref()
+ } else {
+ None
+ }
+ });
+
+ if let Some(c) = hl_c {
+ return Some(c);
+ }
+
+ let vl_c = self.verticals.get(&pos.1).and_then(|l| {
+ if use_top && l.top.is_some() {
+ l.top.as_ref()
+ } else if use_bottom && l.bottom.is_some() {
+ l.bottom.as_ref()
+ } else if !use_top && !use_bottom && l.intersection.is_some() {
+ l.intersection.as_ref()
+ } else {
+ None
+ }
+ });
+
+ if let Some(c) = vl_c {
+ return Some(c);
+ }
+
+ let borders_c = {
+ if use_top && use_left {
+ self.borders.top_left.as_ref()
+ } else if use_top && use_right {
+ self.borders.top_right.as_ref()
+ } else if use_bottom && use_left {
+ self.borders.bottom_left.as_ref()
+ } else if use_bottom && use_right {
+ self.borders.bottom_right.as_ref()
+ } else if use_top {
+ self.borders.top_intersection.as_ref()
+ } else if use_bottom {
+ self.borders.bottom_intersection.as_ref()
+ } else if use_left {
+ self.borders.left_intersection.as_ref()
+ } else if use_right {
+ self.borders.right_intersection.as_ref()
+ } else {
+ self.borders.intersection.as_ref()
+ }
+ };
+
+ if let Some(c) = borders_c {
+ return Some(c);
+ }
+
+ self.global.as_ref()
+ }
+
+ pub(crate) fn has_horizontal(&self, row: usize, count_rows: usize) -> bool {
+ self.global.is_some()
+ || (row == 0 && self.borders.has_top())
+ || (row == count_rows && self.borders.has_bottom())
+ || (row > 0 && row < count_rows && self.borders.has_horizontal())
+ || self.is_horizontal_set(row, count_rows)
+ }
+
+ pub(crate) fn has_vertical(&self, col: usize, count_cols: usize) -> bool {
+ self.global.is_some()
+ || (col == 0 && self.borders.has_left())
+ || (col == count_cols && self.borders.has_right())
+ || (col > 0 && col < count_cols && self.borders.has_vertical())
+ || self.is_vertical_set(col, count_cols)
+ }
+
+ fn is_horizontal_set(&self, row: usize, count_rows: usize) -> bool {
+ (row == 0 && self.layout.top)
+ || (row == count_rows && self.layout.bottom)
+ || (row > 0 && row < count_rows && self.layout.inner_horizontals)
+ || self.layout.horizontals.contains(&row)
+ }
+
+ fn is_vertical_set(&self, col: usize, count_cols: usize) -> bool {
+ (col == 0 && self.layout.left)
+ || (col == count_cols && self.layout.right)
+ || (col > 0 && col < count_cols && self.layout.inner_verticals)
+ || self.layout.verticals.contains(&col)
+ }
+
+ fn check_is_horizontal_set(&self, row: usize, count_rows: usize) -> bool {
+ (row == 0 && self.layout.top)
+ || (row == count_rows && self.layout.bottom)
+ || (row > 0 && row < count_rows && self.layout.inner_horizontals)
+ || self.cells.horizontal.keys().any(|&p| p.0 == row)
+ || self.cells.intersection.keys().any(|&p| p.0 == row)
+ }
+
+ fn check_is_vertical_set(&self, col: usize, count_cols: usize) -> bool {
+ (col == 0 && self.layout.left)
+ || (col == count_cols && self.layout.right)
+ || (col > 0 && col < count_cols && self.layout.inner_verticals)
+ || self.cells.vertical.keys().any(|&p| p.1 == col)
+ || self.cells.intersection.keys().any(|&p| p.1 == col)
+ }
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq)]
+pub(crate) struct BordersMap<T> {
+ vertical: HashMap<Position, T>,
+ horizontal: HashMap<Position, T>,
+ intersection: HashMap<Position, T>,
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq)]
+pub(crate) struct BordersLayout {
+ left: bool,
+ right: bool,
+ top: bool,
+ bottom: bool,
+ inner_verticals: bool,
+ inner_horizontals: bool,
+ horizontals: HashSet<usize>,
+ verticals: HashSet<usize>,
+}
+
+/// A structure for a custom horizontal line.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
+pub struct HorizontalLine<T> {
+ /// Line character.
+ pub main: Option<T>,
+ /// Line intersection character.
+ pub intersection: Option<T>,
+ /// Left intersection character.
+ pub left: Option<T>,
+ /// Right intersection character.
+ pub right: Option<T>,
+}
+
+impl<T> HorizontalLine<T> {
+ /// Verifies if the line has any setting set.
+ pub const fn is_empty(&self) -> bool {
+ self.main.is_none()
+ && self.intersection.is_none()
+ && self.left.is_none()
+ && self.right.is_none()
+ }
+}
+
+/// A structure for a vertical line.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
+pub struct VerticalLine<T> {
+ /// Line character.
+ pub main: Option<T>,
+ /// Line intersection character.
+ pub intersection: Option<T>,
+ /// Left intersection character.
+ pub top: Option<T>,
+ /// Right intersection character.
+ pub bottom: Option<T>,
+}
+
+impl<T> VerticalLine<T> {
+ /// Verifies if the line has any setting set.
+ pub const fn is_empty(&self) -> bool {
+ self.main.is_none()
+ && self.intersection.is_none()
+ && self.top.is_none()
+ && self.bottom.is_none()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_insert_border() {
+ let mut borders = BordersConfig::<char>::default();
+ borders.insert_border((0, 0), Border::filled('x'));
+
+ assert_eq!(borders.get_border((0, 0), (10, 10)), Border::filled(&'x'));
+ assert_eq!(borders.get_border((0, 0), (0, 0)), Border::filled(&'x'));
+
+ assert!(borders.is_horizontal_set(0, 10));
+ assert!(borders.is_horizontal_set(1, 10));
+ assert!(!borders.is_horizontal_set(2, 10));
+ assert!(borders.is_vertical_set(0, 10));
+ assert!(borders.is_vertical_set(1, 10));
+ assert!(!borders.is_vertical_set(2, 10));
+
+ assert!(borders.is_horizontal_set(0, 0));
+ assert!(borders.is_horizontal_set(1, 0));
+ assert!(!borders.is_horizontal_set(2, 0));
+ assert!(borders.is_vertical_set(0, 0));
+ assert!(borders.is_vertical_set(1, 0));
+ assert!(!borders.is_vertical_set(2, 0));
+ }
+
+ #[test]
+ fn test_insert_border_override() {
+ let mut borders = BordersConfig::<char>::default();
+ borders.insert_border((0, 0), Border::filled('x'));
+ borders.insert_border((1, 0), Border::filled('y'));
+ borders.insert_border((0, 1), Border::filled('w'));
+ borders.insert_border((1, 1), Border::filled('q'));
+
+ assert_eq!(
+ borders.get_border((0, 0), (10, 10)).copied(),
+ Border::full('x', 'y', 'x', 'w', 'x', 'w', 'y', 'q')
+ );
+ assert_eq!(
+ borders.get_border((0, 1), (10, 10)).copied(),
+ Border::full('w', 'q', 'w', 'w', 'w', 'w', 'q', 'q')
+ );
+ assert_eq!(
+ borders.get_border((1, 0), (10, 10)).copied(),
+ Border::full('y', 'y', 'y', 'q', 'y', 'q', 'y', 'q')
+ );
+ assert_eq!(
+ borders.get_border((1, 1), (10, 10)).copied(),
+ Border::filled('q')
+ );
+
+ assert!(borders.is_horizontal_set(0, 10));
+ assert!(borders.is_horizontal_set(1, 10));
+ assert!(borders.is_horizontal_set(2, 10));
+ assert!(!borders.is_horizontal_set(3, 10));
+ assert!(borders.is_vertical_set(0, 10));
+ assert!(borders.is_vertical_set(1, 10));
+ assert!(borders.is_vertical_set(2, 10));
+ assert!(!borders.is_vertical_set(3, 10));
+ }
+
+ #[test]
+ fn test_set_global() {
+ let mut borders = BordersConfig::<char>::default();
+ borders.insert_border((0, 0), Border::filled('x'));
+ borders.set_global('l');
+
+ assert_eq!(borders.get_border((0, 0), (10, 10)), Border::filled(&'x'));
+ assert_eq!(borders.get_border((2, 0), (10, 10)), Border::filled(&'l'));
+
+ assert!(borders.is_horizontal_set(0, 10));
+ assert!(borders.is_horizontal_set(1, 10));
+ assert!(!borders.is_horizontal_set(2, 10));
+ assert!(borders.is_vertical_set(0, 10));
+ assert!(borders.is_vertical_set(1, 10));
+ assert!(!borders.is_vertical_set(2, 10));
+
+ assert!(borders.is_horizontal_set(0, 0));
+ assert!(borders.is_horizontal_set(1, 0));
+ assert!(!borders.is_horizontal_set(2, 0));
+ assert!(borders.is_vertical_set(0, 0));
+ assert!(borders.is_vertical_set(1, 0));
+ assert!(!borders.is_vertical_set(2, 0));
+ }
+}
diff --git a/vendor/papergrid/src/config/spanned/entity_map.rs b/vendor/papergrid/src/config/spanned/entity_map.rs
new file mode 100644
index 000000000..472ea5fb2
--- /dev/null
+++ b/vendor/papergrid/src/config/spanned/entity_map.rs
@@ -0,0 +1,108 @@
+use fnv::FnvHashMap;
+
+use crate::config::{Entity, Position};
+
+/// A structure to keep information for [`Entity`] as a key.
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct EntityMap<T> {
+ // we have a global type to allocate in on stack.
+ // because most of the time no changes are made to the [`EntityMap`].
+ global: T,
+ columns: FnvHashMap<usize, T>,
+ rows: FnvHashMap<usize, T>,
+ cells: FnvHashMap<Position, T>,
+}
+
+impl<T> EntityMap<T> {
+ /// Creates an empty [`EntityMap`].
+ pub fn new(global: T) -> Self {
+ Self {
+ global,
+ rows: FnvHashMap::default(),
+ columns: FnvHashMap::default(),
+ cells: FnvHashMap::default(),
+ }
+ }
+
+ /// Get a value for an [`Entity`].
+ pub fn get(&self, entity: Entity) -> &T {
+ if self.rows.is_empty() && self.columns.is_empty() && self.cells.is_empty() {
+ return &self.global;
+ }
+
+ match entity {
+ Entity::Column(col) => self.columns.get(&col).unwrap_or(&self.global),
+ Entity::Row(row) => self.rows.get(&row).unwrap_or(&self.global),
+ Entity::Cell(row, col) => {
+ // todo: optimize;
+ //
+ // Cause we can change rows/columns/cells separately we need to check them separately.
+ // But we often doing this checks in `Grid::fmt` and I believe if we could optimize it it could be beneficial.
+ //
+ // Haven't found a solution for that yet.
+ //
+ // I was wondering if there is a hash function like.
+ // Apparently it doesn't make sense cause we will reset columns/rows on cell insert which is not what we want.
+ //
+ // ```
+ // hash(column, row) == hash(column) == hash(row)
+ // ```
+ //
+ // ref: https://opendsa-server.cs.vt.edu/ODSA/Books/Everything/html/Sparse.html
+ // ref: https://users.rust-lang.org/t/make-hash-return-same-value-whather-the-order-of-element-of-a-tuple/69932/13
+
+ self.cells
+ .get(&(row, col))
+ .or_else(|| self.columns.get(&col))
+ .or_else(|| self.rows.get(&row))
+ .unwrap_or(&self.global)
+ }
+ Entity::Global => &self.global,
+ }
+ }
+
+ /// Removes a value for an [`Entity`].
+ pub fn remove(&mut self, entity: Entity) {
+ match entity {
+ Entity::Global => {
+ self.cells.clear();
+ self.rows.clear();
+ self.columns.clear();
+ }
+ Entity::Column(col) => self.cells.retain(|&(_, c), _| c != col),
+ Entity::Row(row) => self.cells.retain(|&(r, _), _| r != row),
+ Entity::Cell(row, col) => {
+ self.cells.remove(&(row, col));
+ }
+ }
+ }
+}
+
+impl<T: Clone> EntityMap<T> {
+ /// Set a value for an [`Entity`].
+ pub fn insert(&mut self, entity: Entity, value: T) {
+ match entity {
+ Entity::Column(col) => {
+ for &row in self.rows.keys() {
+ self.cells.insert((row, col), value.clone());
+ }
+
+ self.columns.insert(col, value);
+ }
+ Entity::Row(row) => {
+ for &col in self.columns.keys() {
+ self.cells.insert((row, col), value.clone());
+ }
+
+ self.rows.insert(row, value);
+ }
+ Entity::Cell(row, col) => {
+ self.cells.insert((row, col), value);
+ }
+ Entity::Global => {
+ self.remove(Entity::Global);
+ self.global = value
+ }
+ }
+ }
+}
diff --git a/vendor/papergrid/src/config/spanned/formatting.rs b/vendor/papergrid/src/config/spanned/formatting.rs
new file mode 100644
index 000000000..0865f6c5b
--- /dev/null
+++ b/vendor/papergrid/src/config/spanned/formatting.rs
@@ -0,0 +1,21 @@
+/// 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,
+ }
+ }
+}
diff --git a/vendor/papergrid/src/config/spanned/mod.rs b/vendor/papergrid/src/config/spanned/mod.rs
new file mode 100644
index 000000000..a7f71e11c
--- /dev/null
+++ b/vendor/papergrid/src/config/spanned/mod.rs
@@ -0,0 +1,887 @@
+//! A module which contains configuration options for a [`Grid`].
+//!
+//! [`Grid`]: crate::grid::iterable::Grid
+
+mod borders_config;
+mod entity_map;
+mod formatting;
+mod offset;
+
+use std::collections::HashMap;
+
+use crate::color::{AnsiColor, StaticColor};
+use crate::config::compact::CompactConfig;
+use crate::config::{
+ AlignmentHorizontal, AlignmentVertical, Border, Borders, Entity, Indent, Position, Sides,
+};
+use borders_config::BordersConfig;
+
+pub use self::{entity_map::EntityMap, formatting::Formatting, offset::Offset};
+
+/// This structure represents a settings of a grid.
+///
+/// grid: crate::Grid.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct SpannedConfig {
+ margin: Sides<ColoredMarginIndent>,
+ padding: EntityMap<Sides<ColoredIndent>>,
+ alignment_h: EntityMap<AlignmentHorizontal>,
+ alignment_v: EntityMap<AlignmentVertical>,
+ formatting: EntityMap<Formatting>,
+ span_columns: HashMap<Position, usize>,
+ span_rows: HashMap<Position, usize>,
+ borders: BordersConfig<char>,
+ borders_colors: BordersConfig<AnsiColor<'static>>,
+ borders_missing_char: char,
+ horizontal_chars: HashMap<Position, HashMap<Offset, char>>,
+ horizontal_colors: HashMap<Position, HashMap<Offset, AnsiColor<'static>>>,
+ vertical_chars: HashMap<Position, HashMap<Offset, char>>,
+ vertical_colors: HashMap<Position, HashMap<Offset, AnsiColor<'static>>>,
+ justification: EntityMap<char>,
+ justification_color: EntityMap<Option<AnsiColor<'static>>>,
+}
+
+impl Default for SpannedConfig {
+ fn default() -> Self {
+ Self {
+ margin: Sides::default(),
+ padding: EntityMap::default(),
+ formatting: EntityMap::default(),
+ alignment_h: EntityMap::new(AlignmentHorizontal::Left),
+ alignment_v: EntityMap::new(AlignmentVertical::Top),
+ span_columns: HashMap::default(),
+ span_rows: HashMap::default(),
+ borders: BordersConfig::default(),
+ borders_colors: BordersConfig::default(),
+ borders_missing_char: ' ',
+ horizontal_chars: HashMap::default(),
+ horizontal_colors: HashMap::default(),
+ vertical_chars: HashMap::default(),
+ vertical_colors: HashMap::default(),
+ justification: EntityMap::new(' '),
+ justification_color: EntityMap::default(),
+ }
+ }
+}
+
+impl SpannedConfig {
+ /// Set a margin of a grid.
+ pub fn set_margin(&mut self, margin: Sides<Indent>) {
+ self.margin.left.indent = margin.left;
+ self.margin.right.indent = margin.right;
+ self.margin.top.indent = margin.top;
+ self.margin.bottom.indent = margin.bottom;
+ }
+
+ /// Set a color of margin of a grid.
+ pub fn set_margin_color(&mut self, margin: Sides<Option<AnsiColor<'static>>>) {
+ self.margin.left.color = margin.left;
+ self.margin.right.color = margin.right;
+ self.margin.top.color = margin.top;
+ self.margin.bottom.color = margin.bottom;
+ }
+
+ /// Set an offset of margin of a grid.
+ pub fn set_margin_offset(&mut self, margin: Sides<Offset>) {
+ self.margin.left.offset = margin.left;
+ self.margin.right.offset = margin.right;
+ self.margin.top.offset = margin.top;
+ self.margin.bottom.offset = margin.bottom;
+ }
+
+ /// Returns a margin value currently set.
+ pub fn get_margin(&self) -> Sides<Indent> {
+ Sides::new(
+ self.margin.left.indent,
+ self.margin.right.indent,
+ self.margin.top.indent,
+ self.margin.bottom.indent,
+ )
+ }
+
+ /// Returns a margin color value currently set.
+ pub fn get_margin_color(&self) -> Sides<Option<AnsiColor<'static>>> {
+ Sides::new(
+ self.margin.left.color.clone(),
+ self.margin.right.color.clone(),
+ self.margin.top.color.clone(),
+ self.margin.bottom.color.clone(),
+ )
+ }
+
+ /// Returns a margin offset value currently set.
+ pub fn get_margin_offset(&self) -> Sides<Offset> {
+ Sides::new(
+ self.margin.left.offset,
+ self.margin.right.offset,
+ self.margin.top.offset,
+ self.margin.bottom.offset,
+ )
+ }
+
+ /// Clears all theme changes.
+ /// And sets it to default.
+ pub fn clear_theme(&mut self) {
+ self.borders = BordersConfig::default();
+ self.horizontal_chars.clear();
+ self.vertical_chars.clear();
+ self.horizontal_colors.clear();
+ self.vertical_colors.clear();
+ }
+
+ /// Set the [`Borders`] value as correct one.
+ pub fn set_borders(&mut self, borders: Borders<char>) {
+ self.borders.set_borders(borders);
+ }
+
+ /// Gets a global border value if set.
+ pub fn get_global_border(&self) -> Option<&char> {
+ self.borders.get_global()
+ }
+
+ /// Set the all [`Borders`] values to a char.
+ pub fn set_global_border(&mut self, c: char) {
+ self.borders.set_global(c);
+ }
+
+ /// Returns a current [`Borders`] structure.
+ pub fn get_borders(&self) -> &Borders<char> {
+ self.borders.get_borders()
+ }
+
+ /// Set the border line by row index.
+ ///
+ /// Row `0` means the top row.
+ /// Row `grid.count_rows()` means the bottom row.
+ pub fn insert_horizontal_line(&mut self, line: usize, val: HorizontalLine) {
+ self.borders.insert_horizontal_line(line, val);
+ }
+
+ /// Sets off the border line by row index if any were set
+ ///
+ /// Row `0` means the top row.
+ /// Row `grid.count_rows()` means the bottom row.
+ pub fn remove_horizontal_line(&mut self, line: usize, count_rows: usize) {
+ self.borders.remove_horizontal_line(line, count_rows);
+ }
+
+ /// Gets a overridden vertical line.
+ ///
+ /// Row `0` means the left row.
+ /// Row `grid.count_columns()` means the right most row.
+ pub fn get_vertical_line(&self, line: usize) -> Option<&VerticalLine> {
+ self.borders.get_vertical_line(line)
+ }
+
+ /// Set the border line by column index.
+ ///
+ /// Row `0` means the left row.
+ /// Row `grid.count_columns()` means the right most row.
+ pub fn insert_vertical_line(&mut self, line: usize, val: VerticalLine) {
+ self.borders.insert_vertical_line(line, val);
+ }
+
+ /// Sets off the border line by column index if any were set
+ ///
+ /// Row `0` means the left row.
+ /// Row `grid.count_columns()` means the right most row.
+ pub fn remove_vertical_line(&mut self, line: usize, count_columns: usize) {
+ self.borders.remove_vertical_line(line, count_columns);
+ }
+
+ /// Gets a overridden line.
+ ///
+ /// Row `0` means the top row.
+ /// Row `grid.count_rows()` means the bottom row.
+ pub fn get_horizontal_line(&self, line: usize) -> Option<&HorizontalLine> {
+ self.borders.get_horizontal_line(line)
+ }
+
+ /// Override a character on a horizontal line.
+ ///
+ /// If borders are not set the char won't be used.
+ ///
+ /// It takes not cell position but line as row and column of a cell;
+ /// So its range is line <= count_rows && col < count_columns.
+ pub fn set_horizontal_char(&mut self, pos: Position, c: char, offset: Offset) {
+ let chars = self
+ .horizontal_chars
+ .entry(pos)
+ .or_insert_with(|| HashMap::with_capacity(1));
+
+ chars.insert(offset, c);
+ }
+
+ /// Get a list of overridden chars in a horizontal border.
+ ///
+ /// It takes not cell position but line as row and column of a cell;
+ /// So its range is line <= count_rows && col < count_columns.
+ pub fn lookup_horizontal_char(&self, pos: Position, offset: usize, end: usize) -> Option<char> {
+ self.horizontal_chars
+ .get(&pos)
+ .and_then(|chars| {
+ chars.get(&Offset::Begin(offset)).or_else(|| {
+ if end > offset {
+ if end == 0 {
+ chars.get(&Offset::End(0))
+ } else {
+ chars.get(&Offset::End(end - offset - 1))
+ }
+ } else {
+ None
+ }
+ })
+ })
+ .copied()
+ }
+
+ /// Checks if there any char in a horizontal border being overridden.
+ ///
+ /// It takes not cell position but line as row and column of a cell;
+ /// So its range is line <= count_rows && col < count_columns.
+ pub fn is_overridden_horizontal(&self, pos: Position) -> bool {
+ self.horizontal_chars.get(&pos).is_some()
+ }
+
+ /// Removes a list of overridden chars in a horizontal border.
+ ///
+ /// It takes not cell position but line as row and column of a cell;
+ /// So its range is line <= count_rows && col < count_columns.
+ pub fn remove_overridden_horizontal(&mut self, pos: Position) {
+ self.horizontal_chars.remove(&pos);
+ }
+
+ /// Override a vertical split line.
+ ///
+ /// If borders are not set the char won't be used.
+ ///
+ /// It takes not cell position but cell row and column of a line;
+ /// So its range is row < count_rows && col <= count_columns.
+ pub fn set_vertical_char(&mut self, pos: Position, c: char, offset: Offset) {
+ let chars = self
+ .vertical_chars
+ .entry(pos)
+ .or_insert_with(|| HashMap::with_capacity(1));
+
+ chars.insert(offset, c);
+ }
+
+ /// Get a list of overridden chars in a horizontal border.
+ ///
+ /// It takes not cell position but cell row and column of a line;
+ /// So its range is row < count_rows && col <= count_columns.
+ pub fn lookup_vertical_char(&self, pos: Position, offset: usize, end: usize) -> Option<char> {
+ self.vertical_chars
+ .get(&pos)
+ .and_then(|chars| {
+ chars.get(&Offset::Begin(offset)).or_else(|| {
+ if end > offset {
+ if end == 0 {
+ chars.get(&Offset::End(0))
+ } else {
+ chars.get(&Offset::End(end - offset - 1))
+ }
+ } else {
+ None
+ }
+ })
+ })
+ .copied()
+ }
+
+ /// Checks if there any char in a horizontal border being overridden.
+ ///
+ /// It takes not cell position but cell row and column of a line;
+ /// So its range is row < count_rows && col <= count_columns.
+ pub fn is_overridden_vertical(&self, pos: Position) -> bool {
+ self.vertical_chars.get(&pos).is_some()
+ }
+
+ /// Removes a list of overridden chars in a horizontal border.
+ ///
+ /// It takes not cell position but cell row and column of a line;
+ /// So its range is row < count_rows && col <= count_columns.
+ pub fn remove_overridden_vertical(&mut self, pos: Position) {
+ self.vertical_chars.remove(&pos);
+ }
+
+ /// Override a character color on a horizontal line.
+ pub fn set_horizontal_color(&mut self, pos: Position, c: AnsiColor<'static>, offset: Offset) {
+ let chars = self
+ .horizontal_colors
+ .entry(pos)
+ .or_insert_with(|| HashMap::with_capacity(1));
+
+ chars.insert(offset, c);
+ }
+
+ /// Get a overridden color in a horizontal border.
+ pub fn lookup_horizontal_color(
+ &self,
+ pos: Position,
+ offset: usize,
+ end: usize,
+ ) -> Option<&AnsiColor<'static>> {
+ self.horizontal_colors.get(&pos).and_then(|chars| {
+ chars.get(&Offset::Begin(offset)).or_else(|| {
+ if end > offset {
+ if end == 0 {
+ chars.get(&Offset::End(0))
+ } else {
+ chars.get(&Offset::End(end - offset - 1))
+ }
+ } else {
+ None
+ }
+ })
+ })
+ }
+
+ /// Override a character color on a vertical line.
+ pub fn set_vertical_color(&mut self, pos: Position, c: AnsiColor<'static>, offset: Offset) {
+ let chars = self
+ .vertical_colors
+ .entry(pos)
+ .or_insert_with(|| HashMap::with_capacity(1));
+
+ chars.insert(offset, c);
+ }
+
+ /// Get a overridden color in a vertical border.
+ pub fn lookup_vertical_color(
+ &self,
+ pos: Position,
+ offset: usize,
+ end: usize,
+ ) -> Option<&AnsiColor<'static>> {
+ self.vertical_colors.get(&pos).and_then(|chars| {
+ chars.get(&Offset::Begin(offset)).or_else(|| {
+ if end > offset {
+ if end == 0 {
+ chars.get(&Offset::End(0))
+ } else {
+ chars.get(&Offset::End(end - offset - 1))
+ }
+ } else {
+ None
+ }
+ })
+ })
+ }
+
+ /// Set a padding to a given cells.
+ pub fn set_padding(&mut self, entity: Entity, padding: Sides<Indent>) {
+ let mut pad = self.padding.get(entity).clone();
+ pad.left.indent = padding.left;
+ pad.right.indent = padding.right;
+ pad.top.indent = padding.top;
+ pad.bottom.indent = padding.bottom;
+
+ self.padding.insert(entity, pad);
+ }
+
+ /// Set a padding to a given cells.
+ pub fn set_padding_color(
+ &mut self,
+ entity: Entity,
+ padding: Sides<Option<AnsiColor<'static>>>,
+ ) {
+ let mut pad = self.padding.get(entity).clone();
+ pad.left.color = padding.left;
+ pad.right.color = padding.right;
+ pad.top.color = padding.top;
+ pad.bottom.color = padding.bottom;
+
+ self.padding.insert(entity, pad);
+ }
+
+ /// Get a padding for a given [Entity].
+ pub fn get_padding(&self, entity: Entity) -> Sides<Indent> {
+ let pad = self.padding.get(entity);
+ Sides::new(
+ pad.left.indent,
+ pad.right.indent,
+ pad.top.indent,
+ pad.bottom.indent,
+ )
+ }
+
+ /// Get a padding color for a given [Entity].
+ pub fn get_padding_color(&self, entity: Entity) -> Sides<Option<AnsiColor<'static>>> {
+ let pad = self.padding.get(entity);
+ Sides::new(
+ pad.left.color.clone(),
+ pad.right.color.clone(),
+ pad.top.color.clone(),
+ pad.bottom.color.clone(),
+ )
+ }
+
+ /// Set a formatting to a given cells.
+ pub fn set_formatting(&mut self, entity: Entity, formatting: Formatting) {
+ self.formatting.insert(entity, formatting);
+ }
+
+ /// Get a formatting settings for a given [Entity].
+ pub fn get_formatting(&self, entity: Entity) -> &Formatting {
+ self.formatting.get(entity)
+ }
+
+ /// Set a vertical alignment to a given cells.
+ pub fn set_alignment_vertical(&mut self, entity: Entity, alignment: AlignmentVertical) {
+ self.alignment_v.insert(entity, alignment);
+ }
+
+ /// Get a vertical alignment for a given [Entity].
+ pub fn get_alignment_vertical(&self, entity: Entity) -> &AlignmentVertical {
+ self.alignment_v.get(entity)
+ }
+
+ /// Set a horizontal alignment to a given cells.
+ pub fn set_alignment_horizontal(&mut self, entity: Entity, alignment: AlignmentHorizontal) {
+ self.alignment_h.insert(entity, alignment);
+ }
+
+ /// Get a horizontal alignment for a given [Entity].
+ pub fn get_alignment_horizontal(&self, entity: Entity) -> &AlignmentHorizontal {
+ self.alignment_h.get(entity)
+ }
+
+ /// Set border set a border value to all cells in [`Entity`].
+ pub fn set_border(&mut self, pos: Position, border: Border<char>) {
+ self.borders.insert_border(pos, border);
+ }
+
+ /// Returns a border of a cell.
+ pub fn get_border(&self, pos: Position, shape: (usize, usize)) -> Border<char> {
+ self.borders.get_border(pos, shape).copied()
+ }
+
+ /// Returns a border color of a cell.
+ pub fn get_border_color(
+ &self,
+ pos: Position,
+ shape: (usize, usize),
+ ) -> Border<&AnsiColor<'static>> {
+ self.borders_colors.get_border(pos, shape)
+ }
+
+ /// Set a character which will be used in case any misconfiguration of borders.
+ ///
+ /// It will be usde for example when you set a left char for border frame and top but didn't set a top left corner.
+ pub fn set_borders_missing(&mut self, c: char) {
+ self.borders_missing_char = c;
+ }
+
+ /// Get a character which will be used in case any misconfiguration of borders.
+ pub fn get_borders_missing(&self) -> char {
+ self.borders_missing_char
+ }
+
+ /// Gets a color of all borders on the grid.
+ pub fn get_border_color_global(&self) -> Option<&AnsiColor<'static>> {
+ self.borders_colors.get_global()
+ }
+
+ /// Sets a color of all borders on the grid.
+ pub fn set_border_color_global(&mut self, clr: AnsiColor<'static>) {
+ self.borders_colors = BordersConfig::default();
+ self.borders_colors.set_global(clr);
+ }
+
+ /// Gets colors of a borders carcass on the grid.
+ pub fn get_color_borders(&self) -> &Borders<AnsiColor<'static>> {
+ self.borders_colors.get_borders()
+ }
+
+ /// Sets colors of border carcass on the grid.
+ pub fn set_borders_color(&mut self, clrs: Borders<AnsiColor<'static>>) {
+ self.borders_colors.set_borders(clrs);
+ }
+
+ /// Sets a color of border of a cell on the grid.
+ pub fn set_border_color(&mut self, pos: Position, border: Border<AnsiColor<'static>>) {
+ self.borders_colors.insert_border(pos, border)
+ }
+
+ /// Sets off all borders possible on the [`Entity`].
+ ///
+ /// It doesn't changes globally set borders through [`SpannedConfig::set_borders`].
+ //
+ // todo: would be great to remove a shape
+ pub fn remove_border(&mut self, pos: Position, shape: (usize, usize)) {
+ self.borders.remove_border(pos, shape);
+ }
+
+ /// Gets a color of border of a cell on the grid.
+ //
+ // todo: would be great to remove a shape
+ pub fn remove_border_color(&mut self, pos: Position, shape: (usize, usize)) {
+ self.borders_colors.remove_border(pos, shape);
+ }
+
+ /// Get a justification which will be used while expanding cells width/height.
+ pub fn get_justification(&self, entity: Entity) -> char {
+ *self.justification.get(entity)
+ }
+
+ /// Get a justification color which will be used while expanding cells width/height.
+ ///
+ /// `None` means no color.
+ pub fn get_justification_color(&self, entity: Entity) -> Option<&AnsiColor<'static>> {
+ self.justification_color.get(entity).as_ref()
+ }
+
+ /// Set a justification which will be used while expanding cells width/height.
+ pub fn set_justification(&mut self, entity: Entity, c: char) {
+ self.justification.insert(entity, c);
+ }
+
+ /// Set a justification color which will be used while expanding cells width/height.
+ ///
+ /// `None` removes it.
+ pub fn set_justification_color(&mut self, entity: Entity, color: Option<AnsiColor<'static>>) {
+ self.justification_color.insert(entity, color);
+ }
+
+ /// Get a span value of the cell, if any is set.
+ pub fn get_column_spans(&self) -> HashMap<Position, usize> {
+ self.span_columns.clone()
+ }
+
+ /// Get a span value of the cell, if any is set.
+ pub fn get_row_spans(&self) -> HashMap<Position, usize> {
+ self.span_rows.clone()
+ }
+
+ /// Get a span value of the cell, if any is set.
+ pub fn get_column_span(&self, pos: Position) -> Option<usize> {
+ self.span_columns.get(&pos).copied()
+ }
+
+ /// Get a span value of the cell, if any is set.
+ pub fn get_row_span(&self, pos: Position) -> Option<usize> {
+ self.span_rows.get(&pos).copied()
+ }
+
+ /// Removes column spans.
+ pub fn remove_column_spans(&mut self) {
+ self.span_columns.clear()
+ }
+
+ /// Removes row spans.
+ pub fn remove_row_spans(&mut self) {
+ self.span_rows.clear()
+ }
+
+ /// Set a column span to a given cells.
+ ///
+ /// BEWARE
+ ///
+ /// IT'S CALLER RESPONSIBILITY TO MAKE SURE
+ /// THAT THERE NO INTERSECTIONS IN PLACE AND THE SPAN VALUE IS CORRECT
+ pub fn set_column_span(&mut self, pos: Position, span: usize) {
+ set_cell_column_span(self, pos, span);
+ }
+
+ /// Verifies if there's any spans set.
+ pub fn has_column_spans(&self) -> bool {
+ !self.span_columns.is_empty()
+ }
+
+ /// Set a column span to a given cells.
+ ///
+ /// BEWARE
+ ///
+ /// IT'S CALLER RESPONSIBILITY TO MAKE SURE
+ /// THAT THERE NO INTERSECTIONS IN PLACE AND THE SPAN VALUE IS CORRECT
+ pub fn set_row_span(&mut self, pos: Position, span: usize) {
+ set_cell_row_span(self, pos, span);
+ }
+
+ /// Verifies if there's any spans set.
+ pub fn has_row_spans(&self) -> bool {
+ !self.span_rows.is_empty()
+ }
+
+ /// Gets an intersection character which would be rendered on the grid.
+ ///
+ /// grid: crate::Grid
+ pub fn get_intersection(&self, pos: Position, shape: (usize, usize)) -> Option<char> {
+ let c = self.borders.get_intersection(pos, shape);
+ if let Some(c) = c {
+ return Some(*c);
+ }
+
+ if self.has_horizontal(pos.0, shape.0) && self.has_vertical(pos.1, shape.1) {
+ return Some(self.get_borders_missing());
+ }
+
+ None
+ }
+
+ /// Gets a horizontal character which would be rendered on the grid.
+ ///
+ /// grid: crate::Grid
+ pub fn get_horizontal(&self, pos: Position, count_rows: usize) -> Option<char> {
+ let c = self.borders.get_horizontal(pos, count_rows);
+ if let Some(c) = c {
+ return Some(*c);
+ }
+
+ if self.has_horizontal(pos.0, count_rows) {
+ return Some(self.get_borders_missing());
+ }
+
+ None
+ }
+
+ /// Gets a vertical character which would be rendered on the grid.
+ ///
+ /// grid: crate::Grid
+ pub fn get_vertical(&self, pos: Position, count_columns: usize) -> Option<char> {
+ if let Some(c) = self.borders.get_vertical(pos, count_columns) {
+ return Some(*c);
+ }
+
+ if self.has_vertical(pos.1, count_columns) {
+ return Some(self.get_borders_missing());
+ }
+
+ None
+ }
+
+ /// Gets a color of a cell horizontal.
+ pub fn get_horizontal_color(
+ &self,
+ pos: Position,
+ count_rows: usize,
+ ) -> Option<&AnsiColor<'static>> {
+ self.borders_colors.get_horizontal(pos, count_rows)
+ }
+
+ /// Gets a color of a cell vertical.
+ pub fn get_vertical_color(
+ &self,
+ pos: Position,
+ count_columns: usize,
+ ) -> Option<&AnsiColor<'static>> {
+ self.borders_colors.get_vertical(pos, count_columns)
+ }
+
+ /// Gets a color of a cell vertical.
+ pub fn get_intersection_color(
+ &self,
+ pos: Position,
+ shape: (usize, usize),
+ ) -> Option<&AnsiColor<'static>> {
+ self.borders_colors.get_intersection(pos, shape)
+ }
+
+ /// Checks if grid would have a horizontal border with the current configuration.
+ ///
+ /// grid: crate::Grid
+ pub fn has_horizontal(&self, row: usize, count_rows: usize) -> bool {
+ self.borders.has_horizontal(row, count_rows)
+ }
+
+ /// Checks if grid would have a vertical border with the current configuration.
+ ///
+ /// grid: crate::Grid
+ pub fn has_vertical(&self, col: usize, count_columns: usize) -> bool {
+ self.borders.has_vertical(col, count_columns)
+ }
+
+ /// Calculates an amount of horizontal lines would present on the grid.
+ ///
+ /// grid: crate::Grid
+ pub fn count_horizontal(&self, count_rows: usize) -> usize {
+ (0..=count_rows)
+ .filter(|&row| self.has_horizontal(row, count_rows))
+ .count()
+ }
+
+ /// Calculates an amount of vertical lines would present on the grid.
+ ///
+ /// grid: crate::Grid
+ pub fn count_vertical(&self, count_columns: usize) -> usize {
+ (0..=count_columns)
+ .filter(|&col| self.has_vertical(col, count_columns))
+ .count()
+ }
+
+ /// The function returns whether the cells will be rendered or it will be hidden because of a span.
+ pub fn is_cell_visible(&self, pos: Position) -> bool {
+ !(self.is_cell_covered_by_column_span(pos)
+ || self.is_cell_covered_by_row_span(pos)
+ || self.is_cell_covered_by_both_spans(pos))
+ }
+
+ /// The function checks if a cell is hidden because of a row span.
+ pub fn is_cell_covered_by_row_span(&self, pos: Position) -> bool {
+ is_cell_covered_by_row_span(self, pos)
+ }
+
+ /// The function checks if a cell is hidden because of a column span.
+ pub fn is_cell_covered_by_column_span(&self, pos: Position) -> bool {
+ is_cell_covered_by_column_span(self, pos)
+ }
+
+ /// The function checks if a cell is hidden indirectly because of a row and column span combination.
+ pub fn is_cell_covered_by_both_spans(&self, pos: Position) -> bool {
+ is_cell_covered_by_both_spans(self, pos)
+ }
+}
+
+impl From<CompactConfig> for SpannedConfig {
+ fn from(compact: CompactConfig) -> Self {
+ use Entity::Global;
+
+ let mut cfg = Self::default();
+
+ cfg.set_padding(Global, *compact.get_padding());
+ cfg.set_padding_color(Global, to_ansi_color(compact.get_padding_color()));
+ cfg.set_margin(*compact.get_margin());
+ cfg.set_margin_color(to_ansi_color(compact.get_margin_color()));
+ cfg.set_alignment_horizontal(Global, compact.get_alignment_horizontal());
+ cfg.set_borders(*compact.get_borders());
+ cfg.set_borders_color(borders_static_color_to_ansi_color(
+ *compact.get_borders_color(),
+ ));
+
+ if let Some(line) = compact.get_first_horizontal_line() {
+ cfg.insert_horizontal_line(
+ 1,
+ HorizontalLine {
+ intersection: line.intersection,
+ left: line.connect1,
+ right: line.connect2,
+ main: Some(line.main),
+ },
+ );
+ }
+
+ cfg
+ }
+}
+
+fn to_ansi_color(b: Sides<StaticColor>) -> Sides<Option<AnsiColor<'static>>> {
+ Sides::new(
+ Some(b.left.into()),
+ Some(b.right.into()),
+ Some(b.top.into()),
+ Some(b.bottom.into()),
+ )
+}
+
+fn borders_static_color_to_ansi_color(b: Borders<StaticColor>) -> Borders<AnsiColor<'static>> {
+ Borders {
+ left: b.left.map(|c| c.into()),
+ right: b.right.map(|c| c.into()),
+ top: b.top.map(|c| c.into()),
+ bottom: b.bottom.map(|c| c.into()),
+ bottom_intersection: b.bottom_intersection.map(|c| c.into()),
+ bottom_left: b.bottom_left.map(|c| c.into()),
+ bottom_right: b.bottom_right.map(|c| c.into()),
+ horizontal: b.horizontal.map(|c| c.into()),
+ intersection: b.intersection.map(|c| c.into()),
+ left_intersection: b.left_intersection.map(|c| c.into()),
+ right_intersection: b.right_intersection.map(|c| c.into()),
+ top_intersection: b.top_intersection.map(|c| c.into()),
+ top_left: b.top_left.map(|c| c.into()),
+ top_right: b.top_right.map(|c| c.into()),
+ vertical: b.vertical.map(|c| c.into()),
+ }
+}
+
+fn set_cell_row_span(cfg: &mut SpannedConfig, pos: Position, span: usize) {
+ // such spans aren't supported
+ if span == 0 {
+ return;
+ }
+
+ // It's a default span so we can do nothing.
+ // but we check if it's an override of a span.
+ if span == 1 {
+ cfg.span_rows.remove(&pos);
+ return;
+ }
+
+ cfg.span_rows.insert(pos, span);
+}
+
+fn set_cell_column_span(cfg: &mut SpannedConfig, pos: Position, span: usize) {
+ // such spans aren't supported
+ if span == 0 {
+ return;
+ }
+
+ // It's a default span so we can do nothing.
+ // but we check if it's an override of a span.
+ if span == 1 {
+ cfg.span_columns.remove(&pos);
+ return;
+ }
+
+ cfg.span_columns.insert(pos, span);
+}
+
+fn is_cell_covered_by_column_span(cfg: &SpannedConfig, pos: Position) -> bool {
+ cfg.span_columns
+ .iter()
+ .any(|(&(row, col), span)| pos.1 > col && pos.1 < col + span && row == pos.0)
+}
+
+fn is_cell_covered_by_row_span(cfg: &SpannedConfig, pos: Position) -> bool {
+ cfg.span_rows
+ .iter()
+ .any(|(&(row, col), span)| pos.0 > row && pos.0 < row + span && col == pos.1)
+}
+
+fn is_cell_covered_by_both_spans(cfg: &SpannedConfig, pos: Position) -> bool {
+ if !cfg.has_column_spans() || !cfg.has_row_spans() {
+ return false;
+ }
+
+ cfg.span_rows.iter().any(|(p1, row_span)| {
+ cfg.span_columns
+ .iter()
+ .filter(|(p2, _)| &p1 == p2)
+ .any(|(_, col_span)| {
+ pos.0 > p1.0 && pos.0 < p1.0 + row_span && pos.1 > p1.1 && pos.1 < p1.1 + col_span
+ })
+ })
+}
+
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+struct ColoredIndent {
+ indent: Indent,
+ color: Option<AnsiColor<'static>>,
+}
+
+/// A colorefull margin indent.
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct ColoredMarginIndent {
+ /// An indent value.
+ indent: Indent,
+ /// An offset value.
+ offset: Offset,
+ /// An color value.
+ color: Option<AnsiColor<'static>>,
+}
+
+impl Default for ColoredMarginIndent {
+ fn default() -> Self {
+ Self {
+ indent: Indent::default(),
+ offset: Offset::Begin(0),
+ color: None,
+ }
+ }
+}
+
+/// HorizontalLine represents a horizontal border line.
+pub type HorizontalLine = borders_config::HorizontalLine<char>;
+
+/// HorizontalLine represents a vertical border line.
+pub type VerticalLine = borders_config::VerticalLine<char>;
diff --git a/vendor/papergrid/src/config/spanned/offset.rs b/vendor/papergrid/src/config/spanned/offset.rs
new file mode 100644
index 000000000..03055ca34
--- /dev/null
+++ b/vendor/papergrid/src/config/spanned/offset.rs
@@ -0,0 +1,8 @@
+/// The structure represents an offset in a text.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum Offset {
+ /// An offset from the start.
+ Begin(usize),
+ /// An offset from the end.
+ End(usize),
+}