use super::Row;
const HEADERS: [&str; 6] = ["ID", "LINEAGE", "PARENT", "STATE", "DIFF", "SUBJECT"];
pub fn render(rows: &[Row]) -> String {
let cells: Vec<[String; 6]> = rows.iter().map(cells).collect();
let mut widths = HEADERS.map(str::len);
for row in &cells {
for (w, cell) in widths.iter_mut().zip(row) {
*w = (*w).max(cell.chars().count());
}
}
let mut out = line(&HEADERS.map(String::from), &widths);
for row in &cells {
out.push_str(&line(row, &widths));
}
out
}
fn cells(row: &Row) -> [String; 6] {
[
row.id.clone(),
crate::name::pool(&row.lineages),
row.parent.clone(),
match row.fresh {
true => "fresh".into(),
false => "stale".into(),
},
row.diffstat.clone(),
row.subject.clone(),
]
}
fn line(cells: &[String; 6], widths: &[usize; 6]) -> String {
let mut out = String::new();
for (i, (cell, width)) in cells.iter().zip(widths).enumerate() {
if i + 1 == cells.len() {
out.push_str(cell);
} else {
out.push_str(&format!("{cell:<width$} "));
}
}
out.push('\n');
out.trim_end_matches(' ').to_string()
}
#[cfg(test)]
mod tests {
use super::*;
fn row(id: &str, lineages: &[&str], fresh: bool) -> Row {
Row {
id: id.into(),
lineages: lineages.iter().map(|l| (*l).to_string()).collect(),
parent: "9f2c1ab4de01".into(),
fresh,
diffstat: "1 file changed, 4 insertions(+)".into(),
subject: "notes: record what the span taught".into(),
}
}
#[test]
fn the_state_column_reads_the_derivation_both_ways() {
let table = render(&[row("a1-r1", &["default"], true), row("a1-r2", &[], false)]);
let mut lines = table.lines();
assert!(lines.next().unwrap().starts_with("ID "), "{table}");
let fresh = lines.next().unwrap();
assert!(
fresh.contains("default") && fresh.contains("fresh"),
"{fresh}"
);
let stale = lines.next().unwrap();
assert!(
stale.contains("(none)") && stale.contains("stale"),
"{stale}"
);
assert_eq!(lines.next(), None, "one line per row and no more");
}
#[test]
fn an_empty_listing_is_the_headers_and_nothing_else() {
assert_eq!(render(&[]).lines().count(), 1);
}
#[test]
fn columns_are_padded_to_the_widest_cell_and_the_last_is_not() {
let table = render(&[row("a-very-long-reviewer-id", &["default"], true)]);
let header = table.lines().next().unwrap();
assert!(header.starts_with("ID "), "{header}");
assert!(header.ends_with("SUBJECT"), "{header}");
}
}