gooey/widget/textbox/
make_table.rs

1//! Some utilities for making formatted table strings
2
3pub macro make_table {
4  ($([$($item:expr),+]),+$(, ($spacing:expr))?) => {
5    mk_table (
6      vec![],
7      vec![$(box_vec!($($item),+)),+]
8      $(,$spacing)?
9    )
10  }
11}
12
13pub macro box_vec {
14  ($($item:expr),*) => {
15    vec![$(Box::new ($item)),*]
16  }
17}
18
19pub fn mk_table (
20  headers : Vec <Vec <Box <dyn ToString>>>,
21  rows    : Vec <Vec <Box <dyn ToString>>>,
22  spacing : usize
23) -> String {
24  let mut cols = vec![];
25  for row in headers.iter().chain (rows.iter()) {
26    for (i, entry) in row.iter().enumerate() {
27      if cols.get (i).is_none() {
28        cols.push (vec![]);
29      }
30      let col = &mut cols[i];
31      col.push (entry.to_string());
32    }
33  }
34  let col_lens = cols.iter()
35    .map (|col| col.iter().map (|entry| entry.len()).max().unwrap_or (0))
36    .collect::<Vec <_>>();
37  let col_numeric = cols.iter().map (|col|
38      col.iter().skip (headers.len()).all (|entry| entry.parse::<f32>().is_ok())
39    ).collect::<Vec <_>>();
40  let mut out = String::new();
41  for (i, _) in headers.iter().chain (rows.iter()).enumerate() {
42    for (j, col) in cols.iter().enumerate() {
43      if let Some (entry) = col.get (i) {
44        let col_width = col_lens[j];
45        let col_num = if i < headers.len() {
46          false
47        }  else {
48          col_numeric[j]
49        };
50        if col_num {
51          out.push_str (&format!("{:>col_width$}", entry))
52        } else {
53          out.push_str (&format!("{:col_width$}", entry))
54        }
55        out.push_str (&" ".repeat (spacing));
56      } else {
57        break
58      }
59    }
60    out.push ('\n');
61  }
62  out
63}