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  use std::fmt::Write;
25  let mut cols = vec![];
26  for row in headers.iter().chain (rows.iter()) {
27    for (i, entry) in row.iter().enumerate() {
28      if cols.get (i).is_none() {
29        cols.push (vec![]);
30      }
31      let col = &mut cols[i];
32      col.push (entry.to_string());
33    }
34  }
35  let col_lens = cols.iter().map (|col| col.iter().map (String::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          let _ = write!(out, "{entry:>col_width$}");
52        } else {
53          let _ = write!(out, "{entry:col_width$}");
54        }
55        out.push_str (&" ".repeat (spacing));
56      } else {
57        break
58      }
59    }
60    out.push ('\n');
61  }
62  out
63}