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