blob: d55c400cc265f86ddfb1d0b2e733792f9a6932b9 [file] [log] [blame]
//! 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,
}
}