1//! The module contains a set of methods to merge cells together via [`Span`]s.
2//!
3//! [`Span`]: crate::settings::span::Span
4
5use crate::{
6 grid::config::ColoredConfig,
7 grid::records::{ExactRecords, PeekableRecords, Records},
8 settings::TableOption,
9};
10
11/// Merge to combine duplicates together, using [`Span`].
12///
13/// [`Span`]: crate::settings::span::Span
14#[derive(Debug)]
15pub struct Merge;
16
17impl Merge {
18 /// Vertical merge.
19 pub fn vertical() -> MergeDuplicatesVertical {
20 MergeDuplicatesVertical
21 }
22
23 /// Horizontal merge.
24 pub fn horizontal() -> MergeDuplicatesHorizontal {
25 MergeDuplicatesHorizontal
26 }
27}
28
29/// A modificator for [`Table`] which looks up for duplicates in columns and
30/// in case of duplicate merges the cells together using [`Span`].
31///
32/// [`Table`]: crate::Table
33/// [`Span`]: crate::settings::span::Span
34#[derive(Debug)]
35pub struct MergeDuplicatesVertical;
36
37impl<R, D> TableOption<R, D, ColoredConfig> for MergeDuplicatesVertical
38where
39 R: Records + PeekableRecords + ExactRecords,
40{
41 fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
42 let count_rows = records.count_rows();
43 let count_cols = records.count_columns();
44
45 if count_rows == 0 || count_cols == 0 {
46 return;
47 }
48
49 for column in 0..count_cols {
50 let mut repeat_length = 0;
51 let mut repeat_value = String::new();
52 let mut repeat_is_set = false;
53 let mut last_is_row_span = false;
54 for row in (0..count_rows).rev() {
55 if last_is_row_span {
56 last_is_row_span = false;
57 continue;
58 }
59
60 // we need to mitigate messing existing spans
61 let is_cell_visible = cfg.is_cell_visible((row, column));
62 let is_row_span_cell = cfg.get_column_span((row, column)).is_some();
63
64 if !repeat_is_set {
65 if !is_cell_visible {
66 continue;
67 }
68
69 if is_row_span_cell {
70 continue;
71 }
72
73 repeat_length = 1;
74 repeat_value = records.get_text((row, column)).to_owned();
75 repeat_is_set = true;
76 continue;
77 }
78
79 if is_row_span_cell {
80 repeat_is_set = false;
81 last_is_row_span = true;
82 continue;
83 }
84
85 if !is_cell_visible {
86 repeat_is_set = false;
87 continue;
88 }
89
90 let text = records.get_text((row, column));
91 let is_duplicate = text == repeat_value;
92
93 if is_duplicate {
94 repeat_length += 1;
95 continue;
96 }
97
98 if repeat_length > 1 {
99 cfg.set_row_span((row + 1, column), repeat_length);
100 }
101
102 repeat_length = 1;
103 repeat_value = records.get_text((row, column)).to_owned();
104 }
105
106 if repeat_length > 1 {
107 cfg.set_row_span((0, column), repeat_length);
108 }
109 }
110 }
111}
112
113/// A modificator for [`Table`] which looks up for duplicates in rows and
114/// in case of duplicate merges the cells together using [`Span`].
115///
116/// [`Table`]: crate::Table
117/// [`Span`]: crate::settings::span::Span
118#[derive(Debug)]
119pub struct MergeDuplicatesHorizontal;
120
121impl<R, D> TableOption<R, D, ColoredConfig> for MergeDuplicatesHorizontal
122where
123 R: Records + PeekableRecords + ExactRecords,
124{
125 fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
126 let count_rows = records.count_rows();
127 let count_cols = records.count_columns();
128
129 if count_rows == 0 || count_cols == 0 {
130 return;
131 }
132
133 for row in 0..count_rows {
134 let mut repeat_length = 0;
135 let mut repeat_value = String::new();
136 let mut repeat_is_set = false;
137 let mut last_is_col_span = false;
138
139 for column in (0..count_cols).rev() {
140 if last_is_col_span {
141 last_is_col_span = false;
142 continue;
143 }
144
145 // we need to mitigate messing existing spans
146 let is_cell_visible = cfg.is_cell_visible((row, column));
147 let is_col_span_cell = cfg.get_row_span((row, column)).is_some();
148
149 if !repeat_is_set {
150 if !is_cell_visible {
151 continue;
152 }
153
154 if is_col_span_cell {
155 continue;
156 }
157
158 repeat_length = 1;
159 repeat_value = records.get_text((row, column)).to_owned();
160 repeat_is_set = true;
161 continue;
162 }
163
164 if is_col_span_cell {
165 repeat_is_set = false;
166 last_is_col_span = true;
167 continue;
168 }
169
170 if !is_cell_visible {
171 repeat_is_set = false;
172 continue;
173 }
174
175 let text = records.get_text((row, column));
176 let is_duplicate = text == repeat_value;
177
178 if is_duplicate {
179 repeat_length += 1;
180 continue;
181 }
182
183 if repeat_length > 1 {
184 cfg.set_column_span((row, column + 1), repeat_length);
185 }
186
187 repeat_length = 1;
188 repeat_value = records.get_text((row, column)).to_owned();
189 }
190
191 if repeat_length > 1 {
192 cfg.set_column_span((row, 0), repeat_length);
193 }
194 }
195 }
196}
197