Skip to main content

urushi/style/
border.rs

1//! Border character sets for boxed content.
2
3/// The characters used to draw a box border.
4///
5/// Presets cover the common box-drawing styles; custom borders can be built
6/// with struct literal syntax by supplying the four edges and four corners.
7/// All characters are assumed to be one terminal cell wide.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct Border {
10    pub top: char,
11    pub bottom: char,
12    pub left: char,
13    pub right: char,
14    pub top_left: char,
15    pub top_right: char,
16    pub bottom_left: char,
17    pub bottom_right: char,
18}
19
20impl Border {
21    /// Single-line border: `┌─┐`
22    pub const NORMAL: Self = Self {
23        top: '─',
24        bottom: '─',
25        left: '│',
26        right: '│',
27        top_left: '┌',
28        top_right: '┐',
29        bottom_left: '└',
30        bottom_right: '┘',
31    };
32
33    /// Rounded-corner border: `╭─╮`
34    pub const ROUNDED: Self = Self {
35        top_left: '╭',
36        top_right: '╮',
37        bottom_left: '╰',
38        bottom_right: '╯',
39        ..Self::NORMAL
40    };
41
42    /// Heavy-line border: `┏━┓`
43    pub const THICK: Self = Self {
44        top: '━',
45        bottom: '━',
46        left: '┃',
47        right: '┃',
48        top_left: '┏',
49        top_right: '┓',
50        bottom_left: '┗',
51        bottom_right: '┛',
52    };
53
54    /// Double-line border: `╔═╗`
55    pub const DOUBLE: Self = Self {
56        top: '═',
57        bottom: '═',
58        left: '║',
59        right: '║',
60        top_left: '╔',
61        top_right: '╗',
62        bottom_left: '╚',
63        bottom_right: '╝',
64    };
65
66    /// ASCII-only border for terminals without box-drawing glyphs: `+-+`
67    pub const ASCII: Self = Self {
68        top: '-',
69        bottom: '-',
70        left: '|',
71        right: '|',
72        top_left: '+',
73        top_right: '+',
74        bottom_left: '+',
75        bottom_right: '+',
76    };
77
78    /// Invisible border that still occupies one cell on each side.
79    pub const HIDDEN: Self = Self {
80        top: ' ',
81        bottom: ' ',
82        left: ' ',
83        right: ' ',
84        top_left: ' ',
85        top_right: ' ',
86        bottom_left: ' ',
87        bottom_right: ' ',
88    };
89}
90
91#[cfg(test)]
92mod tests {
93    use super::Border;
94
95    #[test]
96    fn presets_define_box_edges_and_corners() {
97        let cases = [
98            (Border::NORMAL, ['─', '─', '│', '│', '┌', '┘']),
99            (Border::ROUNDED, ['─', '─', '│', '│', '╭', '╯']),
100            (Border::THICK, ['━', '━', '┃', '┃', '┏', '┛']),
101            (Border::DOUBLE, ['═', '═', '║', '║', '╔', '╝']),
102            (Border::ASCII, ['-', '-', '|', '|', '+', '+']),
103            (Border::HIDDEN, [' ', ' ', ' ', ' ', ' ', ' ']),
104        ];
105
106        for (border, expected) in cases {
107            assert_eq!(
108                [
109                    border.top,
110                    border.bottom,
111                    border.left,
112                    border.right,
113                    border.top_left,
114                    border.bottom_right,
115                ],
116                expected
117            );
118        }
119    }
120
121    #[test]
122    fn custom_border_can_define_every_box_glyph() {
123        let border = Border {
124            top: 't',
125            bottom: 'b',
126            left: 'l',
127            right: 'r',
128            top_left: '1',
129            top_right: '2',
130            bottom_left: '3',
131            bottom_right: '4',
132        };
133
134        assert_eq!(
135            [
136                border.top,
137                border.bottom,
138                border.left,
139                border.right,
140                border.top_left,
141                border.top_right,
142                border.bottom_left,
143                border.bottom_right,
144            ],
145            ['t', 'b', 'l', 'r', '1', '2', '3', '4']
146        );
147    }
148}