1use self::cell::{Align, Cell};
2use self::style::{Frame, Style};
3
4pub mod cell;
5pub mod style;
6
7type Row<T> = Vec<Cell<T>>;
8
9fn display_row<T>(row: &Row<T>, width_list: &Vec<usize>, align: Align, style: Style) -> String
10where
11 T: std::fmt::Display + Clone,
12{
13 let frame: Frame = style.into();
14
15 row.iter()
16 .zip(width_list.clone())
17 .map(|(cell, width)| {
18 let mut cell = cell.clone();
19 format!("{}", cell.set_width(width).set_align(align))
20 })
21 .collect::<Vec<_>>()
22 .join(&frame.separator)
23}
24
25pub struct Table<T>
26where
27 T: std::fmt::Display + Clone,
28{
29 cols: Vec<Row<T>>,
30 header: Option<Row<T>>,
31 style: Style,
32 align: Align,
33 no_headers: bool,
34 row_len: usize,
36}
37
38impl<T> Table<T>
39where
40 T: std::fmt::Display + Clone,
41{
42 pub fn new() -> Self {
43 Self {
44 cols: Vec::new(),
45 header: None,
46 style: Style::Markdown,
47 align: Align::None,
48 no_headers: false,
49 row_len: 0,
50 }
51 }
52
53 pub fn push_row(&mut self, row: Row<T>) {
54 self.cols.push(row.clone());
55 self.row_len = self.row_len.max(row.len());
56 }
57
58 pub fn set_header(&mut self, header: Option<Row<T>>) -> &mut Self {
59 self.header = header;
60 self
61 }
62
63 pub fn set_align(&mut self, align: Align) -> &mut Self {
64 self.align = align;
65 self
66 }
67
68 pub fn set_style(&mut self, style: Style) -> &mut Self {
69 self.style = style;
70 self
71 }
72
73 pub fn set_no_headers(&mut self, no_headers: bool) -> &mut Self {
74 self.no_headers = no_headers;
75 self
76 }
77
78 fn cell_width_list(&self) -> Vec<usize> {
79 let widths = self
80 .cols
81 .iter()
82 .map(|xs| xs.iter().map(|x| x.width()).collect::<Vec<_>>());
83
84 let widths = (0..self.row_len).map(|idx| {
85 widths
86 .clone()
87 .map(move |x| x.get(idx).map(Clone::clone).unwrap_or_default())
88 .max()
89 .unwrap_or_default()
90 });
91
92 widths
93 .enumerate()
94 .map(|(idx, w)| {
95 self.header
96 .clone()
97 .and_then(|x| x.get(idx).map(|x| x.width()).map(|x| x.max(w)))
98 .unwrap_or(w)
99 })
100 .map(|x| {
101 if let Style::Markdown = self.style {
102 match self.align {
103 Align::Center => x.max(3),
104 Align::Left | Align::Right => x.max(2),
105 Align::None => x,
106 }
107 } else {
108 x
109 }
110 })
111 .collect()
112 }
113}
114
115impl<T> std::fmt::Display for Table<T>
116where
117 T: std::fmt::Display + Clone,
118{
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 let width_list = self.cell_width_list();
121 let frame: Frame = self.style.into();
122
123 if frame.has_cover {
124 write!(
125 f,
126 "{}{}{}\n",
127 frame.top_left,
128 width_list
129 .clone()
130 .into_iter()
131 .map(|x| frame.border.repeat(x))
132 .collect::<Vec<_>>()
133 .join(&frame.top),
134 frame.top_right
135 )?;
136 }
137
138 if !self.no_headers {
139 if let Some(header) = &self.header {
140 write!(
141 f,
142 "{}{}{}\n",
143 frame.separator,
144 display_row(header, &width_list, self.align, self.style),
145 frame.separator,
146 )?;
147 let border = width_list
148 .clone()
149 .into_iter()
150 .map(|x| {
151 if let Style::Markdown = self.style {
152 frame.align_border(&self.align, x)
153 } else {
154 frame.border.repeat(x)
155 }
156 })
157 .collect::<Vec<_>>()
158 .join(&frame.center);
159 write!(f, "{}{}{}\n", frame.left, border, frame.right)?;
160 }
161 }
162
163 let table = self
164 .cols
165 .iter()
166 .map(|row| display_row(row, &width_list, self.align, self.style))
167 .map(|x| format!("{}{}{}", frame.separator, x, frame.separator))
168 .collect::<Vec<_>>()
169 .join("\n");
170 write!(f, "{}", table)?;
171
172 if frame.has_cover {
173 write!(
174 f,
175 "\n{}{}{}",
176 frame.bottom_left,
177 width_list
178 .clone()
179 .into_iter()
180 .map(|x| frame.border.repeat(x))
181 .collect::<Vec<_>>()
182 .join(&frame.bottom),
183 frame.bottom_right
184 )?;
185 }
186
187 Ok(())
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use crate::table::{cell::Cell, Table};
194
195 #[test]
196 fn create_table() {
197 let mut table = Table::new();
198 table.push_row(vec![Cell::new("0"), Cell::new("1"), Cell::new("2")]);
199
200 assert_eq!(table.cols.len(), 1);
201 let row = table.cols.get(0).unwrap();
202 assert_eq!(row.len(), 3);
203 }
204
205 #[test]
206 fn display_table() {
207 let mut table = Table::new();
208 table.push_row(vec![Cell::new("0"), Cell::new("1"), Cell::new("2")]);
209
210 let expected = "|0|1|2|";
211 let actual = format!("{}", table);
212 assert_eq!(expected, actual);
213 }
214
215 #[test]
216 fn display_table_multiline() {
217 let mut table = Table::new();
218 table.push_row(vec![Cell::new("00"), Cell::new("01"), Cell::new("02")]);
219 table.push_row(vec![Cell::new("10"), Cell::new("11"), Cell::new("12")]);
220
221 let expected = "|00|01|02|\n|10|11|12|";
222 let actual = format!("{}", table);
223 assert_eq!(expected, actual);
224 }
225
226 #[test]
227 fn cell_width() {
228 let mut table = Table::new();
229 table.push_row(vec![Cell::new("00000"), Cell::new("0001"), Cell::new("02")]);
230 table.push_row(vec![Cell::new("10"), Cell::new("11"), Cell::new("12")]);
231
232 let expected = vec![5, 4, 2];
233 assert_eq!(expected, table.cell_width_list());
234 }
235
236 #[test]
237 fn cell_width_with_header() {
238 let mut table = Table::new();
239 table.push_row(vec![Cell::new("00000"), Cell::new("0001"), Cell::new("02")]);
240 table.push_row(vec![Cell::new("10"), Cell::new("11"), Cell::new("12")]);
241 table.set_header(Some(vec![
242 Cell::new("hogehogehoge"),
243 Cell::new("abcdefg"),
244 Cell::new("x"),
245 ]));
246
247 let expected = vec![12, 7, 2];
248 assert_eq!(expected, table.cell_width_list());
249 }
250
251 #[test]
252 fn display_table_other_width() {
253 let mut table = Table::new();
254 table.push_row(vec![Cell::new("00000"), Cell::new("0001"), Cell::new("02")]);
255 table.push_row(vec![Cell::new("10"), Cell::new("11"), Cell::new("12")]);
256
257 let expected = "|00000|0001|02|\n|10 |11 |12|";
258 let actual = format!("{}", table);
259 assert_eq!(expected, actual);
260 }
261
262 #[test]
263 fn display_table_width_header() {
264 let mut table = Table::new();
265 table.push_row(vec![Cell::new("00000"), Cell::new("0001"), Cell::new("02")]);
266 table.push_row(vec![Cell::new("10"), Cell::new("11"), Cell::new("12")]);
267 table.set_header(Some(vec![
268 Cell::new("hogehogehoge"),
269 Cell::new("abcdefg"),
270 Cell::new("x"),
271 ]));
272
273 let expected =
274 "|hogehogehoge|abcdefg|x |\n|------------|-------|--|\n|00000 |0001 |02|\n|10 |11 |12|";
275 let actual = format!("{}", table);
276 assert_eq!(expected, actual);
277 }
278}