///|
fn spaces(count : Int) -> String {
if count <= 0 {
""
} else {
String::from_array(Array::make(count, ' '))
}
}
///|
/// Pad a string on the right to a minimum width.
pub fn pad_right(value : String, width : Int) -> String {
if value.length() >= width {
value
} else {
value + spaces(width - value.length())
}
}
///|
fn row_width(row : Array[String], index : Int) -> Int {
if index < row.length() {
row[index].length()
} else {
0
}
}
///|
fn table_widths(rows : Array[Array[String]]) -> Array[Int] {
let widths = []
for row in rows {
for i in 0.. widths[i] {
widths[i] = row_width(row, i)
}
}
}
widths
}
///|
fn render_row(row : Array[String], widths : Array[Int]) -> String {
let mut output = ""
for i in 0.. 0 {
output = output + " | "
}
let is_last = i == row.length() - 1
let cell = if is_last { row[i] } else { pad_right(row[i], widths[i]) }
output = output + cell
}
output
}
///|
/// Render a small aligned text table with no hidden sorting.
pub fn render_text_table(rows : Array[Array[String]]) -> String {
if rows.length() == 0 {
""
} else {
let widths = table_widths(rows)
let mut output = ""
for row in rows {
output = output + render_row(row, widths) + "\n"
}
output
}
}
///|
/// Return the maximum number of columns in a table.
pub fn table_column_count(rows : Array[Array[String]]) -> Int {
let mut result = 0
for row in rows {
if row.length() > result {
result = row.length()
}
}
result
}
///|
/// Return whether all rows have the same number of columns.
pub fn table_is_rectangular(rows : Array[Array[String]]) -> Bool {
if rows.length() == 0 {
true
} else {
let expected = rows[0].length()
let mut result = true
for row in rows {
if row.length() != expected {
result = false
}
}
result
}
}
///|
/// Render plan steps as an aligned table.
pub fn render_plan_table(plan : MeasurementPlan) -> String {
let rows = [["feature", "device", "strategy", "direction", "uncertainty"]]
for step in plan.steps {
rows.push([
step.feature_id,
step.device_id,
strategy_text(step.strategy),
step.direction,
step.uncertainty.to_string(),
])
}
render_text_table(rows)
}
///|
/// Render dashboard cards as an aligned table.
pub fn render_dashboard_table(dashboard : QualityDashboard) -> String {
let rows = [["key", "label", "value", "status"]]
for card in dashboard.cards {
rows.push([card.key, card.label, card.value, card.status])
}
render_text_table(rows)
}
///|
/// Render diagnostic records as an aligned table.
pub fn render_diagnostics_table(diagnostics : Array[Diagnostic]) -> String {
let rows = [["severity", "code", "index", "message"]]
for diagnostic in diagnostics {
rows.push([
severity_text(diagnostic.severity),
diagnostic.code,
diagnostic.index.to_string(),
diagnostic.message,
])
}
render_text_table(rows)
}
///|
/// Render key-value rows while preserving input order.
pub fn render_key_values(values : Array[(String, String)]) -> String {
let rows = []
for pair in values {
rows.push([pair.0, pair.1])
}
render_text_table(rows)
}
///|
/// Truncate text with an ellipsis when it exceeds a limit.
pub fn truncate_text(value : String, limit : Int) -> String {
if limit <= 0 {
""
} else if value.length() <= limit {
value
} else if limit == 1 {
"…"
} else {
let chars = value.to_array()
let prefix = []
for i in 0..<(limit - 1) {
prefix.push(chars[i])
}
String::from_array(prefix) + "…"
}
}
///|
/// Render a one-line metric with a stable key.
pub fn render_metric(key : String, value : Double) -> String {
key + "=" + value.to_string()
}