1#![allow(dead_code)]
2
3pub enum TableHeaderStyle {
4 LeftAlign,
5 CenterAlign,
6 RightAlign,
7}
8
9#[derive(PartialEq)]
10pub enum TableLineStyle {
11 NoLines,
12 Lines,
13}
14
15pub struct TableStyle {
16 header: TableHeaderStyle,
17 lines: TableLineStyle,
18}
19
20pub struct Table<T> {
21 header: Vec<T>,
22 content: Vec<Vec<T>>,
23 max_cols: usize,
24 max_col_size: usize,
25 style: TableStyle,
26}
27
28impl<T: std::fmt::Display> Table<T> {
29 pub fn new() -> Table<T> {
30 let header = vec![];
31 let content = vec![vec![]];
32 let max_cols = header.len();
33 let max_col_size = 0;
34 let style = TableStyle {
35 header: TableHeaderStyle::LeftAlign,
36 lines: TableLineStyle::Lines
37 };
38
39 return Table { header, content, max_cols, max_col_size, style }
40 }
41
42 pub fn header(&mut self, header: Vec<T>) {
43 self.max_cols = usize::max(self.max_cols, header.len());
44 let max_size = header.iter()
45 .map(|item| { item.to_string().len() })
46 .max()
47 .unwrap();
48 self.max_col_size = usize::max(self.max_col_size, max_size);
49 self.header = header;
50 }
51
52 pub fn style(&mut self, style: TableStyle) {
53 self.style = style;
54 }
55
56 pub fn push(&mut self, row: Vec<T>) {
57 self.max_cols = usize::max(self.max_cols, row.len());
58 let max_size = row.iter()
59 .map(|item| { item.to_string().len() })
60 .max()
61 .unwrap();
62 self.max_col_size = usize::max(self.max_col_size, max_size);
63 self.content.push(row);
64 }
65
66 pub fn to_string(&self, padding: usize) -> String {
67 if self.max_cols == 0 {
68 return String::new();
69 }
70
71 let col_width = self.max_col_size + padding;
72 let mut result = String::new();
73
74 for item in &self.header {
75 result.push_str(&format!("{item: <col_width$}"));
76 }
77 result = result.trim_end_matches(' ').to_string();
78
79 result.push('\n');
80
81 if self.style.lines == TableLineStyle::Lines {
82 result.push_str(&"─".repeat(self.max_cols * (self.max_col_size + padding) - padding));
83 }
84
85 for row in &self.content {
86 for item in row {
87 result.push_str(&format!("{item: <col_width$}"));
88 }
89 result = result.trim_end_matches(' ').to_string();
90 result.push('\n');
91 }
92 result = result.trim_end_matches('\n').to_string();
93
94 result
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn empty() {
104 let table: Table<&str> = Table::new();
105 assert_eq!(
106 table.to_string(2),
107 ""
108 )
109 }
110
111 #[test]
112 fn emtpy_with_header() {
113 let mut table = Table::new();
114 table.header(vec!["First", "Second", "Third"]);
115
116 assert_eq!(
117 table.to_string(2),
118"First Second Third
119──────────────────────"
120 )
121 }
122
123 #[test]
124 fn table() {
125 let mut table = Table::new();
126 table.header(vec!["first", "second", "third"]);
127 table.push(vec!["Hello", "World", "!"]);
128 table.push(vec!["How", "are", "you?"]);
129 table.push(vec!["Great", "weather", "right?"]);
130
131 assert_eq!(
132 table.to_string(2),
133"first second third
134─────────────────────────
135Hello World !
136How are you?
137Great weather right?"
138 )
139 }
140}