1use std::cmp::Ordering;
6
7use crate::{records::Records, GridConfig, Position};
8
9use super::Estimate;
10
11pub use super::width_func::{CfgWidthFunction, WidthFunc};
12
13#[derive(Debug, Default, Clone, PartialEq, Eq)]
17pub struct WidthEstimator {
18 widths: Vec<usize>,
19}
20
21impl<R> Estimate<R> for WidthEstimator
22where
23 R: Records,
24{
25 fn estimate(&mut self, records: R, cfg: &GridConfig) {
26 let width_ctrl = CfgWidthFunction::from_cfg(cfg);
27 self.widths = build_widths(&records, cfg, &width_ctrl);
28 }
29
30 fn get(&self, column: usize) -> Option<usize> {
31 self.widths.get(column).copied()
32 }
33
34 fn total(&self) -> usize {
35 self.widths.iter().sum()
36 }
37}
38
39impl From<Vec<usize>> for WidthEstimator {
40 fn from(widths: Vec<usize>) -> Self {
41 Self { widths }
42 }
43}
44
45impl From<WidthEstimator> for Vec<usize> {
46 fn from(val: WidthEstimator) -> Self {
47 val.widths
48 }
49}
50
51fn build_widths<R>(records: &R, cfg: &GridConfig, width_ctrl: &CfgWidthFunction) -> Vec<usize>
52where
53 R: Records,
54{
55 let shape = (records.count_rows(), records.count_columns());
56 let mut widths = vec![0; records.count_columns()];
57 for (col, column) in widths.iter_mut().enumerate() {
58 let max = (0..records.count_rows())
59 .filter(|&row| is_simple_cell(cfg, (row, col), shape))
60 .map(|row| get_cell_width(cfg, records, (row, col), width_ctrl))
61 .max()
62 .unwrap_or(0);
63
64 *column = max;
65 }
66
67 adjust_spans(cfg, width_ctrl, records, &mut widths);
68
69 widths
70}
71
72fn adjust_spans<R>(
73 cfg: &GridConfig,
74 width_ctrl: &CfgWidthFunction,
75 records: &R,
76 widths: &mut [usize],
77) where
78 R: Records,
79{
80 if !cfg.has_column_spans() {
81 return;
82 }
83
84 let mut spans = cfg
88 .iter_column_spans((records.count_rows(), records.count_columns()))
89 .collect::<Vec<_>>();
90 spans.sort_unstable_by(|a, b| match a.1.cmp(&b.1) {
91 Ordering::Equal => a.0.cmp(&b.0),
92 o => o,
93 });
94
95 for ((row, col), span) in spans {
97 adjust_range(cfg, width_ctrl, records, row, col, col + span, widths);
98 }
99}
100
101fn adjust_range<R>(
102 cfg: &GridConfig,
103 width_ctrl: &CfgWidthFunction,
104 records: &R,
105 row: usize,
106 start: usize,
107 end: usize,
108 widths: &mut [usize],
109) where
110 R: Records,
111{
112 let max_span_width = get_cell_width(cfg, records, (row, start), width_ctrl);
113 let range_width = range_width(cfg, start, end, widths);
114
115 if range_width >= max_span_width {
116 return;
117 }
118
119 inc_range_width(widths, max_span_width - range_width, start, end);
120}
121
122fn inc_range_width(widths: &mut [usize], size: usize, start: usize, end: usize) {
123 if widths.is_empty() {
124 return;
125 }
126
127 let span = end - start;
128 let one = size / span;
129 let rest = size - span * one;
130
131 let mut i = start;
132 while i < end {
133 if i == start {
134 widths[i] += one + rest;
135 } else {
136 widths[i] += one;
137 }
138
139 i += 1;
140 }
141}
142
143fn is_simple_cell(cfg: &GridConfig, pos: Position, shape: (usize, usize)) -> bool {
144 cfg.is_cell_visible(pos, shape) && matches!(cfg.get_column_span(pos, shape), None | Some(1))
145}
146
147fn get_cell_width<R>(
148 cfg: &GridConfig,
149 records: &R,
150 pos: Position,
151 width_ctrl: &CfgWidthFunction,
152) -> usize
153where
154 R: Records,
155{
156 let width = records.get_width(pos, width_ctrl);
157 let padding = get_cell_padding(cfg, pos);
158 width + padding
159}
160
161fn get_cell_padding(cfg: &GridConfig, pos: Position) -> usize {
162 let padding = cfg.get_padding(pos.into());
163 padding.left.size + padding.right.size
164}
165
166fn range_width(grid: &GridConfig, start: usize, end: usize, widths: &[usize]) -> usize {
167 let count_borders = count_borders_in_range(grid, start, end, widths.len());
168 let range_width = widths[start..end].iter().sum::<usize>();
169 count_borders + range_width
170}
171
172fn count_borders_in_range(
173 cfg: &GridConfig,
174 start: usize,
175 end: usize,
176 count_columns: usize,
177) -> usize {
178 (start..end)
179 .skip(1)
180 .filter(|&i| cfg.has_vertical(i, count_columns))
181 .count()
182}