1 | //! The module contains a set of methods to merge cells together via [`Span`]s. |
2 | //! |
3 | //! [`Span`]: crate::settings::span::Span |
4 | |
5 | use 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)] |
15 | pub struct Merge; |
16 | |
17 | impl 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)] |
35 | pub struct MergeDuplicatesVertical; |
36 | |
37 | impl<R, D> TableOption<R, D, ColoredConfig> for MergeDuplicatesVertical |
38 | where |
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)] |
119 | pub struct MergeDuplicatesHorizontal; |
120 | |
121 | impl<R, D> TableOption<R, D, ColoredConfig> for MergeDuplicatesHorizontal |
122 | where |
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 | |