tabled/settings/measurement/
mod.rs

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::IterGridDimension,
6    grid::records::{ExactRecords, IntoRecords, PeekableRecords, Records},
7    grid::util::string::{self, get_text_width},
8    settings::{Height, Width},
9};
10
11// todo: Change the trait to not bind to exact Records
12
13/// A width value which can be obtained on behalf of [`Table`].
14///
15/// [`Table`]: crate::Table
16pub trait Measurement<Attribute> {
17    /// Returns a measurement value.
18    fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
19    where
20        R: Records + ExactRecords + PeekableRecords,
21        <R::Iter as IntoRecords>::Cell: AsRef<str>;
22}
23
24impl<T> Measurement<T> for usize {
25    fn measure<R>(&self, _: R, _: &SpannedConfig) -> usize {
26        *self
27    }
28}
29
30/// Max width value.
31#[derive(Debug)]
32pub struct Max;
33
34impl Measurement<Width> for Max {
35    fn measure<R: Records + ExactRecords + PeekableRecords>(
36        &self,
37        records: R,
38        _: &SpannedConfig,
39    ) -> usize {
40        grid_widths(&records)
41            .map(|r| r.max().unwrap_or(0))
42            .max()
43            .unwrap_or(0)
44    }
45}
46
47impl Measurement<Height> for Max {
48    fn measure<R: Records + ExactRecords + PeekableRecords>(
49        &self,
50        records: R,
51        _: &SpannedConfig,
52    ) -> usize {
53        records_heights(&records)
54            .map(|r| r.max().unwrap_or(0))
55            .max()
56            .unwrap_or(0)
57    }
58}
59
60/// Min width value.
61#[derive(Debug)]
62pub struct Min;
63
64impl Measurement<Width> for Min {
65    fn measure<R: Records + ExactRecords + PeekableRecords>(
66        &self,
67        records: R,
68        _: &SpannedConfig,
69    ) -> usize {
70        grid_widths(&records)
71            .map(|r| r.min().unwrap_or(0))
72            .max()
73            .unwrap_or(0)
74    }
75}
76
77impl Measurement<Height> for Min {
78    fn measure<R: Records + ExactRecords + PeekableRecords>(
79        &self,
80        records: R,
81        _: &SpannedConfig,
82    ) -> usize {
83        records_heights(&records)
84            .map(|r| r.max().unwrap_or(0))
85            .min()
86            .unwrap_or(0)
87    }
88}
89
90/// Percent from a total table width.
91#[derive(Debug)]
92pub struct Percent(pub usize);
93
94impl Measurement<Width> for Percent {
95    fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
96    where
97        R: Records,
98        <R::Iter as IntoRecords>::Cell: AsRef<str>,
99    {
100        let total = IterGridDimension::width_total(records, cfg);
101        (total * self.0) / 100
102    }
103}
104
105impl Measurement<Height> for Percent {
106    fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
107    where
108        R: Records + ExactRecords,
109        <R::Iter as IntoRecords>::Cell: AsRef<str>,
110    {
111        let total = IterGridDimension::height_total(records, cfg);
112        (total * self.0) / 100
113    }
114}
115
116fn grid_widths<R>(records: &R) -> impl Iterator<Item = impl Iterator<Item = usize> + '_> + '_
117where
118    R: Records + ExactRecords + PeekableRecords,
119{
120    let (count_rows, count_cols) = (records.count_rows(), records.count_columns());
121    (0..count_rows).map(move |row| {
122        (0..count_cols).map(move |col| get_text_width(records.get_text((row, col).into())))
123    })
124}
125
126fn records_heights<R>(records: &R) -> impl Iterator<Item = impl Iterator<Item = usize> + '_> + '_
127where
128    R: Records + ExactRecords + PeekableRecords,
129{
130    (0..records.count_rows()).map(move |row| {
131        (0..records.count_columns())
132            .map(move |col| string::count_lines(records.get_text((row, col).into())))
133    })
134}