//! 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, VLines = VLineArray<0>> { borders: Borders, horizontals: HLines, verticals: VLines, _top: PhantomData, _bottom: PhantomData, _left: PhantomData, _right: PhantomData, _horizontal: PhantomData, _vertical: PhantomData, } type HLineArray = [HorizontalLine; N]; type VLineArray = [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 { 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 { 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> { 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> { 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 { 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 { 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> { 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 { Style::new( create_borders( Line::full('-', '-', '.', '.'), Line::full('-', '-', '\'', '\''), Line::empty(), Some('|'), Some('|'), Some('|'), ), [], [], ) } } impl Style { /// 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 where for<'a> &'a mut VLines: IntoIterator, { 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 where for<'a> &'a mut VLines: IntoIterator, { 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 where for<'a> &'a mut HLines: IntoIterator, { 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 where for<'a> &'a mut HLines: IntoIterator, { 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 where for<'a> &'a mut VLines: IntoIterator, { 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 where for<'a> &'a mut HLines: IntoIterator, { 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(self, lines: NewLines) -> Style where NewLines: IntoIterator + 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(self, lines: NewLines) -> Style where NewLines: IntoIterator + Clone, { Style::new(self.borders, self.horizontals, lines) } /// Removes all horizontal lines set by [`Style::horizontals`] pub fn remove_horizontals(self) -> Style, VLines> { Style::new(self.borders, [], self.verticals) } /// Removes all verticals lines set by [`Style::verticals`] pub fn remove_verticals(self) -> Style> { Style::new(self.borders, self.horizontals, []) } } impl Style { /// 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 Style { /// 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 Style { /// 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 Style { /// 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 Style { /// 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 Style { /// 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 Style { /// 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 Style { /// 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 Style { /// 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 Style { /// Removes top border. pub fn remove_top( mut self, ) -> Style<(), B, L, R, H, V, HLines, VerticalLineIter> where VLines: IntoIterator + 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 Style { /// Removes bottom border. pub fn remove_bottom( mut self, ) -> Style> where VLines: IntoIterator + 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 Style { /// Removes left border. pub fn remove_left( mut self, ) -> Style, VLines> where HLines: IntoIterator + 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 Style { /// Removes right border. pub fn remove_right( mut self, ) -> Style, VLines> where HLines: IntoIterator + 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 Style { /// Removes horizontal split lines. /// /// Not including custom split lines. pub fn remove_horizontal( mut self, ) -> Style> where VLines: IntoIterator + 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 Style { /// Removes vertical split lines. pub fn remove_vertical( mut self, ) -> Style, VLines> where HLines: IntoIterator + 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 Style { const fn new(borders: Borders, 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 { &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 TableOption for Style where HLines: IntoIterator + Clone, VLines: IntoIterator + 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 TableOption for Style where HLines: IntoIterator + 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 TableOption for Style where HLines: IntoIterator + 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 { iter: I, intersection: bool, left: bool, right: bool, } impl HorizontalLineIter { fn new(iter: I, intersection: bool, left: bool, right: bool) -> Self { Self { iter, intersection, left, right, } } } impl Iterator for HorizontalLineIter where I: Iterator, { type Item = HorizontalLine; fn next(&mut self) -> Option { 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 { iter: I, intersection: bool, top: bool, bottom: bool, } impl VerticalLineIter { fn new(iter: I, intersection: bool, top: bool, bottom: bool) -> Self { Self { iter, intersection, top, bottom, } } } impl Iterator for VerticalLineIter where I: Iterator, { type Item = VerticalLine; fn next(&mut self) -> Option { 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, right: Option, vertical: Option, ) -> Borders { 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, } }