refactor: Migrate from prettytable to comfy-table for output formatting

Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-09-08 18:09:47 -03:00
parent d7f4724f26
commit 9f328a376f
5 changed files with 59 additions and 140 deletions

View File

@@ -12,8 +12,9 @@ use crate::services::item_service::ItemService;
use crate::modes::common::get_format_box_chars_no_border_line_separator;
use chrono::prelude::*;
use is_terminal::IsTerminal;
use prettytable::format;
use prettytable::{Attr, Cell, Row, Table};
use comfy_table::{Table, ContentArrangement, Cell, Attribute};
use comfy_table::presets::UTF8_FULL;
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
pub fn mode_info(
cmd: &mut Command,
@@ -71,136 +72,72 @@ fn show_item(
let mut table = Table::new();
if std::io::stdout().is_terminal() {
table.set_format(get_format_box_chars_no_border_line_separator());
table
.load_preset(UTF8_FULL)
.apply_modifier(UTF8_ROUND_CORNERS);
} else {
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_content_arrangement(ContentArrangement::Dynamic);
}
// Collect all rows to calculate column widths
let mut rows = Vec::new();
// Add all the rows
rows.push(Row::new(vec![
Cell::new("ID").with_style(Attr::Bold),
table.add_row(vec![
Cell::new("ID").add_attribute(Attribute::Bold),
Cell::new(&item_id.to_string()),
]));
]);
let timestamp_str = item.ts.with_timezone(&Local).format("%F %T %Z").to_string();
rows.push(Row::new(vec![
Cell::new("Timestamp").with_style(Attr::Bold),
table.add_row(vec![
Cell::new("Timestamp").add_attribute(Attribute::Bold),
Cell::new(&timestamp_str),
]));
]);
let mut item_path_buf = data_path.clone();
item_path_buf.push(item.id.unwrap().to_string());
let path_str = item_path_buf.to_str().expect("Unable to get item path").to_string();
rows.push(Row::new(vec![
Cell::new("Path").with_style(Attr::Bold),
table.add_row(vec![
Cell::new("Path").add_attribute(Attribute::Bold),
Cell::new(&path_str),
]));
]);
let size_str = match item.size {
Some(size) => format_size(size as u64, settings.human_readable),
None => "Missing".to_string(),
};
rows.push(Row::new(vec![
Cell::new("Stream Size").with_style(Attr::Bold),
table.add_row(vec![
Cell::new("Stream Size").add_attribute(Attribute::Bold),
Cell::new(&size_str),
]));
]);
rows.push(Row::new(vec![
Cell::new("Compression").with_style(Attr::Bold),
table.add_row(vec![
Cell::new("Compression").add_attribute(Attribute::Bold),
Cell::new(&item.compression),
]));
]);
let file_size_str = match item_path_buf.metadata() {
Ok(metadata) => format_size(metadata.len(), settings.human_readable),
Err(_) => "Missing".to_string(),
};
rows.push(Row::new(vec![
Cell::new("File Size").with_style(Attr::Bold),
table.add_row(vec![
Cell::new("File Size").add_attribute(Attribute::Bold),
Cell::new(&file_size_str),
]));
]);
let tags_str = item_tags.join(" ");
rows.push(Row::new(vec![
Cell::new("Tags").with_style(Attr::Bold),
table.add_row(vec![
Cell::new("Tags").add_attribute(Attribute::Bold),
Cell::new(&tags_str),
]));
]);
// Collect meta rows to potentially truncate the last one
let mut meta_rows = Vec::new();
// Add meta rows
for meta in item_with_meta.meta {
let meta_name = format!("Meta: {}", &meta.name);
meta_rows.push((meta_name, meta.value));
table.add_row(vec![
Cell::new(&meta_name).add_attribute(Attribute::Bold),
Cell::new(&meta.value),
]);
}
// Calculate the maximum width for the last column if we're in a terminal
let terminal_width = crate::modes::common::get_terminal_width();
debug!("Terminal width: {}", terminal_width);
if std::io::stdout().is_terminal() && terminal_width > 0 {
// For the info table, we always have exactly 2 columns
// Find the maximum width of the first column (labels)
let max_label_width = rows.iter()
.map(|row| row.get_cell(0).map(|c| c.get_content().chars().count()).unwrap_or(0))
.max()
.unwrap_or(0);
debug!("Max label width: {}", max_label_width);
debug!("Terminal width: {}", terminal_width);
debug!("Calculated max_value_width: {}", terminal_width.saturating_sub(max_label_width + 8));
// Total width used: 1 (left border) + 1 (left padding) + max_label_width + 1 (right padding) +
// 1 (middle separator) + 1 (left padding) + value_width + 1 (right padding) + 1 (right border)
// = max_label_width + value_width + 8
// We want: max_label_width + value_width + 8 <= terminal_width
// So max value width is: terminal_width - max_label_width - 8
let max_value_width = terminal_width.saturating_sub(max_label_width + 8);
debug!("Max value width: {}", max_value_width);
// Process all existing rows to truncate their value cells
for row in &mut rows {
if let Some(value_cell) = row.get_mut_cell(1) {
let content = value_cell.get_content();
if content.chars().count() > max_value_width {
let truncated = crate::modes::common::truncate_with_ellipsis(&content, max_value_width);
*value_cell = Cell::new(&truncated);
}
}
}
// Process meta rows to truncate values if necessary
for (meta_name, meta_value) in meta_rows {
let truncated_value = if max_value_width > 0 {
crate::modes::common::truncate_with_ellipsis(&meta_value, max_value_width)
} else {
// If max_value_width is 0, we can't show anything, but let's at least show something
if max_value_width == 0 {
"".to_string()
} else {
meta_value
}
};
rows.push(Row::new(vec![
Cell::new(&meta_name).with_style(Attr::Bold),
Cell::new(&truncated_value),
]));
}
} else {
// Not a terminal or no terminal width, add all meta rows without truncation
for (meta_name, meta_value) in meta_rows {
rows.push(Row::new(vec![
Cell::new(&meta_name).with_style(Attr::Bold),
Cell::new(&meta_value),
]));
}
}
// Add all rows to the table
for row in rows {
table.add_row(row);
}
table.printstd();
println!("{}", table);
Ok(())
}