strafe_type/
display_table.rs

1use std::fmt::{Display, Formatter, Result as FmtResult};
2
3use numfmt::{Numeric, Precision};
4
5pub struct DisplayTable {
6    column_names: Vec<String>,
7    column_widths: Vec<usize>,
8    row_names: Vec<String>,
9    data: Vec<Vec<String>>,
10    max_print: usize,
11    over_max: bool,
12}
13
14impl DisplayTable {
15    pub fn new<D: ToString + Numeric>(
16        column_names: Vec<String>,
17        row_names: Vec<String>,
18        data: Vec<Vec<D>>,
19        max_print: Option<usize>,
20    ) -> Self {
21        let mut number_formatter = numfmt::Formatter::new().precision(Precision::Significance(5));
22
23        let max_print = max_print.unwrap_or(10);
24        let mut over_max = false;
25
26        let data = data
27            .into_iter()
28            .map(|d| {
29                d.into_iter()
30                    .map(|v| number_formatter.fmt2(v).to_string())
31                    .collect::<Vec<_>>()
32            })
33            .collect::<Vec<_>>();
34
35        let data = if data.len() > max_print {
36            over_max = true;
37            data.iter()
38                .take((max_print as f64 / 2.0).ceil() as usize)
39                .chain(
40                    data.iter()
41                        .rev()
42                        .take((max_print as f64 / 2.0).floor() as usize)
43                        .rev(),
44                )
45                .cloned()
46                .collect()
47        } else {
48            data.clone()
49        };
50        let row_names = if row_names.len() > max_print {
51            row_names
52                .iter()
53                .take((max_print as f64 / 2.0).ceil() as usize)
54                .chain(
55                    row_names
56                        .iter()
57                        .rev()
58                        .take((max_print as f64 / 2.0).floor() as usize)
59                        .rev(),
60                )
61                .cloned()
62                .collect()
63        } else {
64            row_names.clone()
65        };
66
67        let mut column_widths = column_names.iter().map(|c| c.len()).collect::<Vec<_>>();
68
69        for row in &data {
70            for (column, width) in row.iter().zip(column_widths.iter_mut()) {
71                *width = (*width).max(column.len());
72            }
73        }
74
75        if !row_names.is_empty() {
76            column_widths.insert(0, row_names.iter().map(|n| n.len()).max().unwrap());
77        };
78
79        Self {
80            column_names,
81            column_widths,
82            row_names,
83            data,
84            max_print,
85            over_max,
86        }
87    }
88
89    fn write_table_bars(
90        &self,
91        formatter: &mut Formatter,
92        left_bar: &str,
93        middle_blank: &str,
94        middle_bar: &str,
95        right_bar: &str,
96    ) -> FmtResult {
97        write!(formatter, "{left_bar}")?;
98        for (index, &len) in self.column_widths.iter().enumerate() {
99            if len > 0 {
100                for _ in 0..len + 2 {
101                    write!(formatter, "{middle_blank}")?;
102                }
103                if index != self.column_widths.len() - 1 {
104                    write!(formatter, "{middle_bar}")?;
105                }
106            }
107        }
108        writeln!(formatter, "{right_bar}")?;
109
110        Ok(())
111    }
112
113    fn write_data(&self, formatter: &mut Formatter, bar: &str) -> FmtResult {
114        for (row_index, row) in self.data.iter().enumerate() {
115            if self.over_max && row_index == (self.max_print as f64 / 2.0).floor() as usize {
116                for column_index in 0..self.column_widths.len() {
117                    write!(formatter, "│ …")?;
118                    for _ in 0..self.column_widths[column_index] - 1 {
119                        write!(formatter, " ")?;
120                    }
121                    write!(formatter, " ")?;
122                }
123                writeln!(formatter, "│")?;
124            }
125            for (column_index, value) in row.iter().enumerate() {
126                write!(formatter, "{bar} ")?;
127                let column_index = if !self.row_names.is_empty() && column_index == 0 {
128                    self.row_names[row_index].fmt(formatter)?;
129                    for _ in 0..self.column_widths[column_index] - self.row_names[row_index].len() {
130                        write!(formatter, " ")?;
131                    }
132                    write!(formatter, " {bar} ")?;
133                    value.fmt(formatter)?;
134                    column_index + 1
135                } else {
136                    value.fmt(formatter)?;
137                    if !self.row_names.is_empty() {
138                        column_index + 1
139                    } else {
140                        column_index
141                    }
142                };
143                if self.column_widths[column_index] > value.to_string().len() {
144                    for _ in 0..self.column_widths[column_index] - value.to_string().len() {
145                        write!(formatter, " ")?;
146                    }
147                }
148                write!(formatter, " ")?;
149            }
150            writeln!(formatter, "{bar}")?;
151        }
152
153        Ok(())
154    }
155}
156
157impl Display for DisplayTable {
158    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
159        // Print top of the table
160        self.write_table_bars(f, "┌", "─", "┬", "┐")?;
161
162        // Print column headers
163        let mut column_names = self.column_names.clone();
164        if !self.row_names.is_empty() {
165            column_names.insert(0, "".to_string());
166        }
167        for (column_index, name) in column_names.iter().enumerate() {
168            write!(f, "│ ")?;
169            name.fmt(f)?;
170            if self.column_widths[column_index] > name.len() {
171                for _ in 0..self.column_widths[column_index] - name.len() {
172                    write!(f, " ")?;
173                }
174            }
175            write!(f, " ")?;
176        }
177        writeln!(f, "│")?;
178
179        // Print divider
180        self.write_table_bars(f, "╞", "═", "╪", "╡")?;
181
182        // Print data
183        self.write_data(f, "│")?;
184
185        // Print bottom of the table
186        self.write_table_bars(f, "└", "─", "┴", "┘")?;
187
188        Ok(())
189    }
190}