papergrid/dimension/
compact.rs

1//! The module contains a [`CompactGridDimension`] for [`CompactGrid`] height/width estimation.
2//!
3//! [`CompactGrid`]: crate::grid::compact::CompactGrid
4
5use core::cmp::max;
6
7use crate::{
8    config::compact::CompactConfig,
9    dimension::{Dimension, Estimate},
10    records::{IntoRecords, Records},
11    util::string::{count_lines, get_text_width},
12};
13
14/// A [`Dimension`] implementation which calculates exact column/row width/height.
15///
16/// [`Grid`]: crate::grid::iterable::Grid
17#[derive(Debug, Default, Clone, PartialEq, Eq)]
18pub struct CompactGridDimension {
19    width: Vec<usize>,
20}
21
22impl CompactGridDimension {
23    /// Calculates height of rows.
24    pub fn height<R>(records: R, cfg: &CompactConfig) -> Vec<usize>
25    where
26        R: Records,
27        <R::Iter as IntoRecords>::Cell: AsRef<str>,
28    {
29        build_height(records, cfg)
30    }
31
32    /// Calculates width of columns.
33    pub fn width<R>(records: R, cfg: &CompactConfig) -> Vec<usize>
34    where
35        R: Records,
36        <R::Iter as IntoRecords>::Cell: AsRef<str>,
37    {
38        build_width(records, cfg)
39    }
40
41    /// Calculates dimensions of columns.
42    pub fn dimension<R>(records: R, cfg: &CompactConfig) -> (Vec<usize>, Vec<usize>)
43    where
44        R: Records,
45        <R::Iter as IntoRecords>::Cell: AsRef<str>,
46    {
47        build_dimension(records, cfg)
48    }
49}
50
51impl Dimension for CompactGridDimension {
52    fn get_width(&self, column: usize) -> usize {
53        self.width[column]
54    }
55
56    fn get_height(&self, _: usize) -> usize {
57        1
58    }
59}
60
61impl<R> Estimate<R, CompactConfig> for CompactGridDimension
62where
63    R: Records,
64    <R::Iter as IntoRecords>::Cell: AsRef<str>,
65{
66    fn estimate(&mut self, records: R, cfg: &CompactConfig) {
67        self.width = build_width(records, cfg);
68    }
69}
70
71fn build_dimension<R>(records: R, cfg: &CompactConfig) -> (Vec<usize>, Vec<usize>)
72where
73    R: Records,
74    <R::Iter as IntoRecords>::Cell: AsRef<str>,
75{
76    let mut heights = vec![];
77    let mut widths = vec![0; records.count_columns()];
78
79    for columns in records.iter_rows() {
80        let mut row_height = 0;
81        for (col, cell) in columns.into_iter().enumerate() {
82            let height = get_cell_height(cell.as_ref(), cfg);
83            let width = get_cell_width(cell.as_ref(), cfg);
84            row_height = max(row_height, height);
85            widths[col] = max(widths[col], width)
86        }
87
88        heights.push(row_height);
89    }
90
91    (widths, heights)
92}
93
94fn build_height<R>(records: R, cfg: &CompactConfig) -> Vec<usize>
95where
96    R: Records,
97    <R::Iter as IntoRecords>::Cell: AsRef<str>,
98{
99    let mut heights = vec![];
100
101    for columns in records.iter_rows() {
102        let mut row_height = 0;
103        for cell in columns.into_iter() {
104            let height = get_cell_height(cell.as_ref(), cfg);
105            row_height = max(row_height, height);
106        }
107
108        heights.push(row_height);
109    }
110
111    heights
112}
113
114fn build_width<R>(records: R, cfg: &CompactConfig) -> Vec<usize>
115where
116    R: Records,
117    <R::Iter as IntoRecords>::Cell: AsRef<str>,
118{
119    let mut widths = vec![0; records.count_columns()];
120    for columns in records.iter_rows() {
121        for (col, cell) in columns.into_iter().enumerate() {
122            let width = get_cell_width(cell.as_ref(), cfg);
123            widths[col] = max(widths[col], width);
124        }
125    }
126
127    widths
128}
129
130fn get_cell_height(cell: &str, cfg: &CompactConfig) -> usize {
131    let count_lines = max(1, count_lines(cell));
132    let pad = cfg.get_padding();
133
134    count_lines + pad.top.size + pad.bottom.size
135}
136
137fn get_cell_width(text: &str, cfg: &CompactConfig) -> usize {
138    let width = get_text_width(text);
139    let pad = cfg.get_padding();
140
141    width + pad.left.size + pad.right.size
142}