papergrid/config/
border.rs

1/// Border is a representation of a cells's borders (left, right, top, bottom, and the corners)
2///
3///
4/// ```text
5///                         top border
6///                             |
7///                             V
8/// corner top left ------> +_______+  <---- corner top left
9///                         |       |
10/// left border ----------> |  cell |  <---- right border
11///                         |       |
12/// corner bottom right --> +_______+  <---- corner bottom right
13///                             ^
14///                             |
15///                        bottom border
16/// ```
17#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Hash)]
18pub struct Border<T> {
19    /// A character for a top.
20    pub top: Option<T>,
21    /// A character for a bottom.
22    pub bottom: Option<T>,
23    /// A character for a left.
24    pub left: Option<T>,
25    /// A character for a right.
26    pub right: Option<T>,
27    /// A character for a left top corner.
28    pub left_top_corner: Option<T>,
29    /// A character for a left bottom corner.
30    pub left_bottom_corner: Option<T>,
31    /// A character for a right top corner.
32    pub right_top_corner: Option<T>,
33    /// A character for a right bottom corner.
34    pub right_bottom_corner: Option<T>,
35}
36
37impl<T> Border<T> {
38    /// This function constructs a cell borders with all sides set.
39    #[allow(clippy::too_many_arguments)]
40    pub const fn new(
41        top: Option<T>,
42        bottom: Option<T>,
43        left: Option<T>,
44        right: Option<T>,
45        left_top_corner: Option<T>,
46        left_bottom_corner: Option<T>,
47        right_top_corner: Option<T>,
48        right_bottom_corner: Option<T>,
49    ) -> Self {
50        Self {
51            top,
52            bottom,
53            left,
54            right,
55            left_top_corner,
56            left_bottom_corner,
57            right_top_corner,
58            right_bottom_corner,
59        }
60    }
61
62    /// This function constructs a cell borders with all sides set.
63    #[allow(clippy::too_many_arguments)]
64    pub const fn full(
65        top: T,
66        bottom: T,
67        left: T,
68        right: T,
69        top_left: T,
70        top_right: T,
71        bottom_left: T,
72        bottom_right: T,
73    ) -> Self {
74        Self::new(
75            Some(top),
76            Some(bottom),
77            Some(left),
78            Some(right),
79            Some(top_left),
80            Some(bottom_left),
81            Some(top_right),
82            Some(bottom_right),
83        )
84    }
85
86    /// This function constructs a cell borders with all sides being empty (set off).
87    pub const fn empty() -> Self {
88        Self::new(None, None, None, None, None, None, None, None)
89    }
90
91    /// Checks whether any side is set.
92    pub const fn is_empty(&self) -> bool {
93        self.top.is_none()
94            && self.left_top_corner.is_none()
95            && self.right_top_corner.is_none()
96            && self.bottom.is_none()
97            && self.left_bottom_corner.is_none()
98            && self.left_top_corner.is_none()
99            && self.left.is_none()
100            && self.right.is_none()
101    }
102
103    /// Checks whether all sides are equal to one another.
104    pub fn is_same(&self) -> bool
105    where
106        T: PartialEq,
107    {
108        self.top == self.bottom
109            && self.top == self.left
110            && self.top == self.right
111            && self.top == self.left_top_corner
112            && self.top == self.right_top_corner
113            && self.top == self.left_bottom_corner
114            && self.top == self.right_bottom_corner
115    }
116
117    /// Verifies whether anything is set on the top.
118    pub const fn has_top(&self) -> bool {
119        self.top.is_some() || self.left_top_corner.is_some() || self.right_top_corner.is_some()
120    }
121
122    /// Verifies whether anything is set on the bottom.
123    pub const fn has_bottom(&self) -> bool {
124        self.bottom.is_some()
125            || self.left_bottom_corner.is_some()
126            || self.right_bottom_corner.is_some()
127    }
128
129    /// Verifies whether anything is set on the left.
130    pub const fn has_left(&self) -> bool {
131        self.left.is_some() || self.left_top_corner.is_some() || self.left_bottom_corner.is_some()
132    }
133
134    /// Verifies whether anything is set on the right.
135    pub const fn has_right(&self) -> bool {
136        self.right.is_some()
137            || self.right_top_corner.is_some()
138            || self.right_bottom_corner.is_some()
139    }
140
141    /// Converts a border with a given function.
142    pub fn map<F, T1>(self, f: F) -> Border<T1>
143    where
144        F: Fn(T) -> T1,
145    {
146        Border {
147            top: self.top.map(&f),
148            bottom: self.bottom.map(&f),
149            left: self.left.map(&f),
150            right: self.right.map(&f),
151            left_top_corner: self.left_top_corner.map(&f),
152            left_bottom_corner: self.left_bottom_corner.map(&f),
153            right_top_corner: self.right_top_corner.map(&f),
154            right_bottom_corner: self.right_bottom_corner.map(&f),
155        }
156    }
157}
158
159impl<T: Copy> Border<T> {
160    /// This function constructs a cell borders with all sides's char set to a given character.
161    ///
162    /// It behaves like [`Border::full`] with the same character set to each side.
163    pub const fn filled(c: T) -> Self {
164        Self::full(c, c, c, c, c, c, c, c)
165    }
166}
167
168impl<T: Copy> Border<&T> {
169    /// Copies the underlying reference to a new border.
170    pub fn copied(&self) -> Border<T> {
171        Border {
172            top: self.top.copied(),
173            bottom: self.bottom.copied(),
174            left: self.left.copied(),
175            right: self.right.copied(),
176            left_bottom_corner: self.left_bottom_corner.copied(),
177            left_top_corner: self.left_top_corner.copied(),
178            right_bottom_corner: self.right_bottom_corner.copied(),
179            right_top_corner: self.right_top_corner.copied(),
180        }
181    }
182}
183
184impl<T: Clone> Border<&T> {
185    /// Copies the underlying reference to a new border.
186    pub fn cloned(&self) -> Border<T> {
187        Border {
188            top: self.top.cloned(),
189            bottom: self.bottom.cloned(),
190            left: self.left.cloned(),
191            right: self.right.cloned(),
192            left_bottom_corner: self.left_bottom_corner.cloned(),
193            left_top_corner: self.left_top_corner.cloned(),
194            right_bottom_corner: self.right_bottom_corner.cloned(),
195            right_top_corner: self.right_top_corner.cloned(),
196        }
197    }
198}
199
200impl<T> Border<T> {
201    /// Convert all values on the border into another ones.
202    pub fn convert<B>(self) -> Border<B>
203    where
204        B: From<T>,
205    {
206        macro_rules! conv_opt {
207            ($opt:expr) => {
208                match $opt {
209                    Some(opt) => Some(B::from(opt)),
210                    None => None,
211                }
212            };
213        }
214
215        Border {
216            top: conv_opt!(self.top),
217            bottom: conv_opt!(self.bottom),
218            left: conv_opt!(self.left),
219            right: conv_opt!(self.right),
220            left_bottom_corner: conv_opt!(self.left_bottom_corner),
221            left_top_corner: conv_opt!(self.left_top_corner),
222            right_bottom_corner: conv_opt!(self.right_bottom_corner),
223            right_top_corner: conv_opt!(self.right_top_corner),
224        }
225    }
226}