1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! The module contains a [`CompactGridDimension`] for [`CompactGrid`] height/width estimation.
//!
//! [`CompactGrid`]: crate::grid::compact::CompactGrid

use core::cmp::max;

use crate::{
    dimension::{Dimension, Estimate},
    records::{IntoRecords, Records},
    util::string::{count_lines, get_text_width},
};

use crate::config::compact::CompactConfig;

/// A [`Dimension`] implementation which calculates exact column/row width/height.
///
/// [`Grid`]: crate::grid::iterable::Grid
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct CompactGridDimension {
    height: usize,
    width: Vec<usize>,
}

impl CompactGridDimension {
    /// Calculates height of rows.
    pub fn height<R>(records: R, cfg: &CompactConfig) -> Vec<usize>
    where
        R: Records,
        <R::Iter as IntoRecords>::Cell: AsRef<str>,
    {
        build_height(records, cfg)
    }

    /// Calculates width of columns.
    pub fn width<R>(records: R, cfg: &CompactConfig) -> Vec<usize>
    where
        R: Records,
        <R::Iter as IntoRecords>::Cell: AsRef<str>,
    {
        build_width(records, cfg)
    }

    /// Calculates dimensions of columns.
    pub fn dimension<R>(records: R, cfg: &CompactConfig) -> (Vec<usize>, Vec<usize>)
    where
        R: Records,
        <R::Iter as IntoRecords>::Cell: AsRef<str>,
    {
        build_dims(records, cfg)
    }
}

impl Dimension for CompactGridDimension {
    fn get_width(&self, column: usize) -> usize {
        self.width[column]
    }

    fn get_height(&self, _: usize) -> usize {
        self.height
    }
}

impl<R> Estimate<R, CompactConfig> for CompactGridDimension
where
    R: Records,
    <R::Iter as IntoRecords>::Cell: AsRef<str>,
{
    fn estimate(&mut self, records: R, cfg: &CompactConfig) {
        self.width = build_width(records, cfg);
        let pad = cfg.get_padding();
        self.height = 1 + pad.top.size + pad.bottom.size;
    }
}

fn build_dims<R>(records: R, cfg: &CompactConfig) -> (Vec<usize>, Vec<usize>)
where
    R: Records,
    <R::Iter as IntoRecords>::Cell: AsRef<str>,
{
    let mut heights = vec![];
    let mut widths = vec![0; records.count_columns()];

    for columns in records.iter_rows() {
        let mut row_height = 0;
        for (col, cell) in columns.into_iter().enumerate() {
            let height = get_cell_height(cell.as_ref(), cfg);
            let width = get_cell_width(cell.as_ref(), cfg);
            row_height = max(row_height, height);
            widths[col] = max(widths[col], width)
        }

        heights.push(row_height);
    }

    (widths, heights)
}

fn build_height<R>(records: R, cfg: &CompactConfig) -> Vec<usize>
where
    R: Records,
    <R::Iter as IntoRecords>::Cell: AsRef<str>,
{
    let mut heights = vec![];

    for columns in records.iter_rows() {
        let mut row_height = 0;
        for cell in columns.into_iter() {
            let height = get_cell_height(cell.as_ref(), cfg);
            row_height = max(row_height, height);
        }

        heights.push(row_height);
    }

    heights
}

fn build_width<R>(records: R, cfg: &CompactConfig) -> Vec<usize>
where
    R: Records,
    <R::Iter as IntoRecords>::Cell: AsRef<str>,
{
    let mut widths = vec![0; records.count_columns()];
    for columns in records.iter_rows() {
        for (col, cell) in columns.into_iter().enumerate() {
            let width = get_cell_width(cell.as_ref(), cfg);
            widths[col] = max(widths[col], width);
        }
    }

    widths
}

fn get_cell_height(cell: &str, cfg: &CompactConfig) -> usize {
    let count_lines = max(1, count_lines(cell));
    let pad = cfg.get_padding();

    count_lines + pad.top.size + pad.bottom.size
}

fn get_cell_width(text: &str, cfg: &CompactConfig) -> usize {
    let width = get_text_width(text);
    let pad = cfg.get_padding();

    width + pad.left.size + pad.right.size
}