1use std::collections::HashMap;
2use serde::{Deserialize, Serialize};
3
4#[cfg(test)]
5mod tests {
6 #[test]
7 fn it_works() {
8 let result = 2 + 2;
9 assert_eq!(result, 4);
10 }
11}
12
13#[derive(Clone, Debug, Deserialize, Serialize)]
14pub struct Table {
15 num_cols: usize,
16 rows: Vec<Vec<String>>,
17}
18
19#[derive(Clone, Debug)]
20pub enum TableError {
21 WrongNumberCols,
22 NotEnoughCols,
23}
24
25impl Table {
26 pub fn new(headers: Vec<String>) -> Self {
27 let mut rows = Vec::new();
28 let cols = headers.len();
29 rows.push(headers);
30 Self {
31 num_cols: cols,
32 rows: rows,
33 }
34 }
35 pub fn add_row(&mut self, row: Vec<String>) -> Result<(), TableError> {
36 if row.len() == self.num_cols {
37 self.rows.push(row);
38 return Ok(());
39 }
40 return Err(TableError::WrongNumberCols);
41 }
42 pub fn output_csv(&self) -> String {
43 let mut wtr = csv::Writer::from_writer(vec![]);
44 for row in self.rows.clone() {
45 wtr.write_record(row.as_slice()).unwrap();
46 }
47 String::from_utf8(wtr.into_inner().unwrap()).unwrap()
48 }
49 pub fn output_json(&self) -> String {
50 let mut json_vec: Vec<HashMap<String, String>> = Vec::new();
51 let headers = self.rows[0].clone();
52 for row in self.rows.clone()[1..].iter() {
53 let mut json_hashmap: HashMap<String, String> = HashMap::new();
54 for spot_header in headers.iter().enumerate() {
55 json_hashmap.insert(spot_header.1.clone(), row[spot_header.0].clone());
56 }
57 json_vec.push(json_hashmap);
58 }
59 serde_json::to_string_pretty(&json_vec).unwrap()
60 }
61 pub fn output_table_html(&self, name: Option<impl Into<String>>) -> String {
62 let mut data = String::new();
63 if let Some(table_name) = name {
64 data.push_str(format!("<p>{}</p>\n", html_escape::encode_text(table_name.into().as_str())).as_str())
65 }
66 data.push_str("<table>\n");
67 data.push_str(format!("<th>{}</th>", self.rows[0].iter().map(|x| html_escape::encode_text(x).to_string()).collect::<Vec<String>>().join("</th><th>")).as_str());
68 data.push_str("\n");
69 for row in self.rows[1..].iter() {
70 data.push_str("<tr>\n");
71 data.push_str(format!("<td>\n{}</td>", row.iter().map(|x| html_escape::encode_text(x).to_string()).collect::<Vec<String>>().join("</td>\n<td>")).as_str());
72 data.push_str("\n</tr>\n");
73 }
74 data.push_str("</table>\n");
75 data
76 }
77 fn create_line(input: Vec<String>, wrap_space: usize) -> String {
78 let mut line = String::new();
79 line.push_str("|");
80 for in_string in input {
82 line.push_str(format!(" {}{}|", in_string, vec![" "; wrap_space + 1 - in_string.chars().count()].join("")).as_str());
84 }
85 line.push('\n');
86 line
87 }
88 pub fn output_pretty_table(&self, width: Option<usize>) -> Result<String, TableError> {
89 let mut data = String::new();
90 let term_cols: usize;
91 if let Some(w) = width {
92 term_cols = w;
93 } else if let Some(term_size) = termsize::get() {
94 term_cols = term_size.cols.into();
95 } else {
96 term_cols = 200;
97 }
98 let headers = self.rows[0].clone();
99 let mut total_chars: usize = (headers.iter().map(|x| x.chars().count() + 3).max().unwrap()) * headers.len() + 1;
100 if total_chars > term_cols {
101 return Err(TableError::NotEnoughCols);
102 }
103 let mut test_add = 1;
104 while (headers.iter().map(|x| x.chars().count() + 3 + test_add).max().unwrap()) * headers.len() + 1 <= term_cols {
105 total_chars = (headers.iter().map(|x| x.chars().count() + 3 + test_add).max().unwrap()) * headers.len() + 1;
106 test_add += 1
107 }
108 let spacing: usize = (total_chars / headers.len()) - 1;
111 let wrap_space = spacing - 2;
114 let mut horiz_border = String::new();
118 horiz_border.push('+');
119 for _count in 0..headers.len() {
120 for _count2 in 0..spacing {
121 horiz_border.push('=');
122 }
123 horiz_border.push('+');
124 }
125 horiz_border.push('\n');
126 data.push_str(horiz_border.as_str());
127 data.push_str(Self::create_line(headers, wrap_space).as_str());
128 data.push_str(horiz_border.as_str());
129 horiz_border = horiz_border.replace("=", "-");
130 for row in self.rows[1..].iter() {
131 let mut max_num_lines = 1;
132 let mut vals_lines_vec: Vec<Vec<String>> = Vec::new();
133 for val in row {
134 let lines = textwrap::fill(val, wrap_space).replace("\r", "").split("\n").map(|x| x.to_string()).collect::<Vec<String>>();
135 let num = lines.len();
136 vals_lines_vec.push(lines);
137 if num > max_num_lines {
138 max_num_lines = num;
139 }
140 }
141 for count in 0..max_num_lines {
142 let val_lines_data: Vec<String> = vals_lines_vec.iter().map(|x| {
144 if x.len() - 1 < count {
145 String::new()
146 } else {
147 x[count].clone()
148 }
149 }).collect();
150 data.push_str(Self::create_line(val_lines_data, wrap_space).as_str());
151 }
152 data.push_str(horiz_border.as_str());
153 }
154 Ok(data)
156 }
157}