tabled/display/
expanded_display.rs
1#![cfg_attr(feature = "derive", doc = "```")]
5#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
6use papergrid::util::{cut_str_basic, string_width};
52
53use crate::Tabled;
54
55#[derive(Debug, Clone)]
80pub struct ExpandedDisplay {
81 fields: Vec<String>,
82 records: Vec<Vec<String>>,
83}
84
85impl ExpandedDisplay {
86 pub fn new<T>(iter: impl IntoIterator<Item = T>) -> Self
88 where
89 T: Tabled,
90 {
91 let data = iter
92 .into_iter()
93 .map(|i| {
94 i.fields()
95 .into_iter()
96 .map(|s| s.escape_debug().to_string())
97 .collect()
98 })
99 .collect();
100 let header = T::headers()
101 .into_iter()
102 .map(|s| s.escape_debug().to_string())
103 .collect();
104
105 Self {
106 records: data,
107 fields: header,
108 }
109 }
110
111 pub fn truncate(&mut self, max: usize, suffix: &str) -> bool {
119 let teplate_width = self.records.len().to_string().len() + 13;
121 let min_width = teplate_width;
122 if max < min_width {
123 return false;
124 }
125
126 let suffix_width = string_width(suffix);
127 if max < suffix_width {
128 return false;
129 }
130
131 let max = max - suffix_width;
132
133 let fields_max_width = self
134 .fields
135 .iter()
136 .map(|s| string_width(s))
137 .max()
138 .unwrap_or_default();
139
140 let fields_affected = max < fields_max_width + 3;
142 if fields_affected {
143 if max < 3 {
144 return false;
145 }
146
147 let max = max - 3;
148
149 if max < suffix_width {
150 return false;
151 }
152
153 let max = max - suffix_width;
154
155 truncate_fields(&mut self.fields, max, suffix);
156 truncate_records(&mut self.records, 0, suffix);
157 } else {
158 let max = max - fields_max_width - 3 - suffix_width;
159 truncate_records(&mut self.records, max, suffix);
160 }
161
162 true
163 }
164}
165
166impl std::fmt::Display for ExpandedDisplay {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 if self.records.is_empty() {
169 return Ok(());
170 }
171
172 let fields = self.fields.iter().collect::<Vec<_>>();
175
176 let max_field_width = fields
177 .iter()
178 .map(|s| string_width(s))
179 .max()
180 .unwrap_or_default();
181
182 let max_values_length = self
183 .records
184 .iter()
185 .map(|record| record.iter().map(|s| string_width(s)).max())
186 .max()
187 .unwrap_or_default()
188 .unwrap_or_default();
189
190 for (i, records) in self.records.iter().enumerate() {
191 write_header_template(f, i, max_field_width, max_values_length)?;
192
193 for (value, field) in records.iter().zip(fields.iter()) {
194 writeln!(f)?;
195 write_record(f, field, value, max_field_width)?;
196 }
197
198 let is_last_record = i + 1 == self.records.len();
199 if !is_last_record {
200 writeln!(f)?;
201 }
202 }
203
204 Ok(())
205 }
206}
207
208fn truncate_records(records: &mut Vec<Vec<String>>, max_width: usize, suffix: &str) {
209 for fields in records {
210 truncate_fields(fields, max_width, suffix);
211 }
212}
213
214fn truncate_fields(records: &mut Vec<String>, max_width: usize, suffix: &str) {
215 for text in records {
216 truncate(text, max_width, suffix);
217 }
218}
219
220fn write_header_template(
221 f: &mut std::fmt::Formatter<'_>,
222 index: usize,
223 max_field_width: usize,
224 max_values_length: usize,
225) -> std::fmt::Result {
226 let mut template = format!("-[ RECORD {} ]-", index);
227 let default_template_length = template.len();
228
229 let max_line_width = std::cmp::max(
231 max_field_width + 3 + max_values_length,
232 default_template_length,
233 );
234 let rest_to_print = max_line_width - default_template_length;
235 if rest_to_print > 0 {
236 if max_field_width + 2 > default_template_length {
238 let part1 = (max_field_width + 1) - default_template_length;
239 let part2 = rest_to_print - part1 - 1;
240
241 template.extend(
242 std::iter::repeat('-')
243 .take(part1)
244 .chain(std::iter::once('+'))
245 .chain(std::iter::repeat('-').take(part2)),
246 );
247 } else {
248 template.extend(std::iter::repeat('-').take(rest_to_print));
249 }
250 }
251
252 write!(f, "{}", template)?;
253
254 Ok(())
255}
256
257fn write_record(
258 f: &mut std::fmt::Formatter<'_>,
259 field: &str,
260 value: &str,
261 max_field_width: usize,
262) -> std::fmt::Result {
263 write!(f, "{:width$} | {}", field, value, width = max_field_width)
264}
265
266fn truncate(text: &mut String, max: usize, suffix: &str) {
267 let original_len = text.len();
268
269 if max == 0 || text.is_empty() {
270 *text = String::new();
271 } else {
272 *text = cut_str_basic(text, max).into_owned();
273 }
274
275 let cut_was_done = text.len() < original_len;
276 if !suffix.is_empty() && cut_was_done {
277 text.push_str(suffix);
278 }
279}