// Pure text-formatting helpers for CLI output. These have no IO and are
// straightforward to test in isolation.
///|
/// Renders a grid of rows as an ASCII table. The first row is treated as
/// a header and separated from the body by a rule. Column widths are the
/// widest cell in each column (measured in UTF-16 code units).
fn render_ascii_table(rows : Array[Array[String]]) -> String {
let ncols = rows.fold(init=0, (n, r) => {
if r.length() > n {
r.length()
} else {
n
}
})
if rows.length() == 0 || ncols == 0 {
return "(empty sheet)"
}
let widths = Array::make(ncols, 0)
for r in rows {
for c in 0.. widths[c] {
widths[c] = cell.length()
}
}
}
let border = ascii_border(widths)
let lines = [border, ascii_row(rows[0], widths)]
if rows.length() > 1 {
lines.push(border)
for i in 1.. String {
"+" + widths.map(w => "-".repeat(w + 2)).join("+") + "+"
}
///|
/// A `| a | b |` table row, padding each cell to its column width.
fn ascii_row(cells : Array[String], widths : Array[Int32]) -> String {
let segs = []
for c in 0.. String {
if value.contains(",") ||
value.contains("\"") ||
value.contains("\n") ||
value.contains("\r") {
"\"" + value.replace_all(old="\"", new="\"\"") + "\""
} else {
value
}
}
///|
test "render_ascii_table renders a header and body" {
inspect(
render_ascii_table([["Name", "Score"], ["Alice", "90"], ["Bob", "7"]]),
content=(
#|+-------+-------+
#|| Name | Score |
#|+-------+-------+
#|| Alice | 90 |
#|| Bob | 7 |
#|+-------+-------+
),
)
}
///|
test "render_ascii_table handles a single row and an empty grid" {
inspect(
render_ascii_table([["a", "bb"]]),
content=(
#|+---+----+
#|| a | bb |
#|+---+----+
),
)
inspect(render_ascii_table([]), content="(empty sheet)")
}
///|
test "render_ascii_table pads jagged rows to the widest column" {
inspect(
render_ascii_table([["x", "y"], ["long"]]),
content=(
#|+------+---+
#|| x | y |
#|+------+---+
#|| long | |
#|+------+---+
),
)
}
///|
test "csv_field quotes only when necessary" {
inspect(csv_field("plain"), content="plain")
inspect(
csv_field("a,b"),
content=(
#|"a,b"
),
)
inspect(
csv_field("she said \"hi\""),
content=(
#|"she said ""hi"""
),
)
}