1use core::marker::PhantomData;
2
3use crate::{
4 grid::config::Border as GridBorder,
5 settings::style::{On, Style},
6};
7
8#[cfg(feature = "std")]
9use crate::{
10 grid::config::{ColoredConfig, Entity, Position},
11 grid::records::{ExactRecords, Records},
12 settings::{CellOption, TableOption},
13};
14
15#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
39pub 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
47impl<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
59impl Border<(), (), (), ()> {
60 pub const fn new() -> Self {
62 Self::from_border(GridBorder::empty())
63 }
64}
65
66impl Border<On, On, On, On> {
67 #[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 pub const fn filled(c: char) -> Self {
94 Self::full(c, c, c, c, c, c, c, c)
95 }
96
97 pub const fn empty() -> EmptyBorder {
99 EmptyBorder
100 }
101}
102
103impl<T, B, L, R> Border<T, B, L, R> {
104 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 = style.get_borders();
109 let line = GridBorder::new(
110 borders.top,
111 borders.bottom,
112 borders.left,
113 borders.right,
114 borders.top_left,
115 borders.bottom_left,
116 borders.top_right,
117 borders.bottom_right,
118 );
119
120 Self::from_border(line)
121 }
122}
123
124impl<T, B, L, R> Border<T, B, L, R> {
125 pub const fn 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 pub const fn 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 pub const fn 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 pub const fn 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 pub const fn into_inner(self) -> GridBorder<char> {
151 self.inner
152 }
153}
154
155impl<T, B, L> Border<T, B, L, On> {
156 pub const fn get_right(&self) -> char {
158 get_char(self.inner.right)
159 }
160}
161
162impl<T, B, R> Border<T, B, On, R> {
163 pub const fn get_left(&self) -> char {
165 get_char(self.inner.left)
166 }
167}
168
169impl<B, L, R> Border<On, B, L, R> {
170 pub const fn get_top(&self) -> char {
172 get_char(self.inner.top)
173 }
174}
175
176impl<T, L, R> Border<T, On, L, R> {
177 pub const fn get_bottom(&self) -> char {
179 get_char(self.inner.bottom)
180 }
181}
182
183impl<B, R> Border<On, B, On, R> {
184 pub const fn corner_top_left(mut self, c: char) -> Self {
186 self.inner.left_top_corner = Some(c);
187 self
188 }
189
190 pub const fn get_corner_top_left(&self) -> char {
192 get_char(self.inner.left_top_corner)
193 }
194}
195
196impl<B, L> Border<On, B, L, On> {
197 pub const fn corner_top_right(mut self, c: char) -> Self {
199 self.inner.right_top_corner = Some(c);
200 self
201 }
202
203 pub const fn get_corner_top_right(&self) -> char {
205 get_char(self.inner.right_top_corner)
206 }
207}
208
209impl<T, R> Border<T, On, On, R> {
210 pub const fn corner_bottom_left(mut self, c: char) -> Self {
212 self.inner.left_bottom_corner = Some(c);
213 self
214 }
215
216 pub const fn get_corner_bottom_left(&self) -> char {
218 get_char(self.inner.left_bottom_corner)
219 }
220}
221
222impl<T, L> Border<T, On, L, On> {
223 pub const fn corner_bottom_right(mut self, c: char) -> Self {
225 self.inner.right_bottom_corner = Some(c);
226 self
227 }
228
229 pub const fn get_corner_bottom_right(&self) -> char {
231 get_char(self.inner.right_bottom_corner)
232 }
233}
234
235impl<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")]
242impl<T, B, L, R, Data> CellOption<Data, ColoredConfig> for Border<T, B, L, R>
243where
244 Data: Records + ExactRecords,
245{
246 fn change(self, records: &mut Data, cfg: &mut ColoredConfig, entity: Entity) {
247 CellOption::change(self.inner, records, cfg, entity)
248 }
249}
250
251#[cfg(feature = "std")]
252impl<T, B, L, R, Data, D> TableOption<Data, ColoredConfig, D> for Border<T, B, L, R>
253where
254 Data: Records + ExactRecords,
255{
256 fn change(self, records: &mut Data, cfg: &mut ColoredConfig, dims: &mut D) {
257 let border = self.into_inner();
258 TableOption::change(border, records, cfg, dims);
259 }
260}
261
262#[cfg(feature = "std")]
263impl<R> CellOption<R, ColoredConfig> for GridBorder<char>
264where
265 R: Records + ExactRecords,
266{
267 fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
268 let shape = (records.count_rows(), records.count_columns());
269
270 for pos in entity.iter(shape.0, shape.1) {
271 cfg.set_border(pos, self);
272 }
273 }
274}
275
276#[cfg(feature = "std")]
277impl<R, D> TableOption<R, ColoredConfig, D> for GridBorder<char> {
278 fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
279 let mut borders = *cfg.get_borders();
280 borders.top = self.top;
281 borders.bottom = self.bottom;
282 borders.left = self.left;
283 borders.right = self.right;
284 borders.top_left = self.left_top_corner;
285 borders.top_right = self.right_top_corner;
286 borders.bottom_left = self.left_bottom_corner;
287 borders.bottom_right = self.right_bottom_corner;
288
289 if borders.has_vertical() {
290 borders.top_intersection = self.top;
291 borders.bottom_intersection = self.bottom;
292 }
293
294 if borders.has_horizontal() {
295 borders.left_intersection = self.left;
296 borders.right_intersection = self.right;
297 }
298
299 cfg.set_borders(borders);
300 }
301}
302
303#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
304pub struct EmptyBorder;
305
306#[cfg(feature = "std")]
307impl<R> CellOption<R, ColoredConfig> for EmptyBorder
308where
309 R: Records + ExactRecords,
310{
311 fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
312 let shape = (records.count_rows(), records.count_columns());
313
314 for pos in entity.iter(shape.0, shape.1) {
315 cfg.remove_border(pos, shape);
316 }
317 }
318}
319
320#[cfg(feature = "std")]
321impl<R, D> TableOption<R, ColoredConfig, D> for EmptyBorder
322where
323 R: Records + ExactRecords,
324{
325 fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
326 let count_rows = records.count_rows();
327 let count_columns = records.count_columns();
328 let shape = (count_rows, count_columns);
329
330 if count_rows == 0 || count_columns == 0 {
331 return;
332 }
333
334 let mut borders = *cfg.get_borders();
335 borders.top = None;
336 borders.top_intersection = None;
337 borders.top_left = None;
338 borders.top_right = None;
339 borders.bottom = None;
340 borders.bottom_intersection = None;
341 borders.bottom_left = None;
342 borders.bottom_right = None;
343 borders.left = None;
344 borders.left_intersection = None;
345 borders.right = None;
346 borders.right_intersection = None;
347
348 cfg.set_borders(borders);
349
350 for col in 0..count_columns {
351 let pos = Position::new(0, col);
352 let mut border = cfg.get_border(pos, shape);
353 if border.top.is_some() || border.right_top_corner.is_some() {
354 border.top = None;
355 border.right_top_corner = None;
356 cfg.set_border(pos, border);
357 }
358
359 let pos = Position::new(count_rows - 1, col);
360 let mut border = cfg.get_border(pos, shape);
361 if border.bottom.is_some() || border.right_bottom_corner.is_some() {
362 border.bottom = None;
363 border.right_bottom_corner = None;
364 cfg.set_border(pos, border);
365 }
366 }
367
368 for row in 0..count_rows {
369 let pos = Position::new(row, 0);
370 let mut border = cfg.get_border(pos, shape);
371 if border.left.is_some() || border.left_bottom_corner.is_some() {
372 border.left = None;
373 border.left_bottom_corner = None;
374 cfg.set_border(pos, border);
375 }
376
377 let pos = Position::new(row, count_columns - 1);
378 let mut border = cfg.get_border(pos, shape);
379 if border.right.is_some() || border.right_bottom_corner.is_some() {
380 border.right = None;
381 border.right_bottom_corner = None;
382 cfg.set_border(pos, border);
383 }
384 }
385
386 let pos = Position::new(0, 0);
387 let mut b = cfg.get_border(pos, shape);
388 if b.left_top_corner.is_some() {
389 b.left_top_corner = None;
390 cfg.set_border(pos, b);
391 }
392
393 let pos = Position::new(0, count_columns - 1);
394 let mut b = cfg.get_border(pos, shape);
395 if b.right_top_corner.is_some() {
396 b.right_top_corner = None;
397 cfg.set_border(pos, b);
398 }
399
400 let pos = Position::new(count_rows - 1, 0);
401 let mut b = cfg.get_border(pos, shape);
402 if b.left_bottom_corner.is_some() {
403 b.left_bottom_corner = None;
404 cfg.set_border(pos, b);
405 }
406
407 let pos = Position::new(count_rows - 1, count_columns - 1);
408 let mut b = cfg.get_border(pos, shape);
409 if b.right_bottom_corner.is_some() {
410 b.right_bottom_corner = None;
411 cfg.set_border(pos, b);
412 }
413 }
414}
415
416const fn get_char(c: Option<char>) -> char {
417 match c {
418 Some(c) => c,
419 None => unreachable!(),
420 }
421}