1 | use core::marker::PhantomData; |
2 | |
3 | use crate::{ |
4 | grid::config::Border as GridBorder, |
5 | settings::{style::On, style::Style}, |
6 | }; |
7 | |
8 | #[cfg (feature = "std" )] |
9 | use crate::{ |
10 | grid::config::{ColoredConfig, Entity}, |
11 | grid::records::{ExactRecords, Records}, |
12 | settings::CellOption, |
13 | }; |
14 | |
15 | /// Border represents a border of a Cell. |
16 | /// |
17 | /// ```text |
18 | /// top border |
19 | /// | |
20 | /// V |
21 | /// corner top left ------> +_______+ <---- corner top left |
22 | /// | | |
23 | /// left border ----------> | cell | <---- right border |
24 | /// | | |
25 | /// corner bottom right --> +_______+ <---- corner bottom right |
26 | /// ^ |
27 | /// | |
28 | /// bottom border |
29 | /// ``` |
30 | /// |
31 | /// ```rust,no_run |
32 | /// # use tabled::{Table, settings::{style::{Style, Border}, object::Rows}}; |
33 | /// # let data: Vec<&'static str> = Vec::new(); |
34 | /// let table = Table::new(&data) |
35 | /// .with(Style::ascii()) |
36 | /// .modify(Rows::single(0), Border::new().set_top('x' )); |
37 | /// ``` |
38 | #[derive (Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
39 | pub struct Border<T, B, L, R> { |
40 | inner: GridBorder<char>, |
41 | _top: PhantomData<T>, |
42 | _bottom: PhantomData<B>, |
43 | _left: PhantomData<L>, |
44 | _right: PhantomData<R>, |
45 | } |
46 | |
47 | impl<T, B, L, R> Border<T, B, L, R> { |
48 | pub(crate) const fn from_border(inner: GridBorder<char>) -> Border<T, B, L, R> { |
49 | Border { |
50 | inner, |
51 | _top: PhantomData, |
52 | _bottom: PhantomData, |
53 | _left: PhantomData, |
54 | _right: PhantomData, |
55 | } |
56 | } |
57 | } |
58 | |
59 | impl Border<(), (), (), ()> { |
60 | /// Creates an empty border. |
61 | pub const fn new() -> Self { |
62 | Self::from_border(inner:GridBorder::empty()) |
63 | } |
64 | } |
65 | |
66 | impl Border<On, On, On, On> { |
67 | /// This function constructs a cell borders with all sides set. |
68 | #[allow (clippy::too_many_arguments)] |
69 | pub const fn full( |
70 | top: char, |
71 | bottom: char, |
72 | left: char, |
73 | right: char, |
74 | top_left: char, |
75 | top_right: char, |
76 | bottom_left: char, |
77 | bottom_right: char, |
78 | ) -> Self { |
79 | Border::from_border(GridBorder::full( |
80 | top, |
81 | bottom, |
82 | left, |
83 | right, |
84 | top_left, |
85 | top_right, |
86 | bottom_left, |
87 | bottom_right, |
88 | )) |
89 | } |
90 | |
91 | /// This function constructs a cell borders with all sides's char set to a given character. |
92 | /// It behaves like [`Border::full`] with the same character set to each side. |
93 | pub const fn filled(c: char) -> Self { |
94 | Self::full(c, c, c, c, c, c, c, c) |
95 | } |
96 | |
97 | /// Using this function you deconstruct the existing borders. |
98 | pub const fn empty() -> EmptyBorder { |
99 | EmptyBorder |
100 | } |
101 | } |
102 | |
103 | impl<T, B, L, R> Border<T, B, L, R> { |
104 | /// Fetches outer border from a style. |
105 | pub const fn inherit<H, V, const HSIZE: usize, const VSIZE: usize>( |
106 | style: Style<T, B, L, R, H, V, HSIZE, VSIZE>, |
107 | ) -> Self { |
108 | let borders: Borders = style.get_borders(); |
109 | let line: Border = GridBorder::new( |
110 | borders.top, |
111 | borders.bottom, |
112 | borders.left, |
113 | borders.right, |
114 | left_top_corner:borders.top_left, |
115 | left_bottom_corner:borders.bottom_left, |
116 | right_top_corner:borders.top_right, |
117 | right_bottom_corner:borders.bottom_right, |
118 | ); |
119 | |
120 | Self::from_border(inner:line) |
121 | } |
122 | } |
123 | |
124 | impl<T, B, L, R> Border<T, B, L, R> { |
125 | /// Set a top border character. |
126 | pub const fn set_top(mut self, c: char) -> Border<On, B, L, R> { |
127 | self.inner.top = Some(c); |
128 | Border::from_border(self.inner) |
129 | } |
130 | |
131 | /// Set a bottom border character. |
132 | pub const fn set_bottom(mut self, c: char) -> Border<T, On, L, R> { |
133 | self.inner.bottom = Some(c); |
134 | Border::from_border(self.inner) |
135 | } |
136 | |
137 | /// Set a left border character. |
138 | pub const fn set_left(mut self, c: char) -> Border<T, B, On, R> { |
139 | self.inner.left = Some(c); |
140 | Border::from_border(self.inner) |
141 | } |
142 | |
143 | /// Set a right border character. |
144 | pub const fn set_right(mut self, c: char) -> Border<T, B, L, On> { |
145 | self.inner.right = Some(c); |
146 | Border::from_border(self.inner) |
147 | } |
148 | |
149 | /// Converts a border into a general data structure. |
150 | pub const fn into_inner(self) -> GridBorder<char> { |
151 | self.inner |
152 | } |
153 | } |
154 | |
155 | impl<T, B, L> Border<T, B, L, On> { |
156 | /// Get a right character. |
157 | pub const fn get_right(&self) -> char { |
158 | get_char(self.inner.right) |
159 | } |
160 | } |
161 | |
162 | impl<T, B, R> Border<T, B, On, R> { |
163 | /// Get a left character. |
164 | pub const fn get_left(&self) -> char { |
165 | get_char(self.inner.left) |
166 | } |
167 | } |
168 | |
169 | impl<B, L, R> Border<On, B, L, R> { |
170 | /// Get a top character. |
171 | pub const fn get_top(&self) -> char { |
172 | get_char(self.inner.top) |
173 | } |
174 | } |
175 | |
176 | impl<T, L, R> Border<T, On, L, R> { |
177 | /// Get a bottom character. |
178 | pub const fn get_bottom(&self) -> char { |
179 | get_char(self.inner.bottom) |
180 | } |
181 | } |
182 | |
183 | impl<B, R> Border<On, B, On, R> { |
184 | /// Set a top left intersection character. |
185 | pub const fn set_corner_top_left(mut self, c: char) -> Self { |
186 | self.inner.left_top_corner = Some(c); |
187 | self |
188 | } |
189 | |
190 | /// Get a top left intersection character. |
191 | pub const fn get_corner_top_left(&self) -> char { |
192 | get_char(self.inner.left_top_corner) |
193 | } |
194 | } |
195 | |
196 | impl<B, L> Border<On, B, L, On> { |
197 | /// Set a top right intersection character. |
198 | pub const fn set_corner_top_right(mut self, c: char) -> Self { |
199 | self.inner.right_top_corner = Some(c); |
200 | self |
201 | } |
202 | |
203 | /// Get a top right intersection character. |
204 | pub const fn get_corner_top_right(&self) -> char { |
205 | get_char(self.inner.right_top_corner) |
206 | } |
207 | } |
208 | |
209 | impl<T, R> Border<T, On, On, R> { |
210 | /// Set a bottom left intersection character. |
211 | pub const fn set_corner_bottom_left(mut self, c: char) -> Self { |
212 | self.inner.left_bottom_corner = Some(c); |
213 | self |
214 | } |
215 | |
216 | /// Get a bottom left intersection character. |
217 | pub const fn get_corner_bottom_left(&self) -> char { |
218 | get_char(self.inner.left_bottom_corner) |
219 | } |
220 | } |
221 | |
222 | impl<T, L> Border<T, On, L, On> { |
223 | /// Set a bottom right intersection character. |
224 | pub const fn set_corner_bottom_right(mut self, c: char) -> Self { |
225 | self.inner.right_bottom_corner = Some(c); |
226 | self |
227 | } |
228 | |
229 | /// Get a bottom left intersection character. |
230 | pub const fn get_corner_bottom_right(&self) -> char { |
231 | get_char(self.inner.right_bottom_corner) |
232 | } |
233 | } |
234 | |
235 | impl<T, B, L, R> From<Border<T, B, L, R>> for GridBorder<char> { |
236 | fn from(value: Border<T, B, L, R>) -> Self { |
237 | value.inner |
238 | } |
239 | } |
240 | |
241 | #[cfg (feature = "std" )] |
242 | |
243 | impl<T, B, L, R, Data> CellOption<Data, ColoredConfig> for Border<T, B, L, R> |
244 | where |
245 | Data: Records + ExactRecords, |
246 | { |
247 | fn change(self, records: &mut Data, cfg: &mut ColoredConfig, entity: Entity) { |
248 | CellOption::change(self.inner, records, cfg, entity) |
249 | } |
250 | } |
251 | |
252 | #[cfg (feature = "std" )] |
253 | impl<R> CellOption<R, ColoredConfig> for GridBorder<char> |
254 | where |
255 | R: Records + ExactRecords, |
256 | { |
257 | fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) { |
258 | let shape: (usize, usize) = (records.count_rows(), records.count_columns()); |
259 | |
260 | for pos: (usize, usize) in entity.iter(count_rows:shape.0, count_cols:shape.1) { |
261 | cfg.set_border(pos, self); |
262 | } |
263 | } |
264 | } |
265 | |
266 | #[derive (Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
267 | pub struct EmptyBorder; |
268 | |
269 | #[cfg (feature = "std" )] |
270 | impl<R> CellOption<R, ColoredConfig> for EmptyBorder |
271 | where |
272 | R: Records + ExactRecords, |
273 | { |
274 | fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) { |
275 | let shape: (usize, usize) = (records.count_rows(), records.count_columns()); |
276 | |
277 | for pos: (usize, usize) in entity.iter(count_rows:shape.0, count_cols:shape.1) { |
278 | cfg.remove_border(pos, shape); |
279 | } |
280 | } |
281 | } |
282 | |
283 | const fn get_char(c: Option<char>) -> char { |
284 | match c { |
285 | Some(c: char) => c, |
286 | None => unreachable!(), |
287 | } |
288 | } |
289 | |