tabled/features/style/
style.rs

1//! This module contains a list of primitives which can be applied to change [`Table`] style.
2//!
3//! ## [`Style`]
4//!
5//! It is responsible for a table border style.
6//! An individual cell border can be set by [`Border`].
7//!  
8//! ### Example
9//!
10//! ```
11//! use tabled::{Table, Style};
12//!
13//! let data = vec!["Hello", "2022"];
14//! let mut table = Table::new(&data);
15//! table.with(Style::psql());
16//!
17//! assert_eq!(
18//!     table.to_string(),
19//!     concat!(
20//!         " &str  \n",
21//!         "-------\n",
22//!         " Hello \n",
23//!         " 2022  ",
24//!     )
25//! )
26//! ```
27//!
28//! ## [`BorderText`]
29//!
30//! It's used to override a border with a custom text.
31//!
32//! ### Example
33//!
34//! ```
35//! use tabled::{Table, BorderText, Style};
36//!
37//! let data = vec!["Hello", "2022"];
38//! let table = Table::new(&data)
39//!     .with(Style::psql())
40//!     .with(BorderText::new(1, "Santa"))
41//!     .to_string();
42//!
43//! assert_eq!(
44//!     table,
45//!     concat!(
46//!         " &str  \n",
47//!         "Santa--\n",
48//!         " Hello \n",
49//!         " 2022  ",
50//!     )
51//! )
52//! ```
53//!
54//! ## [`Border`]
55//!
56//! [`Border`] can be used to modify cell's borders.
57//!
58//! It's possible to set a collored border when `color` feature is on.
59//!
60//! ### Example
61//!
62//! ```
63//! use tabled::{Table, Style, Modify, object::Cell};
64//!
65//! let data = vec!["Hello", "2022"];
66//! let table = Table::new(&data)
67//!     .with(Style::psql())
68//!     .with(Modify::new(Cell(0, 0)).with(Style::modern().get_frame()))
69//!     .to_string();
70//!
71//! assert_eq!(
72//!     table,
73//!     concat!(
74//!         "┌───────┐\n",
75//!         "│ &str  │\n",
76//!         "└───────┘\n",
77//!         "  Hello  \n",
78//!         "  2022   ",
79//!     )
80//! )
81//! ```
82//!
83//! ## [`RawStyle`]
84//!
85//! A different representatio of [`Style`].
86//! With no checks in place.
87//!
88//! It also contains a list of types to support colors.
89//!
90//! [`Table`]: crate::Table
91//! [`BorderText`]: crate::border_text::BorderText
92//! [`RawStyle`]: crate::raw_style::RawStyle
93
94use std::marker::PhantomData;
95
96use papergrid::{records::Records, Borders};
97
98use crate::{style::StyleCorrectSpan, Border, Table, TableOption};
99
100use super::{HorizontalLine, Line, VerticalLine};
101
102/// Style is represents a theme of a [`Table`].
103///
104/// It tries to limit an controlling a valid state of it.
105/// It doesn't allow to call method [`Style::top_left_corner`] unless [`Style::left`] and [`Style::top`] is set.
106///
107/// You can turn [`Style`] into [`RawStyle`] to have more controll using [`Into`] implementation.
108///
109/// # Example
110///
111/// ```rust,no_run
112/// use tabled::{Table, Style};
113///
114/// let style = Style::ascii()
115///                 .bottom('*')
116///                 .inner_intersection(' ');
117///
118/// let data = vec!["Hello", "2021"];
119/// let table = Table::new(&data).with(style).to_string();
120///
121/// println!("{}", table);
122/// ```
123///
124/// [`Table`]: crate::Table
125/// [`RawStyle`]: crate::style::RawStyle
126#[derive(Debug, Clone)]
127pub struct Style<T, B, L, R, H, V, HLines = HLineArray<0>, VLines = VLineArray<0>> {
128    pub(crate) borders: Borders<char>,
129    pub(crate) horizontals: HLines,
130    pub(crate) verticals: VLines,
131    _top: PhantomData<T>,
132    _bottom: PhantomData<B>,
133    _left: PhantomData<L>,
134    _right: PhantomData<R>,
135    _horizontal: PhantomData<H>,
136    _vertical: PhantomData<V>,
137}
138
139type HLineArray<const N: usize> = [HorizontalLine; N];
140
141type VLineArray<const N: usize> = [VerticalLine; N];
142
143/// A marker struct which is used in [`Style`].
144#[derive(Debug, Clone)]
145pub struct On;
146
147impl Style<(), (), (), (), (), (), (), ()> {
148    /// This style is a style with no styling options on,
149    ///
150    /// ```text
151    ///      id  destribution            link
152    ///      0      Fedora      https://getfedora.org/
153    ///      2     OpenSUSE    https://www.opensuse.org/
154    ///      3   Endeavouros   https://endeavouros.com/
155    /// ```
156    ///
157    /// Note: The cells in the example have 1-left and 1-right indent.
158    ///
159    /// This style can be used as a base style to build a custom one.
160    ///
161    /// ```rust,no_run
162    /// # use tabled::Style;
163    /// let style = Style::empty()
164    ///     .top('*')
165    ///     .bottom('*')
166    ///     .vertical('#')
167    ///     .bottom_intersection('^')
168    ///     .top_intersection('*');
169    /// ```
170    pub const fn empty() -> Style<(), (), (), (), (), ()> {
171        Style::new(
172            create_borders(
173                Line::empty(),
174                Line::empty(),
175                Line::empty(),
176                None,
177                None,
178                None,
179            ),
180            [],
181            [],
182        )
183    }
184
185    /// This style is analog of `empty` but with a vertical space(' ') line.
186    ///
187    /// ```text
188    ///      id   destribution             link
189    ///      0       Fedora       https://getfedora.org/
190    ///      2      OpenSUSE     https://www.opensuse.org/
191    ///      3    Endeavouros    https://endeavouros.com/
192    /// ```
193    pub const fn blank() -> Style<(), (), (), (), (), On> {
194        Style::new(
195            create_borders(
196                Line::empty(),
197                Line::empty(),
198                Line::empty(),
199                None,
200                None,
201                Some(' '),
202            ),
203            [],
204            [],
205        )
206    }
207
208    /// This is a style which relays only on ASCII charset.
209    ///
210    /// It has horizontal and vertical lines.
211    ///
212    /// ```text
213    ///     +----+--------------+---------------------------+
214    ///     | id | destribution |           link            |
215    ///     +----+--------------+---------------------------+
216    ///     | 0  |    Fedora    |  https://getfedora.org/   |
217    ///     +----+--------------+---------------------------+
218    ///     | 2  |   OpenSUSE   | https://www.opensuse.org/ |
219    ///     +----+--------------+---------------------------+
220    ///     | 3  | Endeavouros  | https://endeavouros.com/  |
221    ///     +----+--------------+---------------------------+
222    /// ```
223    pub const fn ascii() -> Style<On, On, On, On, On, On> {
224        Style::new(
225            create_borders(
226                Line::full('-', '+', '+', '+'),
227                Line::full('-', '+', '+', '+'),
228                Line::full('-', '+', '+', '+'),
229                Some('|'),
230                Some('|'),
231                Some('|'),
232            ),
233            [],
234            [],
235        )
236    }
237
238    /// `psql` style looks like a table style `PostgreSQL` uses.
239    ///
240    /// It has only 1 horizontal line which splits header.
241    /// And no left and right vertical lines.
242    ///
243    /// ```text
244    ///      id | destribution |           link
245    ///     ----+--------------+---------------------------
246    ///      0  |    Fedora    |  https://getfedora.org/
247    ///      2  |   OpenSUSE   | https://www.opensuse.org/
248    ///      3  | Endeavouros  | https://endeavouros.com/
249    /// ```
250    pub const fn psql() -> Style<(), (), (), (), (), On, HLineArray<1>> {
251        Style::new(
252            create_borders(
253                Line::empty(),
254                Line::empty(),
255                Line::empty(),
256                None,
257                None,
258                Some('|'),
259            ),
260            [HorizontalLine::new(1, Line::empty())
261                .main(Some('-'))
262                .intersection(Some('+'))],
263            [],
264        )
265    }
266
267    /// `markdown` style mimics a `Markdown` table style.
268    ///
269    /// ```text
270    ///     | id | destribution |           link            |
271    ///     |----|--------------|---------------------------|
272    ///     | 0  |    Fedora    |  https://getfedora.org/   |
273    ///     | 2  |   OpenSUSE   | https://www.opensuse.org/ |
274    ///     | 3  | Endeavouros  | https://endeavouros.com/  |
275    /// ```
276    pub const fn markdown() -> Style<(), (), On, On, (), On, HLineArray<1>> {
277        Style::new(
278            create_borders(
279                Line::empty(),
280                Line::empty(),
281                Line::empty(),
282                Some('|'),
283                Some('|'),
284                Some('|'),
285            ),
286            [HorizontalLine::new(1, Line::full('-', '|', '|', '|'))],
287            [],
288        )
289    }
290
291    /// This style is analog of [`Style::ascii`] which uses UTF-8 charset.
292    ///
293    /// It has vertical and horizontal split lines.
294    ///
295    /// ```text
296    ///     ┌────┬──────────────┬───────────────────────────┐
297    ///     │ id │ destribution │           link            │
298    ///     ├────┼──────────────┼───────────────────────────┤
299    ///     │ 0  │    Fedora    │  https://getfedora.org/   │
300    ///     ├────┼──────────────┼───────────────────────────┤
301    ///     │ 2  │   OpenSUSE   │ https://www.opensuse.org/ │
302    ///     ├────┼──────────────┼───────────────────────────┤
303    ///     │ 3  │ Endeavouros  │ https://endeavouros.com/  │
304    ///     └────┴──────────────┴───────────────────────────┘
305    /// ```
306    pub const fn modern() -> Style<On, On, On, On, On, On> {
307        Style::new(
308            create_borders(
309                Line::full('─', '┬', '┌', '┐'),
310                Line::full('─', '┴', '└', '┘'),
311                Line::full('─', '┼', '├', '┤'),
312                Some('│'),
313                Some('│'),
314                Some('│'),
315            ),
316            [],
317            [],
318        )
319    }
320
321    /// This style looks like a [`Style::modern`] but without horozizontal lines except a header.
322    ///
323    /// Beware: It uses UTF-8 characters.
324    ///
325    /// ```text
326    ///     ┌────┬──────────────┬───────────────────────────┐
327    ///     │ id │ destribution │           link            │
328    ///     ├────┼──────────────┼───────────────────────────┤
329    ///     │ 0  │    Fedora    │  https://getfedora.org/   │
330    ///     │ 2  │   OpenSUSE   │ https://www.opensuse.org/ │
331    ///     │ 3  │ Endeavouros  │ https://endeavouros.com/  │
332    ///     └────┴──────────────┴───────────────────────────┘
333    /// ```
334    pub const fn sharp() -> Style<On, On, On, On, (), On, HLineArray<1>> {
335        Style::new(
336            create_borders(
337                Line::full('─', '┬', '┌', '┐'),
338                Line::full('─', '┴', '└', '┘'),
339                Line::empty(),
340                Some('│'),
341                Some('│'),
342                Some('│'),
343            ),
344            [HorizontalLine::new(1, Line::full('─', '┼', '├', '┤'))],
345            [],
346        )
347    }
348
349    /// This style looks like a [`Style::sharp`] but with rounded corners.
350    ///
351    /// Beware: It uses UTF-8 characters.
352    ///
353    /// ```text
354    ///     ╭────┬──────────────┬───────────────────────────╮
355    ///     │ id │ destribution │           link            │
356    ///     ├────┼──────────────┼───────────────────────────┤
357    ///     │ 0  │    Fedora    │  https://getfedora.org/   │
358    ///     │ 2  │   OpenSUSE   │ https://www.opensuse.org/ │
359    ///     │ 3  │ Endeavouros  │ https://endeavouros.com/  │
360    ///     ╰────┴──────────────┴───────────────────────────╯
361    /// ```
362    pub const fn rounded() -> Style<On, On, On, On, (), On, HLineArray<1>> {
363        Style::new(
364            create_borders(
365                Line::full('─', '┬', '╭', '╮'),
366                Line::full('─', '┴', '╰', '╯'),
367                Line::empty(),
368                Some('│'),
369                Some('│'),
370                Some('│'),
371            ),
372            [HorizontalLine::new(1, Line::full('─', '┼', '├', '┤'))],
373            [],
374        )
375    }
376
377    /// This style uses a chars which resembles '2 lines'.
378    ///
379    /// Beware: It uses UTF8 characters.
380    ///
381    /// ```text
382    ///     ╔════╦══════════════╦═══════════════════════════╗
383    ///     ║ id ║ destribution ║           link            ║
384    ///     ╠════╬══════════════╬═══════════════════════════╣
385    ///     ║ 0  ║    Fedora    ║  https://getfedora.org/   ║
386    ///     ╠════╬══════════════╬═══════════════════════════╣
387    ///     ║ 2  ║   OpenSUSE   ║ https://www.opensuse.org/ ║
388    ///     ╠════╬══════════════╬═══════════════════════════╣
389    ///     ║ 3  ║ Endeavouros  ║ https://endeavouros.com/  ║
390    ///     ╚════╩══════════════╩═══════════════════════════╝
391    /// ```
392    pub const fn extended() -> Style<On, On, On, On, On, On> {
393        Style::new(
394            create_borders(
395                Line::full('═', '╦', '╔', '╗'),
396                Line::full('═', '╩', '╚', '╝'),
397                Line::full('═', '╬', '╠', '╣'),
398                Some('║'),
399                Some('║'),
400                Some('║'),
401            ),
402            [],
403            [],
404        )
405    }
406
407    /// This is a style uses only '.' and ':' chars.
408    /// It has a vertical and horizontal split lines.
409    ///
410    /// ```text
411    ///     .................................................
412    ///     : id : destribution :           link            :
413    ///     :....:..............:...........................:
414    ///     : 0  :    Fedora    :  https://getfedora.org/   :
415    ///     :....:..............:...........................:
416    ///     : 2  :   OpenSUSE   : https://www.opensuse.org/ :
417    ///     :....:..............:...........................:
418    ///     : 3  : Endeavouros  : https://endeavouros.com/  :
419    ///     :....:..............:...........................:
420    /// ```
421    pub const fn dots() -> Style<On, On, On, On, On, On> {
422        Style::new(
423            create_borders(
424                Line::full('.', '.', '.', '.'),
425                Line::full('.', ':', ':', ':'),
426                Line::full('.', ':', ':', ':'),
427                Some(':'),
428                Some(':'),
429                Some(':'),
430            ),
431            [],
432            [],
433        )
434    }
435
436    /// This style is one of table views in `ReStructuredText`.
437    ///
438    /// ```text
439    ///     ==== ============== ===========================
440    ///      id   destribution             link            
441    ///     ==== ============== ===========================
442    ///      0       Fedora       https://getfedora.org/   
443    ///      2      OpenSUSE     https://www.opensuse.org/
444    ///      3    Endeavouros    https://endeavouros.com/  
445    ///     ==== ============== ===========================
446    /// ```
447    pub const fn re_structured_text() -> Style<On, On, (), (), (), On, HLineArray<1>> {
448        Style::new(
449            create_borders(
450                Line::new(Some('='), Some(' '), None, None),
451                Line::new(Some('='), Some(' '), None, None),
452                Line::empty(),
453                None,
454                None,
455                Some(' '),
456            ),
457            [HorizontalLine::new(
458                1,
459                Line::new(Some('='), Some(' '), None, None),
460            )],
461            [],
462        )
463    }
464
465    /// This is a theme analog of [`Style::rounded`], but in using ascii charset and
466    /// with no horizontal lines.
467    ///
468    /// ```text
469    ///     .-----------------------------------------------.
470    ///     | id | destribution |           link            |
471    ///     | 0  |    Fedora    |  https://getfedora.org/   |
472    ///     | 2  |   OpenSUSE   | https://www.opensuse.org/ |
473    ///     | 3  | Endeavouros  | https://endeavouros.com/  |
474    ///     '-----------------------------------------------'
475    /// ```
476    pub const fn ascii_rounded() -> Style<On, On, On, On, (), On> {
477        Style::new(
478            create_borders(
479                Line::full('-', '-', '.', '.'),
480                Line::full('-', '-', '\'', '\''),
481                Line::empty(),
482                Some('|'),
483                Some('|'),
484                Some('|'),
485            ),
486            [],
487            [],
488        )
489    }
490
491    /// Try to fix the style when table contains spans.
492    ///
493    /// By default [`Style`] doesn't implies any logic to better render split lines when
494    /// [`Span`] is used.
495    ///
496    /// So this function can be used to set the split lines in regard of spans used.
497    ///
498    /// # Example
499    ///
500    /// ```
501    /// use tabled::{TableIteratorExt, Style, Modify, format::Format, Span, object::Cell};
502    ///
503    /// let data = vec![
504    ///     ("09", "June", "2022"),
505    ///     ("10", "July", "2022"),
506    /// ];
507    ///
508    /// let mut table = data.table();
509    /// table
510    ///     .with(
511    ///         Modify::new(Cell(0, 0))
512    ///             .with(Format::new(|_| String::from("date")))
513    ///             .with(Span::column(3))
514    ///     );
515    ///
516    /// assert_eq!(
517    ///     table.to_string(),
518    ///     concat!(
519    ///         "+----+------+------+\n",
520    ///         "| date             |\n",
521    ///         "+----+------+------+\n",
522    ///         "| 09 | June | 2022 |\n",
523    ///         "+----+------+------+\n",
524    ///         "| 10 | July | 2022 |\n",
525    ///         "+----+------+------+",
526    ///     )
527    /// );
528    ///
529    /// table.with(Style::correct_spans());
530    ///
531    /// assert_eq!(
532    ///     table.to_string(),
533    ///     concat!(
534    ///         "+------------------+\n",
535    ///         "| date             |\n",
536    ///         "+----+------+------+\n",
537    ///         "| 09 | June | 2022 |\n",
538    ///         "+----+------+------+\n",
539    ///         "| 10 | July | 2022 |\n",
540    ///         "+----+------+------+",
541    ///     )
542    /// );
543    /// ```
544    ///
545    /// [`Span`]: crate::Span
546    pub const fn correct_spans() -> StyleCorrectSpan {
547        StyleCorrectSpan
548    }
549}
550
551impl<T, B, L, R, H, V, HLines, VLines> Style<T, B, L, R, H, V, HLines, VLines> {
552    /// Frame function returns a frame as a border.
553    ///
554    /// # Example
555    ///
556    /// ```
557    /// use tabled::{Table, Style, Highlight, object::Rows};
558    ///
559    /// let data = [["10:52:19", "Hello"], ["10:52:20", "World"]];
560    /// let mut table = Table::new(data);
561    /// table.with(Highlight::new(Rows::first(), Style::modern().get_frame()));
562    ///
563    /// assert_eq!(
564    ///     table.to_string(),
565    ///     concat!(
566    ///         "┌──────────────────┐\n",
567    ///         "│ 0        | 1     │\n",
568    ///         "└──────────────────┘\n",
569    ///         "| 10:52:19 | Hello |\n",
570    ///         "+----------+-------+\n",
571    ///         "| 10:52:20 | World |\n",
572    ///         "+----------+-------+",
573    ///     )
574    /// );
575    /// ```
576    pub const fn get_frame(&self) -> Border {
577        Border::new_raw(Some(papergrid::Border {
578            top: self.borders.top,
579            bottom: self.borders.bottom,
580            left: self.borders.vertical_left,
581            right: self.borders.vertical_right,
582            left_top_corner: self.borders.top_left,
583            right_top_corner: self.borders.top_right,
584            left_bottom_corner: self.borders.bottom_left,
585            right_bottom_corner: self.borders.bottom_right,
586        }))
587    }
588
589    /// Get a [`Style`]'s default horizontal line.
590    ///
591    /// It doesn't return an overloaded line via [`Style::horizontals`].
592    ///
593    /// # Example
594    ///
595    /// ```
596    /// use tabled::{style::{Style, HorizontalLine, Line}, TableIteratorExt};
597    ///
598    /// let table = (0..3)
599    ///    .map(|i| ("Hello", "World", i))
600    ///    .table()
601    ///    .with(Style::ascii().off_horizontal().horizontals([HorizontalLine::new(1, Style::modern().get_horizontal())]))
602    ///    .to_string();
603    ///
604    /// assert_eq!(
605    ///     table,
606    ///     concat!(
607    ///         "+-------+-------+-----+\n",
608    ///         "| &str  | &str  | i32 |\n",
609    ///         "├───────┼───────┼─────┤\n",
610    ///         "| Hello | World | 0   |\n",
611    ///         "| Hello | World | 1   |\n",
612    ///         "| Hello | World | 2   |\n",
613    ///         "+-------+-------+-----+",
614    ///     )
615    /// )
616    /// ```
617    pub const fn get_horizontal(&self) -> Line {
618        Line::new(
619            self.borders.horizontal,
620            self.borders.intersection,
621            self.borders.horizontal_left,
622            self.borders.horizontal_right,
623        )
624    }
625
626    /// Get a [`Style`]'s default horizontal line.
627    ///
628    /// It doesn't return an overloaded line via [`Style::verticals`].
629    ///
630    /// # Example
631    ///
632    /// ```
633    /// use tabled::{style::{Style, VerticalLine, Line}, TableIteratorExt};
634    ///
635    /// let table = (0..3)
636    ///    .map(|i| ("Hello", "World", i))
637    ///    .table()
638    ///    .with(Style::ascii().off_horizontal().verticals([VerticalLine::new(1, Style::modern().get_vertical())]))
639    ///    .to_string();
640    ///
641    /// assert_eq!(
642    ///     table,
643    ///     concat!(
644    ///         "+-------┬-------+-----+\n",
645    ///         "| &str  │ &str  | i32 |\n",
646    ///         "| Hello │ World | 0   |\n",
647    ///         "| Hello │ World | 1   |\n",
648    ///         "| Hello │ World | 2   |\n",
649    ///         "+-------┴-------+-----+",
650    ///     )
651    /// )
652    /// ```
653    pub const fn get_vertical(&self) -> Line {
654        Line::new(
655            self.borders.vertical,
656            self.borders.intersection,
657            self.borders.top_intersection,
658            self.borders.bottom_intersection,
659        )
660    }
661
662    /// Sets a top border.
663    ///
664    /// Any corners and intersections which were set will be overridden.
665    pub fn top(mut self, c: char) -> Style<On, B, L, R, H, V, HLines, VLines>
666    where
667        for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
668    {
669        self.borders.top = Some(c);
670
671        if self.borders.has_left() {
672            self.borders.top_left = Some(c);
673        }
674
675        if self.borders.has_right() {
676            self.borders.top_right = Some(c);
677        }
678
679        if self.borders.has_vertical() {
680            self.borders.top_intersection = Some(c);
681        }
682
683        for vl in &mut self.verticals {
684            if let Some(line) = &mut vl.line {
685                line.connector1 = Some(c);
686            }
687        }
688
689        Style::new(self.borders, self.horizontals, self.verticals)
690    }
691
692    /// Sets a bottom border.
693    ///
694    /// Any corners and intersections which were set will be overridden.
695    pub fn bottom(mut self, c: char) -> Style<T, On, L, R, H, V, HLines, VLines>
696    where
697        for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
698    {
699        self.borders.bottom = Some(c);
700
701        if self.borders.has_left() {
702            self.borders.bottom_left = Some(c);
703        }
704
705        if self.borders.has_right() {
706            self.borders.bottom_right = Some(c);
707        }
708
709        if self.borders.has_vertical() {
710            self.borders.bottom_intersection = Some(c);
711        }
712
713        for vl in &mut self.verticals {
714            if let Some(line) = &mut vl.line {
715                line.connector2 = Some(c);
716            }
717        }
718
719        Style::new(self.borders, self.horizontals, self.verticals)
720    }
721
722    /// Sets a left border.
723    ///
724    /// Any corners and intersections which were set will be overridden.
725    pub fn left(mut self, c: char) -> Style<T, B, On, R, H, V, HLines, VLines>
726    where
727        for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
728    {
729        self.borders.vertical_left = Some(c);
730
731        if self.borders.has_top() {
732            self.borders.top_left = Some(c);
733        }
734
735        if self.borders.has_bottom() {
736            self.borders.bottom_left = Some(c);
737        }
738
739        if self.borders.has_horizontal() {
740            self.borders.horizontal_left = Some(c);
741        }
742
743        for hl in &mut self.horizontals {
744            if let Some(line) = &mut hl.line {
745                line.connector1 = Some(c);
746            }
747        }
748
749        Style::new(self.borders, self.horizontals, self.verticals)
750    }
751
752    /// Sets a right border.
753    ///
754    /// Any corners and intersections which were set will be overridden.
755    pub fn right(mut self, c: char) -> Style<T, B, L, On, H, V, HLines, VLines>
756    where
757        for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
758    {
759        self.borders.vertical_right = Some(c);
760
761        if self.borders.has_top() {
762            self.borders.top_right = Some(c);
763        }
764
765        if self.borders.has_bottom() {
766            self.borders.bottom_right = Some(c);
767        }
768
769        if self.borders.has_horizontal() {
770            self.borders.horizontal_right = Some(c);
771        }
772
773        for hl in &mut self.horizontals {
774            if let Some(line) = &mut hl.line {
775                line.connector2 = Some(c);
776            }
777        }
778
779        Style::new(self.borders, self.horizontals, self.verticals)
780    }
781
782    /// Sets a horizontal split line.
783    ///
784    /// Any corners and intersections which were set will be overridden.
785    pub fn horizontal(mut self, c: char) -> Style<T, B, L, R, On, V, HLines, VLines>
786    where
787        for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
788    {
789        self.borders.horizontal = Some(c);
790
791        if self.borders.has_vertical() {
792            self.borders.intersection = Some(c);
793        }
794
795        if self.borders.has_left() {
796            self.borders.horizontal_left = Some(c);
797        }
798
799        if self.borders.has_right() {
800            self.borders.horizontal_right = Some(c);
801        }
802
803        for vl in &mut self.verticals {
804            if let Some(line) = &mut vl.line {
805                line.intersection = Some(c);
806            }
807        }
808
809        Style::new(self.borders, self.horizontals, self.verticals)
810    }
811
812    /// Sets a vertical split line.
813    ///
814    /// Any corners and intersections which were set will be overridden.
815    pub fn vertical(mut self, c: char) -> Style<T, B, L, R, H, On, HLines, VLines>
816    where
817        for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
818    {
819        self.borders.vertical = Some(c);
820
821        if self.borders.has_horizontal() {
822            self.borders.intersection = Some(c);
823        }
824
825        if self.borders.has_top() {
826            self.borders.top_intersection = Some(c);
827        }
828
829        if self.borders.has_bottom() {
830            self.borders.bottom_intersection = Some(c);
831        }
832
833        for hl in &mut self.horizontals {
834            if let Some(line) = &mut hl.line {
835                line.intersection = Some(c);
836            }
837        }
838
839        Style::new(self.borders, self.horizontals, self.verticals)
840    }
841
842    /// Set border horizontal lines.
843    ///
844    /// # Example
845    ///
846    /// ```
847    /// use tabled::{style::{Style, HorizontalLine, Line}, TableIteratorExt};
848    ///
849    /// let table = (0..3)
850    ///    .map(|i| ("Hello", i))
851    ///    .table()
852    ///    .with(Style::rounded().horizontals((1..4).map(|i| HorizontalLine::new(i, Line::filled('#')))))
853    ///    .to_string();
854    ///
855    /// assert_eq!(
856    ///     table,
857    ///     concat!(
858    ///         "╭───────┬─────╮\n",
859    ///         "│ &str  │ i32 │\n",
860    ///         "###############\n",
861    ///         "│ Hello │ 0   │\n",
862    ///         "###############\n",
863    ///         "│ Hello │ 1   │\n",
864    ///         "###############\n",
865    ///         "│ Hello │ 2   │\n",
866    ///         "╰───────┴─────╯",
867    ///     )
868    /// )
869    /// ```
870    pub fn horizontals<NewLines>(self, lines: NewLines) -> Style<T, B, L, R, H, V, NewLines, VLines>
871    where
872        NewLines: IntoIterator<Item = HorizontalLine> + Clone,
873    {
874        Style::new(self.borders, lines, self.verticals)
875    }
876
877    /// Set border vertical lines.
878    ///
879    /// # Example
880    ///
881    /// ```
882    /// use tabled::{style::{Style, VerticalLine, Line}, TableIteratorExt};
883    ///
884    /// let table = (0..3)
885    ///    .map(|i| ("Hello", i))
886    ///    .table()
887    ///    .with(Style::rounded().verticals((0..3).map(|i| VerticalLine::new(i, Line::filled('#')))))
888    ///    .to_string();
889    ///
890    /// assert_eq!(
891    ///     table,
892    ///     concat!(
893    ///         "#───────#─────#\n",
894    ///         "# &str  # i32 #\n",
895    ///         "├───────┼─────┤\n",
896    ///         "# Hello # 0   #\n",
897    ///         "# Hello # 1   #\n",
898    ///         "# Hello # 2   #\n",
899    ///         "#───────#─────#",
900    ///     )
901    /// )
902    /// ```
903    pub fn verticals<NewLines>(self, lines: NewLines) -> Style<T, B, L, R, H, V, HLines, NewLines>
904    where
905        NewLines: IntoIterator<Item = VerticalLine> + Clone,
906    {
907        Style::new(self.borders, self.horizontals, lines)
908    }
909
910    /// Removes all horizontal lines set by [`Style::horizontals`]
911    pub fn off_horizontals(self) -> Style<T, B, L, R, H, V, HLineArray<0>, VLines> {
912        Style::new(self.borders, [], self.verticals)
913    }
914
915    /// Removes all verticals lines set by [`Style::verticals`]
916    pub fn off_verticals(self) -> Style<T, B, L, R, H, V, HLines, VLineArray<0>> {
917        Style::new(self.borders, self.horizontals, [])
918    }
919}
920
921impl<B, R, H, V, HLines, VLines> Style<On, B, On, R, H, V, HLines, VLines> {
922    /// Sets a top left corner.
923    pub fn top_left_corner(mut self, c: char) -> Self {
924        self.borders.top_left = Some(c);
925
926        Style::new(self.borders, self.horizontals, self.verticals)
927    }
928}
929
930impl<B, L, H, V, HLines, VLines> Style<On, B, L, On, H, V, HLines, VLines> {
931    /// Sets a top right corner.
932    pub fn top_right_corner(mut self, c: char) -> Self {
933        self.borders.top_right = Some(c);
934
935        Style::new(self.borders, self.horizontals, self.verticals)
936    }
937}
938
939impl<T, L, H, V, HLines, VLines> Style<T, On, L, On, H, V, HLines, VLines> {
940    /// Sets a bottom right corner.
941    pub fn bottom_right_corner(mut self, c: char) -> Self {
942        self.borders.bottom_right = Some(c);
943
944        Style::new(self.borders, self.horizontals, self.verticals)
945    }
946}
947
948impl<T, R, H, V, HLines, VLines> Style<T, On, On, R, H, V, HLines, VLines> {
949    /// Sets a bottom left corner.
950    pub fn bottom_left_corner(mut self, c: char) -> Self {
951        self.borders.bottom_left = Some(c);
952
953        Style::new(self.borders, self.horizontals, self.verticals)
954    }
955}
956
957impl<T, B, R, V, HLines, VLines> Style<T, B, On, R, On, V, HLines, VLines> {
958    /// Sets a left intersection char.
959    pub fn left_intersection(mut self, c: char) -> Self {
960        self.borders.horizontal_left = Some(c);
961
962        Style::new(self.borders, self.horizontals, self.verticals)
963    }
964}
965
966impl<T, B, L, V, HLines, VLines> Style<T, B, L, On, On, V, HLines, VLines> {
967    /// Sets a right intersection char.
968    pub fn right_intersection(mut self, c: char) -> Self {
969        self.borders.horizontal_right = Some(c);
970
971        Style::new(self.borders, self.horizontals, self.verticals)
972    }
973}
974
975impl<B, L, R, H, HLines, VLines> Style<On, B, L, R, H, On, HLines, VLines> {
976    /// Sets a top intersection char.
977    pub fn top_intersection(mut self, c: char) -> Self {
978        self.borders.top_intersection = Some(c);
979
980        Style::new(self.borders, self.horizontals, self.verticals)
981    }
982}
983
984impl<T, L, R, H, HLines, VLines> Style<T, On, L, R, H, On, HLines, VLines> {
985    /// Sets a bottom intersection char.
986    pub fn bottom_intersection(mut self, c: char) -> Self {
987        self.borders.bottom_intersection = Some(c);
988
989        Style::new(self.borders, self.horizontals, self.verticals)
990    }
991}
992
993impl<T, B, L, R, HLines, VLines> Style<T, B, L, R, On, On, HLines, VLines> {
994    /// Sets an inner intersection char.
995    /// A char between horizontal and vertical split lines.
996    pub fn inner_intersection(mut self, c: char) -> Self {
997        self.borders.intersection = Some(c);
998
999        Style::new(self.borders, self.horizontals, self.verticals)
1000    }
1001}
1002
1003impl<B, L, R, H, V, HLines, VLines> Style<On, B, L, R, H, V, HLines, VLines> {
1004    /// Removes top border.
1005    pub fn off_top(mut self) -> Style<(), B, L, R, H, V, HLines, VerticalLineIter<VLines::IntoIter>>
1006    where
1007        VLines: IntoIterator<Item = VerticalLine> + Clone,
1008    {
1009        self.borders.top = None;
1010        self.borders.top_intersection = None;
1011        self.borders.top_left = None;
1012        self.borders.top_right = None;
1013
1014        let iter = VerticalLineIter::new(self.verticals.into_iter(), false, true, false);
1015        Style::new(self.borders, self.horizontals, iter)
1016    }
1017}
1018
1019impl<T, L, R, H, V, HLines, VLines> Style<T, On, L, R, H, V, HLines, VLines> {
1020    /// Removes bottom border.
1021    pub fn off_bottom(
1022        mut self,
1023    ) -> Style<T, (), L, R, H, V, HLines, VerticalLineIter<VLines::IntoIter>>
1024    where
1025        VLines: IntoIterator<Item = VerticalLine> + Clone,
1026    {
1027        self.borders.bottom = None;
1028        self.borders.bottom_intersection = None;
1029        self.borders.bottom_left = None;
1030        self.borders.bottom_right = None;
1031
1032        let iter = VerticalLineIter::new(self.verticals.into_iter(), false, false, true);
1033        Style::new(self.borders, self.horizontals, iter)
1034    }
1035}
1036
1037impl<T, B, R, H, V, HLines, VLines> Style<T, B, On, R, H, V, HLines, VLines> {
1038    /// Removes left border.
1039    pub fn off_left(
1040        mut self,
1041    ) -> Style<T, B, (), R, H, V, HorizontalLineIter<HLines::IntoIter>, VLines>
1042    where
1043        HLines: IntoIterator<Item = HorizontalLine> + Clone,
1044    {
1045        self.borders.vertical_left = None;
1046        self.borders.horizontal_left = None;
1047        self.borders.top_left = None;
1048        self.borders.bottom_left = None;
1049
1050        let iter = HorizontalLineIter::new(self.horizontals.into_iter(), false, true, false);
1051        Style::new(self.borders, iter, self.verticals)
1052    }
1053}
1054
1055impl<T, B, L, H, V, HLines, VLines> Style<T, B, L, On, H, V, HLines, VLines> {
1056    /// Removes right border.
1057    pub fn off_right(
1058        mut self,
1059    ) -> Style<T, B, L, (), H, V, HorizontalLineIter<HLines::IntoIter>, VLines>
1060    where
1061        HLines: IntoIterator<Item = HorizontalLine> + Clone,
1062    {
1063        self.borders.vertical_right = None;
1064        self.borders.horizontal_right = None;
1065        self.borders.top_right = None;
1066        self.borders.bottom_right = None;
1067
1068        let iter = HorizontalLineIter::new(self.horizontals.into_iter(), false, false, true);
1069        Style::new(self.borders, iter, self.verticals)
1070    }
1071}
1072
1073impl<T, B, L, R, V, HLines, VLines> Style<T, B, L, R, On, V, HLines, VLines> {
1074    /// Removes horizontal split lines.
1075    ///
1076    /// Not including custom split lines.
1077    pub fn off_horizontal(
1078        mut self,
1079    ) -> Style<T, B, L, R, (), V, HLines, VerticalLineIter<VLines::IntoIter>>
1080    where
1081        VLines: IntoIterator<Item = VerticalLine> + Clone,
1082    {
1083        self.borders.horizontal = None;
1084        self.borders.horizontal_left = None;
1085        self.borders.horizontal_right = None;
1086        self.borders.intersection = None;
1087
1088        let iter = VerticalLineIter::new(self.verticals.into_iter(), true, false, false);
1089        Style::new(self.borders, self.horizontals, iter)
1090    }
1091}
1092
1093impl<T, B, L, R, H, HLines, VLines> Style<T, B, L, R, H, On, HLines, VLines> {
1094    /// Removes vertical split lines.
1095    pub fn off_vertical(
1096        mut self,
1097    ) -> Style<T, B, L, R, H, (), HorizontalLineIter<HLines::IntoIter>, VLines>
1098    where
1099        HLines: IntoIterator<Item = HorizontalLine> + Clone,
1100    {
1101        self.borders.vertical = None;
1102        self.borders.top_intersection = None;
1103        self.borders.bottom_intersection = None;
1104        self.borders.intersection = None;
1105
1106        let iter = HorizontalLineIter::new(self.horizontals.into_iter(), true, false, false);
1107        Style::new(self.borders, iter, self.verticals)
1108    }
1109}
1110
1111impl<T, B, L, R, H, V, HLines, VLines> Style<T, B, L, R, H, V, HLines, VLines> {
1112    const fn new(borders: Borders, horizontals: HLines, verticals: VLines) -> Self {
1113        Self {
1114            borders,
1115            horizontals,
1116            verticals,
1117            _top: PhantomData,
1118            _bottom: PhantomData,
1119            _left: PhantomData,
1120            _right: PhantomData,
1121            _horizontal: PhantomData,
1122            _vertical: PhantomData,
1123        }
1124    }
1125}
1126
1127impl<T, B, L, R, H, V, HLines, VLines, I> TableOption<I> for Style<T, B, L, R, H, V, HLines, VLines>
1128where
1129    I: Records,
1130    HLines: IntoIterator<Item = HorizontalLine> + Clone,
1131    VLines: IntoIterator<Item = VerticalLine> + Clone,
1132{
1133    fn change(&mut self, table: &mut Table<I>) {
1134        table.get_config_mut().clear_theme();
1135        table.get_config_mut().set_borders(self.borders.clone());
1136
1137        if table.shape().0 > 1 {
1138            for mut hl in self.horizontals.clone() {
1139                hl.change(table);
1140            }
1141        }
1142
1143        if table.shape().1 > 1 {
1144            for mut vl in self.verticals.clone() {
1145                vl.change(table);
1146            }
1147        }
1148
1149        table.destroy_width_cache();
1150        table.destroy_height_cache();
1151    }
1152}
1153
1154const fn create_borders(
1155    top: Line,
1156    bottom: Line,
1157    horizontal: Line,
1158    left: Option<char>,
1159    right: Option<char>,
1160    vertical: Option<char>,
1161) -> Borders {
1162    Borders {
1163        top: top.main,
1164        bottom: bottom.main,
1165        top_left: top.connector1,
1166        top_right: top.connector2,
1167        bottom_left: bottom.connector1,
1168        bottom_right: bottom.connector2,
1169        top_intersection: top.intersection,
1170        bottom_intersection: bottom.intersection,
1171        horizontal_left: horizontal.connector1,
1172        horizontal_right: horizontal.connector2,
1173        horizontal: horizontal.main,
1174        intersection: horizontal.intersection,
1175        vertical_left: left,
1176        vertical_right: right,
1177        vertical,
1178    }
1179}
1180
1181/// An interator which limits [`Line`] influence on iterations over lines for in [`Style`].
1182#[derive(Debug, Clone)]
1183pub struct HorizontalLineIter<I> {
1184    iter: I,
1185    intersection: bool,
1186    left: bool,
1187    right: bool,
1188}
1189
1190impl<I> HorizontalLineIter<I> {
1191    fn new(iter: I, intersection: bool, left: bool, right: bool) -> Self {
1192        Self {
1193            iter,
1194            intersection,
1195            left,
1196            right,
1197        }
1198    }
1199}
1200
1201impl<I> Iterator for HorizontalLineIter<I>
1202where
1203    I: Iterator<Item = HorizontalLine>,
1204{
1205    type Item = HorizontalLine;
1206
1207    fn next(&mut self) -> Option<Self::Item> {
1208        let mut hl = self.iter.next()?;
1209
1210        if let Some(mut line) = hl.line {
1211            if self.intersection {
1212                line.intersection = None;
1213            }
1214
1215            if self.left {
1216                line.connector1 = None;
1217            }
1218
1219            if self.right {
1220                line.connector2 = None;
1221            }
1222
1223            hl.line = Some(line);
1224        }
1225
1226        Some(hl)
1227    }
1228}
1229
1230/// An interator which limits [`Line`] influence on iterations over lines for in [`Style`].
1231#[derive(Debug, Clone)]
1232pub struct VerticalLineIter<I> {
1233    iter: I,
1234    intersection: bool,
1235    top: bool,
1236    bottom: bool,
1237}
1238
1239impl<I> VerticalLineIter<I> {
1240    fn new(iter: I, intersection: bool, top: bool, bottom: bool) -> Self {
1241        Self {
1242            iter,
1243            intersection,
1244            top,
1245            bottom,
1246        }
1247    }
1248}
1249
1250impl<I> Iterator for VerticalLineIter<I>
1251where
1252    I: Iterator<Item = VerticalLine>,
1253{
1254    type Item = VerticalLine;
1255
1256    fn next(&mut self) -> Option<Self::Item> {
1257        let mut hl = self.iter.next()?;
1258
1259        if let Some(mut line) = hl.line {
1260            if self.intersection {
1261                line.intersection = None;
1262            }
1263
1264            if self.top {
1265                line.connector1 = None;
1266            }
1267
1268            if self.bottom {
1269                line.connector2 = None;
1270            }
1271
1272            hl.line = Some(line);
1273        }
1274
1275        Some(hl)
1276    }
1277}