1use std::{borrow::Cow, fmt, iter::FromIterator};
4
5use papergrid::{
6 height::HeightEstimator,
7 records::{
8 cell_info::CellInfo,
9 vec_records::{CellMut, VecRecords},
10 Records, RecordsMut,
11 },
12 width::{CfgWidthFunction, WidthEstimator},
13 Estimate, Grid, GridConfig,
14};
15
16use crate::{
17 builder::Builder, height::get_table_total_height, object::Entity, width::get_table_total_width,
18 Tabled,
19};
20
21pub trait TableOption<R> {
23 fn change(&mut self, table: &mut Table<R>);
25}
26
27impl<T, R> TableOption<R> for &mut T
28where
29 T: TableOption<R> + ?Sized,
30{
31 fn change(&mut self, table: &mut Table<R>) {
32 T::change(self, table);
33 }
34}
35
36pub trait CellOption<R> {
43 fn change_cell(&mut self, table: &mut Table<R>, entity: Entity);
45}
46
47#[derive(Debug, Clone)]
81pub struct Table<R = VecRecords<CellInfo<'static>>> {
82 records: R,
83 cfg: GridConfig,
84 has_header: bool,
85 widths: Option<Vec<usize>>,
86 heights: Option<Vec<usize>>,
87}
88
89impl Table<VecRecords<CellInfo<'static>>> {
90 pub fn new<I, T>(iter: I) -> Self
95 where
96 I: IntoIterator<Item = T>,
97 T: Tabled,
98 {
99 let ctrl = CfgWidthFunction::new(4);
100
101 let mut header = vec![CellInfo::default(); T::LENGTH];
102 for (text, cell) in T::headers().into_iter().zip(header.iter_mut()) {
103 CellMut::set(cell, text, &ctrl);
104 }
105
106 let mut records = vec![header];
107 for row in iter.into_iter() {
108 let mut list = vec![CellInfo::default(); T::LENGTH];
109 for (text, cell) in row.fields().into_iter().zip(list.iter_mut()) {
110 CellMut::set(cell, text.into_owned(), &ctrl);
111 }
112
113 records.push(list);
114 }
115
116 let mut b = Builder::custom(VecRecords::from(records));
117 b.with_header();
118 b.build()
119 }
120}
121
122impl Table<()> {
123 #[cfg_attr(feature = "derive", doc = "```")]
129 #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
130 pub fn builder<I, T>(iter: I) -> Builder<'static>
171 where
172 T: Tabled,
173 I: IntoIterator<Item = T>,
174 {
175 let ctrl = CfgWidthFunction::new(4);
176 let mut records = Vec::new();
177 for row in iter {
178 let mut list = vec![CellInfo::default(); T::LENGTH];
179 for (text, cell) in row.fields().into_iter().zip(list.iter_mut()) {
180 CellMut::set(cell, text.into_owned(), &ctrl);
181 }
182
183 records.push(list);
184 }
185
186 let mut b = Builder::from(records);
187 b.hint_column_size(T::LENGTH);
188 b.set_columns(T::headers());
189
190 b
191 }
192}
193
194impl<R> Table<R> {
195 pub fn get_config(&self) -> &GridConfig {
197 &self.cfg
198 }
199
200 pub fn get_config_mut(&mut self) -> &mut GridConfig {
202 &mut self.cfg
203 }
204
205 pub fn get_records(&self) -> &R {
207 &self.records
208 }
209
210 pub fn get_records_mut(&mut self) -> &mut R {
212 &mut self.records
213 }
214
215 pub fn with<O>(&mut self, mut option: O) -> &mut Self
219 where
220 O: TableOption<R>,
221 {
222 option.change(self);
223 self
224 }
225
226 pub fn has_header(&self) -> bool {
231 self.has_header
232 }
233
234 pub(crate) fn cache_width(&mut self, widths: Vec<usize>) {
235 self.widths = Some(widths);
236 }
237
238 pub(crate) fn destroy_width_cache(&mut self) {
239 self.widths = None;
240 }
241
242 pub(crate) fn cache_height(&mut self, widths: Vec<usize>) {
243 self.heights = Some(widths);
244 }
245
246 pub(crate) fn destroy_height_cache(&mut self) {
247 self.heights = None;
248 }
249
250 pub(crate) fn set_header_flag(&mut self, has_header: bool) {
251 self.has_header = has_header;
252 }
253}
254
255impl<R> Table<R>
256where
257 R: Records,
258{
259 pub fn shape(&self) -> (usize, usize) {
261 let records = self.get_records();
262 (records.count_rows(), records.count_columns())
263 }
264
265 pub fn count_rows(&self) -> usize {
267 self.get_records().count_rows()
268 }
269
270 pub fn count_columns(&self) -> usize {
272 self.get_records().count_columns()
273 }
274
275 pub fn is_empty(&self) -> bool {
277 let (count_rows, count_cols) = self.shape();
278 count_rows == 0 || count_cols == 0
279 }
280
281 pub fn total_width(&self) -> usize {
283 let ctrl = self.get_width_ctrl();
284 get_table_total_width(&self.records, &self.cfg, &ctrl)
285 }
286
287 pub fn total_height(&self) -> usize {
289 let ctrl = self.get_height_ctrl();
290 get_table_total_height(&self.records, &self.cfg, &ctrl)
291 }
292
293 fn get_width_ctrl(&self) -> CachedEstimator<'_, WidthEstimator> {
294 match &self.widths {
295 Some(widths) => CachedEstimator::Cached(widths),
296 None => {
297 let mut w = WidthEstimator::default();
298 w.estimate(&self.records, &self.cfg);
299 CachedEstimator::Ctrl(w)
300 }
301 }
302 }
303
304 fn get_height_ctrl(&self) -> CachedEstimator<'_, HeightEstimator> {
305 match &self.heights {
306 Some(heights) => CachedEstimator::Cached(heights),
307 None => {
308 let mut w = HeightEstimator::default();
309 w.estimate(&self.records, &self.cfg);
310 CachedEstimator::Ctrl(w)
311 }
312 }
313 }
314}
315
316impl<R> Table<R>
317where
318 R: Records + RecordsMut<String>,
319{
320 pub(crate) fn update_records(&mut self) {
321 let ctrl = CfgWidthFunction::from_cfg(self.get_config());
322
323 for row in 0..self.get_records().count_rows() {
324 for col in 0..self.get_records().count_columns() {
325 let records = self.get_records_mut();
326 records.update((row, col), &ctrl);
327 }
328 }
329 }
330}
331
332impl<R> fmt::Display for Table<R>
333where
334 R: Records,
335{
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 let mut cfg = Cow::Borrowed(&self.cfg);
338 set_align_table(f, &mut cfg);
339 set_width_table(f, &mut cfg, self);
340
341 let width = self.get_width_ctrl();
342 let height = self.get_height_ctrl();
343
344 let grid = Grid::new(&self.records, &cfg, &width, &height);
345
346 write!(f, "{}", grid)
347 }
348}
349
350impl<R> From<R> for Table<R>
351where
352 R: Records,
353{
354 fn from(records: R) -> Self {
355 Self {
356 records,
357 cfg: GridConfig::default(),
358 has_header: false,
359 widths: None,
360 heights: None,
361 }
362 }
363}
364
365impl<'a, T> FromIterator<&'a T> for Table<VecRecords<CellInfo<'a>>>
366where
367 T: Tabled + 'a,
368{
369 fn from_iter<I>(iter: I) -> Self
370 where
371 I: IntoIterator<Item = &'a T>,
372 {
373 let ctrl = CfgWidthFunction::new(4);
374
375 let mut header = vec![CellInfo::default(); T::LENGTH];
376 for (text, cell) in T::headers().into_iter().zip(header.iter_mut()) {
377 CellMut::set(cell, text, &ctrl);
378 }
379
380 let mut records = vec![header];
381 for row in iter.into_iter() {
382 let mut list = vec![CellInfo::default(); T::LENGTH];
383 for (text, cell) in row.fields().into_iter().zip(list.iter_mut()) {
384 CellMut::set(cell, text, &ctrl);
385 }
386
387 records.push(list);
388 }
389
390 Builder::custom(VecRecords::from(records)).build()
391 }
392}
393
394#[derive(Debug)]
395enum CachedEstimator<'a, E> {
396 Cached(&'a [usize]),
397 Ctrl(E),
398}
399
400impl<R, E> Estimate<R> for CachedEstimator<'_, E>
401where
402 R: Records,
403 E: Estimate<R>,
404{
405 fn estimate(&mut self, _: R, _: &GridConfig) {}
406
407 fn get(&self, i: usize) -> Option<usize> {
408 match self {
409 Self::Cached(list) => list.get(i).copied(),
410 Self::Ctrl(e) => Estimate::<R>::get(e, i),
411 }
412 }
413
414 fn total(&self) -> usize {
415 match self {
416 Self::Cached(list) => list.iter().sum(),
417 Self::Ctrl(e) => Estimate::<R>::total(e),
418 }
419 }
420}
421
422fn set_align_table(f: &fmt::Formatter<'_>, cfg: &mut Cow<'_, GridConfig>) {
423 if let Some(alignment) = f.align() {
424 let alignment = convert_fmt_alignment(alignment);
425
426 match cfg {
427 Cow::Borrowed(c) => {
428 let mut new = c.clone();
429 new.set_alignment_horizontal(Entity::Global, alignment);
430 *cfg = Cow::Owned(new);
431 }
432 Cow::Owned(cfg) => {
433 cfg.set_alignment_horizontal(Entity::Global, alignment);
434 }
435 }
436 }
437}
438
439fn set_width_table<R>(f: &fmt::Formatter<'_>, cfg: &mut Cow<'_, GridConfig>, table: &Table<R>)
440where
441 R: Records,
442{
443 if let Some(width) = f.width() {
444 let total_width = table.total_width();
445 if total_width >= width {
446 return;
447 }
448
449 let mut fill = f.fill();
450 if fill == char::default() {
451 fill = ' ';
452 }
453
454 let available = width - total_width;
455 let alignment = f.align().unwrap_or(fmt::Alignment::Left);
456 let (left, right) = table_padding(alignment, available);
457
458 let mut margin = *cfg.get_margin();
459 margin.left.size += left;
460 margin.right.size += right;
461
462 if (margin.left.size > 0 && margin.left.fill == char::default()) || fill != char::default()
463 {
464 margin.left.fill = fill;
465 }
466
467 if (margin.right.size > 0 && margin.right.fill == char::default())
468 || fill != char::default()
469 {
470 margin.right.fill = fill;
471 }
472
473 match cfg {
474 Cow::Borrowed(c) => {
475 let mut new = c.clone();
476 new.set_margin(margin);
477 *cfg = Cow::Owned(new);
478 }
479 Cow::Owned(cfg) => cfg.set_margin(margin),
480 }
481 }
482}
483
484fn convert_fmt_alignment(alignment: fmt::Alignment) -> papergrid::AlignmentHorizontal {
485 match alignment {
486 fmt::Alignment::Left => papergrid::AlignmentHorizontal::Left,
487 fmt::Alignment::Right => papergrid::AlignmentHorizontal::Right,
488 fmt::Alignment::Center => papergrid::AlignmentHorizontal::Center,
489 }
490}
491
492fn table_padding(alignment: fmt::Alignment, available: usize) -> (usize, usize) {
493 match alignment {
494 fmt::Alignment::Left => (available, 0),
495 fmt::Alignment::Right => (0, available),
496 fmt::Alignment::Center => {
497 let left = available / 2;
498 let right = available - left;
499 (left, right)
500 }
501 }
502}