papergrid/config/
mod.rs

1mod alignment;
2mod border;
3mod borders;
4mod entity;
5mod entity_map;
6mod formatting;
7mod offset;
8mod sides;
9
10use std::collections::HashMap;
11
12pub use self::{
13    alignment::{AlignmentHorizontal, AlignmentVertical},
14    border::Border,
15    borders::{Borders, HorizontalLine, VerticalLine},
16    entity::{Entity, EntityIterator, Position},
17    formatting::Formatting,
18    offset::Offset,
19    sides::{Indent, Sides},
20};
21
22#[cfg(feature = "color")]
23use crate::AnsiColor;
24
25use self::{borders::BordersConfig, entity_map::EntityMap};
26
27/// This structure represents a settings of a grid.
28///
29/// grid: crate::Grid.
30#[derive(Debug, Clone)]
31pub struct GridConfig {
32    tab_width: usize,
33    margin: Margin,
34    margin_offset: Sides<Offset>,
35    padding: EntityMap<Padding>,
36    alignment_h: EntityMap<AlignmentHorizontal>,
37    alignment_v: EntityMap<AlignmentVertical>,
38    formatting: EntityMap<Formatting>,
39    span_columns: HashMap<Position, usize>,
40    span_rows: HashMap<Position, usize>,
41    borders: BordersConfig<char>,
42    borders_missing_char: char,
43    override_horizontal_lines: HashMap<usize, (String, Offset)>,
44    override_horizontal_borders: HashMap<Position, HashMap<Offset, char>>,
45    override_vertical_borders: HashMap<Position, HashMap<Offset, char>>,
46    #[cfg(feature = "color")]
47    margin_color: MarginColor<'static>,
48    #[cfg(feature = "color")]
49    padding_color: EntityMap<PaddingColor<'static>>,
50    #[cfg(feature = "color")]
51    border_colors: BordersConfig<AnsiColor<'static>>,
52}
53
54impl Default for GridConfig {
55    fn default() -> Self {
56        let margin_offset = Sides::new(
57            Offset::Begin(0),
58            Offset::Begin(0),
59            Offset::Begin(0),
60            Offset::Begin(0),
61        );
62
63        Self {
64            tab_width: 4,
65            margin: Margin::default(),
66            margin_offset,
67            padding: EntityMap::default(),
68            formatting: EntityMap::default(),
69            alignment_h: EntityMap::new(AlignmentHorizontal::Left),
70            alignment_v: EntityMap::new(AlignmentVertical::Top),
71            borders: BordersConfig::default(),
72            borders_missing_char: ' ',
73            span_columns: HashMap::default(),
74            span_rows: HashMap::default(),
75            override_horizontal_lines: HashMap::default(),
76            override_horizontal_borders: HashMap::default(),
77            override_vertical_borders: HashMap::default(),
78            #[cfg(feature = "color")]
79            margin_color: MarginColor::default(),
80            #[cfg(feature = "color")]
81            padding_color: EntityMap::default(),
82            #[cfg(feature = "color")]
83            border_colors: BordersConfig::default(),
84        }
85    }
86}
87
88impl GridConfig {
89    /// Set a column span to a given cells.
90    pub fn set_column_span(&mut self, pos: Position, span: usize) {
91        set_cell_column_span(self, pos, span);
92    }
93
94    /// Get a span value of the cell, if any is set.
95    pub fn get_column_span(&self, pos: Position, shape: (usize, usize)) -> Option<usize> {
96        match self.span_columns.get(&pos) {
97            Some(&span) if is_column_span_valid(pos, span, shape) => Some(span),
98            _ => None,
99        }
100    }
101
102    /// Verifies if there's any spans set.
103    pub fn has_column_spans(&self) -> bool {
104        !self.span_columns.is_empty()
105    }
106
107    /// Get a span value of the cell, if any is set.
108    pub fn iter_column_spans(
109        &self,
110        shape: (usize, usize),
111    ) -> impl Iterator<Item = (Position, usize)> + '_ {
112        self.span_columns
113            .iter()
114            .map(|(&pos, &span)| (pos, span))
115            .filter(move |&(pos, span)| is_column_span_valid(pos, span, shape))
116    }
117
118    /// Set a column span to a given cells.
119    pub fn set_row_span(&mut self, pos: Position, span: usize) {
120        set_cell_row_span(self, pos, span);
121    }
122
123    /// Get a span value of the cell, if any is set.
124    pub fn get_row_span(&self, pos: Position, shape: (usize, usize)) -> Option<usize> {
125        match self.span_rows.get(&pos) {
126            Some(&span) if is_row_span_valid(pos, span, shape) => Some(span),
127            _ => None,
128        }
129    }
130
131    /// Verifies if there's any spans set.
132    pub fn has_row_spans(&self) -> bool {
133        !self.span_rows.is_empty()
134    }
135
136    /// Get a span value of the cell, if any is set.
137    pub fn iter_row_spans(
138        &self,
139        shape: (usize, usize),
140    ) -> impl Iterator<Item = (Position, usize)> + '_ {
141        self.span_rows
142            .iter()
143            .map(|(&pos, &span)| (pos, span))
144            .filter(move |&(pos, span)| is_row_span_valid(pos, span, shape))
145    }
146
147    /// Set a [`Margin`] value.
148    pub fn set_margin(&mut self, margin: Margin) {
149        self.margin = margin;
150    }
151
152    /// Returns a [`Margin`] value currently set.
153    pub fn get_margin(&self) -> &Margin {
154        &self.margin
155    }
156
157    /// Set [`Margin`] offset.
158    pub fn set_margin_offset(&mut self, margin: Sides<Offset>) {
159        self.margin_offset = margin;
160    }
161
162    /// Returns a [`Margin`] offset.
163    pub fn get_margin_offset(&self) -> &Sides<Offset> {
164        &self.margin_offset
165    }
166
167    /// Clears all theme changes.
168    /// And sets it to default.
169    pub fn clear_theme(&mut self) {
170        self.borders = BordersConfig::default();
171        self.override_horizontal_lines.clear();
172        self.override_horizontal_borders.clear();
173        self.override_vertical_borders.clear();
174    }
175
176    /// Set the [`Borders`] value as currect one.
177    pub fn set_borders(&mut self, borders: Borders<char>) {
178        self.borders.set_borders(borders);
179    }
180
181    /// Gets a global border value if set.
182    pub fn get_global_border(&self) -> Option<&char> {
183        self.borders.get_global()
184    }
185
186    /// Set the all [`Borders`] values to a char.
187    pub fn set_global_border(&mut self, c: char) {
188        self.borders.set_global(c);
189    }
190
191    /// Set tab width in spaces.
192    pub fn set_tab_width(&mut self, width: usize) {
193        self.tab_width = width;
194    }
195
196    /// Get tab width value in spaces.
197    pub fn get_tab_width(&self) -> usize {
198        self.tab_width
199    }
200
201    /// Returns a current [`Borders`] structure.
202    pub fn get_borders(&self) -> &Borders<char> {
203        self.borders.get_borders()
204    }
205
206    /// Set the border line by row index.
207    ///
208    /// Row `0` means the top row.
209    /// Row `grid.count_rows()` means the bottom row.
210    pub fn set_horizontal_line(&mut self, row: usize, line: HorizontalLine<char>) {
211        self.borders.insert_horizontal_line(row, line);
212    }
213
214    /// Sets off the border line by row index if any were set
215    ///
216    /// Row `0` means the top row.
217    /// Row `grid.count_rows()` means the bottom row.
218    pub fn remove_horizontal_line(&mut self, row: usize) {
219        self.borders.remove_horizontal_line(row);
220    }
221
222    /// Gets a overriden vertical line.
223    ///
224    /// Row `0` means the top row.
225    /// Row `grid.count_rows()` means the bottom row.
226    pub fn get_vertical_line(&self, row: usize) -> Option<&VerticalLine<char>> {
227        self.borders.get_vertical_line(row)
228    }
229
230    /// Set the border line by column index.
231    ///
232    /// Row `0` means the top row.
233    /// Row `grid.count_rows()` means the bottom row.
234    pub fn set_vertical_line(&mut self, row: usize, line: VerticalLine<char>) {
235        self.borders.insert_vertical_line(row, line);
236    }
237
238    /// Sets off the border line by row index if any were set
239    ///
240    /// Row `0` means the top row.
241    /// Row `grid.count_rows()` means the bottom row.
242    pub fn remove_vertical_line(&mut self, row: usize) {
243        self.borders.remove_vertical_line(row);
244    }
245
246    /// Gets a overriden line.
247    ///
248    /// Row `0` means the top row.
249    /// Row `grid.count_rows()` means the bottom row.
250    pub fn get_horizontal_line(&self, row: usize) -> Option<&HorizontalLine<char>> {
251        self.borders.get_horizontal_line(row)
252    }
253
254    /// Override the split line with a custom text.
255    ///
256    /// If borders are not set the string won't be rendered.
257    pub fn override_split_line(&mut self, row: usize, line: impl Into<String>, offset: Offset) {
258        self.override_horizontal_lines
259            .insert(row, (line.into(), offset));
260    }
261
262    /// Gets a set text to a border line by index
263    pub fn get_split_line_text(&self, row: usize) -> Option<&str> {
264        self.override_horizontal_lines
265            .get(&row)
266            .map(|(s, _)| s.as_str())
267    }
268
269    /// Gets a set text to a border line by index
270    pub fn get_split_line_offset(&self, row: usize) -> Option<Offset> {
271        self.override_horizontal_lines
272            .get(&row)
273            .map(|(_, offset)| offset)
274            .copied()
275    }
276
277    /// Removes a split line text if any set.
278    pub fn remove_split_line_text(&mut self, row: usize) -> Option<(String, Offset)> {
279        self.override_horizontal_lines.remove(&row)
280    }
281
282    /// Override a character on a horizontal line.
283    ///
284    /// If borders are not set the char won't be used.
285    pub fn override_horizontal_border(&mut self, pos: Position, c: char, offset: Offset) {
286        let chars = self
287            .override_horizontal_borders
288            .entry(pos)
289            .or_insert_with(|| HashMap::with_capacity(1));
290
291        chars.insert(offset, c);
292    }
293
294    /// Get a list of overriden chars in a horizontal border.
295    pub fn lookup_overidden_horizontal(
296        &self,
297        pos: Position,
298        offset: usize,
299        end: usize,
300    ) -> Option<char> {
301        self.override_horizontal_borders
302            .get(&pos)
303            .and_then(|chars| {
304                chars.get(&Offset::Begin(offset)).or_else(|| {
305                    if end > offset {
306                        if end == 0 {
307                            chars.get(&Offset::End(0))
308                        } else {
309                            chars.get(&Offset::End(end - offset - 1))
310                        }
311                    } else {
312                        None
313                    }
314                })
315            })
316            .copied()
317    }
318
319    /// Checks if there any char in a horizontal border being overriden.
320    pub fn is_overidden_horizontal(&self, pos: Position) -> bool {
321        self.override_horizontal_borders.get(&pos).is_some()
322    }
323
324    /// Removes a list of overriden chars in a horizontal border.
325    pub fn remove_overidden_horizontal(&mut self, pos: Position) {
326        self.override_horizontal_borders.remove(&pos);
327    }
328
329    /// Override a vertical split line.
330    ///
331    /// If borders are not set the char won't be used.
332    pub fn override_vertical_border(&mut self, pos: Position, c: char, offset: Offset) {
333        let chars = self
334            .override_vertical_borders
335            .entry(pos)
336            .or_insert_with(|| HashMap::with_capacity(1));
337
338        chars.insert(offset, c);
339    }
340
341    /// Get a list of overriden chars in a horizontal border.
342    pub fn lookup_overidden_vertical(
343        &self,
344        pos: Position,
345        offset: usize,
346        end: usize,
347    ) -> Option<char> {
348        self.override_vertical_borders
349            .get(&pos)
350            .and_then(|chars| {
351                chars.get(&Offset::Begin(offset)).or_else(|| {
352                    if end > offset {
353                        if end == 0 {
354                            chars.get(&Offset::End(0))
355                        } else {
356                            chars.get(&Offset::End(end - offset - 1))
357                        }
358                    } else {
359                        None
360                    }
361                })
362            })
363            .copied()
364    }
365
366    /// Checks if there any char in a horizontal border being overriden.
367    pub fn is_overidden_vertical(&self, pos: Position) -> bool {
368        self.override_vertical_borders.get(&pos).is_some()
369    }
370
371    /// Removes a list of overriden chars in a horizontal border.
372    pub fn remove_overidden_vertical(&mut self, pos: Position) {
373        self.override_vertical_borders.remove(&pos);
374    }
375
376    /// Set a padding to a given cells.
377    pub fn set_padding(&mut self, entity: Entity, padding: Padding) {
378        self.padding.set(entity, padding);
379    }
380
381    /// Get a padding for a given [Entity].
382    pub fn get_padding(&self, entity: Entity) -> &Padding {
383        self.padding.lookup(entity)
384    }
385
386    /// Set a formatting to a given cells.
387    pub fn set_formatting(&mut self, entity: Entity, formatting: Formatting) {
388        self.formatting.set(entity, formatting);
389    }
390
391    /// Get a formatting settings for a given [Entity].
392    pub fn get_formatting(&self, entity: Entity) -> &Formatting {
393        self.formatting.lookup(entity)
394    }
395
396    /// Set a vertical alignment to a given cells.
397    pub fn set_alignment_vertical(&mut self, entity: Entity, alignment: AlignmentVertical) {
398        self.alignment_v.set(entity, alignment);
399    }
400
401    /// Get a vertical alignment for a given [Entity].
402    pub fn get_alignment_vertical(&self, entity: Entity) -> &AlignmentVertical {
403        self.alignment_v.lookup(entity)
404    }
405
406    /// Set a horizontal alignment to a given cells.
407    pub fn set_alignment_horizontal(&mut self, entity: Entity, alignment: AlignmentHorizontal) {
408        self.alignment_h.set(entity, alignment);
409    }
410
411    /// Get a horizontal alignment for a given [Entity].
412    pub fn get_alignment_horizontal(&self, entity: Entity) -> &AlignmentHorizontal {
413        self.alignment_h.lookup(entity)
414    }
415
416    /// The function returns whether the cells will be rendered or it will be hidden because of a span.
417    pub fn is_cell_visible(&self, pos: Position, shape: (usize, usize)) -> bool {
418        !(self.is_cell_covered_by_column_span(pos, shape)
419            || self.is_cell_covered_by_row_span(pos, shape)
420            || self.is_cell_covered_by_both_spans(pos, shape))
421    }
422
423    /// The function checks if a cell is hidden because of a row span.
424    pub fn is_cell_covered_by_row_span(&self, pos: Position, shape: (usize, usize)) -> bool {
425        is_cell_covered_by_row_span(self, pos, shape)
426    }
427
428    /// The function checks if a cell is hidden because of a column span.
429    pub fn is_cell_covered_by_column_span(&self, pos: Position, shape: (usize, usize)) -> bool {
430        is_cell_covered_by_column_span(self, pos, shape)
431    }
432
433    /// The function checks if a cell is hidden indirectly because of a row and column span combination.
434    pub fn is_cell_covered_by_both_spans(&self, pos: Position, shape: (usize, usize)) -> bool {
435        is_cell_covered_by_both_spans(self, pos, shape)
436    }
437
438    // todo: move to Grid as static methods
439
440    /// Checks if grid would have a vertical border with the current configuration.
441    ///
442    /// grid: crate::Grid
443    pub fn has_vertical(&self, col: usize, count_columns: usize) -> bool {
444        self.borders.has_vertical(col, count_columns)
445    }
446
447    /// Checks if grid would have a horizontal border with the current configuration.
448    ///
449    /// grid: crate::Grid
450    pub fn has_horizontal(&self, row: usize, count_rows: usize) -> bool {
451        self.borders.has_horizontal(row, count_rows)
452    }
453
454    /// Set border set a border value to all cells in [`Entity`].
455    pub fn set_border(&mut self, pos: Position, border: Border) {
456        self.borders.insert_border(pos, border);
457    }
458
459    /// Sets off all borders possible on the [`Entity`].
460    ///
461    /// It doesn't changes globaly set borders through [`GridConfig::set_borders`].
462    pub fn remove_border(&mut self, pos: Position, shape: (usize, usize)) {
463        self.borders.remove_border(pos, shape);
464    }
465
466    /// Set a character wich will be used in case any missconfiguration of borders.
467    ///
468    /// 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.
469    pub fn set_borders_missing(&mut self, c: char) {
470        self.borders_missing_char = c;
471    }
472
473    /// Calculates an amount of vertical lines would present on the grid.
474    ///
475    /// grid: crate::Grid
476    pub fn count_vertical(&self, count_columns: usize) -> usize {
477        (0..=count_columns)
478            .filter(|&col| self.has_vertical(col, count_columns))
479            .count()
480    }
481
482    /// Calculates an amount of horizontal lines would present on the grid.
483    ///
484    /// grid: crate::Grid
485    pub fn count_horizontal(&self, count_rows: usize) -> usize {
486        (0..=count_rows)
487            .filter(|&row| self.has_horizontal(row, count_rows))
488            .count()
489    }
490
491    /// Returns a border of a cell.
492    pub fn get_border(&self, pos: Position, shape: (usize, usize)) -> Border<char> {
493        self.borders.get_border(pos, shape.0, shape.1).copied()
494    }
495
496    /// Gets a vertical character which would be rendered on the grid.
497    ///
498    /// grid: crate::Grid
499    pub fn get_vertical(&self, pos: Position, count_columns: usize) -> Option<&char> {
500        let c = self.borders.get_vertical(pos, count_columns);
501        if c.is_some() {
502            return c;
503        }
504
505        if self.has_vertical(pos.1, count_columns) {
506            return Some(&self.borders_missing_char);
507        }
508
509        None
510    }
511
512    /// Gets a horizontal character which would be rendered on the grid.
513    ///
514    /// grid: crate::Grid
515    pub fn get_horizontal(&self, pos: Position, count_rows: usize) -> Option<&char> {
516        let c = self.borders.get_horizontal(pos, count_rows);
517        if c.is_some() {
518            return c;
519        }
520
521        if self.has_horizontal(pos.0, count_rows) {
522            return Some(&self.borders_missing_char);
523        }
524
525        None
526    }
527
528    /// Gets an intersection character which would be rendered on the grid.
529    ///
530    /// grid: crate::Grid
531    pub fn get_intersection(&self, pos: Position, shape: (usize, usize)) -> Option<&char> {
532        let c = self.borders.get_intersection(pos, shape.0, shape.1);
533        if c.is_some() {
534            return c;
535        }
536
537        if self.has_horizontal(pos.0, shape.0) && self.has_vertical(pos.1, shape.1) {
538            return Some(&self.borders_missing_char);
539        }
540
541        None
542    }
543}
544
545#[cfg(feature = "color")]
546impl GridConfig {
547    /// Gets a color of all borders on the grid.
548    pub fn get_border_color_global(&self) -> Option<&AnsiColor<'_>> {
549        self.border_colors.get_global()
550    }
551
552    /// Sets a color of all borders on the grid.
553    pub fn set_border_color_global(&mut self, clr: AnsiColor<'static>) {
554        self.border_colors = BordersConfig::default();
555        self.border_colors.set_global(clr);
556    }
557
558    /// Gets colors of a borders carcass on the grid.
559    pub fn get_color_borders(&self) -> &Borders<AnsiColor<'_>> {
560        self.border_colors.get_borders()
561    }
562
563    /// Sets colors of border carcass on the grid.
564    pub fn set_borders_color(&mut self, clrs: Borders<AnsiColor<'static>>) {
565        self.border_colors.set_borders(clrs);
566    }
567
568    /// Sets a color of border of a cell on the grid.
569    pub fn set_border_color(&mut self, pos: Position, border: Border<AnsiColor<'static>>) {
570        self.border_colors.insert_border(pos, border)
571    }
572
573    /// Gets a color of border of a cell on the grid.
574    pub fn get_border_color(&self, pos: Position, shape: (usize, usize)) -> Border<&AnsiColor<'_>> {
575        self.border_colors.get_border(pos, shape.0, shape.1)
576    }
577
578    /// Gets a color of border of a cell on the grid.
579    pub fn remove_border_color(&mut self, pos: Position, shape: (usize, usize)) {
580        self.border_colors.remove_border(pos, shape);
581    }
582
583    /// Get colors for a [`Margin`] value.
584    pub fn get_margin_color(&self) -> &MarginColor<'_> {
585        &self.margin_color
586    }
587
588    /// Set colors for a [`Margin`] value.
589    pub fn set_margin_color(&mut self, color: MarginColor<'static>) {
590        self.margin_color = color;
591    }
592
593    /// Get a padding to a given cells.
594    pub fn get_padding_color(&self, entity: Entity) -> &PaddingColor<'_> {
595        self.padding_color.lookup(entity)
596    }
597
598    /// Set a padding to a given cells.
599    pub fn set_padding_color(&mut self, entity: Entity, color: PaddingColor<'static>) {
600        self.padding_color.set(entity, color);
601    }
602
603    /// Gets a color of a cell horizontal.
604    pub fn get_horizontal_color(&self, pos: Position, count_rows: usize) -> Option<&AnsiColor<'_>> {
605        self.border_colors.get_horizontal(pos, count_rows)
606    }
607
608    /// Gets a color of a cell vertical.
609    pub fn get_vertical_color(
610        &self,
611        pos: Position,
612        count_columns: usize,
613    ) -> Option<&AnsiColor<'_>> {
614        self.border_colors.get_vertical(pos, count_columns)
615    }
616
617    /// Gets a color of a cell intersection.
618    pub fn get_intersection_color(
619        &self,
620        pos: Position,
621        shape: (usize, usize),
622    ) -> Option<&AnsiColor<'_>> {
623        self.border_colors.get_intersection(pos, shape.0, shape.1)
624    }
625}
626
627/// Margin represent a 4 indents of table as a whole.
628pub type Margin = Sides<Indent>;
629
630/// Padding represent a 4 indents of cell.
631pub type Padding = Sides<Indent>;
632
633#[cfg(feature = "color")]
634/// Margin represent a 4 indents of table as a whole.
635pub type MarginColor<'a> = Sides<AnsiColor<'a>>;
636
637#[cfg(feature = "color")]
638/// PaddingColor represent a 4 indents of a cell.
639pub type PaddingColor<'a> = Sides<AnsiColor<'a>>;
640
641fn set_cell_row_span(cfg: &mut GridConfig, (mut row, col): Position, mut span: usize) {
642    // such spans aren't supported
643    if row == 0 && span == 0 {
644        return;
645    }
646
647    // It's a default span so we can do nothing.
648    // but we check if it's an override of a span.
649    if span == 1 {
650        cfg.span_rows.remove(&(row, col));
651        return;
652    }
653
654    if span == 0 && row > 0 {
655        match closest_visible_row(cfg, (row - 1, col)) {
656            Some(c) => {
657                span += 1 + row - c;
658                row = c;
659            }
660            None => return,
661        }
662    }
663
664    cfg.span_rows.insert((row, col), span);
665}
666
667fn closest_visible_row(cfg: &GridConfig, mut pos: Position) -> Option<usize> {
668    loop {
669        if cfg.is_cell_visible(pos, (std::usize::MAX, std::usize::MAX)) {
670            return Some(pos.0);
671        }
672
673        if pos.0 == 0 {
674            return None;
675        }
676
677        pos.0 -= 1;
678    }
679}
680
681fn set_cell_column_span(cfg: &mut GridConfig, (row, mut col): Position, mut span: usize) {
682    // such spans aren't supported
683    if col == 0 && span == 0 {
684        return;
685    }
686
687    // It's a default span so we can do nothing.
688    // but we check if it's an override of a span.
689    if span == 1 {
690        cfg.span_columns.remove(&(row, col));
691        return;
692    }
693
694    if span == 0 && col > 0 {
695        match closest_visible_column(cfg, (row, col - 1)) {
696            Some(c) => {
697                span += 1 + col - c;
698                col = c;
699            }
700            None => return,
701        }
702    }
703
704    cfg.span_columns.insert((row, col), span);
705}
706
707fn closest_visible_column(cfg: &GridConfig, mut pos: Position) -> Option<usize> {
708    loop {
709        if cfg.is_cell_visible(pos, (std::usize::MAX, std::usize::MAX)) {
710            return Some(pos.1);
711        }
712
713        if pos.1 == 0 {
714            return None;
715        }
716
717        pos.1 -= 1;
718    }
719}
720
721fn is_cell_covered_by_column_span(cfg: &GridConfig, pos: Position, shape: (usize, usize)) -> bool {
722    if cfg.span_columns.is_empty() {
723        return false;
724    }
725
726    cfg.span_columns
727        .iter()
728        .filter(|(&pos, &span)| is_column_span_valid(pos, span, shape))
729        .any(|(&(row, col), span)| pos.1 > col && pos.1 < col + span && row == pos.0)
730}
731
732fn is_cell_covered_by_row_span(cfg: &GridConfig, pos: Position, shape: (usize, usize)) -> bool {
733    if cfg.span_rows.is_empty() {
734        return false;
735    }
736
737    cfg.span_rows
738        .iter()
739        .filter(|(&pos, &span)| is_row_span_valid(pos, span, shape))
740        .any(|(&(row, col), span)| pos.0 > row && pos.0 < row + span && col == pos.1)
741}
742
743fn is_cell_covered_by_both_spans(cfg: &GridConfig, pos: Position, shape: (usize, usize)) -> bool {
744    if cfg.span_rows.is_empty() || cfg.span_columns.is_empty() {
745        return false;
746    }
747
748    cfg.span_rows
749        .iter()
750        .filter(|(&pos, &span)| is_row_span_valid(pos, span, shape))
751        .any(|(p1, row_span)| {
752            cfg.span_columns
753                .iter()
754                .filter(|(&pos, &span)| is_column_span_valid(pos, span, shape))
755                .filter(|(p2, _)| &p1 == p2)
756                .any(|(_, col_span)| {
757                    pos.0 > p1.0
758                        && pos.0 < p1.0 + row_span
759                        && pos.1 > p1.1
760                        && pos.1 < p1.1 + col_span
761                })
762        })
763}
764
765fn is_column_span_valid(
766    pos: Position,
767    span: usize,
768    (count_rows, count_cols): (usize, usize),
769) -> bool {
770    // ignore spans which are invlid
771    let pos_correct = pos.1 < count_cols && pos.0 < count_rows;
772    // ignore a span range wich begger then count rows
773    let span_correct = span + pos.1 <= count_cols;
774
775    pos_correct && span_correct
776}
777
778fn is_row_span_valid(pos: Position, span: usize, (count_rows, count_cols): (usize, usize)) -> bool {
779    // ignore spans which are invlid
780    let pos_correct = pos.1 < count_cols && pos.0 < count_rows;
781    // ignore a span range wich begger then count columns
782    let span_correct = span + pos.0 <= count_rows;
783
784    pos_correct && span_correct
785}