1//! The module contains [`Measurement`] trait and its implementations to be used in [`Height`] and [`Width`].;
2
3use crate::{
4 grid::config::SpannedConfig,
5 grid::dimension::SpannedGridDimension,
6 grid::records::{ExactRecords, PeekableRecords, Records},
7 grid::util::string::{self, string_width_multiline},
8 settings::{Height, Width},
9};
10
11/// A width value which can be obtained on behalf of [`Table`].
12///
13/// [`Table`]: crate::Table
14pub trait Measurement<Attribute> {
15 /// Returns a measurement value.
16 fn measure<R: Records + ExactRecords + PeekableRecords>(
17 &self,
18 records: R,
19 cfg: &SpannedConfig,
20 ) -> usize;
21}
22
23impl<T> Measurement<T> for usize {
24 fn measure<R>(&self, _: R, _: &SpannedConfig) -> usize {
25 *self
26 }
27}
28
29/// Max width value.
30#[derive(Debug)]
31pub struct Max;
32
33impl Measurement<Width> for Max {
34 fn measure<R: Records + ExactRecords + PeekableRecords>(
35 &self,
36 records: R,
37 _: &SpannedConfig,
38 ) -> usize {
39 grid_widths(&records)
40 .map(|r| r.max().unwrap_or(0))
41 .max()
42 .unwrap_or(default:0)
43 }
44}
45
46impl Measurement<Height> for Max {
47 fn measure<R: Records + ExactRecords + PeekableRecords>(
48 &self,
49 records: R,
50 _: &SpannedConfig,
51 ) -> usize {
52 records_heights(&records)
53 .map(|r| r.max().unwrap_or(0))
54 .max()
55 .unwrap_or(default:0)
56 }
57}
58
59/// Min width value.
60#[derive(Debug)]
61pub struct Min;
62
63impl Measurement<Width> for Min {
64 fn measure<R: Records + ExactRecords + PeekableRecords>(
65 &self,
66 records: R,
67 _: &SpannedConfig,
68 ) -> usize {
69 grid_widths(&records)
70 .map(|r| r.min().unwrap_or(0))
71 .max()
72 .unwrap_or(default:0)
73 }
74}
75
76impl Measurement<Height> for Min {
77 fn measure<R: Records + ExactRecords + PeekableRecords>(
78 &self,
79 records: R,
80 _: &SpannedConfig,
81 ) -> usize {
82 records_heights(&records)
83 .map(|r| r.max().unwrap_or(0))
84 .min()
85 .unwrap_or(default:0)
86 }
87}
88
89/// Percent from a total table width.
90#[derive(Debug)]
91pub struct Percent(pub usize);
92
93impl Measurement<Width> for Percent {
94 fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
95 where
96 R: Records,
97 {
98 let (_, total: usize) = get_table_widths_with_total(records, cfg);
99 (total * self.0) / 100
100 }
101}
102
103impl Measurement<Height> for Percent {
104 fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
105 where
106 R: Records + ExactRecords,
107 {
108 let (_, total: usize) = get_table_heights_width_total(records, cfg);
109 (total * self.0) / 100
110 }
111}
112
113fn grid_widths<R: Records + ExactRecords + PeekableRecords>(
114 records: &R,
115) -> impl Iterator<Item = impl Iterator<Item = usize> + '_> + '_ {
116 let (count_rows: usize, count_cols: usize) = (records.count_rows(), records.count_columns());
117 (0..count_rows).map(move |row: usize| {
118 (0..count_cols).map(move |col: usize| string_width_multiline(records.get_text((row, col))))
119 })
120}
121
122fn get_table_widths_with_total<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, usize)
123where
124 R: Records,
125{
126 let widths: Vec = SpannedGridDimension::width(records, cfg);
127 let total_width: usize = get_table_total_width(&widths, cfg);
128 (widths, total_width)
129}
130
131fn get_table_total_width(list: &[usize], cfg: &SpannedConfig) -> usize {
132 let total: usize = list.iter().sum::<usize>();
133
134 total + cfg.count_vertical(count_columns:list.len())
135}
136
137fn records_heights<R>(records: &R) -> impl Iterator<Item = impl Iterator<Item = usize> + '_> + '_
138where
139 R: Records + ExactRecords + PeekableRecords,
140{
141 (0..records.count_rows()).map(move |row: usize| {
142 (0..records.count_columns())
143 .map(move |col: usize| string::count_lines(records.get_text((row, col))))
144 })
145}
146
147fn get_table_heights_width_total<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, usize)
148where
149 R: Records,
150{
151 let list: Vec = SpannedGridDimension::height(records, cfg);
152 let total: usize = get_table_total_height(&list, cfg);
153 (list, total)
154}
155
156fn get_table_total_height(list: &[usize], cfg: &SpannedConfig) -> usize {
157 let total: usize = list.iter().sum::<usize>();
158 let counth: usize = cfg.count_horizontal(count_rows:list.len());
159
160 total + counth
161}
162