pub macro make_table {
($([$($item:expr),+]),+$(, ($spacing:expr))?) => {
mk_table (
vec![],
vec![$(box_vec!($($item),+)),+]
$(,$spacing)?
)
}
}
pub macro box_vec {
($($item:expr),*) => {
vec![$(Box::new ($item)),*]
}
}
pub fn mk_table (
headers : Vec <Vec <Box <dyn ToString>>>,
rows : Vec <Vec <Box <dyn ToString>>>,
spacing : usize
) -> String {
use std::fmt::Write;
let mut cols = vec![];
for row in headers.iter().chain (rows.iter()) {
for (i, entry) in row.iter().enumerate() {
if cols.get (i).is_none() {
cols.push (vec![]);
}
let col = &mut cols[i];
col.push (entry.to_string());
}
}
let col_lens = cols.iter().map (|col| col.iter().map (String::len).max().unwrap_or (0))
.collect::<Vec <_>>();
let col_numeric = cols.iter().map (
|col| col.iter().skip (headers.len()).all (|entry| entry.parse::<f32>().is_ok())
).collect::<Vec <_>>();
let mut out = String::new();
for (i, _) in headers.iter().chain (rows.iter()).enumerate() {
for (j, col) in cols.iter().enumerate() {
if let Some (entry) = col.get (i) {
let col_width = col_lens[j];
let col_num = if i < headers.len() {
false
} else {
col_numeric[j]
};
if col_num {
let _ = write!(out, "{entry:>col_width$}");
} else {
let _ = write!(out, "{entry:col_width$}");
}
out.push_str (&" ".repeat (spacing));
} else {
break
}
}
out.push ('\n');
}
out
}