#![allow(dead_code)]
use std::io::{self, Write};
use comfy_table::presets::UTF8_FULL;
use comfy_table::{ContentArrangement, Table};
use crate::cli::GlobalArgs;
use crate::render::ux;
pub fn render_table(globals: &GlobalArgs, headers: &[&str], rows: &[Vec<String>]) -> String {
let mut table = Table::new();
table
.load_preset(UTF8_FULL)
.set_content_arrangement(ContentArrangement::Dynamic);
if !ux::colors_enabled(globals) {
table.force_no_tty();
}
table.set_header(headers.iter().copied());
let width = headers.len();
for row in rows {
let mut padded: Vec<String> = row.iter().take(width).cloned().collect();
while padded.len() < width {
padded.push(String::new());
}
table.add_row(padded);
}
table.to_string()
}
pub fn print_table(globals: &GlobalArgs, headers: &[&str], rows: &[Vec<String>]) -> io::Result<()> {
let rendered = render_table(globals, headers, rows);
let stdout = io::stdout();
let mut handle = stdout.lock();
handle.write_all(rendered.as_bytes())?;
handle.write_all(b"\n")
}
#[cfg(test)]
mod tests {
use super::*;
fn no_globals() -> GlobalArgs {
GlobalArgs {
json: false,
ledger_path: None,
no_color: false,
}
}
#[test]
fn renders_header_and_rows() {
let rendered = render_table(
&no_globals(),
&["model", "turns"],
&[
vec!["claude-sonnet-4-6".into(), "12".into()],
vec!["claude-haiku-4-5".into(), "3".into()],
],
);
assert!(rendered.contains("model"));
assert!(rendered.contains("claude-sonnet-4-6"));
assert!(rendered.contains("12"));
}
#[test]
fn pads_short_rows() {
let rendered = render_table(
&no_globals(),
&["a", "b", "c"],
&[vec!["x".into(), "y".into()]],
);
assert!(rendered.contains('a'));
assert!(rendered.contains('b'));
assert!(rendered.contains('c'));
}
fn no_color_globals() -> GlobalArgs {
GlobalArgs {
json: false,
ledger_path: None,
no_color: true,
}
}
#[test]
fn no_color_preserves_non_ascii_cell_contents() {
let rendered = render_table(
&no_color_globals(),
&["lang", "greeting"],
&[
vec!["ja".into(), "日本語".into()],
vec!["emoji".into(), "🔥".into()],
],
);
assert!(
rendered.contains("日本語"),
"non-ASCII cell content was corrupted: {rendered}"
);
assert!(
rendered.contains("🔥"),
"emoji cell content was corrupted: {rendered}"
);
assert!(
rendered.contains('─') || rendered.contains('│'),
"box-drawing characters were corrupted: {rendered}"
);
assert!(
!rendered.contains('\u{1b}'),
"ANSI escape leaked through despite no_color: {rendered:?}"
);
}
#[test]
fn print_table_returns_ok_on_happy_path() {
let result = print_table(
&no_globals(),
&["model", "turns"],
&[vec!["claude-sonnet-4-6".into(), "12".into()]],
);
assert!(result.is_ok(), "print_table happy path returned {result:?}");
}
#[test]
fn no_color_strips_pre_styled_cell_ansi() {
let rendered = render_table(&no_color_globals(), &["k"], &[vec!["plain".into()]]);
assert!(!rendered.contains('\u{1b}'));
}
}