summaryrefslogtreecommitdiffstats
path: root/vendor/tabled/src/settings/style
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
commitef24de24a82fe681581cc130f342363c47c0969a (patch)
tree0d494f7e1a38b95c92426f58fe6eaa877303a86c /vendor/tabled/src/settings/style
parentReleasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz
rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/tabled/src/settings/style')
-rw-r--r--vendor/tabled/src/settings/style/border.rs159
-rw-r--r--vendor/tabled/src/settings/style/border_char.rs99
-rw-r--r--vendor/tabled/src/settings/style/border_color.rs155
-rw-r--r--vendor/tabled/src/settings/style/border_text.rs253
-rw-r--r--vendor/tabled/src/settings/style/builder.rs1208
-rw-r--r--vendor/tabled/src/settings/style/horizontal_line.rs69
-rw-r--r--vendor/tabled/src/settings/style/line.rs91
-rw-r--r--vendor/tabled/src/settings/style/mod.rs127
-rw-r--r--vendor/tabled/src/settings/style/offset.rs19
-rw-r--r--vendor/tabled/src/settings/style/raw_style.rs438
-rw-r--r--vendor/tabled/src/settings/style/span_border_correction.rs216
-rw-r--r--vendor/tabled/src/settings/style/vertical_line.rs50
12 files changed, 2884 insertions, 0 deletions
diff --git a/vendor/tabled/src/settings/style/border.rs b/vendor/tabled/src/settings/style/border.rs
new file mode 100644
index 000000000..a8cbefb5d
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border.rs
@@ -0,0 +1,159 @@
+use crate::{
+ grid::{
+ config::{Border as GBorder, ColoredConfig, Entity},
+ records::{ExactRecords, Records},
+ },
+ settings::CellOption,
+};
+
+/// Border represents a border of a Cell.
+///
+/// ```text
+/// top border
+/// |
+/// V
+/// corner top left ------> +_______+ <---- corner top left
+/// | |
+/// left border ----------> | cell | <---- right border
+/// | |
+/// corner bottom right --> +_______+ <---- corner bottom right
+/// ^
+/// |
+/// bottom border
+/// ```
+///
+/// ```rust,no_run
+/// # use tabled::{Table, settings::{Modify, style::{Style, Border}, object::Rows}};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data)
+/// .with(Style::ascii())
+/// .with(Modify::new(Rows::single(0)).with(Border::default().top('x')));
+/// ```
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Border(GBorder<char>);
+
+impl Border {
+ /// This function constructs a cell borders with all sides set.
+ #[allow(clippy::too_many_arguments)]
+ pub const fn full(
+ top: char,
+ bottom: char,
+ left: char,
+ right: char,
+ top_left: char,
+ top_right: char,
+ bottom_left: char,
+ bottom_right: char,
+ ) -> Self {
+ Self(GBorder::full(
+ top,
+ bottom,
+ left,
+ right,
+ top_left,
+ top_right,
+ bottom_left,
+ bottom_right,
+ ))
+ }
+
+ /// 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 const fn filled(c: char) -> Self {
+ Self::full(c, c, c, c, c, c, c, c)
+ }
+
+ /// Using this function you deconstruct the existing borders.
+ pub const fn empty() -> EmptyBorder {
+ EmptyBorder
+ }
+
+ /// Set a top border character.
+ pub const fn top(mut self, c: char) -> Self {
+ self.0.top = Some(c);
+ self
+ }
+
+ /// Set a bottom border character.
+ pub const fn bottom(mut self, c: char) -> Self {
+ self.0.bottom = Some(c);
+ self
+ }
+
+ /// Set a left border character.
+ pub const fn left(mut self, c: char) -> Self {
+ self.0.left = Some(c);
+ self
+ }
+
+ /// Set a right border character.
+ pub const fn right(mut self, c: char) -> Self {
+ self.0.right = Some(c);
+ self
+ }
+
+ /// Set a top left intersection character.
+ pub const fn corner_top_left(mut self, c: char) -> Self {
+ self.0.left_top_corner = Some(c);
+ self
+ }
+
+ /// Set a top right intersection character.
+ pub const fn corner_top_right(mut self, c: char) -> Self {
+ self.0.right_top_corner = Some(c);
+ self
+ }
+
+ /// Set a bottom left intersection character.
+ pub const fn corner_bottom_left(mut self, c: char) -> Self {
+ self.0.left_bottom_corner = Some(c);
+ self
+ }
+
+ /// Set a bottom right intersection character.
+ pub const fn corner_bottom_right(mut self, c: char) -> Self {
+ self.0.right_bottom_corner = Some(c);
+ self
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for Border
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let shape = (records.count_rows(), records.count_columns());
+
+ for pos in entity.iter(shape.0, shape.1) {
+ cfg.set_border(pos, self.0);
+ }
+ }
+}
+
+impl From<GBorder<char>> for Border {
+ fn from(b: GBorder<char>) -> Border {
+ Border(b)
+ }
+}
+
+impl From<Border> for GBorder<char> {
+ fn from(value: Border) -> Self {
+ value.0
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct EmptyBorder;
+
+impl<R> CellOption<R, ColoredConfig> for EmptyBorder
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let shape = (records.count_rows(), records.count_columns());
+
+ for pos in entity.iter(shape.0, shape.1) {
+ cfg.remove_border(pos, shape);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_char.rs b/vendor/tabled/src/settings/style/border_char.rs
new file mode 100644
index 000000000..b1e04c579
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_char.rs
@@ -0,0 +1,99 @@
+use crate::{
+ grid::config::{ColoredConfig, Entity, Position, SpannedConfig},
+ grid::records::{ExactRecords, Records},
+ settings::CellOption,
+};
+
+use super::Offset;
+
+/// [`BorderChar`] sets a char to a specific location on a horizontal line.
+///
+/// # Example
+///
+/// ```rust
+/// use tabled::{Table, settings::{style::{Style, BorderChar, Offset}, Modify, object::{Object, Rows, Columns}}};
+///
+/// let mut table = Table::new(["Hello World"]);
+/// table
+/// .with(Style::markdown())
+/// .with(Modify::new(Rows::single(1))
+/// .with(BorderChar::horizontal(':', Offset::Begin(0)))
+/// .with(BorderChar::horizontal(':', Offset::End(0)))
+/// )
+/// .with(Modify::new((1, 0).and((1, 1))).with(BorderChar::vertical('#', Offset::Begin(0))));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "| &str |\n",
+/// "|:-----------:|\n",
+/// "# Hello World #",
+/// ),
+/// );
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct BorderChar {
+ c: char,
+ offset: Offset,
+ horizontal: bool,
+}
+
+impl BorderChar {
+ /// Creates a [`BorderChar`] which overrides horizontal line.
+ pub fn horizontal(c: char, offset: Offset) -> Self {
+ Self {
+ c,
+ offset,
+ horizontal: true,
+ }
+ }
+
+ /// Creates a [`BorderChar`] which overrides vertical line.
+ pub fn vertical(c: char, offset: Offset) -> Self {
+ Self {
+ c,
+ offset,
+ horizontal: false,
+ }
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for BorderChar
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let cells = entity.iter(records.count_rows(), records.count_columns());
+
+ match self.horizontal {
+ true => add_char_horizontal(cfg, self.c, self.offset, cells),
+ false => add_char_vertical(cfg, self.c, self.offset, cells),
+ }
+ }
+}
+
+fn add_char_vertical<I: Iterator<Item = Position>>(
+ cfg: &mut SpannedConfig,
+ c: char,
+ offset: Offset,
+ cells: I,
+) {
+ let offset = offset.into();
+
+ for pos in cells {
+ cfg.set_vertical_char(pos, c, offset);
+ }
+}
+
+fn add_char_horizontal<I: Iterator<Item = Position>>(
+ cfg: &mut SpannedConfig,
+ c: char,
+ offset: Offset,
+ cells: I,
+) {
+ let offset = offset.into();
+
+ for pos in cells {
+ cfg.set_horizontal_char(pos, c, offset);
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_color.rs b/vendor/tabled/src/settings/style/border_color.rs
new file mode 100644
index 000000000..6b3fa2b1a
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_color.rs
@@ -0,0 +1,155 @@
+//! This module contains a configuration of a Border to set its color via [`BorderColor`].
+
+use crate::{
+ grid::{
+ color::AnsiColor,
+ config::{Border, ColoredConfig, Entity},
+ records::{ExactRecords, Records},
+ },
+ settings::{color::Color, CellOption, TableOption},
+};
+
+/// BorderColored represents a colored border of a Cell.
+///
+/// ```rust,no_run
+/// # use tabled::{settings::{style::BorderColor, Style, Color, object::Rows, Modify}, Table};
+/// #
+/// # let data: Vec<&'static str> = Vec::new();
+/// #
+/// let table = Table::new(&data)
+/// .with(Style::ascii())
+/// .with(Modify::new(Rows::single(0)).with(BorderColor::default().top(Color::FG_RED)));
+/// ```
+#[derive(Debug, Clone, Default, Eq, PartialEq)]
+pub struct BorderColor(Border<AnsiColor<'static>>);
+
+impl BorderColor {
+ /// This function constructs a cell borders with all sides set.
+ #[allow(clippy::too_many_arguments)]
+ pub fn full(
+ top: Color,
+ bottom: Color,
+ left: Color,
+ right: Color,
+ top_left: Color,
+ top_right: Color,
+ bottom_left: Color,
+ bottom_right: Color,
+ ) -> Self {
+ Self(Border::full(
+ top.into(),
+ bottom.into(),
+ left.into(),
+ right.into(),
+ top_left.into(),
+ top_right.into(),
+ bottom_left.into(),
+ bottom_right.into(),
+ ))
+ }
+
+ /// 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: Color) -> Self {
+ let c: AnsiColor<'_> = c.into();
+
+ Self(Border {
+ top: Some(c.clone()),
+ bottom: Some(c.clone()),
+ left: Some(c.clone()),
+ right: Some(c.clone()),
+ left_bottom_corner: Some(c.clone()),
+ left_top_corner: Some(c.clone()),
+ right_bottom_corner: Some(c.clone()),
+ right_top_corner: Some(c),
+ })
+ }
+
+ /// Set a top border character.
+ pub fn top(mut self, c: Color) -> Self {
+ self.0.top = Some(c.into());
+ self
+ }
+
+ /// Set a bottom border character.
+ pub fn bottom(mut self, c: Color) -> Self {
+ self.0.bottom = Some(c.into());
+ self
+ }
+
+ /// Set a left border character.
+ pub fn left(mut self, c: Color) -> Self {
+ self.0.left = Some(c.into());
+ self
+ }
+
+ /// Set a right border character.
+ pub fn right(mut self, c: Color) -> Self {
+ self.0.right = Some(c.into());
+ self
+ }
+
+ /// Set a top left intersection character.
+ pub fn corner_top_left(mut self, c: Color) -> Self {
+ self.0.left_top_corner = Some(c.into());
+ self
+ }
+
+ /// Set a top right intersection character.
+ pub fn corner_top_right(mut self, c: Color) -> Self {
+ self.0.right_top_corner = Some(c.into());
+ self
+ }
+
+ /// Set a bottom left intersection character.
+ pub fn corner_bottom_left(mut self, c: Color) -> Self {
+ self.0.left_bottom_corner = Some(c.into());
+ self
+ }
+
+ /// Set a bottom right intersection character.
+ pub fn corner_bottom_right(mut self, c: Color) -> Self {
+ self.0.right_bottom_corner = Some(c.into());
+ self
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for BorderColor
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let border_color = &self.0;
+
+ for pos in entity.iter(count_rows, count_columns) {
+ cfg.set_border_color(pos, border_color.clone());
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderColor
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let border_color = &self.0;
+
+ for row in 0..count_rows {
+ for col in 0..count_columns {
+ cfg.set_border_color((row, col), border_color.clone());
+ }
+ }
+ }
+}
+
+impl From<BorderColor> for Border<AnsiColor<'static>> {
+ fn from(val: BorderColor) -> Self {
+ val.0
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_text.rs b/vendor/tabled/src/settings/style/border_text.rs
new file mode 100644
index 000000000..163a19821
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_text.rs
@@ -0,0 +1,253 @@
+use crate::{
+ grid::{
+ color::AnsiColor,
+ config::{self, ColoredConfig, SpannedConfig},
+ dimension::{Dimension, Estimate},
+ records::{ExactRecords, Records},
+ },
+ settings::{
+ object::{FirstRow, LastRow},
+ Color, TableOption,
+ },
+};
+
+use super::Offset;
+
+/// [`BorderText`] writes a custom text on a border.
+///
+/// # Example
+///
+/// ```rust
+/// use tabled::{Table, settings::style::BorderText, settings::object::Rows};
+///
+/// let mut table = Table::new(["Hello World"]);
+/// table
+/// .with(BorderText::new("+-.table").horizontal(Rows::first()));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-.table------+\n\
+/// | &str |\n\
+/// +-------------+\n\
+/// | Hello World |\n\
+/// +-------------+"
+/// );
+/// ```
+#[derive(Debug)]
+pub struct BorderText<L> {
+ text: String,
+ offset: Offset,
+ color: Option<AnsiColor<'static>>,
+ line: L,
+}
+
+impl BorderText<()> {
+ /// Creates a [`BorderText`] instance.
+ ///
+ /// Lines are numbered from 0 to the `count_rows` included
+ /// (`line >= 0 && line <= count_rows`).
+ pub fn new<S: Into<String>>(text: S) -> Self {
+ BorderText {
+ text: text.into(),
+ line: (),
+ offset: Offset::Begin(0),
+ color: None,
+ }
+ }
+}
+
+impl<Line> BorderText<Line> {
+ /// Set a line on which we will set the text.
+ pub fn horizontal<L>(self, line: L) -> BorderText<L> {
+ BorderText {
+ line,
+ text: self.text,
+ offset: self.offset,
+ color: self.color,
+ }
+ }
+
+ /// Set an offset from which the text will be started.
+ pub fn offset(self, offset: Offset) -> Self {
+ BorderText {
+ offset,
+ text: self.text,
+ line: self.line,
+ color: self.color,
+ }
+ }
+
+ /// Set a color of the text.
+ pub fn color(self, color: Color) -> Self {
+ BorderText {
+ color: Some(color.into()),
+ text: self.text,
+ line: self.line,
+ offset: self.offset,
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<usize>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = self.line;
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<FirstRow>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = 0;
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<LastRow>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = records.count_rows();
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+fn set_horizontal_chars<D: Dimension>(
+ cfg: &mut SpannedConfig,
+ dims: &D,
+ offset: Offset,
+ line: usize,
+ text: &str,
+ color: &Option<AnsiColor<'static>>,
+ shape: (usize, usize),
+) {
+ let (_, count_columns) = shape;
+ let pos = get_start_pos(cfg, dims, offset, count_columns);
+ let pos = match pos {
+ Some(pos) => pos,
+ None => return,
+ };
+
+ let mut chars = text.chars();
+ let mut i = cfg.has_vertical(0, count_columns) as usize;
+ if i == 1 && pos == 0 {
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ let mut b = cfg.get_border((line, 0), shape);
+ b.left_top_corner = b.left_top_corner.map(|_| c);
+ cfg.set_border((line, 0), b);
+
+ if let Some(color) = color.as_ref() {
+ let mut b = cfg.get_border_color((line, 0), shape).cloned();
+ b.left_top_corner = Some(color.clone());
+ cfg.set_border_color((line, 0), b);
+ }
+ }
+
+ for col in 0..count_columns {
+ let w = dims.get_width(col);
+ if i + w > pos {
+ for off in 0..w {
+ if i + off < pos {
+ continue;
+ }
+
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ cfg.set_horizontal_char((line, col), c, config::Offset::Begin(off));
+ if let Some(color) = color.as_ref() {
+ cfg.set_horizontal_color(
+ (line, col),
+ color.clone(),
+ config::Offset::Begin(off),
+ );
+ }
+ }
+ }
+
+ i += w;
+
+ if cfg.has_vertical(col + 1, count_columns) {
+ i += 1;
+
+ if i > pos {
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ let mut b = cfg.get_border((line, col), shape);
+ b.right_top_corner = b.right_top_corner.map(|_| c);
+ cfg.set_border((line, col), b);
+
+ if let Some(color) = color.as_ref() {
+ let mut b = cfg.get_border_color((line, col), shape).cloned();
+ b.right_top_corner = Some(color.clone());
+ cfg.set_border_color((line, col), b);
+ }
+ }
+ }
+ }
+}
+
+fn get_start_pos<D: Dimension>(
+ cfg: &SpannedConfig,
+ dims: &D,
+ offset: Offset,
+ count_columns: usize,
+) -> Option<usize> {
+ let totalw = total_width(cfg, dims, count_columns);
+ match offset {
+ Offset::Begin(i) => {
+ if i > totalw {
+ None
+ } else {
+ Some(i)
+ }
+ }
+ Offset::End(i) => {
+ if i > totalw {
+ None
+ } else {
+ Some(totalw - i)
+ }
+ }
+ }
+}
+
+fn total_width<D: Dimension>(cfg: &SpannedConfig, dims: &D, count_columns: usize) -> usize {
+ let mut totalw = cfg.has_vertical(0, count_columns) as usize;
+ for col in 0..count_columns {
+ totalw += dims.get_width(col);
+ totalw += cfg.has_vertical(col + 1, count_columns) as usize;
+ }
+
+ totalw
+}
diff --git a/vendor/tabled/src/settings/style/builder.rs b/vendor/tabled/src/settings/style/builder.rs
new file mode 100644
index 000000000..d55c400cc
--- /dev/null
+++ b/vendor/tabled/src/settings/style/builder.rs
@@ -0,0 +1,1208 @@
+//! This module contains a compile time style builder [`Style`].
+
+use core::marker::PhantomData;
+
+use crate::{
+ grid::config::{Borders, CompactConfig, CompactMultilineConfig},
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::config::ColoredConfig;
+
+use super::{HorizontalLine, Line, VerticalLine};
+
+/// Style is represents a theme of a [`Table`].
+///
+/// ```text
+/// corner top left top intersection corner top right
+/// . | .
+/// . V .
+/// ╭───┬───┬───┬───┬───┬───┬────┬────┬────╮
+/// │ i │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
+/// ├───┼───┼───┼───┼───┼───┼────┼────┼────┤ <- this horizontal line is custom 'horizontals'
+/// │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ other lines horizontal lines are not set they called 'horizontal'
+/// │ 1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
+/// │ 2 │ 0 │ 2 │ 4 │ 6 │ 8 │ 10 │ 12 │ 14 │
+/// ╰───┴───┴───┴───┴───┴───┴────┴────┴────╯
+/// . ^ ^ .
+/// . | | .
+/// corner bottom left | bottom intersection corner bottom right
+/// |
+/// |
+/// all this vertical lines are called 'vertical'
+/// ```
+///
+///
+/// ```text
+/// ┌───┬───┬───┬───┬───┐
+/// │ 0 │ 1 │ 2 │ 3 │ 4 │
+/// intersection left ->├───X───X───X───X───┤ <- all this horizontal lines are called 'horizontal'
+/// │ 1 │ 2 │ 3 │ 4 │ 5 │
+/// ├───X───X───X───X───┤ <- intersection right
+/// │ 2 │ 3 │ 4 │ 5 │ 6 │
+/// └───┴───┴───┴───┴───┘
+///
+/// All 'X' positions are called 'intersection'.
+/// It's a place where 'vertical' and 'horizontal' lines intersect.
+/// ```
+///
+/// It tries to limit an controlling a valid state of it.
+/// For example, it won't allow to call method [`Style::corner_top_left`] unless [`Style::left`] and [`Style::top`] is set.
+///
+/// You can turn [`Style`] into [`RawStyle`] to have more control using [`Into`] implementation.
+///
+/// # Example
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// use tabled::{Table, settings::Style};
+///
+/// let style = Style::ascii()
+/// .bottom('*')
+/// .intersection(' ');
+///
+/// let data = vec!["Hello", "2021"];
+/// let table = Table::new(&data).with(style).to_string();
+///
+/// println!("{}", table);
+/// ```
+///
+/// [`Table`]: crate::Table
+/// [`RawStyle`]: crate::settings::style::RawStyle
+/// [`Style::corner_top_left`]: Style::corner_top_left
+/// [`Style::left`]: Style.left
+/// [`Style::top`]: Style.function.top
+#[derive(Debug, Clone)]
+pub struct Style<T, B, L, R, H, V, HLines = HLineArray<0>, VLines = VLineArray<0>> {
+ borders: Borders<char>,
+ horizontals: HLines,
+ verticals: VLines,
+ _top: PhantomData<T>,
+ _bottom: PhantomData<B>,
+ _left: PhantomData<L>,
+ _right: PhantomData<R>,
+ _horizontal: PhantomData<H>,
+ _vertical: PhantomData<V>,
+}
+
+type HLineArray<const N: usize> = [HorizontalLine; N];
+
+type VLineArray<const N: usize> = [VerticalLine; N];
+
+/// A marker struct which is used in [`Style`].
+#[derive(Debug, Clone)]
+pub struct On;
+
+impl Style<(), (), (), (), (), (), (), ()> {
+ /// This style is a style with no styling options on,
+ ///
+ /// ```text
+ /// id destribution link
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ```
+ ///
+ /// Note: The cells in the example have 1-left and 1-right indent.
+ ///
+ /// This style can be used as a base style to build a custom one.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Style;
+ /// let style = Style::empty()
+ /// .top('*')
+ /// .bottom('*')
+ /// .vertical('#')
+ /// .intersection_top('*');
+ /// ```
+ pub const fn empty() -> Style<(), (), (), (), (), ()> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ None,
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style is analog of `empty` but with a vertical space(' ') line.
+ ///
+ /// ```text
+ /// id destribution link
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ```
+ pub const fn blank() -> Style<(), (), (), (), (), On> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ Some(' '),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This is a style which relays only on ASCII charset.
+ ///
+ /// It has horizontal and vertical lines.
+ ///
+ /// ```text
+ /// +----+--------------+---------------------------+
+ /// | id | destribution | link |
+ /// +----+--------------+---------------------------+
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// +----+--------------+---------------------------+
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// +----+--------------+---------------------------+
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// +----+--------------+---------------------------+
+ /// ```
+ pub const fn ascii() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('-', '+', '+', '+'),
+ Line::full('-', '+', '+', '+'),
+ Line::full('-', '+', '+', '+'),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// `psql` style looks like a table style `PostgreSQL` uses.
+ ///
+ /// It has only 1 horizontal line which splits header.
+ /// And no left and right vertical lines.
+ ///
+ /// ```text
+ /// id | destribution | link
+ /// ----+--------------+---------------------------
+ /// 0 | Fedora | https://getfedora.org/
+ /// 2 | OpenSUSE | https://www.opensuse.org/
+ /// 3 | Endeavouros | https://endeavouros.com/
+ /// ```
+ pub const fn psql() -> Style<(), (), (), (), (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ Some('|'),
+ ),
+ [HorizontalLine::new(1, Line::empty())
+ .main(Some('-'))
+ .intersection(Some('+'))],
+ [],
+ )
+ }
+
+ /// `markdown` style mimics a `Markdown` table style.
+ ///
+ /// ```text
+ /// | id | destribution | link |
+ /// |----|--------------|---------------------------|
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// ```
+ pub const fn markdown() -> Style<(), (), On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [HorizontalLine::new(1, Line::full('-', '|', '|', '|'))],
+ [],
+ )
+ }
+
+ /// This style is analog of [`Style::ascii`] which uses UTF-8 charset.
+ ///
+ /// It has vertical and horizontal split lines.
+ ///
+ /// ```text
+ /// ┌────┬──────────────┬───────────────────────────┐
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// └────┴──────────────┴───────────────────────────┘
+ /// ```
+ pub const fn modern() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '┌', '┐'),
+ Line::full('─', '┴', '└', '┘'),
+ Line::full('─', '┼', '├', '┤'),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style looks like a [`Style::modern`] but without horozizontal lines except a header.
+ ///
+ /// Beware: It uses UTF-8 characters.
+ ///
+ /// ```text
+ /// ┌────┬──────────────┬───────────────────────────┐
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// └────┴──────────────┴───────────────────────────┘
+ /// ```
+ pub const fn sharp() -> Style<On, On, On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '┌', '┐'),
+ Line::full('─', '┴', '└', '┘'),
+ Line::empty(),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [HorizontalLine::new(1, Line::full('─', '┼', '├', '┤'))],
+ [],
+ )
+ }
+
+ /// This style looks like a [`Style::sharp`] but with rounded corners.
+ ///
+ /// Beware: It uses UTF-8 characters.
+ ///
+ /// ```text
+ /// ╭────┬──────────────┬───────────────────────────╮
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// ╰────┴──────────────┴───────────────────────────╯
+ /// ```
+ pub const fn rounded() -> Style<On, On, On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '╭', '╮'),
+ Line::full('─', '┴', '╰', '╯'),
+ Line::empty(),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [HorizontalLine::new(1, Line::full('─', '┼', '├', '┤'))],
+ [],
+ )
+ }
+
+ /// This style uses a chars which resembles '2 lines'.
+ ///
+ /// Beware: It uses UTF8 characters.
+ ///
+ /// ```text
+ /// ╔════╦══════════════╦═══════════════════════════╗
+ /// ║ id ║ destribution ║ link ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 0 ║ Fedora ║ https://getfedora.org/ ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 2 ║ OpenSUSE ║ https://www.opensuse.org/ ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 3 ║ Endeavouros ║ https://endeavouros.com/ ║
+ /// ╚════╩══════════════╩═══════════════════════════╝
+ /// ```
+ pub const fn extended() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('═', '╦', '╔', '╗'),
+ Line::full('═', '╩', '╚', '╝'),
+ Line::full('═', '╬', '╠', '╣'),
+ Some('║'),
+ Some('║'),
+ Some('║'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This is a style uses only '.' and ':' chars.
+ /// It has a vertical and horizontal split lines.
+ ///
+ /// ```text
+ /// .................................................
+ /// : id : destribution : link :
+ /// :....:..............:...........................:
+ /// : 0 : Fedora : https://getfedora.org/ :
+ /// :....:..............:...........................:
+ /// : 2 : OpenSUSE : https://www.opensuse.org/ :
+ /// :....:..............:...........................:
+ /// : 3 : Endeavouros : https://endeavouros.com/ :
+ /// :....:..............:...........................:
+ /// ```
+ pub const fn dots() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('.', '.', '.', '.'),
+ Line::full('.', ':', ':', ':'),
+ Line::full('.', ':', ':', ':'),
+ Some(':'),
+ Some(':'),
+ Some(':'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style is one of table views in `ReStructuredText`.
+ ///
+ /// ```text
+ /// ==== ============== ===========================
+ /// id destribution link
+ /// ==== ============== ===========================
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ==== ============== ===========================
+ /// ```
+ pub const fn re_structured_text() -> Style<On, On, (), (), (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::new(Some('='), Some(' '), None, None),
+ Line::new(Some('='), Some(' '), None, None),
+ Line::empty(),
+ None,
+ None,
+ Some(' '),
+ ),
+ [HorizontalLine::new(
+ 1,
+ Line::new(Some('='), Some(' '), None, None),
+ )],
+ [],
+ )
+ }
+
+ /// This is a theme analog of [`Style::rounded`], but in using ascii charset and
+ /// with no horizontal lines.
+ ///
+ /// ```text
+ /// .-----------------------------------------------.
+ /// | id | destribution | link |
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// '-----------------------------------------------'
+ /// ```
+ pub const fn ascii_rounded() -> Style<On, On, On, On, (), On> {
+ Style::new(
+ create_borders(
+ Line::full('-', '-', '.', '.'),
+ Line::full('-', '-', '\'', '\''),
+ Line::empty(),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [],
+ [],
+ )
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> Style<T, B, L, R, H, V, HLines, VLines> {
+ /// Frame function returns a frame as a border.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Style, Highlight, object::Rows}};
+ ///
+ /// let data = [["10:52:19", "Hello"], ["10:52:20", "World"]];
+ /// let table = Table::new(data)
+ /// .with(Highlight::new(Rows::first(), Style::modern().get_frame()))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "┌──────────────────┐\n",
+ /// "│ 0 | 1 │\n",
+ /// "└──────────────────┘\n",
+ /// "| 10:52:19 | Hello |\n",
+ /// "+----------+-------+\n",
+ /// "| 10:52:20 | World |\n",
+ /// "+----------+-------+",
+ /// )
+ /// );
+ /// ```
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub const fn get_frame(&self) -> super::Border {
+ let mut border = super::Border::filled(' ');
+
+ if let Some(c) = self.borders.top {
+ border = border.top(c);
+ }
+
+ if let Some(c) = self.borders.bottom {
+ border = border.bottom(c);
+ }
+
+ if let Some(c) = self.borders.left {
+ border = border.left(c);
+ }
+
+ if let Some(c) = self.borders.right {
+ border = border.right(c);
+ }
+
+ if let Some(c) = self.borders.top_left {
+ border = border.corner_top_left(c);
+ }
+
+ if let Some(c) = self.borders.bottom_left {
+ border = border.corner_bottom_left(c);
+ }
+
+ if let Some(c) = self.borders.top_right {
+ border = border.corner_top_right(c);
+ }
+
+ if let Some(c) = self.borders.bottom_right {
+ border = border.corner_bottom_right(c);
+ }
+
+ border
+ }
+
+ /// Get a [`Style`]'s default horizontal line.
+ ///
+ /// It doesn't return an overloaded line via [`Style::horizontals`].
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "std", doc = "```")]
+ #[cfg_attr(not(feature = "std"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, HorizontalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", "World", i)))
+ /// .with(Style::ascii().remove_horizontal().horizontals([HorizontalLine::new(1, Style::modern().get_horizontal())]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+-------+-------+-----+\n",
+ /// "| &str | &str | i32 |\n",
+ /// "├───────┼───────┼─────┤\n",
+ /// "| Hello | World | 0 |\n",
+ /// "| Hello | World | 1 |\n",
+ /// "| Hello | World | 2 |\n",
+ /// "+-------+-------+-----+",
+ /// )
+ /// )
+ /// ```
+ pub const fn get_horizontal(&self) -> Line {
+ Line::new(
+ self.borders.horizontal,
+ self.borders.intersection,
+ self.borders.left_intersection,
+ self.borders.right_intersection,
+ )
+ }
+
+ /// Get a [`Style`]'s default horizontal line.
+ ///
+ /// It doesn't return an overloaded line via [`Style::verticals`].
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "std", doc = "```")]
+ #[cfg_attr(not(feature = "std"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, VerticalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", "World", i)))
+ /// .with(Style::ascii().remove_horizontal().verticals([VerticalLine::new(1, Style::modern().get_vertical())]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+-------┬-------+-----+\n",
+ /// "| &str │ &str | i32 |\n",
+ /// "| Hello │ World | 0 |\n",
+ /// "| Hello │ World | 1 |\n",
+ /// "| Hello │ World | 2 |\n",
+ /// "+-------┴-------+-----+",
+ /// )
+ /// )
+ /// ```
+ pub const fn get_vertical(&self) -> Line {
+ Line::new(
+ self.borders.vertical,
+ self.borders.intersection,
+ self.borders.top_intersection,
+ self.borders.bottom_intersection,
+ )
+ }
+
+ /// Sets a top border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn top(mut self, c: char) -> Style<On, B, L, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.top = Some(c);
+
+ if self.borders.has_left() {
+ self.borders.top_left = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.top_right = Some(c);
+ }
+
+ if self.borders.has_vertical() {
+ self.borders.top_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.connector1 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a bottom border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn bottom(mut self, c: char) -> Style<T, On, L, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.bottom = Some(c);
+
+ if self.borders.has_left() {
+ self.borders.bottom_left = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.bottom_right = Some(c);
+ }
+
+ if self.borders.has_vertical() {
+ self.borders.bottom_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.connector2 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a left border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn left(mut self, c: char) -> Style<T, B, On, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.left = Some(c);
+
+ if self.borders.has_top() {
+ self.borders.top_left = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_left = Some(c);
+ }
+
+ if self.borders.has_horizontal() {
+ self.borders.left_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.connector1 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a right border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn right(mut self, c: char) -> Style<T, B, L, On, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.right = Some(c);
+
+ if self.borders.has_top() {
+ self.borders.top_right = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_right = Some(c);
+ }
+
+ if self.borders.has_horizontal() {
+ self.borders.right_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.connector2 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a horizontal split line.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn horizontal(mut self, c: char) -> Style<T, B, L, R, On, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.horizontal = Some(c);
+
+ if self.borders.has_vertical() {
+ self.borders.intersection = Some(c);
+ }
+
+ if self.borders.has_left() {
+ self.borders.left_intersection = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.right_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.intersection = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a vertical split line.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn vertical(mut self, c: char) -> Style<T, B, L, R, H, On, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.vertical = Some(c);
+
+ if self.borders.has_horizontal() {
+ self.borders.intersection = Some(c);
+ }
+
+ if self.borders.has_top() {
+ self.borders.top_intersection = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.intersection = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Set border horizontal lines.
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "derive", doc = "```")]
+ #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, HorizontalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(Style::rounded().horizontals((1..4).map(|i| HorizontalLine::new(i, Line::filled('#')))))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "╭───────┬─────╮\n",
+ /// "│ &str │ i32 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 0 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 1 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 2 │\n",
+ /// "╰───────┴─────╯",
+ /// )
+ /// )
+ /// ```
+ pub fn horizontals<NewLines>(self, lines: NewLines) -> Style<T, B, L, R, H, V, NewLines, VLines>
+ where
+ NewLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ Style::new(self.borders, lines, self.verticals)
+ }
+
+ /// Set border vertical lines.
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "derive", doc = "```")]
+ #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+ /// use tabled::{Table, settings::style::{Style, VerticalLine, Line}};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(Style::rounded().verticals((0..3).map(|i| VerticalLine::new(i, Line::filled('#')))))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "#───────#─────#\n",
+ /// "# &str # i32 #\n",
+ /// "├───────┼─────┤\n",
+ /// "# Hello # 0 #\n",
+ /// "# Hello # 1 #\n",
+ /// "# Hello # 2 #\n",
+ /// "#───────#─────#",
+ /// )
+ /// )
+ /// ```
+ pub fn verticals<NewLines>(self, lines: NewLines) -> Style<T, B, L, R, H, V, HLines, NewLines>
+ where
+ NewLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ Style::new(self.borders, self.horizontals, lines)
+ }
+
+ /// Removes all horizontal lines set by [`Style::horizontals`]
+ pub fn remove_horizontals(self) -> Style<T, B, L, R, H, V, HLineArray<0>, VLines> {
+ Style::new(self.borders, [], self.verticals)
+ }
+
+ /// Removes all verticals lines set by [`Style::verticals`]
+ pub fn remove_verticals(self) -> Style<T, B, L, R, H, V, HLines, VLineArray<0>> {
+ Style::new(self.borders, self.horizontals, [])
+ }
+}
+
+impl<B, R, H, V, HLines, VLines> Style<On, B, On, R, H, V, HLines, VLines> {
+ /// Sets a top left corner.
+ pub fn corner_top_left(mut self, c: char) -> Self {
+ self.borders.top_left = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, H, V, HLines, VLines> Style<On, B, L, On, H, V, HLines, VLines> {
+ /// Sets a top right corner.
+ pub fn corner_top_right(mut self, c: char) -> Self {
+ self.borders.top_right = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, L, H, V, HLines, VLines> Style<T, On, L, On, H, V, HLines, VLines> {
+ /// Sets a bottom right corner.
+ pub fn corner_bottom_right(mut self, c: char) -> Self {
+ self.borders.bottom_right = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, R, H, V, HLines, VLines> Style<T, On, On, R, H, V, HLines, VLines> {
+ /// Sets a bottom left corner.
+ pub fn corner_bottom_left(mut self, c: char) -> Self {
+ self.borders.bottom_left = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, R, V, HLines, VLines> Style<T, B, On, R, On, V, HLines, VLines> {
+ /// Sets a left intersection char.
+ pub fn intersection_left(mut self, c: char) -> Self {
+ self.borders.left_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, L, V, HLines, VLines> Style<T, B, L, On, On, V, HLines, VLines> {
+ /// Sets a right intersection char.
+ pub fn intersection_right(mut self, c: char) -> Self {
+ self.borders.right_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, R, H, HLines, VLines> Style<On, B, L, R, H, On, HLines, VLines> {
+ /// Sets a top intersection char.
+ pub fn intersection_top(mut self, c: char) -> Self {
+ self.borders.top_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, L, R, H, HLines, VLines> Style<T, On, L, R, H, On, HLines, VLines> {
+ /// Sets a bottom intersection char.
+ pub fn intersection_bottom(mut self, c: char) -> Self {
+ self.borders.bottom_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, L, R, HLines, VLines> Style<T, B, L, R, On, On, HLines, VLines> {
+ /// Sets an inner intersection char.
+ /// A char between horizontal and vertical split lines.
+ pub fn intersection(mut self, c: char) -> Self {
+ self.borders.intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, R, H, V, HLines, VLines> Style<On, B, L, R, H, V, HLines, VLines> {
+ /// Removes top border.
+ pub fn remove_top(
+ mut self,
+ ) -> Style<(), B, L, R, H, V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.top = None;
+ self.borders.top_intersection = None;
+ self.borders.top_left = None;
+ self.borders.top_right = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), false, true, false);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, L, R, H, V, HLines, VLines> Style<T, On, L, R, H, V, HLines, VLines> {
+ /// Removes bottom border.
+ pub fn remove_bottom(
+ mut self,
+ ) -> Style<T, (), L, R, H, V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.bottom = None;
+ self.borders.bottom_intersection = None;
+ self.borders.bottom_left = None;
+ self.borders.bottom_right = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), false, false, true);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, B, R, H, V, HLines, VLines> Style<T, B, On, R, H, V, HLines, VLines> {
+ /// Removes left border.
+ pub fn remove_left(
+ mut self,
+ ) -> Style<T, B, (), R, H, V, HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.left = None;
+ self.borders.left_intersection = None;
+ self.borders.top_left = None;
+ self.borders.bottom_left = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), false, true, false);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, H, V, HLines, VLines> Style<T, B, L, On, H, V, HLines, VLines> {
+ /// Removes right border.
+ pub fn remove_right(
+ mut self,
+ ) -> Style<T, B, L, (), H, V, HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.right = None;
+ self.borders.right_intersection = None;
+ self.borders.top_right = None;
+ self.borders.bottom_right = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), false, false, true);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, R, V, HLines, VLines> Style<T, B, L, R, On, V, HLines, VLines> {
+ /// Removes horizontal split lines.
+ ///
+ /// Not including custom split lines.
+ pub fn remove_horizontal(
+ mut self,
+ ) -> Style<T, B, L, R, (), V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.horizontal = None;
+ self.borders.left_intersection = None;
+ self.borders.right_intersection = None;
+ self.borders.intersection = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), true, false, false);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, B, L, R, H, HLines, VLines> Style<T, B, L, R, H, On, HLines, VLines> {
+ /// Removes vertical split lines.
+ pub fn remove_vertical(
+ mut self,
+ ) -> Style<T, B, L, R, H, (), HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.vertical = None;
+ self.borders.top_intersection = None;
+ self.borders.bottom_intersection = None;
+ self.borders.intersection = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), true, false, false);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> Style<T, B, L, R, H, V, HLines, VLines> {
+ const fn new(borders: Borders<char>, horizontals: HLines, verticals: VLines) -> Self {
+ Self {
+ borders,
+ horizontals,
+ verticals,
+ _top: PhantomData,
+ _bottom: PhantomData,
+ _left: PhantomData,
+ _right: PhantomData,
+ _horizontal: PhantomData,
+ _vertical: PhantomData,
+ }
+ }
+
+ /// Return borders of a table.
+ pub const fn get_borders(&self) -> &Borders<char> {
+ &self.borders
+ }
+
+ /// Return custom horizontals which were set.
+ pub const fn get_horizontals(&self) -> &HLines {
+ &self.horizontals
+ }
+
+ /// Return custom verticals which were set.
+ pub const fn get_verticals(&self) -> &VLines {
+ &self.verticals
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, ColoredConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut ColoredConfig, dimension: &mut D) {
+ cfg.clear_theme();
+
+ cfg.set_borders(self.borders);
+
+ for hl in self.horizontals {
+ hl.change(records, cfg, dimension);
+ }
+
+ for vl in self.verticals {
+ vl.change(records, cfg, dimension);
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, CompactConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut CompactConfig, dimension: &mut D) {
+ *cfg = cfg.set_borders(self.borders);
+
+ let first_line = self.horizontals.into_iter().next();
+ if let Some(line) = first_line {
+ line.change(records, cfg, dimension);
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, CompactMultilineConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
+
+/// An iterator which limits [`Line`] influence on iterations over lines for in [`Style`].
+#[derive(Debug, Clone)]
+pub struct HorizontalLineIter<I> {
+ iter: I,
+ intersection: bool,
+ left: bool,
+ right: bool,
+}
+
+impl<I> HorizontalLineIter<I> {
+ fn new(iter: I, intersection: bool, left: bool, right: bool) -> Self {
+ Self {
+ iter,
+ intersection,
+ left,
+ right,
+ }
+ }
+}
+
+impl<I> Iterator for HorizontalLineIter<I>
+where
+ I: Iterator<Item = HorizontalLine>,
+{
+ type Item = HorizontalLine;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut hl = self.iter.next()?;
+
+ if self.intersection {
+ hl.line.intersection = None;
+ }
+
+ if self.left {
+ hl.line.connector1 = None;
+ }
+
+ if self.right {
+ hl.line.connector2 = None;
+ }
+
+ Some(hl)
+ }
+}
+
+/// An iterator which limits [`Line`] influence on iterations over lines for in [`Style`].
+#[derive(Debug, Clone)]
+pub struct VerticalLineIter<I> {
+ iter: I,
+ intersection: bool,
+ top: bool,
+ bottom: bool,
+}
+
+impl<I> VerticalLineIter<I> {
+ fn new(iter: I, intersection: bool, top: bool, bottom: bool) -> Self {
+ Self {
+ iter,
+ intersection,
+ top,
+ bottom,
+ }
+ }
+}
+
+impl<I> Iterator for VerticalLineIter<I>
+where
+ I: Iterator<Item = VerticalLine>,
+{
+ type Item = VerticalLine;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut hl = self.iter.next()?;
+
+ if self.intersection {
+ hl.line.intersection = None;
+ }
+
+ if self.top {
+ hl.line.connector1 = None;
+ }
+
+ if self.bottom {
+ hl.line.connector2 = None;
+ }
+
+ Some(hl)
+ }
+}
+
+const fn create_borders(
+ top: Line,
+ bottom: Line,
+ horizontal: Line,
+ left: Option<char>,
+ right: Option<char>,
+ vertical: Option<char>,
+) -> Borders<char> {
+ Borders {
+ top: top.main,
+ bottom: bottom.main,
+ top_left: top.connector1,
+ top_right: top.connector2,
+ bottom_left: bottom.connector1,
+ bottom_right: bottom.connector2,
+ top_intersection: top.intersection,
+ bottom_intersection: bottom.intersection,
+ left_intersection: horizontal.connector1,
+ right_intersection: horizontal.connector2,
+ horizontal: horizontal.main,
+ intersection: horizontal.intersection,
+ left,
+ right,
+ vertical,
+ }
+}
diff --git a/vendor/tabled/src/settings/style/horizontal_line.rs b/vendor/tabled/src/settings/style/horizontal_line.rs
new file mode 100644
index 000000000..bbcdc3fb4
--- /dev/null
+++ b/vendor/tabled/src/settings/style/horizontal_line.rs
@@ -0,0 +1,69 @@
+use crate::{
+ grid::config::{CompactConfig, CompactMultilineConfig},
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::config::{ColoredConfig, HorizontalLine as GridLine};
+
+use super::Line;
+
+/// A horizontal split line which can be used to set a border.
+#[cfg_attr(not(feature = "std"), allow(dead_code))]
+#[derive(Debug, Clone)]
+pub struct HorizontalLine {
+ pub(super) index: usize,
+ pub(super) line: Line,
+}
+
+impl HorizontalLine {
+ /// Creates a new horizontal split line.
+ pub const fn new(index: usize, line: Line) -> Self {
+ Self { index, line }
+ }
+
+ /// Sets a horizontal character.
+ pub const fn main(mut self, c: Option<char>) -> Self {
+ self.line.main = c;
+ self
+ }
+
+ /// Sets a vertical intersection character.
+ pub const fn intersection(mut self, c: Option<char>) -> Self {
+ self.line.intersection = c;
+ self
+ }
+
+ /// Sets a left character.
+ pub const fn left(mut self, c: Option<char>) -> Self {
+ self.line.connector1 = c;
+ self
+ }
+
+ /// Sets a right character.
+ pub const fn right(mut self, c: Option<char>) -> Self {
+ self.line.connector2 = c;
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D> TableOption<R, D, ColoredConfig> for HorizontalLine {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.insert_horizontal_line(self.index, GridLine::from(self.line))
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactConfig> for HorizontalLine {
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ if self.index == 1 {
+ *cfg = cfg.set_first_horizontal_line(papergrid::config::Line::from(self.line));
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for HorizontalLine {
+ fn change(self, records: &mut R, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
diff --git a/vendor/tabled/src/settings/style/line.rs b/vendor/tabled/src/settings/style/line.rs
new file mode 100644
index 000000000..10b3895c4
--- /dev/null
+++ b/vendor/tabled/src/settings/style/line.rs
@@ -0,0 +1,91 @@
+#[cfg(feature = "std")]
+use crate::grid::config::{HorizontalLine, VerticalLine};
+
+/// The structure represent a vertical or horizontal line.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct Line {
+ pub(crate) main: Option<char>,
+ pub(crate) intersection: Option<char>,
+ pub(crate) connector1: Option<char>,
+ pub(crate) connector2: Option<char>,
+}
+
+impl Line {
+ /// Creates a new [`Line`] object.
+ pub const fn new(
+ main: Option<char>,
+ intersection: Option<char>,
+ connector1: Option<char>,
+ connector2: Option<char>,
+ ) -> Self {
+ Self {
+ main,
+ intersection,
+ connector1,
+ connector2,
+ }
+ }
+
+ /// Creates a new [`Line`] object with all chars set.
+ pub const fn full(main: char, intersection: char, connector1: char, connector2: char) -> Self {
+ Self::new(
+ Some(main),
+ Some(intersection),
+ Some(connector1),
+ Some(connector2),
+ )
+ }
+
+ /// Creates a new [`Line`] object with all chars set to the provided one.
+ pub const fn filled(c: char) -> Self {
+ Self::full(c, c, c, c)
+ }
+
+ /// Creates a new [`Line`] object with all chars not set.
+ pub const fn empty() -> Self {
+ Self::new(None, None, None, None)
+ }
+
+ /// Checks if the line has nothing set.
+ pub const fn is_empty(&self) -> bool {
+ self.main.is_none()
+ && self.intersection.is_none()
+ && self.connector1.is_none()
+ && self.connector2.is_none()
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Line> for HorizontalLine {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main,
+ intersection: l.intersection,
+ left: l.connector1,
+ right: l.connector2,
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Line> for VerticalLine {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main,
+ intersection: l.intersection,
+ top: l.connector1,
+ bottom: l.connector2,
+ }
+ }
+}
+
+impl From<Line> for papergrid::config::Line<char> {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main.unwrap_or(' '),
+ intersection: l.intersection,
+ connect1: l.connector1,
+ connect2: l.connector2,
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/mod.rs b/vendor/tabled/src/settings/style/mod.rs
new file mode 100644
index 000000000..b8d7f688d
--- /dev/null
+++ b/vendor/tabled/src/settings/style/mod.rs
@@ -0,0 +1,127 @@
+//! This module contains a list of primitives which can be applied to change [`Table`] style.
+//!
+//! ## [`Style`]
+//!
+//! It is responsible for a table border style.
+//! An individual cell border can be set by [`Border`].
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::Style};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let mut table = Table::new(&data);
+//! table.with(Style::psql());
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! " &str \n",
+//! "-------\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`BorderText`]
+//!
+//! It's used to override a border with a custom text.
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::style::{BorderText, Style}};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let table = Table::new(&data)
+//! .with(Style::psql())
+//! .with(BorderText::new("Santa").horizontal(1))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! " &str \n",
+//! "Santa--\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`Border`]
+//!
+//! [`Border`] can be used to modify cell's borders.
+//!
+//! It's possible to set a collored border when `color` feature is on.
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::{Modify, Style}};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let table = Table::new(&data)
+//! .with(Style::psql())
+//! .with(Modify::new((0, 0)).with(Style::modern().get_frame()))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "┌───────┐\n",
+//! "│ &str │\n",
+//! "└───────┘\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`RawStyle`]
+//!
+//! A different representation of [`Style`].
+//! With no checks in place.
+//!
+//! It also contains a list of types to support colors.
+//!
+//! [`Table`]: crate::Table
+//! [`BorderText`]: crate::settings::style::BorderText
+//! [`RawStyle`]: crate::settings::style::RawStyle
+
+#[cfg(feature = "std")]
+mod border;
+#[cfg(feature = "std")]
+mod border_char;
+#[cfg(feature = "std")]
+mod border_color;
+#[cfg(feature = "std")]
+mod border_text;
+#[cfg(feature = "std")]
+mod offset;
+#[cfg(feature = "std")]
+mod raw_style;
+#[cfg(feature = "std")]
+mod span_border_correction;
+
+mod builder;
+mod horizontal_line;
+mod line;
+mod vertical_line;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use self::{
+ border::Border, border_char::BorderChar, border_color::BorderColor, border_text::BorderText,
+ offset::Offset, raw_style::RawStyle, span_border_correction::BorderSpanCorrection,
+};
+
+pub use builder::{HorizontalLineIter, On, Style, VerticalLineIter};
+pub use horizontal_line::HorizontalLine;
+pub use line::Line;
+pub use vertical_line::VerticalLine;
diff --git a/vendor/tabled/src/settings/style/offset.rs b/vendor/tabled/src/settings/style/offset.rs
new file mode 100644
index 000000000..3f8a4f85e
--- /dev/null
+++ b/vendor/tabled/src/settings/style/offset.rs
@@ -0,0 +1,19 @@
+use crate::grid::config;
+
+/// 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),
+}
+
+impl From<Offset> for config::Offset {
+ fn from(o: Offset) -> Self {
+ match o {
+ Offset::Begin(i) => config::Offset::Begin(i),
+ Offset::End(i) => config::Offset::End(i),
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/raw_style.rs b/vendor/tabled/src/settings/style/raw_style.rs
new file mode 100644
index 000000000..eedb48e79
--- /dev/null
+++ b/vendor/tabled/src/settings/style/raw_style.rs
@@ -0,0 +1,438 @@
+//! This module contains [`RawStyle`] structure, which is analogues to [`Style`] but not generic,
+//! so sometimes it can be used more conviently.
+
+use std::collections::HashMap;
+
+use crate::{
+ grid::{color::AnsiColor, config, config::Borders, config::ColoredConfig, records::Records},
+ settings::{Color, TableOption},
+};
+
+use super::{Border, HorizontalLine, Line, Style, VerticalLine};
+
+/// A raw style data, which can be produced safely from [`Style`].
+///
+/// It can be useful in order to not have a generics and be able to use it as a variable more conveniently.
+#[derive(Default, Debug, Clone)]
+pub struct RawStyle {
+ borders: Borders<char>,
+ colors: Borders<AnsiColor<'static>>,
+ horizontals: HashMap<usize, Line>,
+ verticals: HashMap<usize, Line>,
+}
+
+impl RawStyle {
+ /// Set a top border character.
+ pub fn set_top(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top = s;
+ self
+ }
+
+ /// Set a top border color.
+ pub fn set_color_top(&mut self, color: Color) -> &mut Self {
+ self.colors.top = Some(color.into());
+ self
+ }
+
+ /// Set a bottom border character.
+ pub fn set_bottom(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom = s;
+ self
+ }
+
+ /// Set a bottom border color.
+ pub fn set_color_bottom(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom = Some(color.into());
+ self
+ }
+
+ /// Set a left border character.
+ pub fn set_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.left = s;
+ self
+ }
+
+ /// Set a left border color.
+ pub fn set_color_left(&mut self, color: Color) -> &mut Self {
+ self.colors.left = Some(color.into());
+ self
+ }
+
+ /// Set a right border character.
+ pub fn set_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.right = s;
+ self
+ }
+
+ /// Set a right border color.
+ pub fn set_color_right(&mut self, color: Color) -> &mut Self {
+ self.colors.right = Some(color.into());
+ self
+ }
+
+ /// Set a top intersection character.
+ pub fn set_intersection_top(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_intersection = s;
+ self
+ }
+
+ /// Set a top intersection color.
+ pub fn set_color_intersection_top(&mut self, color: Color) -> &mut Self {
+ self.colors.top_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a bottom intersection character.
+ pub fn set_intersection_bottom(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_bottom(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a left split character.
+ pub fn set_intersection_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.left_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_left(&mut self, color: Color) -> &mut Self {
+ self.colors.left_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a right split character.
+ pub fn set_intersection_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.right_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_right(&mut self, color: Color) -> &mut Self {
+ self.colors.right_intersection = Some(color.into());
+ self
+ }
+
+ /// Set an internal character.
+ pub fn set_intersection(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection(&mut self, color: Color) -> &mut Self {
+ self.colors.intersection = Some(color.into());
+ self
+ }
+
+ /// Set a vertical character.
+ pub fn set_vertical(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.vertical = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_vertical(&mut self, color: Color) -> &mut Self {
+ self.colors.vertical = Some(color.into());
+ self
+ }
+
+ /// Set a horizontal character.
+ pub fn set_horizontal(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.horizontal = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_horizontal(&mut self, color: Color) -> &mut Self {
+ self.colors.horizontal = Some(color.into());
+ self
+ }
+
+ /// Set a character for a top left corner.
+ pub fn set_corner_top_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_left = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_top_left(&mut self, color: Color) -> &mut Self {
+ self.colors.top_left = Some(color.into());
+ self
+ }
+
+ /// Set a character for a top right corner.
+ pub fn set_corner_top_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_right = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_top_right(&mut self, color: Color) -> &mut Self {
+ self.colors.top_right = Some(color.into());
+ self
+ }
+
+ /// Set a character for a bottom left corner.
+ pub fn set_corner_bottom_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_left = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_bottom_left(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_left = Some(color.into());
+ self
+ }
+
+ /// Set a character for a bottom right corner.
+ pub fn set_corner_bottom_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_right = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_bottom_right(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_right = Some(color.into());
+ self
+ }
+
+ /// Set horizontal border lines.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::collections::HashMap;
+ /// use tabled::{Table, settings::style::{Style, Line, RawStyle}};
+ ///
+ /// let mut style = RawStyle::from(Style::re_structured_text());
+ ///
+ /// let mut lines = HashMap::new();
+ /// lines.insert(1, Style::extended().get_horizontal());
+ /// style.set_horizontals(lines);
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(style)
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// " ======= ===== \n",
+ /// " &str i32 \n",
+ /// "╠═══════╬═════╣\n",
+ /// " Hello 0 \n",
+ /// " Hello 1 \n",
+ /// " Hello 2 \n",
+ /// " ======= ===== ",
+ /// ),
+ /// )
+ /// ```
+ pub fn set_horizontals(&mut self, lines: HashMap<usize, Line>) -> &mut Self {
+ self.horizontals = lines;
+ self
+ }
+
+ /// Insert a horizontal line to a specific row location.
+ pub fn insert_horizontal(&mut self, row: usize, line: Line) -> &mut Self {
+ let _ = self.horizontals.insert(row, line);
+ self
+ }
+
+ /// Insert a horizontal line to a specific row location.
+ pub fn get_horizontal(&self, row: usize) -> Option<Line> {
+ self.horizontals.get(&row).cloned()
+ }
+
+ /// Set vertical border lines.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::collections::HashMap;
+ /// use tabled::{Table, settings::style::{Style, Line, RawStyle}};
+ ///
+ /// let mut style = RawStyle::from(Style::re_structured_text());
+ ///
+ /// let mut lines = HashMap::new();
+ /// lines.insert(1, Style::extended().get_horizontal());
+ /// style.set_verticals(lines);
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(style)
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "=======╠=====\n",
+ /// " &str ═ i32 \n",
+ /// "======= =====\n",
+ /// " Hello ═ 0 \n",
+ /// " Hello ═ 1 \n",
+ /// " Hello ═ 2 \n",
+ /// "=======╣=====",
+ /// ),
+ /// )
+ /// ```
+ pub fn set_verticals(&mut self, lines: HashMap<usize, Line>) -> &mut Self {
+ self.verticals = lines;
+ self
+ }
+
+ /// Insert a vertical line into specific column location.
+ pub fn insert_vertical(&mut self, column: usize, line: Line) -> &mut Self {
+ let _ = self.verticals.insert(column, line);
+ self
+ }
+
+ /// Get a left char.
+ pub fn get_left(&self) -> Option<char> {
+ self.borders.left
+ }
+
+ /// Get a left intersection char.
+ pub fn get_left_intersection(&self) -> Option<char> {
+ self.borders.left_intersection
+ }
+
+ /// Get a right char.
+ pub fn get_right(&self) -> Option<char> {
+ self.borders.right
+ }
+
+ /// Get a right intersection char.
+ pub fn get_right_intersection(&self) -> Option<char> {
+ self.borders.right_intersection
+ }
+
+ /// Get a top char.
+ pub fn get_top(&self) -> Option<char> {
+ self.borders.top
+ }
+
+ /// Get a top left char.
+ pub fn get_top_left(&self) -> Option<char> {
+ self.borders.top_left
+ }
+
+ /// Get a top right char.
+ pub fn get_top_right(&self) -> Option<char> {
+ self.borders.top_right
+ }
+
+ /// Get a top intersection char.
+ pub fn get_top_intersection(&self) -> Option<char> {
+ self.borders.top_intersection
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom(&self) -> Option<char> {
+ self.borders.bottom
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_left(&self) -> Option<char> {
+ self.borders.bottom_left
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_right(&self) -> Option<char> {
+ self.borders.bottom_right
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_intersection(&self) -> Option<char> {
+ self.borders.bottom_intersection
+ }
+
+ /// Returns an outer border of the style.
+ pub fn get_frame(&self) -> Border {
+ Border::from(crate::grid::config::Border {
+ top: self.borders.top,
+ bottom: self.borders.bottom,
+ left: self.borders.left,
+ right: self.borders.right,
+ left_top_corner: self.borders.top_left,
+ right_top_corner: self.borders.top_right,
+ left_bottom_corner: self.borders.bottom_left,
+ right_bottom_corner: self.borders.bottom_right,
+ })
+ }
+
+ /// Returns an general borders configuration of the style.
+ pub fn get_borders(&self) -> Borders<char> {
+ self.borders
+ }
+}
+
+impl From<Borders<char>> for RawStyle {
+ fn from(borders: Borders<char>) -> Self {
+ Self {
+ borders,
+ horizontals: HashMap::new(),
+ verticals: HashMap::new(),
+ colors: Borders::default(),
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for RawStyle
+where
+ R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dimension: &mut D) {
+ (&self).change(records, cfg, dimension)
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for &RawStyle {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.clear_theme();
+
+ cfg.set_borders(self.borders);
+
+ for (&row, line) in &self.horizontals {
+ cfg.insert_horizontal_line(row, config::HorizontalLine::from(*line));
+ }
+
+ for (&col, line) in &self.verticals {
+ cfg.insert_vertical_line(col, config::VerticalLine::from(*line));
+ }
+
+ if !self.colors.is_empty() {
+ cfg.set_borders_color(self.colors.clone());
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> From<Style<T, B, L, R, H, V, HLines, VLines>> for RawStyle
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+{
+ fn from(style: Style<T, B, L, R, H, V, HLines, VLines>) -> Self {
+ let horizontals = style
+ .get_horizontals()
+ .clone()
+ .into_iter()
+ .map(|hr| (hr.index, hr.line))
+ .collect();
+
+ let verticals = style
+ .get_verticals()
+ .clone()
+ .into_iter()
+ .map(|hr| (hr.index, hr.line))
+ .collect();
+
+ Self {
+ borders: *style.get_borders(),
+ horizontals,
+ verticals,
+ colors: Borders::default(),
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/span_border_correction.rs b/vendor/tabled/src/settings/style/span_border_correction.rs
new file mode 100644
index 000000000..665a12788
--- /dev/null
+++ b/vendor/tabled/src/settings/style/span_border_correction.rs
@@ -0,0 +1,216 @@
+//! This module contains [`BorderSpanCorrection`] structure, which can be useful when [`Span`] is used, and
+//! you want to fix the intersections symbols which are left intact by default.
+//!
+//! [`Span`]: crate::settings::span::Span
+
+use crate::{
+ grid::{
+ config::{ColoredConfig, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::TableOption,
+};
+
+/// A correctness function of style for [`Table`] which has [`Span`]s.
+///
+/// Try to fix the style when table contains spans.
+///
+/// By default [`Style`] doesn't implies any logic to better render split lines when
+/// [`Span`] is used.
+///
+/// So this function can be used to set the split lines in regard of spans used.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Modify, style::{Style, BorderSpanCorrection},
+/// Format, Span, object::Cell
+/// }
+/// };
+///
+/// let data = vec![
+/// ("09", "June", "2022"),
+/// ("10", "July", "2022"),
+/// ];
+///
+/// let mut table = Table::new(data);
+/// table.with(Modify::new((0, 0)).with("date").with(Span::column(3)));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "+----+------+------+\n",
+/// "| date |\n",
+/// "+----+------+------+\n",
+/// "| 09 | June | 2022 |\n",
+/// "+----+------+------+\n",
+/// "| 10 | July | 2022 |\n",
+/// "+----+------+------+",
+/// )
+/// );
+///
+/// table.with(BorderSpanCorrection);
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "+------------------+\n",
+/// "| date |\n",
+/// "+----+------+------+\n",
+/// "| 09 | June | 2022 |\n",
+/// "+----+------+------+\n",
+/// "| 10 | July | 2022 |\n",
+/// "+----+------+------+",
+/// )
+/// );
+/// ```
+/// See [`BorderSpanCorrection`].
+///
+/// [`Table`]: crate::Table
+/// [`Span`]: crate::settings::span::Span
+/// [`Style`]: crate::settings::Style
+/// [`Style::correct_spans`]: crate::settings::style::BorderSpanCorrection
+#[derive(Debug)]
+pub struct BorderSpanCorrection;
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderSpanCorrection
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let shape = (records.count_rows(), records.count_columns());
+ correct_span_styles(cfg, shape);
+ }
+}
+
+fn correct_span_styles(cfg: &mut SpannedConfig, shape: (usize, usize)) {
+ for ((row, c), span) in cfg.get_column_spans() {
+ for col in c..c + span {
+ if col == 0 {
+ continue;
+ }
+
+ let is_first = col == c;
+ let has_up = row > 0 && has_left(cfg, (row - 1, col), shape);
+ let has_down = row + 1 < shape.0 && has_left(cfg, (row + 1, col), shape);
+
+ let borders = cfg.get_borders();
+
+ let mut border = cfg.get_border((row, col), shape);
+
+ let has_top_border = border.left_top_corner.is_some() && border.top.is_some();
+ if has_top_border {
+ if has_up && is_first {
+ border.left_top_corner = borders.intersection;
+ } else if has_up {
+ border.left_top_corner = borders.bottom_intersection;
+ } else if is_first {
+ border.left_top_corner = borders.top_intersection;
+ } else {
+ border.left_top_corner = border.top;
+ }
+ }
+
+ let has_bottom_border = border.left_bottom_corner.is_some() && border.bottom.is_some();
+ if has_bottom_border {
+ if has_down && is_first {
+ border.left_bottom_corner = borders.intersection;
+ } else if has_down {
+ border.left_bottom_corner = borders.top_intersection;
+ } else if is_first {
+ border.left_bottom_corner = borders.bottom_intersection;
+ } else {
+ border.left_bottom_corner = border.bottom;
+ }
+ }
+
+ cfg.set_border((row, col), border);
+ }
+ }
+
+ for ((r, col), span) in cfg.get_row_spans() {
+ for row in r + 1..r + span {
+ let mut border = cfg.get_border((row, col), shape);
+ let borders = cfg.get_borders();
+
+ let has_left_border = border.left_top_corner.is_some();
+ if has_left_border {
+ let has_left = col > 0 && has_top(cfg, (row, col - 1), shape);
+ if has_left {
+ border.left_top_corner = borders.right_intersection;
+ } else {
+ border.left_top_corner = borders.vertical;
+ }
+ }
+
+ let has_right_border = border.right_top_corner.is_some();
+ if has_right_border {
+ let has_right = col + 1 < shape.1 && has_top(cfg, (row, col + 1), shape);
+ if has_right {
+ border.right_top_corner = borders.left_intersection;
+ } else {
+ border.right_top_corner = borders.vertical;
+ }
+ }
+
+ cfg.set_border((row, col), border);
+ }
+ }
+
+ let cells = iter_totaly_spanned_cells(cfg, shape).collect::<Vec<_>>();
+ for (row, col) in cells {
+ if row == 0 {
+ continue;
+ }
+
+ let mut border = cfg.get_border((row, col), shape);
+ let borders = cfg.get_borders();
+
+ let has_right = col + 1 < shape.1 && has_top(cfg, (row, col + 1), shape);
+ let has_up = has_left(cfg, (row - 1, col), shape);
+ if has_up && !has_right {
+ border.right_top_corner = borders.right_intersection;
+ }
+
+ let has_down = row + 1 < shape.0 && has_left(cfg, (row + 1, col), shape);
+ if has_down {
+ border.left_bottom_corner = borders.top_intersection;
+ }
+
+ cfg.set_border((row, col), border);
+ }
+}
+
+fn has_left(cfg: &SpannedConfig, pos: Position, shape: (usize, usize)) -> bool {
+ if cfg.is_cell_covered_by_both_spans(pos) || cfg.is_cell_covered_by_column_span(pos) {
+ return false;
+ }
+
+ let border = cfg.get_border(pos, shape);
+ border.left.is_some() || border.left_top_corner.is_some() || border.left_bottom_corner.is_some()
+}
+
+fn has_top(cfg: &SpannedConfig, pos: Position, shape: (usize, usize)) -> bool {
+ if cfg.is_cell_covered_by_both_spans(pos) || cfg.is_cell_covered_by_row_span(pos) {
+ return false;
+ }
+
+ let border = cfg.get_border(pos, shape);
+ border.top.is_some() || border.left_top_corner.is_some() || border.right_top_corner.is_some()
+}
+
+fn iter_totaly_spanned_cells(
+ cfg: &SpannedConfig,
+ shape: (usize, usize),
+) -> impl Iterator<Item = Position> + '_ {
+ // todo: can be optimized
+ let (count_rows, count_cols) = shape;
+ (0..count_rows).flat_map(move |row| {
+ (0..count_cols)
+ .map(move |col| (row, col))
+ .filter(move |&p| cfg.is_cell_covered_by_both_spans(p))
+ })
+}
diff --git a/vendor/tabled/src/settings/style/vertical_line.rs b/vendor/tabled/src/settings/style/vertical_line.rs
new file mode 100644
index 000000000..adbef2c82
--- /dev/null
+++ b/vendor/tabled/src/settings/style/vertical_line.rs
@@ -0,0 +1,50 @@
+#[cfg(feature = "std")]
+use crate::grid::config::{ColoredConfig, VerticalLine as VLine};
+
+use super::Line;
+
+/// A horizontal split line which can be used to set a border.
+#[cfg_attr(not(feature = "std"), allow(dead_code))]
+#[derive(Debug, Clone)]
+pub struct VerticalLine {
+ pub(crate) index: usize,
+ pub(crate) line: Line,
+}
+
+impl VerticalLine {
+ /// Creates a new horizontal split line.
+ pub const fn new(index: usize, line: Line) -> Self {
+ Self { index, line }
+ }
+
+ /// Sets a horizontal character.
+ pub const fn main(mut self, c: Option<char>) -> Self {
+ self.line.main = c;
+ self
+ }
+
+ /// Sets a vertical intersection character.
+ pub const fn intersection(mut self, c: Option<char>) -> Self {
+ self.line.intersection = c;
+ self
+ }
+
+ /// Sets a top character.
+ pub const fn top(mut self, c: Option<char>) -> Self {
+ self.line.connector1 = c;
+ self
+ }
+
+ /// Sets a bottom character.
+ pub const fn bottom(mut self, c: Option<char>) -> Self {
+ self.line.connector2 = c;
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D> crate::settings::TableOption<R, D, ColoredConfig> for VerticalLine {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.insert_vertical_line(self.index, VLine::from(self.line));
+ }
+}