1use std::iter::repeat_n;
6
7use papergrid::dimension::Estimate;
8
9use crate::{
10 grid::{
11 config::{ColoredConfig, Entity, Position},
12 dimension::CompleteDimension,
13 records::{
14 vec_records::Cell, ExactRecords, IntoRecords, PeekableRecords, Records, RecordsMut,
15 },
16 util::string::{get_line_width, get_lines},
17 },
18 settings::{
19 measurement::Measurement,
20 peaker::{Peaker, PriorityNone},
21 CellOption, TableOption, Width,
22 },
23};
24
25use super::util::get_table_total_width;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
63pub struct MinWidth<W = usize, P = PriorityNone> {
64 width: W,
65 fill: char,
66 priority: P,
67}
68
69impl<W> MinWidth<W>
70where
71 W: Measurement<Width>,
72{
73 pub const fn new(width: W) -> Self {
75 Self {
76 width,
77 fill: ' ',
78 priority: PriorityNone::new(),
79 }
80 }
81}
82
83impl<W, P> MinWidth<W, P> {
84 pub fn fill_with(mut self, c: char) -> Self {
89 self.fill = c;
90 self
91 }
92
93 pub fn priority<PP: Peaker>(self, peacker: PP) -> MinWidth<W, PP> {
102 MinWidth {
103 fill: self.fill,
104 width: self.width,
105 priority: peacker,
106 }
107 }
108}
109
110impl<W, R, P> CellOption<R, ColoredConfig> for MinWidth<W, P>
111where
112 W: Measurement<Width>,
113 R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
114 for<'a> &'a R: Records,
115 for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
116{
117 fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
118 let width = self.width.measure(&*records, cfg);
119
120 let count_rows = records.count_rows();
121 let count_columns = records.count_columns();
122 let max_pos = Position::new(count_rows, count_columns);
123
124 for pos in entity.iter(count_rows, count_columns) {
125 if !max_pos.has_coverage(pos) {
126 continue;
127 }
128
129 let cell_width = records.get_width(pos);
130 if cell_width >= width {
131 continue;
132 }
133
134 let cell = records.get_text(pos);
135 let content = increase_width(cell, width, self.fill);
136 records.set(pos, content);
137 }
138 }
139}
140
141impl<W, P, R> TableOption<R, ColoredConfig, CompleteDimension> for MinWidth<W, P>
142where
143 W: Measurement<Width>,
144 P: Peaker,
145 R: Records + ExactRecords + PeekableRecords,
146 for<'a> &'a R: Records,
147 for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: Cell + AsRef<str>,
148{
149 fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut CompleteDimension) {
150 if records.count_rows() == 0 || records.count_columns() == 0 {
151 return;
152 }
153
154 let minwidth = self.width.measure(&*records, cfg);
155
156 dims.estimate(&*records, cfg);
157 let widths = dims.get_widths().expect("must be present");
158
159 let total_width = get_table_total_width(widths, cfg);
160 if total_width >= minwidth {
161 return;
162 }
163
164 let widths = get_increase_list(widths, minwidth, total_width, self.priority);
165 dims.set_widths(widths);
166 }
167
168 fn hint_change(&self) -> Option<Entity> {
169 None
174 }
175}
176
177fn get_increase_list<F>(
180 widths: &[usize],
181 need: usize,
182 mut current: usize,
183 mut peaker: F,
184) -> Vec<usize>
185where
186 F: Peaker,
187{
188 let mut widths = widths.to_vec();
189
190 while need != current {
191 let col = match peaker.peak(&[], &widths) {
192 Some(col) => col,
193 None => break,
194 };
195
196 widths[col] += 1;
197 current += 1;
198 }
199
200 widths
201}
202
203fn increase_width(s: &str, width: usize, fill_with: char) -> String {
204 let mut buf = String::new();
205 for (i, line) in get_lines(s).enumerate() {
206 if i > 0 {
207 buf.push('\n');
208 }
209
210 buf.push_str(&line);
211
212 let length = get_line_width(&line);
213 if length < width {
214 let remain = width - length;
215 buf.extend(repeat_n(fill_with, remain));
216 }
217 }
218
219 buf
220}