1//! The module contains a [`SpannedVecRecordsDimension`] for [`Grid`] height/width estimation.
2//!
3//! [`Grid`]: crate::grid::iterable::Grid
4
5use std::{
6 cmp::{max, Ordering},
7 collections::HashMap,
8};
9
10use crate::{
11 config::Position,
12 dimension::{Dimension, Estimate},
13 records::{
14 vec_records::{Cell, VecRecords},
15 Records,
16 },
17};
18
19use crate::config::spanned::SpannedConfig;
20
21/// A [`Dimension`] implementation which calculates exact column/row width/height for [`VecRecords`].
22///
23/// It is a specialization of [`SpannedGridDimension`] for [`VecRecords`].
24///
25/// [`SpannedGridDimension`]: crate::dimension::spanned::SpannedGridDimension
26#[derive(Debug, Default, Clone, PartialEq, Eq)]
27pub struct SpannedVecRecordsDimension {
28 height: Vec<usize>,
29 width: Vec<usize>,
30}
31
32impl SpannedVecRecordsDimension {
33 /// Calculates height of rows.
34 pub fn height<T: Cell + AsRef<str>>(
35 records: &VecRecords<T>,
36 cfg: &SpannedConfig,
37 ) -> Vec<usize> {
38 build_height(records, cfg)
39 }
40
41 /// Calculates width of columns.
42 pub fn width<T: Cell + AsRef<str>>(records: &VecRecords<T>, cfg: &SpannedConfig) -> Vec<usize> {
43 build_width(records, cfg)
44 }
45
46 /// Return width and height lists.
47 pub fn get_values(self) -> (Vec<usize>, Vec<usize>) {
48 (self.width, self.height)
49 }
50}
51
52impl Dimension for SpannedVecRecordsDimension {
53 fn get_width(&self, column: usize) -> usize {
54 self.width[column]
55 }
56
57 fn get_height(&self, row: usize) -> usize {
58 self.height[row]
59 }
60}
61
62impl<T> Estimate<&VecRecords<T>, SpannedConfig> for SpannedVecRecordsDimension
63where
64 T: Cell + AsRef<str>,
65{
66 fn estimate(&mut self, records: &VecRecords<T>, cfg: &SpannedConfig) {
67 let (width: Vec, height: Vec) = build_dimensions(records, cfg);
68 self.width = width;
69 self.height = height;
70 }
71}
72
73fn build_dimensions<T: Cell + AsRef<str>>(
74 records: &VecRecords<T>,
75 cfg: &SpannedConfig,
76) -> (Vec<usize>, Vec<usize>) {
77 let count_columns = records.count_columns();
78
79 let mut widths = vec![0; count_columns];
80 let mut heights = vec![];
81
82 let mut vspans = HashMap::new();
83 let mut hspans = HashMap::new();
84
85 for (row, columns) in records.iter_rows().enumerate() {
86 let mut row_height = 0;
87 for (col, cell) in columns.iter().enumerate() {
88 let pos = (row, col);
89 if !cfg.is_cell_visible(pos) {
90 continue;
91 }
92
93 let width = cell.width();
94 let height = cell.count_lines();
95 let pad = cfg.get_padding(pos.into());
96 let width = width + pad.left.size + pad.right.size;
97 let height = height + pad.top.size + pad.bottom.size;
98
99 match cfg.get_column_span(pos) {
100 Some(n) if n > 1 => {
101 vspans.insert(pos, (n, width));
102 }
103 _ => widths[col] = max(widths[col], width),
104 }
105
106 match cfg.get_row_span(pos) {
107 Some(n) if n > 1 => {
108 hspans.insert(pos, (n, height));
109 }
110 _ => row_height = max(row_height, height),
111 }
112 }
113
114 heights.push(row_height);
115 }
116
117 let count_rows = heights.len();
118
119 adjust_vspans(cfg, count_columns, &vspans, &mut widths);
120 adjust_hspans(cfg, count_rows, &hspans, &mut heights);
121
122 (widths, heights)
123}
124
125fn adjust_hspans(
126 cfg: &SpannedConfig,
127 len: usize,
128 spans: &HashMap<Position, (usize, usize)>,
129 heights: &mut [usize],
130) {
131 if spans.is_empty() {
132 return;
133 }
134
135 let mut spans_ordered: Vec<((usize, usize), (usize, …))> = spansimpl Iterator
136 .iter()
137 .map(|(k: &(usize, usize), v: &(usize, usize))| ((k.0, k.1), *v))
138 .collect::<Vec<_>>();
139 spans_ordered.sort_unstable_by(|(arow: &(usize, usize), acol: &(usize, usize)), (brow: &(usize, usize), bcol: &(usize, usize))| match arow.cmp(brow) {
140 Ordering::Equal => acol.cmp(bcol),
141 ord: Ordering => ord,
142 });
143
144 for ((row: usize, _), (span: usize, height: usize)) in spans_ordered {
145 adjust_row_range(cfg, max_span_height:height, len, start:row, end:row + span, heights);
146 }
147}
148
149fn adjust_row_range(
150 cfg: &SpannedConfig,
151 max_span_height: usize,
152 len: usize,
153 start: usize,
154 end: usize,
155 heights: &mut [usize],
156) {
157 let range_height: usize = range_height(cfg, len, start, end, heights);
158 if range_height >= max_span_height {
159 return;
160 }
161
162 inc_range(list:heights, size:max_span_height - range_height, start, end);
163}
164
165fn range_height(
166 cfg: &SpannedConfig,
167 len: usize,
168 start: usize,
169 end: usize,
170 heights: &[usize],
171) -> usize {
172 let count_borders: usize = count_horizontal_borders(cfg, len, start, end);
173 let range_height: usize = heights[start..end].iter().sum::<usize>();
174 count_borders + range_height
175}
176
177fn count_horizontal_borders(cfg: &SpannedConfig, len: usize, start: usize, end: usize) -> usize {
178 (start..end)
179 .skip(1)
180 .filter(|&i: usize| cfg.has_horizontal(row:i, count_rows:len))
181 .count()
182}
183
184fn inc_range(list: &mut [usize], size: usize, start: usize, end: usize) {
185 if list.is_empty() {
186 return;
187 }
188
189 let span: usize = end - start;
190 let one: usize = size / span;
191 let rest: usize = size - span * one;
192
193 let mut i: usize = start;
194 while i < end {
195 if i == start {
196 list[i] += one + rest;
197 } else {
198 list[i] += one;
199 }
200
201 i += 1;
202 }
203}
204
205fn adjust_vspans(
206 cfg: &SpannedConfig,
207 len: usize,
208 spans: &HashMap<Position, (usize, usize)>,
209 widths: &mut [usize],
210) {
211 if spans.is_empty() {
212 return;
213 }
214
215 // The overall width distribution will be different depend on the order.
216 //
217 // We sort spans in order to prioritize the smaller spans first.
218 let mut spans_ordered: Vec<((usize, usize), (usize, …))> = spansimpl Iterator
219 .iter()
220 .map(|(k: &(usize, usize), v: &(usize, usize))| ((k.0, k.1), *v))
221 .collect::<Vec<_>>();
222 spans_ordered.sort_unstable_by(|a: &((usize, usize), (usize, …)), b: &((usize, usize), (usize, …))| match a.1 .0.cmp(&b.1 .0) {
223 Ordering::Equal => a.0.cmp(&b.0),
224 o: Ordering => o,
225 });
226
227 for ((_, col: usize), (span: usize, width: usize)) in spans_ordered {
228 adjust_column_range(cfg, max_span_width:width, len, start:col, end:col + span, widths);
229 }
230}
231
232fn adjust_column_range(
233 cfg: &SpannedConfig,
234 max_span_width: usize,
235 len: usize,
236 start: usize,
237 end: usize,
238 widths: &mut [usize],
239) {
240 let range_width: usize = range_width(cfg, len, start, end, widths);
241 if range_width >= max_span_width {
242 return;
243 }
244
245 inc_range(list:widths, size:max_span_width - range_width, start, end);
246}
247
248fn get_cell_padding_horizontal(cfg: &SpannedConfig, pos: Position) -> usize {
249 let padding: Sides = cfg.get_padding(entity:pos.into());
250 padding.left.size + padding.right.size
251}
252
253fn get_cell_vertical_padding(cfg: &SpannedConfig, pos: Position) -> usize {
254 let padding: Sides = cfg.get_padding(entity:pos.into());
255 padding.top.size + padding.bottom.size
256}
257
258fn range_width(
259 cfg: &SpannedConfig,
260 len: usize,
261 start: usize,
262 end: usize,
263 widths: &[usize],
264) -> usize {
265 let count_borders: usize = count_vertical_borders(cfg, len, start, end);
266 let range_width: usize = widths[start..end].iter().sum::<usize>();
267 count_borders + range_width
268}
269
270fn count_vertical_borders(cfg: &SpannedConfig, len: usize, start: usize, end: usize) -> usize {
271 (start..end)
272 .skip(1)
273 .filter(|&i: usize| cfg.has_vertical(col:i, count_columns:len))
274 .count()
275}
276
277fn build_height<T: Cell + AsRef<str>>(records: &VecRecords<T>, cfg: &SpannedConfig) -> Vec<usize> {
278 let mut heights = vec![];
279 let mut hspans = HashMap::new();
280
281 for (row, columns) in records.iter_rows().enumerate() {
282 let mut row_height = 0;
283 for (col, cell) in columns.iter().enumerate() {
284 let pos = (row, col);
285 if !cfg.is_cell_visible(pos) {
286 continue;
287 }
288
289 let height = cell.count_lines() + get_cell_vertical_padding(cfg, pos);
290 match cfg.get_row_span(pos) {
291 Some(n) if n > 1 => {
292 hspans.insert(pos, (n, height));
293 }
294 _ => row_height = max(row_height, height),
295 }
296 }
297
298 heights.push(row_height);
299 }
300
301 adjust_hspans(cfg, heights.len(), &hspans, &mut heights);
302
303 heights
304}
305
306fn build_width<T: Cell + AsRef<str>>(records: &VecRecords<T>, cfg: &SpannedConfig) -> Vec<usize> {
307 let count_columns = records.count_columns();
308
309 let mut widths = vec![0; count_columns];
310 let mut vspans = HashMap::new();
311
312 for (row, columns) in records.iter_rows().enumerate() {
313 for (col, cell) in columns.iter().enumerate() {
314 let pos = (row, col);
315 if !cfg.is_cell_visible(pos) {
316 continue;
317 }
318
319 let width = cell.width() + get_cell_padding_horizontal(cfg, (row, col));
320 match cfg.get_column_span(pos) {
321 Some(n) if n > 1 => {
322 vspans.insert(pos, (n, width));
323 }
324 _ => widths[col] = max(widths[col], width),
325 }
326 }
327 }
328
329 adjust_vspans(cfg, count_columns, &vspans, &mut widths);
330
331 widths
332}
333