tabled/settings/themes/
theme.rs

1//! This module contains [`RawStyle`] structure, which is analogues to [`Style`] but not generic,
2//! so sometimes it can be used more conveniently.
3
4// todo: StyleFromTable()
5//       table.with(&mut StyleFromTable);
6//       vs
7//       Theme::from(table.get_config());
8//
9// not sure what the best interface is
10// IMHO 2
11
12use std::collections::HashMap;
13
14use crate::{
15    grid::config::{
16        Border, Borders, ColoredConfig, CompactConfig, CompactMultilineConfig, HorizontalLine,
17        VerticalLine,
18    },
19    settings::{style::Style, Color, TableOption},
20};
21
22/// A raw style data, which can be produced safely from [`Style`].
23///
24/// It can be useful in order to not have a generics and be able to use it as a variable more conveniently.
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct Theme {
27    chars: Borders<char>,
28    colors: Borders<Color>,
29    lines_horizontals: Option<HashMap<usize, HorizontalLine<char>>>,
30    lines_verticals: Option<HashMap<usize, VerticalLine<char>>>,
31    lines_horizontal1: Option<HorizontalLine<char>>,
32}
33
34impl Theme {
35    /// Creates a new empty style.
36    ///
37    /// It's an analog of [`Style::empty`]
38    pub const fn new() -> Self {
39        Self::gen(Borders::empty(), Borders::empty(), None, None, None)
40    }
41
42    /// Build a theme out of a style builder.
43    pub const fn from_style<T, B, L, R, H, V, const HS: usize, const VS: usize>(
44        style: Style<T, B, L, R, H, V, HS, VS>,
45    ) -> Self {
46        let chars = style.get_borders();
47        let hlines = style.get_horizontals();
48        let hlines1 = hlines_find(hlines, 1);
49
50        Self::gen(chars, Borders::empty(), None, None, hlines1)
51    }
52
53    /// Returns an outer border of the style.
54    pub fn set_frame(&mut self, frame: Border<char>) {
55        self.chars.top = frame.top;
56        self.chars.bottom = frame.bottom;
57        self.chars.left = frame.left;
58        self.chars.right = frame.right;
59        self.chars.top_left = frame.left_top_corner;
60        self.chars.top_right = frame.right_top_corner;
61        self.chars.bottom_left = frame.left_bottom_corner;
62        self.chars.bottom_right = frame.right_bottom_corner;
63    }
64
65    /// Returns an outer border of the style.
66    pub fn set_frame_colors(&mut self, frame: Border<Color>) {
67        self.colors.top = frame.top;
68        self.colors.bottom = frame.bottom;
69        self.colors.left = frame.left;
70        self.colors.right = frame.right;
71        self.colors.top_left = frame.left_top_corner;
72        self.colors.top_right = frame.right_top_corner;
73        self.colors.bottom_left = frame.left_bottom_corner;
74        self.colors.bottom_right = frame.right_bottom_corner;
75    }
76
77    /// Set borders structure.
78    pub fn set_borders(&mut self, borders: Borders<char>) {
79        self.chars = borders;
80    }
81
82    /// Set borders structure.
83    pub fn set_colors(&mut self, borders: Borders<Color>) {
84        self.colors = borders;
85    }
86
87    /// Get borders structure.
88    pub const fn get_borders(&self) -> &Borders<char> {
89        &self.chars
90    }
91
92    /// Get borders color structure.
93    pub const fn get_borders_colors(&self) -> &Borders<Color> {
94        &self.colors
95    }
96
97    /// Get borders structure.
98    pub fn get_borders_mut(&mut self) -> &mut Borders<char> {
99        &mut self.chars
100    }
101
102    /// Get borders color structure.
103    pub fn get_colors_mut(&mut self) -> &mut Borders<Color> {
104        &mut self.colors
105    }
106
107    /// Remove borders.
108    pub fn remove_borders(&mut self) {
109        self.set_borders(Borders::empty());
110    }
111
112    /// Remove colors.
113    pub fn remove_colors(&mut self) {
114        self.set_colors(Borders::empty());
115    }
116
117    /// Remove horizontal lines.
118    pub fn remove_horizontal_lines(&mut self) {
119        self.set_horizontal_lines(HashMap::new());
120        self.lines_horizontal1 = None;
121        self.chars.horizontal = None;
122        self.chars.left_intersection = None;
123        self.chars.right_intersection = None;
124        self.chars.intersection = None;
125    }
126
127    /// Remove vertical lines.
128    pub fn remove_vertical_lines(&mut self) {
129        self.set_vertical_lines(HashMap::new());
130        self.chars.vertical = None;
131        self.chars.top_intersection = None;
132        self.chars.bottom_intersection = None;
133        self.chars.intersection = None;
134    }
135
136    /// Set an outer border.
137    pub const fn get_frame(&self) -> Border<char> {
138        Border {
139            top: self.chars.top,
140            bottom: self.chars.bottom,
141            left: self.chars.left,
142            right: self.chars.right,
143            left_top_corner: self.chars.top_left,
144            right_top_corner: self.chars.top_right,
145            left_bottom_corner: self.chars.bottom_left,
146            right_bottom_corner: self.chars.bottom_right,
147        }
148    }
149
150    /// Set an outer border.
151    pub const fn get_frame_colors(&self) -> Border<&Color> {
152        Border {
153            top: self.colors.top.as_ref(),
154            bottom: self.colors.bottom.as_ref(),
155            left: self.colors.left.as_ref(),
156            right: self.colors.right.as_ref(),
157            left_top_corner: self.colors.top_left.as_ref(),
158            right_top_corner: self.colors.top_right.as_ref(),
159            left_bottom_corner: self.colors.bottom_left.as_ref(),
160            right_bottom_corner: self.colors.bottom_right.as_ref(),
161        }
162    }
163
164    /// Set horizontal border lines.
165    ///
166    /// # Example
167    ///
168    /// ```
169    /// use std::collections::HashMap;
170    /// use tabled::{Table, settings::style::{Style, HorizontalLine}, settings::themes::Theme};
171    ///
172    /// let mut style = Theme::from(Style::re_structured_text());
173    ///
174    /// let mut lines = HashMap::new();
175    /// lines.insert(1, HorizontalLine::inherit(Style::extended()).into());
176    ///
177    /// style.set_horizontal_lines(lines);
178    ///
179    /// let data = (0..3).map(|i| ("Hello", i));
180    /// let table = Table::new(data).with(style).to_string();
181    ///
182    /// assert_eq!(
183    ///     table,
184    ///     concat!(
185    ///         " ======= ===== \n",
186    ///         "  &str    i32  \n",
187    ///         "╠═══════╬═════╣\n",
188    ///         "  Hello   0    \n",
189    ///         "  Hello   1    \n",
190    ///         "  Hello   2    \n",
191    ///         " ======= ===== ",
192    ///     ),
193    /// )
194    /// ```
195    pub fn set_horizontal_lines(&mut self, lines: HashMap<usize, HorizontalLine<char>>) {
196        self.lines_horizontals = Some(lines);
197    }
198
199    /// Set vertical border lines.
200    ///
201    /// # Example
202    ///
203    /// ```
204    /// use std::collections::HashMap;
205    /// use tabled::{
206    ///     Table,
207    ///     settings::style::{Style, HorizontalLine},
208    ///     settings::themes::Theme,
209    /// };
210    ///
211    ///
212    /// let mut style = Theme::from_style(Style::re_structured_text());
213    ///
214    /// let mut lines = HashMap::new();
215    /// lines.insert(1, HorizontalLine::inherit(Style::extended()).into());
216    ///
217    /// style.set_vertical_lines(lines);
218    ///
219    /// let data = (0..3).map(|i| ("Hello", i));
220    /// let table = Table::new(data).with(style).to_string();
221    ///
222    /// assert_eq!(
223    ///     table,
224    ///     concat!(
225    ///         "=======╠=====\n",
226    ///         " &str  ═ i32 \n",
227    ///         "======= =====\n",
228    ///         " Hello ═ 0   \n",
229    ///         " Hello ═ 1   \n",
230    ///         " Hello ═ 2   \n",
231    ///         "=======╣=====",
232    ///     ),
233    /// )
234    /// ```
235    pub fn set_vertical_lines(&mut self, lines: HashMap<usize, VerticalLine<char>>) {
236        self.lines_verticals = Some(lines);
237    }
238
239    /// Insert a vertical line into specific column location.
240    pub fn insert_vertical_line<L>(&mut self, line: usize, vertical: L)
241    where
242        L: Into<VerticalLine<char>>,
243    {
244        let vertical = vertical.into();
245
246        let verticals = match &mut self.lines_verticals {
247            Some(verticals) => verticals,
248            None => {
249                self.lines_verticals = Some(HashMap::with_capacity(1));
250                self.lines_verticals.as_mut().expect("checked")
251            }
252        };
253
254        let _ = verticals.insert(line, vertical);
255    }
256
257    /// Insert a horizontal line to a specific row location.
258    pub fn insert_horizontal_line<L>(&mut self, line: usize, horizontal: L)
259    where
260        L: Into<HorizontalLine<char>>,
261    {
262        let horizontal = horizontal.into();
263
264        let horizontals = match &mut self.lines_horizontals {
265            Some(horizontals) => horizontals,
266            None => {
267                self.lines_horizontals = Some(HashMap::with_capacity(1));
268                self.lines_horizontals.as_mut().expect("checked")
269            }
270        };
271
272        let _ = horizontals.insert(line, horizontal);
273    }
274
275    /// Get a vertical line at the row if any set.
276    pub fn get_vertical_line(&self, column: usize) -> Option<&VerticalLine<char>> {
277        self.lines_verticals.as_ref().and_then(|m| m.get(&column))
278    }
279
280    /// Get a horizontal line at the row if any set.
281    pub fn get_horizontal_line(&self, row: usize) -> Option<&HorizontalLine<char>> {
282        let line = self.lines_horizontals.as_ref().and_then(|m| m.get(&row));
283        if line.is_some() {
284            return line;
285        }
286
287        if row == 1 && self.lines_horizontal1.is_some() {
288            return self.lines_horizontal1.as_ref();
289        }
290
291        None
292    }
293
294    /// Verifies if borders has left line set on the frame.
295    pub const fn borders_has_left(&self) -> bool {
296        self.chars.has_left()
297    }
298
299    /// Verifies if borders has right line set on the frame.
300    pub const fn borders_has_right(&self) -> bool {
301        self.chars.has_right()
302    }
303
304    /// Verifies if borders has top line set on the frame.
305    pub const fn borders_has_top(&self) -> bool {
306        self.chars.has_top()
307    }
308
309    /// Verifies if borders has bottom line set on the frame.
310    pub const fn borders_has_bottom(&self) -> bool {
311        self.chars.has_bottom()
312    }
313
314    /// Verifies if borders has horizontal lines set.
315    pub const fn borders_has_horizontal(&self) -> bool {
316        self.chars.has_horizontal()
317    }
318
319    /// Verifies if borders has vertical lines set.
320    pub const fn borders_has_vertical(&self) -> bool {
321        self.chars.has_vertical()
322    }
323
324    const fn gen(
325        chars: Borders<char>,
326        colors: Borders<Color>,
327        horizontals: Option<HashMap<usize, HorizontalLine<char>>>,
328        verticals: Option<HashMap<usize, VerticalLine<char>>>,
329        horizontal1: Option<HorizontalLine<char>>,
330    ) -> Self {
331        Self {
332            chars,
333            colors,
334            lines_horizontals: horizontals,
335            lines_verticals: verticals,
336            lines_horizontal1: horizontal1,
337        }
338    }
339}
340
341impl From<Borders<char>> for Theme {
342    fn from(borders: Borders<char>) -> Self {
343        Self::gen(
344            borders,
345            Borders::empty(),
346            Default::default(),
347            Default::default(),
348            None,
349        )
350    }
351}
352
353impl Default for Theme {
354    fn default() -> Self {
355        Self::new()
356    }
357}
358
359impl<R, D> TableOption<R, ColoredConfig, D> for Theme {
360    fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
361        cfg_clear_borders(cfg);
362        cfg_set_custom_lines(
363            cfg,
364            self.lines_horizontals,
365            self.lines_verticals,
366            self.lines_horizontal1,
367        );
368        cfg_set_borders(cfg, self.chars, self.colors);
369    }
370}
371
372impl<R, D> TableOption<R, CompactConfig, D> for Theme {
373    fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
374        *cfg = cfg.set_borders(self.chars);
375    }
376}
377
378impl<R, D> TableOption<R, CompactMultilineConfig, D> for Theme {
379    fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
380        cfg.set_borders(self.chars);
381    }
382}
383
384impl<T, B, L, R, H, V, const HSIZE: usize, const VSIZE: usize>
385    From<Style<T, B, L, R, H, V, HSIZE, VSIZE>> for Theme
386{
387    fn from(style: Style<T, B, L, R, H, V, HSIZE, VSIZE>) -> Self {
388        Self::from_style(style)
389    }
390}
391
392impl From<ColoredConfig> for Theme {
393    fn from(cfg: ColoredConfig) -> Self {
394        let borders = *cfg.get_borders();
395        let colors = cfg.get_color_borders().clone().convert_into();
396        let horizontals = cfg.get_horizontal_lines().into_iter().collect();
397        let verticals = cfg.get_vertical_lines().into_iter().collect();
398
399        Self::gen(borders, colors, Some(horizontals), Some(verticals), None)
400    }
401}
402
403macro_rules! func_set_chars {
404    ($name:ident, $arg:ident, $desc:expr) => {
405        #[doc = concat!("Set a border character", " ", "", $desc, "", " ", ".")]
406        pub fn $name(&mut self, c: char) {
407            self.chars.$arg = Some(c);
408        }
409    };
410}
411
412macro_rules! func_remove_chars {
413    ($name:ident, $arg:ident, $desc:expr) => {
414        #[doc = concat!("Remove a border character", " ", "", $desc, "", " ", ".")]
415        pub fn $name(&mut self) {
416            self.chars.$arg = None;
417        }
418    };
419}
420
421macro_rules! func_get_chars {
422    ($name:ident, $arg:ident, $desc:expr) => {
423        #[doc = concat!("Get a border character", " ", "", $desc, "", " ", ".")]
424        pub const fn $name(&self) -> Option<char> {
425            self.chars.$arg
426        }
427    };
428}
429
430macro_rules! func_set_colors {
431    ($name:ident, $arg:ident, $desc:expr) => {
432        #[doc = concat!("Set a border color", " ", "", $desc, "", " ", ".")]
433        pub fn $name(&mut self, color: Color) {
434            self.colors.$arg = Some(color);
435        }
436    };
437}
438
439macro_rules! func_remove_colors {
440    ($name:ident, $arg:ident, $desc:expr) => {
441        #[doc = concat!("Remove a border color", " ", "", $desc, "", " ", ".")]
442        pub fn $name(&mut self) {
443            self.colors.$arg = None;
444        }
445    };
446}
447
448macro_rules! func_get_colors {
449    ($name:ident, $arg:ident, $desc:expr) => {
450        #[doc = concat!("Get a border color", " ", "", $desc, "", " ", ".")]
451        pub fn $name(&self) -> Option<&Color> {
452            self.colors.$arg.as_ref()
453        }
454    };
455}
456
457#[rustfmt::skip]
458impl Theme {
459    func_set_chars!(set_borders_top,                      top,                        "top");
460    func_set_chars!(set_borders_bottom,                   bottom,                     "bottom");
461    func_set_chars!(set_borders_left,                     left,                       "left");
462    func_set_chars!(set_borders_right,                    right,                      "right");
463    func_set_chars!(set_borders_corner_top_left,          top_left,                   "top left corner");
464    func_set_chars!(set_borders_corner_top_right,         top_right,                  "top right corner");
465    func_set_chars!(set_borders_corner_bottom_left,       bottom_left,                "bottom left corner");
466    func_set_chars!(set_borders_corner_bottom_right,      bottom_right,               "bottom right corner");
467    func_set_chars!(set_borders_intersection_top,         top_intersection,           "top intersection with a vertical line");
468    func_set_chars!(set_borders_intersection_bottom,      bottom_intersection,        "bottom intersection with a vertical line");
469    func_set_chars!(set_borders_intersection_left,        left_intersection,          "left intersection with a horizontal line");
470    func_set_chars!(set_borders_intersection_right,       right_intersection,         "right intersection with a horizontal line");
471    func_set_chars!(set_borders_intersection,             intersection,               "intersection of horizontal and vertical line");
472    func_set_chars!(set_borders_horizontal,               horizontal,                 "horizontal");
473    func_set_chars!(set_borders_vertical,                 vertical,                   "vertical");
474}
475
476#[rustfmt::skip]
477impl Theme {
478    func_get_chars!(get_borders_top,                      top,                        "top");
479    func_get_chars!(get_borders_bottom,                   bottom,                     "bottom");
480    func_get_chars!(get_borders_left,                     left,                       "left");
481    func_get_chars!(get_borders_right,                    right,                      "right");
482    func_get_chars!(get_borders_corner_top_left,          top_left,                   "top left corner");
483    func_get_chars!(get_borders_corner_top_right,         top_right,                  "top right corner");
484    func_get_chars!(get_borders_corner_bottom_left,       bottom_left,                "bottom left corner");
485    func_get_chars!(get_borders_corner_bottom_right,      bottom_right,               "bottom right corner");
486    func_get_chars!(get_borders_intersection_top,         top_intersection,           "top intersection with a vertical line");
487    func_get_chars!(get_borders_intersection_bottom,      bottom_intersection,        "bottom intersection with a vertical line");
488    func_get_chars!(get_borders_intersection_left,        left_intersection,          "left intersection with a horizontal line");
489    func_get_chars!(get_borders_intersection_right,       right_intersection,         "right intersection with a horizontal line");
490    func_get_chars!(get_borders_intersection,             intersection,               "intersection of horizontal and vertical line");
491    func_get_chars!(get_borders_horizontal,               horizontal,                 "horizontal");
492    func_get_chars!(get_borders_vertical,                 vertical,                   "vertical");
493}
494
495#[rustfmt::skip]
496impl Theme {
497    func_remove_chars!(remove_borders_top,                      top,                        "top");
498    func_remove_chars!(remove_borders_bottom,                   bottom,                     "bottom");
499    func_remove_chars!(remove_borders_left,                     left,                       "left");
500    func_remove_chars!(remove_borders_right,                    right,                      "right");
501    func_remove_chars!(remove_borders_corner_top_left,          top_left,                   "top left corner");
502    func_remove_chars!(remove_borders_corner_top_right,         top_right,                  "top right corner");
503    func_remove_chars!(remove_borders_corner_bottom_left,       bottom_left,                "bottom left corner");
504    func_remove_chars!(remove_borders_corner_bottom_right,      bottom_right,               "bottom right corner");
505    func_remove_chars!(remove_borders_intersection_top,         top_intersection,           "top intersection with a vertical line");
506    func_remove_chars!(remove_borders_intersection_bottom,      bottom_intersection,        "bottom intersection with a vertical line");
507    func_remove_chars!(remove_borders_intersection_left,        left_intersection,          "left intersection with a horizontal line");
508    func_remove_chars!(remove_borders_intersection_right,       right_intersection,         "right intersection with a horizontal line");
509    func_remove_chars!(remove_borders_intersection,             intersection,               "intersection of horizontal and vertical line");
510    func_remove_chars!(remove_borders_horizontal,               horizontal,                 "horizontal");
511    func_remove_chars!(remove_borders_vertical,                 vertical,                   "vertical");
512}
513
514#[rustfmt::skip]
515impl Theme {
516    func_set_colors!(set_colors_top,                      top,                        "top");
517    func_set_colors!(set_colors_bottom,                   bottom,                     "bottom");
518    func_set_colors!(set_colors_left,                     left,                       "left");
519    func_set_colors!(set_colors_right,                    right,                      "right");
520    func_set_colors!(set_colors_corner_top_left,          top_left,                   "top left corner");
521    func_set_colors!(set_colors_corner_top_right,         top_right,                  "top right corner");
522    func_set_colors!(set_colors_corner_bottom_left,       bottom_left,                "bottom left corner");
523    func_set_colors!(set_colors_corner_bottom_right,      bottom_right,               "bottom right corner");
524    func_set_colors!(set_colors_intersection_top,         top_intersection,           "top intersection with a vertical line");
525    func_set_colors!(set_colors_intersection_bottom,      bottom_intersection,        "bottom intersection with a vertical line");
526    func_set_colors!(set_colors_intersection_left,        left_intersection,          "left intersection with a horizontal line");
527    func_set_colors!(set_colors_intersection_right,       right_intersection,         "right intersection with a horizontal line");
528    func_set_colors!(set_colors_intersection,             intersection,               "intersection of horizontal and vertical line");
529    func_set_colors!(set_colors_horizontal,               horizontal,                 "horizontal");
530    func_set_colors!(set_colors_vertical,                 vertical,                   "vertical");
531}
532
533#[rustfmt::skip]
534impl Theme {
535    func_remove_colors!(remove_colors_top,                      top,                        "top");
536    func_remove_colors!(remove_colors_bottom,                   bottom,                     "bottom");
537    func_remove_colors!(remove_colors_left,                     left,                       "left");
538    func_remove_colors!(remove_colors_right,                    right,                      "right");
539    func_remove_colors!(remove_colors_corner_top_left,          top_left,                   "top left corner");
540    func_remove_colors!(remove_colors_corner_top_right,         top_right,                  "top right corner");
541    func_remove_colors!(remove_colors_corner_bottom_left,       bottom_left,                "bottom left corner");
542    func_remove_colors!(remove_colors_corner_bottom_right,      bottom_right,               "bottom right corner");
543    func_remove_colors!(remove_colors_intersection_top,         top_intersection,           "top intersection with a vertical line");
544    func_remove_colors!(remove_colors_intersection_bottom,      bottom_intersection,        "bottom intersection with a vertical line");
545    func_remove_colors!(remove_colors_intersection_left,        left_intersection,          "left intersection with a horizontal line");
546    func_remove_colors!(remove_colors_intersection_right,       right_intersection,         "right intersection with a horizontal line");
547    func_remove_colors!(remove_colors_intersection,             intersection,               "intersection of horizontal and vertical line");
548    func_remove_colors!(remove_colors_horizontal,               horizontal,                 "horizontal");
549    func_remove_colors!(remove_colors_vertical,                 vertical,                   "vertical");
550}
551
552#[rustfmt::skip]
553impl Theme {
554    func_get_colors!(get_colors_top,                      top,                        "top");
555    func_get_colors!(get_colors_bottom,                   bottom,                     "bottom");
556    func_get_colors!(get_colors_left,                     left,                       "left");
557    func_get_colors!(get_colors_right,                    right,                      "right");
558    func_get_colors!(get_colors_corner_top_left,          top_left,                   "top left corner");
559    func_get_colors!(get_colors_corner_top_right,         top_right,                  "top right corner");
560    func_get_colors!(get_colors_corner_bottom_left,       bottom_left,                "bottom left corner");
561    func_get_colors!(get_colors_corner_bottom_right,      bottom_right,               "bottom right corner");
562    func_get_colors!(get_colors_intersection_top,         top_intersection,           "top intersection with a vertical line");
563    func_get_colors!(get_colors_intersection_bottom,      bottom_intersection,        "bottom intersection with a vertical line");
564    func_get_colors!(get_colors_intersection_left,        left_intersection,          "left intersection with a horizontal line");
565    func_get_colors!(get_colors_intersection_right,       right_intersection,         "right intersection with a horizontal line");
566    func_get_colors!(get_colors_intersection,             intersection,               "intersection of horizontal and vertical line");
567    func_get_colors!(get_colors_horizontal,               horizontal,                 "horizontal");
568    func_get_colors!(get_colors_vertical,                 vertical,                   "vertical");
569}
570
571fn cfg_clear_borders(cfg: &mut ColoredConfig) {
572    cfg.remove_borders();
573    cfg.remove_borders_colors();
574    cfg.remove_vertical_chars();
575    cfg.remove_horizontal_chars();
576    cfg.remove_color_line_horizontal();
577    cfg.remove_color_line_vertical();
578}
579
580fn cfg_set_borders(cfg: &mut ColoredConfig, borders: Borders<char>, colors: Borders<Color>) {
581    cfg.set_borders(borders);
582
583    if !colors.is_empty() {
584        cfg.set_borders_color(colors.convert_into());
585    }
586}
587
588fn cfg_set_custom_lines(
589    cfg: &mut ColoredConfig,
590    horizontals: Option<HashMap<usize, HorizontalLine<char>>>,
591    verticals: Option<HashMap<usize, VerticalLine<char>>>,
592    horizontal1: Option<HorizontalLine<char>>,
593) {
594    if let Some(line) = horizontal1 {
595        cfg.insert_horizontal_line(1, line);
596    }
597
598    if let Some(horizontals) = horizontals {
599        for (row, line) in horizontals {
600            cfg.insert_horizontal_line(row, line);
601        }
602    }
603
604    if let Some(verticals) = verticals {
605        for (col, line) in verticals {
606            cfg.insert_vertical_line(col, line);
607        }
608    }
609}
610
611const fn hlines_find<const N: usize>(
612    lines: [(usize, HorizontalLine<char>); N],
613    search: usize,
614) -> Option<HorizontalLine<char>> {
615    let mut line = None;
616
617    let mut i = 0;
618    while i < lines.len() {
619        let (num, hline) = lines[i];
620        if num == search {
621            line = Some(hline);
622        }
623
624        i += 1;
625    }
626
627    line
628}