1use std::{
4 cmp::{max, Ordering},
5 collections::HashMap,
6};
7
8use crate::{
9 config::{spanned::SpannedConfig, Position},
10 dimension::{Dimension, Estimate},
11 records::{vec_records::Cell, IntoRecords, Records},
12};
13
14#[derive(Debug, Default, Clone, PartialEq, Eq)]
20pub struct PeekableGridDimension {
21 height: Vec<usize>,
22 width: Vec<usize>,
23}
24
25impl PeekableGridDimension {
26 pub fn height<R>(records: R, cfg: &SpannedConfig) -> Vec<usize>
28 where
29 R: Records,
30 <R::Iter as IntoRecords>::Cell: Cell,
31 {
32 build_height(records, cfg)
33 }
34
35 pub fn width<R>(records: R, cfg: &SpannedConfig) -> Vec<usize>
37 where
38 R: Records,
39 <R::Iter as IntoRecords>::Cell: Cell,
40 {
41 build_width(records, cfg)
42 }
43
44 pub fn dimension<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, Vec<usize>)
46 where
47 R: Records,
48 <R::Iter as IntoRecords>::Cell: Cell,
49 {
50 build_dimensions(records, cfg)
51 }
52
53 pub fn get_values(self) -> (Vec<usize>, Vec<usize>) {
55 (self.width, self.height)
56 }
57}
58
59impl Dimension for PeekableGridDimension {
60 fn get_width(&self, column: usize) -> usize {
61 self.width[column]
62 }
63
64 fn get_height(&self, row: usize) -> usize {
65 self.height[row]
66 }
67}
68
69impl<R> Estimate<R, SpannedConfig> for PeekableGridDimension
70where
71 R: Records,
72 <R::Iter as IntoRecords>::Cell: Cell,
73{
74 fn estimate(&mut self, records: R, cfg: &SpannedConfig) {
75 let (width, height) = build_dimensions(records, cfg);
76 self.width = width;
77 self.height = height;
78 }
79}
80
81fn build_dimensions<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, Vec<usize>)
82where
83 R: Records,
84 <R::Iter as IntoRecords>::Cell: Cell,
85{
86 if cfg.has_column_spans() || cfg.has_row_spans() {
87 build_dimensions_spanned(records, cfg)
88 } else {
89 build_dimensions_basic(records, cfg)
90 }
91}
92
93fn build_dimensions_basic<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, Vec<usize>)
94where
95 R: Records,
96 <R::Iter as IntoRecords>::Cell: Cell,
97{
98 let count_columns = records.count_columns();
99
100 let mut widths = vec![0; count_columns];
101 let mut heights = vec![];
102 if let Some(count_rows) = records.hint_count_rows() {
103 heights.reserve(count_rows);
104 }
105
106 for (row, columns) in records.iter_rows().into_iter().enumerate() {
107 let mut row_height = 0;
108 for (col, cell) in columns.into_iter().enumerate() {
109 let pos = (row, col).into();
110
111 let width = cell.width();
112 let height = cell.count_lines();
113 let pad = cfg.get_padding(pos);
114 let width = width + pad.left.size + pad.right.size;
115 let height = height + pad.top.size + pad.bottom.size;
116
117 widths[col] = max(widths[col], width);
118 row_height = max(row_height, height);
119 }
120
121 heights.push(row_height);
122 }
123
124 (widths, heights)
125}
126
127fn build_dimensions_spanned<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, Vec<usize>)
128where
129 R: Records,
130 <R::Iter as IntoRecords>::Cell: Cell,
131{
132 let count_columns = records.count_columns();
133
134 let mut widths = vec![0; count_columns];
135 let mut heights = vec![];
136 if let Some(count_rows) = records.hint_count_rows() {
137 heights.reserve(count_rows);
138 }
139
140 let mut vspans = HashMap::new();
141 let mut hspans = HashMap::new();
142
143 for (row, columns) in records.iter_rows().into_iter().enumerate() {
144 let mut row_height = 0;
145 for (col, cell) in columns.into_iter().enumerate() {
146 let pos = (row, col).into();
147 if !cfg.is_cell_visible(pos) {
148 continue;
149 }
150
151 let width = cell.width();
152 let height = cell.count_lines();
153 let pad = cfg.get_padding(pos);
154 let width = width + pad.left.size + pad.right.size;
155 let height = height + pad.top.size + pad.bottom.size;
156
157 match cfg.get_column_span(pos) {
158 Some(n) if n > 1 => {
159 vspans.insert(pos, (n, width));
160 }
161 _ => widths[col] = max(widths[col], width),
162 }
163
164 match cfg.get_row_span(pos) {
165 Some(n) if n > 1 => {
166 hspans.insert(pos, (n, height));
167 }
168 _ => row_height = max(row_height, height),
169 }
170 }
171
172 heights.push(row_height);
173 }
174
175 let count_rows = heights.len();
176
177 adjust_vspans(cfg, count_columns, &vspans, &mut widths);
178 adjust_hspans(cfg, count_rows, &hspans, &mut heights);
179
180 (widths, heights)
181}
182
183fn adjust_hspans(
184 cfg: &SpannedConfig,
185 len: usize,
186 spans: &HashMap<Position, (usize, usize)>,
187 heights: &mut [usize],
188) {
189 if spans.is_empty() {
190 return;
191 }
192
193 let mut spans_ordered = spans.iter().map(|(k, v)| (*k, *v)).collect::<Vec<_>>();
194 spans_ordered.sort_unstable_by(|(arow, acol), (brow, bcol)| match arow.cmp(brow) {
195 Ordering::Equal => acol.cmp(bcol),
196 ord => ord,
197 });
198
199 for (pos, (span, height)) in spans_ordered {
200 adjust_row_range(cfg, height, len, pos.row, pos.row + span, heights);
201 }
202}
203
204fn adjust_row_range(
205 cfg: &SpannedConfig,
206 max_span_height: usize,
207 len: usize,
208 start: usize,
209 end: usize,
210 heights: &mut [usize],
211) {
212 let range_height = range_height(cfg, len, start, end, heights);
213 if range_height >= max_span_height {
214 return;
215 }
216
217 inc_range(heights, max_span_height - range_height, start, end);
218}
219
220fn range_height(
221 cfg: &SpannedConfig,
222 len: usize,
223 start: usize,
224 end: usize,
225 heights: &[usize],
226) -> usize {
227 let count_borders = count_horizontal_borders(cfg, len, start, end);
228 let range_height = heights[start..end].iter().sum::<usize>();
229 count_borders + range_height
230}
231
232fn count_horizontal_borders(cfg: &SpannedConfig, len: usize, start: usize, end: usize) -> usize {
233 (start..end)
234 .skip(1)
235 .filter(|&i| cfg.has_horizontal(i, len))
236 .count()
237}
238
239fn inc_range(list: &mut [usize], size: usize, start: usize, end: usize) {
240 if list.is_empty() {
241 return;
242 }
243
244 let span = end - start;
245 let one = size / span;
246 let rest = size - span * one;
247
248 let mut i = start;
249 while i < end {
250 if i == start {
251 list[i] += one + rest;
252 } else {
253 list[i] += one;
254 }
255
256 i += 1;
257 }
258}
259
260fn adjust_vspans(
261 cfg: &SpannedConfig,
262 len: usize,
263 spans: &HashMap<Position, (usize, usize)>,
264 widths: &mut [usize],
265) {
266 if spans.is_empty() {
267 return;
268 }
269
270 let mut spans_ordered = spans.iter().map(|(k, v)| (*k, *v)).collect::<Vec<_>>();
274 spans_ordered.sort_unstable_by(|a, b| match a.1 .0.cmp(&b.1 .0) {
275 Ordering::Equal => a.0.cmp(&b.0),
276 o => o,
277 });
278
279 for (pos, (span, width)) in spans_ordered {
280 adjust_column_range(cfg, width, len, pos.col, pos.col + span, widths);
281 }
282}
283
284fn adjust_column_range(
285 cfg: &SpannedConfig,
286 max_span_width: usize,
287 len: usize,
288 start: usize,
289 end: usize,
290 widths: &mut [usize],
291) {
292 let range_width = range_width(cfg, len, start, end, widths);
293 if range_width >= max_span_width {
294 return;
295 }
296
297 inc_range(widths, max_span_width - range_width, start, end);
298}
299
300fn range_width(
301 cfg: &SpannedConfig,
302 len: usize,
303 start: usize,
304 end: usize,
305 widths: &[usize],
306) -> usize {
307 let count_borders = count_vertical_borders(cfg, len, start, end);
308 let range_width = widths[start..end].iter().sum::<usize>();
309 count_borders + range_width
310}
311
312fn count_vertical_borders(cfg: &SpannedConfig, len: usize, start: usize, end: usize) -> usize {
313 (start..end)
314 .skip(1)
315 .filter(|&i| cfg.has_vertical(i, len))
316 .count()
317}
318
319fn build_height<R>(records: R, cfg: &SpannedConfig) -> Vec<usize>
320where
321 R: Records,
322 <R::Iter as IntoRecords>::Cell: Cell,
323{
324 if cfg.has_column_spans() || cfg.has_row_spans() {
325 build_height_spanned(records, cfg)
326 } else {
327 build_height_basic(records, cfg)
328 }
329}
330
331fn build_height_basic<R>(records: R, cfg: &SpannedConfig) -> Vec<usize>
332where
333 R: Records,
334 <R::Iter as IntoRecords>::Cell: Cell,
335{
336 let mut heights = vec![];
337 if let Some(count_rows) = records.hint_count_rows() {
338 heights.reserve(count_rows);
339 }
340
341 for (row, columns) in records.iter_rows().into_iter().enumerate() {
342 let mut row_height = 0;
343 for (col, cell) in columns.into_iter().enumerate() {
344 let pos = (row, col).into();
345 let pad = cfg.get_padding(pos);
346 let height = cell.count_lines() + pad.bottom.size + pad.top.size;
347 row_height = max(row_height, height);
348 }
349
350 heights.push(row_height);
351 }
352
353 heights
354}
355
356fn build_height_spanned<R>(records: R, cfg: &SpannedConfig) -> Vec<usize>
357where
358 R: Records,
359 <R::Iter as IntoRecords>::Cell: Cell,
360{
361 let mut hspans = HashMap::new();
362 let mut heights = vec![];
363 if let Some(count_rows) = records.hint_count_rows() {
364 heights.reserve(count_rows);
365 }
366
367 for (row, columns) in records.iter_rows().into_iter().enumerate() {
368 let mut row_height = 0;
369 for (col, cell) in columns.into_iter().enumerate() {
370 let pos = (row, col).into();
371 if !cfg.is_cell_visible(pos) {
372 continue;
373 }
374
375 let pad = cfg.get_padding(pos);
376 let height = cell.count_lines() + pad.bottom.size + pad.top.size;
377 match cfg.get_row_span(pos) {
378 Some(n) if n > 1 => {
379 hspans.insert(pos, (n, height));
380 }
381 _ => row_height = max(row_height, height),
382 }
383 }
384
385 heights.push(row_height);
386 }
387
388 adjust_hspans(cfg, heights.len(), &hspans, &mut heights);
389
390 heights
391}
392
393fn build_width<R>(records: R, cfg: &SpannedConfig) -> Vec<usize>
394where
395 R: Records,
396 <R::Iter as IntoRecords>::Cell: Cell,
397{
398 if cfg.has_column_spans() || cfg.has_row_spans() {
399 build_width_spanned(records, cfg)
400 } else {
401 build_width_basic(records, cfg)
402 }
403}
404
405fn build_width_basic<R>(records: R, cfg: &SpannedConfig) -> Vec<usize>
406where
407 R: Records,
408 <R::Iter as IntoRecords>::Cell: Cell,
409{
410 let count_columns = records.count_columns();
411 let mut widths = vec![0; count_columns];
412
413 for (row, columns) in records.iter_rows().into_iter().enumerate() {
414 for (col, cell) in columns.into_iter().enumerate() {
415 let pos = (row, col).into();
416 let pad = cfg.get_padding(pos);
417 let width = cell.width() + pad.left.size + pad.right.size;
418 widths[col] = max(widths[col], width);
419 }
420 }
421
422 widths
423}
424
425fn build_width_spanned<R>(records: R, cfg: &SpannedConfig) -> Vec<usize>
426where
427 R: Records,
428 <R::Iter as IntoRecords>::Cell: Cell,
429{
430 let count_columns = records.count_columns();
431
432 let mut widths = vec![0; count_columns];
433 let mut vspans = HashMap::new();
434
435 for (row, columns) in records.iter_rows().into_iter().enumerate() {
436 for (col, cell) in columns.into_iter().enumerate() {
437 let pos = (row, col).into();
438 if !cfg.is_cell_visible(pos) {
439 continue;
440 }
441
442 let pad = cfg.get_padding(pos);
443 let width = cell.width() + pad.left.size + pad.right.size;
444 match cfg.get_column_span(pos) {
445 Some(n) if n > 1 => {
446 vspans.insert(pos, (n, width));
447 }
448 _ => widths[col] = max(widths[col], width),
449 }
450 }
451 }
452
453 adjust_vspans(cfg, count_columns, &vspans, &mut widths);
454
455 widths
456}