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:
@@ -4,7 +4,6 @@ use crate::meta_plugin::MetaPluginType;
|
|||||||
use clap::Command;
|
use clap::Command;
|
||||||
use clap::error::ErrorKind;
|
use clap::error::ErrorKind;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use prettytable::format::TableFormat;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
@@ -111,25 +110,6 @@ impl ColumnType {
|
|||||||
// impl TryFrom<&str> for ColumnType is already implemented by strum_macros
|
// impl TryFrom<&str> for ColumnType is already implemented by strum_macros
|
||||||
// so we remove this conflicting implementation
|
// so we remove this conflicting implementation
|
||||||
|
|
||||||
pub fn get_format_box_chars_no_border_line_separator() -> TableFormat {
|
|
||||||
prettytable::format::FormatBuilder::new()
|
|
||||||
.column_separator('│')
|
|
||||||
.borders('│')
|
|
||||||
.separators(
|
|
||||||
&[prettytable::format::LinePosition::Top],
|
|
||||||
prettytable::format::LineSeparator::new('─', '┬', '┌', '┐'),
|
|
||||||
)
|
|
||||||
.separators(
|
|
||||||
&[prettytable::format::LinePosition::Title],
|
|
||||||
prettytable::format::LineSeparator::new('─', '┼', '├', '┤'),
|
|
||||||
)
|
|
||||||
.separators(
|
|
||||||
&[prettytable::format::LinePosition::Bottom],
|
|
||||||
prettytable::format::LineSeparator::new('─', '┴', '└', '┘'),
|
|
||||||
)
|
|
||||||
.padding(1, 1)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ use crate::services::item_service::ItemService;
|
|||||||
use crate::modes::common::get_format_box_chars_no_border_line_separator;
|
use crate::modes::common::get_format_box_chars_no_border_line_separator;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use is_terminal::IsTerminal;
|
use is_terminal::IsTerminal;
|
||||||
use prettytable::format;
|
use comfy_table::{Table, ContentArrangement, Cell, Attribute};
|
||||||
use prettytable::{Attr, Cell, Row, Table};
|
use comfy_table::presets::UTF8_FULL;
|
||||||
|
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
|
||||||
|
|
||||||
pub fn mode_info(
|
pub fn mode_info(
|
||||||
cmd: &mut Command,
|
cmd: &mut Command,
|
||||||
@@ -71,136 +72,72 @@ fn show_item(
|
|||||||
|
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
if std::io::stdout().is_terminal() {
|
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 {
|
} 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
|
// Add all the rows
|
||||||
rows.push(Row::new(vec![
|
table.add_row(vec![
|
||||||
Cell::new("ID").with_style(Attr::Bold),
|
Cell::new("ID").add_attribute(Attribute::Bold),
|
||||||
Cell::new(&item_id.to_string()),
|
Cell::new(&item_id.to_string()),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
let timestamp_str = item.ts.with_timezone(&Local).format("%F %T %Z").to_string();
|
let timestamp_str = item.ts.with_timezone(&Local).format("%F %T %Z").to_string();
|
||||||
rows.push(Row::new(vec![
|
table.add_row(vec![
|
||||||
Cell::new("Timestamp").with_style(Attr::Bold),
|
Cell::new("Timestamp").add_attribute(Attribute::Bold),
|
||||||
Cell::new(×tamp_str),
|
Cell::new(×tamp_str),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
let mut item_path_buf = data_path.clone();
|
let mut item_path_buf = data_path.clone();
|
||||||
item_path_buf.push(item.id.unwrap().to_string());
|
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();
|
let path_str = item_path_buf.to_str().expect("Unable to get item path").to_string();
|
||||||
rows.push(Row::new(vec![
|
table.add_row(vec![
|
||||||
Cell::new("Path").with_style(Attr::Bold),
|
Cell::new("Path").add_attribute(Attribute::Bold),
|
||||||
Cell::new(&path_str),
|
Cell::new(&path_str),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
let size_str = match item.size {
|
let size_str = match item.size {
|
||||||
Some(size) => format_size(size as u64, settings.human_readable),
|
Some(size) => format_size(size as u64, settings.human_readable),
|
||||||
None => "Missing".to_string(),
|
None => "Missing".to_string(),
|
||||||
};
|
};
|
||||||
rows.push(Row::new(vec![
|
table.add_row(vec![
|
||||||
Cell::new("Stream Size").with_style(Attr::Bold),
|
Cell::new("Stream Size").add_attribute(Attribute::Bold),
|
||||||
Cell::new(&size_str),
|
Cell::new(&size_str),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
rows.push(Row::new(vec![
|
table.add_row(vec![
|
||||||
Cell::new("Compression").with_style(Attr::Bold),
|
Cell::new("Compression").add_attribute(Attribute::Bold),
|
||||||
Cell::new(&item.compression),
|
Cell::new(&item.compression),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
let file_size_str = match item_path_buf.metadata() {
|
let file_size_str = match item_path_buf.metadata() {
|
||||||
Ok(metadata) => format_size(metadata.len(), settings.human_readable),
|
Ok(metadata) => format_size(metadata.len(), settings.human_readable),
|
||||||
Err(_) => "Missing".to_string(),
|
Err(_) => "Missing".to_string(),
|
||||||
};
|
};
|
||||||
rows.push(Row::new(vec![
|
table.add_row(vec![
|
||||||
Cell::new("File Size").with_style(Attr::Bold),
|
Cell::new("File Size").add_attribute(Attribute::Bold),
|
||||||
Cell::new(&file_size_str),
|
Cell::new(&file_size_str),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
let tags_str = item_tags.join(" ");
|
let tags_str = item_tags.join(" ");
|
||||||
rows.push(Row::new(vec![
|
table.add_row(vec![
|
||||||
Cell::new("Tags").with_style(Attr::Bold),
|
Cell::new("Tags").add_attribute(Attribute::Bold),
|
||||||
Cell::new(&tags_str),
|
Cell::new(&tags_str),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
// Collect meta rows to potentially truncate the last one
|
// Add meta rows
|
||||||
let mut meta_rows = Vec::new();
|
|
||||||
for meta in item_with_meta.meta {
|
for meta in item_with_meta.meta {
|
||||||
let meta_name = format!("Meta: {}", &meta.name);
|
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
|
println!("{}", table);
|
||||||
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();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ use crate::modes::common::ColumnType;
|
|||||||
use crate::modes::common::{size_column, string_column, OutputFormat};
|
use crate::modes::common::{size_column, string_column, OutputFormat};
|
||||||
use anyhow::{Result};
|
use anyhow::{Result};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use comfy_table::{Table, ContentArrangement, Cell, Color, Attribute};
|
use comfy_table::{Table, ContentArrangement, Cell, Row, Color, Attribute};
|
||||||
use comfy_table::presets::UTF8_FULL;
|
use comfy_table::presets::UTF8_FULL;
|
||||||
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
|
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
|
||||||
|
use comfy_table::CellAlignment;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use serde_yaml;
|
use serde_yaml;
|
||||||
@@ -84,7 +85,7 @@ pub fn mode_list(
|
|||||||
let mut item_path = data_path.clone();
|
let mut item_path = data_path.clone();
|
||||||
item_path.push(item.id.unwrap().to_string());
|
item_path.push(item.id.unwrap().to_string());
|
||||||
|
|
||||||
let mut table_row = Row::new(vec![]);
|
let mut table_row = Row::new();
|
||||||
|
|
||||||
for column in &settings.list_format {
|
for column in &settings.list_format {
|
||||||
let column_type = ColumnType::from_str(&column.name)
|
let column_type = ColumnType::from_str(&column.name)
|
||||||
@@ -144,10 +145,10 @@ pub fn mode_list(
|
|||||||
Cell::new(&string_column(item.id.unwrap_or(0).to_string(), column_width));
|
Cell::new(&string_column(item.id.unwrap_or(0).to_string(), column_width));
|
||||||
match column.align {
|
match column.align {
|
||||||
crate::config::ColumnAlignment::Right => {
|
crate::config::ColumnAlignment::Right => {
|
||||||
cell.align(Alignment::RIGHT);
|
cell = cell.alignment(CellAlignment::Right);
|
||||||
}
|
}
|
||||||
crate::config::ColumnAlignment::Left => {
|
crate::config::ColumnAlignment::Left => {
|
||||||
cell.align(Alignment::LEFT);
|
cell = cell.alignment(CellAlignment::Left);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cell
|
cell
|
||||||
@@ -190,11 +191,11 @@ pub fn mode_list(
|
|||||||
None => {
|
None => {
|
||||||
let mut cell = match item_path.metadata() {
|
let mut cell = match item_path.metadata() {
|
||||||
Ok(_) => Cell::new("Unknown")
|
Ok(_) => Cell::new("Unknown")
|
||||||
.with_style(Attr::ForegroundColor(color::YELLOW))
|
.fg(Color::Yellow)
|
||||||
.with_style(Attr::Bold),
|
.add_attribute(Attribute::Bold),
|
||||||
Err(_) => Cell::new("Missing")
|
Err(_) => Cell::new("Missing")
|
||||||
.with_style(Attr::ForegroundColor(color::RED))
|
.fg(Color::Red)
|
||||||
.with_style(Attr::Bold),
|
.add_attribute(Attribute::Bold),
|
||||||
};
|
};
|
||||||
match column.align {
|
match column.align {
|
||||||
crate::config::ColumnAlignment::Right => {
|
crate::config::ColumnAlignment::Right => {
|
||||||
@@ -239,8 +240,8 @@ pub fn mode_list(
|
|||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let mut cell = Cell::new("Missing")
|
let mut cell = Cell::new("Missing")
|
||||||
.with_style(Attr::ForegroundColor(color::RED))
|
.fg(Color::Red)
|
||||||
.with_style(Attr::Bold);
|
.add_attribute(Attribute::Bold);
|
||||||
match column.align {
|
match column.align {
|
||||||
crate::config::ColumnAlignment::Right => {
|
crate::config::ColumnAlignment::Right => {
|
||||||
cell.align(Alignment::RIGHT);
|
cell.align(Alignment::RIGHT);
|
||||||
@@ -326,7 +327,7 @@ pub fn mode_list(
|
|||||||
table.add_row(table_row);
|
table.add_row(table_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.printstd();
|
println!("{}", table);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,17 +234,17 @@ pub fn mode_status(
|
|||||||
match output_format {
|
match output_format {
|
||||||
OutputFormat::Table => {
|
OutputFormat::Table => {
|
||||||
println!("CONFIG:");
|
println!("CONFIG:");
|
||||||
build_config_table(settings).printstd();
|
println!("{}", build_config_table(settings));
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
println!("PATHS:");
|
println!("PATHS:");
|
||||||
build_path_table(&status_info.paths).printstd();
|
println!("{}", build_path_table(&status_info.paths));
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
// Always try to print META PLUGINS CONFIGURED section using status_info
|
// Always try to print META PLUGINS CONFIGURED section using status_info
|
||||||
if let Some(meta_plugins_table) = build_meta_plugins_configured_table(&status_info) {
|
if let Some(meta_plugins_table) = build_meta_plugins_configured_table(&status_info) {
|
||||||
println!("META PLUGINS CONFIGURED:");
|
println!("META PLUGINS CONFIGURED:");
|
||||||
meta_plugins_table.printstd();
|
println!("{}", meta_plugins_table);
|
||||||
println!();
|
println!();
|
||||||
} else {
|
} else {
|
||||||
println!("META PLUGINS CONFIGURED:");
|
println!("META PLUGINS CONFIGURED:");
|
||||||
|
|||||||
@@ -142,16 +142,16 @@ fn build_compression_table(compression_info: &Vec<CompressionInfo>) -> Table {
|
|||||||
compression_table.add_row(vec![
|
compression_table.add_row(vec![
|
||||||
info.compression_type.clone(),
|
info.compression_type.clone(),
|
||||||
match info.found {
|
match info.found {
|
||||||
true => Cell::new("Yes").fg(Color::Green),
|
true => Cell::new("Yes").fg(Color::Green).to_string(),
|
||||||
false => Cell::new("No").fg(Color::Red),
|
false => Cell::new("No").fg(Color::Red).to_string(),
|
||||||
},
|
},
|
||||||
match info.default {
|
match info.default {
|
||||||
true => Cell::new("Yes").fg(Color::Green),
|
true => Cell::new("Yes").fg(Color::Green).to_string(),
|
||||||
false => Cell::new("No"),
|
false => Cell::new("No").to_string(),
|
||||||
},
|
},
|
||||||
match info.binary.as_str() {
|
match info.binary.as_str() {
|
||||||
"<INTERNAL>" => Cell::new(&info.binary).fg(Color::DarkGrey),
|
"<INTERNAL>" => Cell::new(&info.binary).fg(Color::DarkGrey).to_string(),
|
||||||
_ => Cell::new(&info.binary),
|
_ => info.binary.clone(),
|
||||||
},
|
},
|
||||||
info.compress.clone(),
|
info.compress.clone(),
|
||||||
info.decompress.clone(),
|
info.decompress.clone(),
|
||||||
@@ -287,15 +287,16 @@ pub fn mode_status_plugins(
|
|||||||
match output_format {
|
match output_format {
|
||||||
OutputFormat::Table => {
|
OutputFormat::Table => {
|
||||||
println!("META PLUGINS:");
|
println!("META PLUGINS:");
|
||||||
build_meta_plugin_table(&status_info.meta_plugins).printstd();
|
println!("META PLUGINS:");
|
||||||
|
println!("{}", build_meta_plugin_table(&status_info.meta_plugins));
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
println!("COMPRESSION PLUGINS:");
|
println!("COMPRESSION PLUGINS:");
|
||||||
build_compression_table(&status_info.compression).printstd();
|
println!("{}", build_compression_table(&status_info.compression));
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
println!("FILTER PLUGINS:");
|
println!("FILTER PLUGINS:");
|
||||||
build_filter_plugin_table(&status_info.filter_plugins).printstd();
|
println!("{}", build_filter_plugin_table(&status_info.filter_plugins));
|
||||||
println!();
|
println!();
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user