strafe_type/
display_table.rs1use 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 self.write_table_bars(f, "┌", "─", "┬", "┐")?;
161
162 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 self.write_table_bars(f, "╞", "═", "╪", "╡")?;
181
182 self.write_data(f, "│")?;
184
185 self.write_table_bars(f, "└", "─", "┴", "┘")?;
187
188 Ok(())
189 }
190}