#[derive(Clone, Debug)]
pub(crate) struct TableLine {
indent_front: usize,
left_column: String,
right_column: String,
}
impl TableLine {
pub(crate) fn new<LC: ToString, RC: ToString>(
indent_front: usize,
left_column: &LC,
right_column: &RC,
) -> Self {
let mut right_column = right_column.to_string();
if right_column.ends_with(" B") {
right_column = right_column.replace(" B", " B"); }
Self {
indent_front,
left_column: left_column.to_string(),
right_column,
}
}
}
pub(crate) fn two_row_table(
min_padding_middle: usize,
lines: Vec<TableLine>,
align_first_line: bool,
) -> String {
let mut first_line: Option<String> = None;
#[allow(clippy::shadow_same)]
let mut lines = lines;
if !align_first_line && !lines.is_empty() {
first_line = Some(lines.remove(0).left_column);
}
let total_entries = lines.len();
let max_len_left_col: usize = if align_first_line {
lines
.iter()
.map(|line| line.left_column.len())
.max()
.unwrap_or(0)
} else {
lines
.iter()
.skip(1)
.map(|line| line.left_column.len())
.max()
.unwrap_or(0)
};
let max_len_right_col: usize = lines
.iter()
.map(|line| line.right_column.len())
.max()
.unwrap_or(0);
let max_indent_front: usize = lines
.iter()
.map(|line| line.indent_front)
.max()
.unwrap_or(0);
let max_indent_front_chars: usize = max_indent_front * 2;
let line_length: usize =
max_len_left_col + max_len_right_col + min_padding_middle + max_indent_front_chars;
let mut table = String::with_capacity({
line_length * total_entries
});
match first_line {
None => {}
Some(line) => table.push_str(&line),
}
for line in lines {
let indent_front_len = line.indent_front * 2;
table.push_str(&" ".repeat(indent_front_len));
table.push_str(&line.left_column);
let spaces = line_length
- (indent_front_len
+ line.left_column.len()
+ min_padding_middle
+ line.right_column.len());
table.push_str(&" ".repeat(min_padding_middle + spaces));
table.push_str(&line.right_column);
table.push('\n');
}
table
}
pub(crate) fn format_table(table: &[Vec<String>], padding: usize) -> String {
const SEPARATOR: &str = " ";
let mut out = String::new();
if table.is_empty() {
return out;
}
let mut max_lengths: Vec<usize> = vec![0; table[0].len()];
for row in table {
for (idx, cell) in row.iter().enumerate() {
if cell.len() > max_lengths[idx] {
max_lengths[idx] = cell.len();
}
}
}
for row in table {
let mut new_row = String::new();
for (idx, cell) in row.iter().enumerate() {
if cell.len() < max_lengths[idx] {
let diff = max_lengths[idx] - cell.len();
let mut cell_new = cell.clone();
cell_new.push_str(&" ".repeat(diff)); cell_new.push_str(&" ".repeat(padding));
new_row.push_str(&cell_new);
} else {
let mut cell = cell.clone();
cell.push_str(&" ".repeat(padding));
new_row.push_str(&cell);
}
new_row.push_str(SEPARATOR);
}
let mut row2 = new_row.trim().to_string();
row2.push('\n');
out.push_str(&row2);
}
out
}
#[cfg(test)]
mod format_table_tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn empty() {
let v = vec![Vec::new()];
let t = format_table(&v, 0);
let output = String::from("\n");
assert_eq!(t, output);
}
#[test]
fn one_cell() {
let v = vec![vec!["hello".into()]];
let t = format_table(&v, 0);
let output = String::from("hello\n");
assert_eq!(t, output);
}
#[test]
fn one_row() {
let v = vec![vec![
"hello".into(),
"a".into(),
"shrt".into(),
"very long perhaps a few words".into(),
]];
let t = format_table(&v, 0);
let output = String::from("hello a shrt very long perhaps a few words\n");
assert_eq!(t, output);
}
#[test]
fn one_column() {
let v = vec![
vec!["hello".into()],
vec!["a".into()],
vec!["shrt".into()],
vec!["very long perhaps a few words".into()],
];
let t = format_table(&v, 0);
let output = String::from("hello\na\nshrt\nvery long perhaps a few words\n");
assert_eq!(t, output);
}
#[test]
fn matrix() {
let v = vec![
vec![
String::from("wasdwasdwasd"),
String::from("word"),
String::from("word"),
],
vec![
String::from("oh"),
String::from("why"),
String::from("this"),
],
vec![
String::from("AAAAAA"),
String::new(),
String::from("I don't get it"),
],
];
let t = format_table(&v, 0);
let output = String::from(
"wasdwasdwasd word word\noh why this\nAAAAAA I don\'t get it\n",
);
assert_eq!(t, output);
}
}