papergrid/config/spanned/
mod.rs

1//! A module which contains configuration options for a grid.
2
3mod borders_config;
4mod entity_map;
5
6use std::collections::HashMap;
7
8use crate::ansi::{ANSIBuf, ANSIStr};
9use crate::config::compact::CompactConfig;
10use crate::config::{
11    AlignmentHorizontal, AlignmentVertical, Border, Borders, Entity, Indent, Offset, Position,
12    Sides,
13};
14use borders_config::BordersConfig;
15
16pub use self::entity_map::EntityMap;
17
18use super::Formatting;
19
20/// HorizontalLine represents a horizontal border line.
21type HorizontalLine = super::HorizontalLine<char>;
22
23/// VerticalLine represents a vertical border line.
24type VerticalLine = super::VerticalLine<char>;
25
26/// This structure represents a settings of a grid.
27///
28/// grid: crate::Grid.
29#[derive(Debug, PartialEq, Eq, Clone)]
30pub struct SpannedConfig {
31    margin: Sides<MarginIndent>,
32    padding: EntityMap<Sides<Indent>>,
33    padding_color: EntityMap<Sides<Option<ANSIBuf>>>,
34    alignment_h: EntityMap<AlignmentHorizontal>,
35    alignment_v: EntityMap<AlignmentVertical>,
36    formatting_trim_h: EntityMap<bool>,
37    formatting_trim_v: EntityMap<bool>,
38    formatting_line_alignment: EntityMap<bool>,
39    span_columns: HashMap<Position, usize>,
40    span_rows: HashMap<Position, usize>,
41    borders: BordersConfig<char>,
42    borders_colors: BordersConfig<ANSIBuf>,
43    borders_missing_char: char,
44    horizontal_chars: HashMap<Position, HashMap<Offset, char>>,
45    horizontal_colors: HashMap<Position, HashMap<Offset, ANSIBuf>>, // squash a map to be HashMap<(Pos, Offset), char>
46    vertical_chars: HashMap<Position, HashMap<Offset, char>>,
47    vertical_colors: HashMap<Position, HashMap<Offset, ANSIBuf>>,
48    justification: EntityMap<char>,
49    justification_color: EntityMap<Option<ANSIBuf>>,
50}
51
52impl Default for SpannedConfig {
53    fn default() -> Self {
54        Self {
55            margin: Sides::default(),
56            padding: EntityMap::default(),
57            padding_color: EntityMap::default(),
58            formatting_trim_h: EntityMap::default(),
59            formatting_trim_v: EntityMap::default(),
60            formatting_line_alignment: EntityMap::default(),
61            alignment_h: EntityMap::new(AlignmentHorizontal::Left),
62            alignment_v: EntityMap::new(AlignmentVertical::Top),
63            span_columns: HashMap::default(),
64            span_rows: HashMap::default(),
65            borders: BordersConfig::default(),
66            borders_colors: BordersConfig::default(),
67            borders_missing_char: ' ',
68            horizontal_chars: HashMap::default(),
69            horizontal_colors: HashMap::default(),
70            vertical_chars: HashMap::default(),
71            vertical_colors: HashMap::default(),
72            justification: EntityMap::new(' '),
73            justification_color: EntityMap::default(),
74        }
75    }
76}
77
78impl SpannedConfig {
79    /// Creates a new spanned config.
80    pub fn new() -> Self {
81        Self::default()
82    }
83
84    /// Set a margin of a grid.
85    pub fn set_margin(&mut self, margin: Sides<Indent>) {
86        self.margin.left.indent = margin.left;
87        self.margin.right.indent = margin.right;
88        self.margin.top.indent = margin.top;
89        self.margin.bottom.indent = margin.bottom;
90    }
91
92    /// Set a color of margin of a grid.
93    pub fn set_margin_color(&mut self, margin: Sides<Option<ANSIBuf>>) {
94        self.margin.left.color = margin.left;
95        self.margin.right.color = margin.right;
96        self.margin.top.color = margin.top;
97        self.margin.bottom.color = margin.bottom;
98    }
99
100    /// Set an offset of margin of a grid.
101    pub fn set_margin_offset(&mut self, margin: Sides<Offset>) {
102        self.margin.left.offset = margin.left;
103        self.margin.right.offset = margin.right;
104        self.margin.top.offset = margin.top;
105        self.margin.bottom.offset = margin.bottom;
106    }
107
108    /// Returns a margin value currently set.
109    pub fn get_margin(&self) -> Sides<Indent> {
110        Sides::new(
111            self.margin.left.indent,
112            self.margin.right.indent,
113            self.margin.top.indent,
114            self.margin.bottom.indent,
115        )
116    }
117
118    /// Returns a margin color value currently set.
119    pub fn get_margin_color(&self) -> Sides<Option<&ANSIBuf>> {
120        Sides::new(
121            self.margin.left.color.as_ref(),
122            self.margin.right.color.as_ref(),
123            self.margin.top.color.as_ref(),
124            self.margin.bottom.color.as_ref(),
125        )
126    }
127
128    /// Returns a margin offset value currently set.
129    pub fn get_margin_offset(&self) -> Sides<Offset> {
130        Sides::new(
131            self.margin.left.offset,
132            self.margin.right.offset,
133            self.margin.top.offset,
134            self.margin.bottom.offset,
135        )
136    }
137
138    /// Removes border changes.
139    pub fn remove_borders(&mut self) {
140        self.borders = BordersConfig::default();
141    }
142
143    /// Removes border changes.
144    pub fn remove_borders_colors(&mut self) {
145        self.borders_colors = BordersConfig::default();
146    }
147
148    /// Removes border changes.
149    pub fn remove_color_line_horizontal(&mut self) {
150        self.horizontal_colors.clear();
151    }
152
153    /// Removes border changes.
154    pub fn remove_color_line_vertical(&mut self) {
155        self.vertical_colors.clear();
156    }
157
158    /// Removes border changes.
159    pub fn remove_horizontal_chars(&mut self) {
160        self.horizontal_chars.clear();
161    }
162
163    /// Removes border changes.
164    pub fn remove_vertical_chars(&mut self) {
165        self.vertical_chars.clear();
166    }
167
168    /// Set the [`Borders`] value as correct one.
169    pub fn set_borders(&mut self, borders: Borders<char>) {
170        self.borders.set_borders(borders);
171    }
172
173    /// Gets a global border value if set.
174    pub fn get_border_default(&self) -> Option<&char> {
175        self.borders.get_global()
176    }
177
178    /// Set the all [`Borders`] values to a char.
179    pub fn set_border_default(&mut self, c: char) {
180        self.borders.set_global(c);
181    }
182
183    /// Returns a current [`Borders`] structure.
184    pub fn get_borders(&self) -> &Borders<char> {
185        self.borders.get_borders()
186    }
187
188    /// Set the border line by row index.
189    ///
190    /// Row `0` means the top row.
191    /// Row `grid.count_rows()` means the bottom row.
192    pub fn insert_horizontal_line(&mut self, line: usize, val: HorizontalLine) {
193        self.borders.insert_horizontal_line(line, val);
194    }
195
196    /// Sets off the border line by row index if any were set
197    ///
198    /// Row `0` means the top row.
199    /// Row `grid.count_rows()` means the bottom row.
200    pub fn remove_horizontal_line(&mut self, line: usize, count_rows: usize) {
201        self.borders.remove_horizontal_line(line, count_rows);
202    }
203
204    /// Gets a overridden vertical line.
205    ///
206    /// Row `0` means the left row.
207    /// Row `grid.count_columns()` means the right most row.
208    pub fn get_vertical_line(&self, line: usize) -> Option<&VerticalLine> {
209        self.borders.get_vertical_line(line)
210    }
211
212    /// Gets all overridden vertical lines.
213    ///
214    /// Row `0` means the top row.
215    /// Row `grid.count_rows()` means the bottom row.
216    pub fn get_vertical_lines(&self) -> HashMap<usize, VerticalLine> {
217        self.borders.get_vertical_lines()
218    }
219
220    /// Set the border line by column index.
221    ///
222    /// Row `0` means the left row.
223    /// Row `grid.count_columns()` means the right most row.
224    pub fn insert_vertical_line(&mut self, line: usize, val: VerticalLine) {
225        self.borders.insert_vertical_line(line, val);
226    }
227
228    /// Sets off the border line by column index if any were set
229    ///
230    /// Row `0` means the left row.
231    /// Row `grid.count_columns()` means the right most row.
232    pub fn remove_vertical_line(&mut self, line: usize, count_columns: usize) {
233        self.borders.remove_vertical_line(line, count_columns);
234    }
235
236    /// Gets a overridden line.
237    ///
238    /// Row `0` means the top row.
239    /// Row `grid.count_rows()` means the bottom row.
240    pub fn get_horizontal_line(&self, line: usize) -> Option<&HorizontalLine> {
241        self.borders.get_horizontal_line(line)
242    }
243
244    /// Gets all overridden lines.
245    ///
246    /// Row `0` means the top row.
247    /// Row `grid.count_rows()` means the bottom row.
248    pub fn get_horizontal_lines(&self) -> HashMap<usize, HorizontalLine> {
249        self.borders.get_horizontal_lines()
250    }
251
252    /// Override a character on a horizontal line.
253    ///
254    /// If borders are not set the char won't be used.
255    ///
256    /// It takes not cell position but line as row and column of a cell;
257    /// So its range is line <= count_rows && col < count_columns.
258    pub fn set_horizontal_char(&mut self, pos: Position, offset: Offset, c: char) {
259        let chars = self
260            .horizontal_chars
261            .entry(pos)
262            .or_insert_with(|| HashMap::with_capacity(1));
263
264        chars.insert(offset, c);
265    }
266
267    /// Get a list of overridden chars in a horizontal border.
268    ///
269    /// It takes not cell position but line as row and column of a cell;
270    /// So its range is line <= count_rows && col < count_columns.
271    pub fn lookup_horizontal_char(&self, pos: Position, offset: usize, end: usize) -> Option<char> {
272        self.horizontal_chars
273            .get(&pos)
274            .and_then(|chars| {
275                chars.get(&Offset::Start(offset)).or_else(|| {
276                    if end > offset {
277                        if end == 0 {
278                            chars.get(&Offset::End(0))
279                        } else {
280                            chars.get(&Offset::End(end - offset - 1))
281                        }
282                    } else {
283                        None
284                    }
285                })
286            })
287            .copied()
288    }
289
290    /// Checks if there any char in a horizontal border being overridden.
291    ///
292    /// It takes not cell position but line as row and column of a cell;
293    /// So its range is line <= count_rows && col < count_columns.
294    pub fn is_overridden_horizontal(&self, pos: Position) -> bool {
295        self.horizontal_chars.contains_key(&pos)
296    }
297
298    /// Removes a list of overridden chars in a horizontal border.
299    ///
300    /// It takes not cell position but line as row and column of a cell;
301    /// So its range is line <= count_rows && col < count_columns.
302    pub fn remove_overridden_horizontal(&mut self, pos: Position) {
303        self.horizontal_chars.remove(&pos);
304    }
305
306    /// Override a vertical split line.
307    ///
308    /// If borders are not set the char won't be used.
309    ///
310    /// It takes not cell position but cell row and column of a line;
311    /// So its range is row < count_rows && col <= count_columns.
312    pub fn set_vertical_char(&mut self, pos: Position, offset: Offset, c: char) {
313        let chars = self
314            .vertical_chars
315            .entry(pos)
316            .or_insert_with(|| HashMap::with_capacity(1));
317
318        chars.insert(offset, c);
319    }
320
321    /// Get a list of overridden chars in a horizontal border.
322    ///
323    /// It takes not cell position but cell row and column of a line;
324    /// So its range is row < count_rows && col <= count_columns.
325    pub fn lookup_vertical_char(&self, pos: Position, offset: usize, end: usize) -> Option<char> {
326        self.vertical_chars
327            .get(&pos)
328            .and_then(|chars| {
329                chars.get(&Offset::Start(offset)).or_else(|| {
330                    if end > offset {
331                        if end == 0 {
332                            chars.get(&Offset::End(0))
333                        } else {
334                            chars.get(&Offset::End(end - offset - 1))
335                        }
336                    } else {
337                        None
338                    }
339                })
340            })
341            .copied()
342    }
343
344    /// Checks if there any char in a horizontal border being overridden.
345    ///
346    /// It takes not cell position but cell row and column of a line;
347    /// So its range is row < count_rows && col <= count_columns.
348    pub fn is_overridden_vertical(&self, pos: Position) -> bool {
349        self.vertical_chars.contains_key(&pos)
350    }
351
352    /// Removes a list of overridden chars in a horizontal border.
353    ///
354    /// It takes not cell position but cell row and column of a line;
355    /// So its range is row < count_rows && col <= count_columns.
356    pub fn remove_overridden_vertical(&mut self, pos: Position) {
357        self.vertical_chars.remove(&pos);
358    }
359
360    /// Override a character color on a horizontal line.
361    pub fn set_horizontal_char_color(&mut self, pos: Position, offset: Offset, c: ANSIBuf) {
362        let chars = self
363            .horizontal_colors
364            .entry(pos)
365            .or_insert_with(|| HashMap::with_capacity(1));
366
367        chars.insert(offset, c);
368    }
369
370    /// Get a overridden color in a horizontal border.
371    pub fn lookup_horizontal_color(
372        &self,
373        pos: Position,
374        offset: usize,
375        end: usize,
376    ) -> Option<&ANSIBuf> {
377        self.horizontal_colors.get(&pos).and_then(|chars| {
378            chars.get(&Offset::Start(offset)).or_else(|| {
379                if end > offset {
380                    if end == 0 {
381                        chars.get(&Offset::End(0))
382                    } else {
383                        chars.get(&Offset::End(end - offset - 1))
384                    }
385                } else {
386                    None
387                }
388            })
389        })
390    }
391
392    /// Override a character color on a vertical line.
393    pub fn set_vertical_char_color(&mut self, pos: Position, offset: Offset, c: ANSIBuf) {
394        let chars = self
395            .vertical_colors
396            .entry(pos)
397            .or_insert_with(|| HashMap::with_capacity(1));
398
399        chars.insert(offset, c);
400    }
401
402    /// Get a overridden color in a vertical border.
403    pub fn lookup_vertical_color(
404        &self,
405        pos: Position,
406        offset: usize,
407        end: usize,
408    ) -> Option<&ANSIBuf> {
409        self.vertical_colors.get(&pos).and_then(|chars| {
410            chars.get(&Offset::Start(offset)).or_else(|| {
411                if end > offset {
412                    if end == 0 {
413                        chars.get(&Offset::End(0))
414                    } else {
415                        chars.get(&Offset::End(end - offset - 1))
416                    }
417                } else {
418                    None
419                }
420            })
421        })
422    }
423
424    /// Set a padding to a given cells.
425    pub fn set_padding(&mut self, entity: Entity, padding: Sides<Indent>) {
426        self.padding.insert(entity, padding);
427    }
428
429    /// Set a padding to a given cells.
430    pub fn set_padding_color(&mut self, entity: Entity, padding: Sides<Option<ANSIBuf>>) {
431        self.padding_color.insert(entity, padding);
432    }
433
434    /// Get a padding for a given cell by [Position].
435    pub fn get_padding(&self, pos: Position) -> &Sides<Indent> {
436        self.padding.get(pos)
437    }
438
439    /// Get a padding color for a given cell by [Position].
440    pub fn get_padding_color(&self, pos: Position) -> &Sides<Option<ANSIBuf>> {
441        self.padding_color.get(pos)
442    }
443
444    /// Set a formatting to a given cells.
445    pub fn set_trim_horizontal(&mut self, entity: Entity, on: bool) {
446        self.formatting_trim_h.insert(entity, on);
447    }
448
449    /// Get a formatting settings for a given cell by [Position].
450    pub fn get_trim_horizonal(&self, pos: Position) -> bool {
451        *self.formatting_trim_h.get(pos)
452    }
453
454    /// Set a formatting to a given cells.
455    pub fn set_trim_vertical(&mut self, entity: Entity, on: bool) {
456        self.formatting_trim_v.insert(entity, on);
457    }
458
459    /// Get a formatting settings for a given cell by [Position].
460    pub fn get_trim_vertical(&self, pos: Position) -> bool {
461        *self.formatting_trim_v.get(pos)
462    }
463
464    /// Set a formatting to a given cells.
465    pub fn set_line_alignment(&mut self, entity: Entity, on: bool) {
466        self.formatting_line_alignment.insert(entity, on);
467    }
468
469    /// Get a formatting settings for a given cell by [Position].
470    pub fn get_line_alignment(&self, pos: Position) -> bool {
471        *self.formatting_line_alignment.get(pos)
472    }
473
474    /// Get a formatting settings for a given cell by [Position].
475    pub fn get_formatting(&self, pos: Position) -> Formatting {
476        Formatting::new(
477            *self.formatting_trim_h.get(pos),
478            *self.formatting_trim_v.get(pos),
479            *self.formatting_line_alignment.get(pos),
480        )
481    }
482
483    /// Set a vertical alignment to a given cells.
484    pub fn set_alignment_vertical(&mut self, entity: Entity, alignment: AlignmentVertical) {
485        self.alignment_v.insert(entity, alignment);
486    }
487
488    /// Get a vertical alignment for a given cell by [Position].
489    pub fn get_alignment_vertical(&self, pos: Position) -> &AlignmentVertical {
490        self.alignment_v.get(pos)
491    }
492
493    /// Set a horizontal alignment to a given cells.
494    pub fn set_alignment_horizontal(&mut self, entity: Entity, alignment: AlignmentHorizontal) {
495        self.alignment_h.insert(entity, alignment);
496    }
497
498    /// Get a horizontal alignment for a given cell by [Position].
499    pub fn get_alignment_horizontal(&self, pos: Position) -> &AlignmentHorizontal {
500        self.alignment_h.get(pos)
501    }
502
503    /// Set border set a border value to all cells in [`Entity`].
504    pub fn set_border(&mut self, pos: Position, border: Border<char>) {
505        self.borders.insert_border(pos, border);
506    }
507
508    /// Returns a border of a cell.
509    pub fn get_border(&self, pos: Position, shape: (usize, usize)) -> Border<char> {
510        self.borders.get_border(pos, shape).copied()
511    }
512
513    /// Returns a border color of a cell.
514    pub fn get_border_color(&self, pos: Position, shape: (usize, usize)) -> Border<&ANSIBuf> {
515        self.borders_colors.get_border(pos, shape)
516    }
517
518    /// Set a character which will be used in case any misconfiguration of borders.
519    ///
520    /// It will be usde for example when you set a left char for border frame and top but didn't set a top left corner.
521    pub fn set_borders_missing(&mut self, c: char) {
522        self.borders_missing_char = c;
523    }
524
525    /// Get a character which will be used in case any misconfiguration of borders.
526    pub fn get_borders_missing(&self) -> char {
527        self.borders_missing_char
528    }
529
530    /// Gets a color of all borders on the grid.
531    pub fn get_border_color_default(&self) -> Option<&ANSIBuf> {
532        self.borders_colors.get_global()
533    }
534
535    /// Sets a color of all borders on the grid.
536    pub fn set_border_color_default(&mut self, clr: ANSIBuf) {
537        self.borders_colors = BordersConfig::default();
538        self.borders_colors.set_global(clr);
539    }
540
541    /// Gets colors of a borders carcass on the grid.
542    pub fn get_color_borders(&self) -> &Borders<ANSIBuf> {
543        self.borders_colors.get_borders()
544    }
545
546    /// Sets colors of border carcass on the grid.
547    pub fn set_borders_color(&mut self, clrs: Borders<ANSIBuf>) {
548        self.borders_colors.set_borders(clrs);
549    }
550
551    /// Sets a color of border of a cell on the grid.
552    pub fn set_border_color(&mut self, pos: Position, border: Border<ANSIBuf>) {
553        self.borders_colors.insert_border(pos, border)
554    }
555
556    /// Sets off all borders possible on the [`Entity`].
557    ///
558    /// It doesn't changes globally set borders through [`SpannedConfig::set_borders`].
559    //
560    // todo: would be great to remove a shape
561    pub fn remove_border(&mut self, pos: Position, shape: (usize, usize)) {
562        self.borders.remove_border(pos, shape);
563    }
564
565    /// Gets a color of border of a cell on the grid.
566    //
567    // todo: would be great to remove a shape
568    pub fn remove_border_color(&mut self, pos: Position, shape: (usize, usize)) {
569        self.borders_colors.remove_border(pos, shape);
570    }
571
572    /// Get a justification which will be used while expanding cells width/height.
573    pub fn get_justification(&self, pos: Position) -> char {
574        *self.justification.get(pos)
575    }
576
577    /// Get a justification color which will be used while expanding cells width/height.
578    ///
579    /// `None` means no color.
580    pub fn get_justification_color(&self, pos: Position) -> Option<&ANSIBuf> {
581        self.justification_color.get(pos).as_ref()
582    }
583
584    /// Set a justification which will be used while expanding cells width/height.
585    pub fn set_justification(&mut self, entity: Entity, c: char) {
586        self.justification.insert(entity, c);
587    }
588
589    /// Set a justification color which will be used while expanding cells width/height.
590    ///
591    /// `None` removes it.
592    pub fn set_justification_color(&mut self, entity: Entity, color: Option<ANSIBuf>) {
593        self.justification_color.insert(entity, color);
594    }
595
596    /// Get a span value of the cell, if any is set.
597    pub fn get_column_spans(&self) -> HashMap<Position, usize> {
598        self.span_columns.clone()
599    }
600
601    /// Get a span value of the cell, if any is set.
602    pub fn get_row_spans(&self) -> HashMap<Position, usize> {
603        self.span_rows.clone()
604    }
605
606    /// Get a span value of the cell, if any is set.
607    pub fn get_column_span(&self, pos: Position) -> Option<usize> {
608        self.span_columns.get(&pos).copied()
609    }
610
611    /// Get a span value of the cell, if any is set.
612    pub fn get_row_span(&self, pos: Position) -> Option<usize> {
613        self.span_rows.get(&pos).copied()
614    }
615
616    /// Removes column spans.
617    pub fn remove_column_spans(&mut self) {
618        self.span_columns.clear()
619    }
620
621    /// Removes row spans.
622    pub fn remove_row_spans(&mut self) {
623        self.span_rows.clear()
624    }
625
626    /// Set a column span to a given cells.
627    ///
628    /// BEWARE
629    ///
630    /// IT'S CALLER RESPONSIBILITY TO MAKE SURE
631    /// THAT THERE NO INTERSECTIONS IN PLACE AND THE SPAN VALUE IS CORRECT
632    pub fn set_column_span(&mut self, pos: Position, span: usize) {
633        set_cell_column_span(self, pos, span);
634    }
635
636    /// Verifies if there's any spans set.
637    pub fn has_column_spans(&self) -> bool {
638        !self.span_columns.is_empty()
639    }
640
641    /// Set a column span to a given cells.
642    ///
643    /// BEWARE
644    ///
645    /// IT'S CALLER RESPONSIBILITY TO MAKE SURE
646    /// THAT THERE NO INTERSECTIONS IN PLACE AND THE SPAN VALUE IS CORRECT
647    pub fn set_row_span(&mut self, pos: Position, span: usize) {
648        set_cell_row_span(self, pos, span);
649    }
650
651    /// Verifies if there's any spans set.
652    pub fn has_row_spans(&self) -> bool {
653        !self.span_rows.is_empty()
654    }
655
656    /// Verifies if there's any colors set for a borders.
657    pub fn has_border_colors(&self) -> bool {
658        !self.borders_colors.is_empty()
659    }
660
661    /// Verifies if there's any colors set for a borders.
662    pub fn has_offset_chars(&self) -> bool {
663        !self.horizontal_chars.is_empty() || !self.vertical_chars.is_empty()
664    }
665
666    /// Verifies if there's any colors set for a borders.
667    pub fn has_justification(&self) -> bool {
668        !self.justification.is_empty()
669            || !self.justification_color.is_empty()
670            || self.justification_color.as_ref().is_some()
671    }
672
673    /// Verifies if there's any custom padding set.
674    pub fn has_padding(&self) -> bool {
675        !self.padding.is_empty()
676    }
677
678    /// Verifies if there's any custom padding set.
679    pub fn has_padding_color(&self) -> bool {
680        if !self.padding_color.is_empty() {
681            let map = HashMap::from(self.padding_color.clone());
682            for (entity, value) in map {
683                if matches!(entity, Entity::Global) {
684                    continue;
685                }
686
687                if !value.is_empty() {
688                    return true;
689                }
690            }
691        }
692
693        !self.padding_color.as_ref().is_empty()
694    }
695
696    /// Verifies if there's any custom formatting set.
697    pub fn has_formatting(&self) -> bool {
698        !self.formatting_trim_h.is_empty()
699            || !self.formatting_trim_v.is_empty()
700            || !self.formatting_line_alignment.is_empty()
701    }
702
703    /// Verifies if there's any custom alignment vertical set.
704    pub fn has_alignment_vertical(&self) -> bool {
705        !self.alignment_v.is_empty()
706    }
707
708    /// Verifies if there's any custom alignment horizontal set.
709    pub fn has_alignment_horizontal(&self) -> bool {
710        !self.alignment_h.is_empty()
711    }
712
713    /// Gets an intersection character which would be rendered on the grid.
714    ///
715    /// grid: crate::Grid
716    pub fn get_intersection(&self, pos: Position, shape: (usize, usize)) -> Option<char> {
717        let c = self.borders.get_intersection(pos, shape);
718        if let Some(c) = c {
719            return Some(*c);
720        }
721
722        if self.has_horizontal(pos.row, shape.0) && self.has_vertical(pos.col, shape.1) {
723            return Some(self.get_borders_missing());
724        }
725
726        None
727    }
728
729    /// Gets a horizontal character which would be rendered on the grid.
730    ///
731    /// grid: crate::Grid
732    pub fn get_horizontal(&self, pos: Position, count_rows: usize) -> Option<char> {
733        let c = self.borders.get_horizontal(pos, count_rows);
734        if let Some(c) = c {
735            return Some(*c);
736        }
737
738        if self.has_horizontal(pos.row, count_rows) {
739            return Some(self.get_borders_missing());
740        }
741
742        None
743    }
744
745    /// Gets a vertical character which would be rendered on the grid.
746    ///
747    /// grid: crate::Grid
748    pub fn get_vertical(&self, pos: Position, count_columns: usize) -> Option<char> {
749        if let Some(c) = self.borders.get_vertical(pos, count_columns) {
750            return Some(*c);
751        }
752
753        if self.has_vertical(pos.col, count_columns) {
754            return Some(self.get_borders_missing());
755        }
756
757        None
758    }
759
760    /// Gets a color of a cell horizontal.
761    pub fn get_horizontal_color(&self, pos: Position, count_rows: usize) -> Option<&ANSIBuf> {
762        self.borders_colors.get_horizontal(pos, count_rows)
763    }
764
765    /// Gets a color of a cell vertical.
766    pub fn get_vertical_color(&self, pos: Position, count_columns: usize) -> Option<&ANSIBuf> {
767        self.borders_colors.get_vertical(pos, count_columns)
768    }
769
770    /// Gets a color of a cell vertical.
771    pub fn get_intersection_color(&self, pos: Position, shape: (usize, usize)) -> Option<&ANSIBuf> {
772        self.borders_colors.get_intersection(pos, shape)
773    }
774
775    /// Checks if grid would have a horizontal border with the current configuration.
776    ///
777    /// grid: crate::Grid
778    pub fn has_horizontal(&self, row: usize, count_rows: usize) -> bool {
779        self.borders.has_horizontal(row, count_rows)
780    }
781
782    /// Checks if grid would have a vertical border with the current configuration.
783    ///
784    /// grid: crate::Grid
785    pub fn has_vertical(&self, col: usize, count_columns: usize) -> bool {
786        self.borders.has_vertical(col, count_columns)
787    }
788
789    /// Calculates an amount of horizontal lines would present on the grid.
790    ///
791    /// grid: crate::Grid
792    pub fn count_horizontal(&self, count_rows: usize) -> usize {
793        (0..=count_rows)
794            .filter(|&row| self.has_horizontal(row, count_rows))
795            .count()
796    }
797
798    /// Calculates an amount of vertical lines would present on the grid.
799    ///
800    /// grid: crate::Grid
801    pub fn count_vertical(&self, count_columns: usize) -> usize {
802        (0..=count_columns)
803            .filter(|&col| self.has_vertical(col, count_columns))
804            .count()
805    }
806
807    /// The function returns whether the cells will be rendered or it will be hidden because of a span.
808    pub fn is_cell_visible(&self, pos: Position) -> bool {
809        !(self.is_cell_covered_by_column_span(pos)
810            || self.is_cell_covered_by_row_span(pos)
811            || self.is_cell_covered_by_both_spans(pos))
812    }
813
814    /// The function checks if a cell is hidden because of a row span.
815    pub fn is_cell_covered_by_row_span(&self, pos: Position) -> bool {
816        is_cell_covered_by_row_span(self, pos)
817    }
818
819    /// The function checks if a cell is hidden because of a column span.
820    pub fn is_cell_covered_by_column_span(&self, pos: Position) -> bool {
821        is_cell_covered_by_column_span(self, pos)
822    }
823
824    /// The function checks if a cell is hidden indirectly because of a row and column span combination.
825    pub fn is_cell_covered_by_both_spans(&self, pos: Position) -> bool {
826        is_cell_covered_by_both_spans(self, pos)
827    }
828}
829
830impl From<CompactConfig> for SpannedConfig {
831    fn from(compact: CompactConfig) -> Self {
832        use Entity::Global;
833
834        let mut cfg = Self::default();
835
836        cfg.set_padding(Global, *compact.get_padding());
837        cfg.set_padding_color(Global, to_ansi_color(*compact.get_padding_color()));
838        cfg.set_margin(*compact.get_margin());
839        cfg.set_margin_color(to_ansi_color(*compact.get_margin_color()));
840        cfg.set_alignment_horizontal(Global, compact.get_alignment_horizontal());
841        cfg.set_borders(*compact.get_borders());
842        cfg.set_borders_color(compact.get_borders_color().convert_into());
843
844        cfg
845    }
846}
847
848fn to_ansi_color(b: Sides<ANSIStr<'_>>) -> Sides<Option<ANSIBuf>> {
849    Sides::new(
850        Some(b.left.into()),
851        Some(b.right.into()),
852        Some(b.top.into()),
853        Some(b.bottom.into()),
854    )
855}
856
857fn set_cell_row_span(cfg: &mut SpannedConfig, pos: Position, span: usize) {
858    // such spans aren't supported
859    if span == 0 {
860        return;
861    }
862
863    // It's a default span so we can do nothing.
864    // but we check if it's an override of a span.
865    if span == 1 {
866        cfg.span_rows.remove(&pos);
867        return;
868    }
869
870    cfg.span_rows.insert(pos, span);
871}
872
873fn set_cell_column_span(cfg: &mut SpannedConfig, pos: Position, span: usize) {
874    // such spans aren't supported
875    if span == 0 {
876        return;
877    }
878
879    // It's a default span so we can do nothing.
880    // but we check if it's an override of a span.
881    if span == 1 {
882        cfg.span_columns.remove(&pos);
883        return;
884    }
885
886    cfg.span_columns.insert(pos, span);
887}
888
889fn is_cell_covered_by_column_span(cfg: &SpannedConfig, pos: Position) -> bool {
890    cfg.span_columns
891        .iter()
892        .any(|(p, span)| p.row == pos.row && pos.col > p.col && pos.col < p.col + span)
893}
894
895fn is_cell_covered_by_row_span(cfg: &SpannedConfig, pos: Position) -> bool {
896    cfg.span_rows
897        .iter()
898        .any(|(p, span)| p.col == pos.col && pos.row > p.row && pos.row < p.row + span)
899}
900
901fn is_cell_covered_by_both_spans(cfg: &SpannedConfig, pos: Position) -> bool {
902    if !cfg.has_column_spans() || !cfg.has_row_spans() {
903        return false;
904    }
905
906    cfg.span_rows.iter().any(|(p1, row_span)| {
907        cfg.span_columns
908            .iter()
909            .filter(|(p2, _)| &p1 == p2)
910            .any(|(_, col_span)| {
911                pos.row > p1.row
912                    && pos.row < p1.row + row_span
913                    && pos.col > p1.col
914                    && pos.col < p1.col + col_span
915            })
916    })
917}
918
919/// A colorefull margin indent.
920#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
921struct MarginIndent {
922    /// An indent value.
923    indent: Indent,
924    /// An offset value.
925    offset: Offset,
926    /// An color value.
927    color: Option<ANSIBuf>,
928}
929
930impl Default for MarginIndent {
931    fn default() -> Self {
932        Self {
933            indent: Indent::default(),
934            offset: Offset::Start(0),
935            color: None,
936        }
937    }
938}