fix: all tables respect table_config from settings

Extract shared render_item_info_table() and render_list_table() in
modes/common.rs. Update client/info, client/list, client/status,
info, status, and status_plugins to use create_table_with_config
with settings.table_config instead of hardcoded presets.

Previously only local --list used table_config; all other tables
(client modes, status, status-plugins) ignored it.
This commit is contained in:
2026-03-14 19:49:31 -03:00
parent 0bc8d9c909
commit f5bae46620
7 changed files with 181 additions and 140 deletions

View File

@@ -1,5 +1,7 @@
use crate::client::KeepClient;
use crate::modes::common::{OutputFormat, format_size, settings_output_format};
use crate::modes::common::{
DisplayItemInfo, OutputFormat, format_size, render_item_info_table, settings_output_format,
};
use clap::Command;
use log::debug;
@@ -36,27 +38,24 @@ pub fn mode(
println!("{}", serde_yaml::to_string(&item)?);
}
OutputFormat::Table => {
use comfy_table::{Table, presets::UTF8_FULL};
let mut table = Table::new();
table.load_preset(UTF8_FULL);
let size_str = item
.size
.map(|s| format_size(s as u64, settings.human_readable))
.unwrap_or_else(|| "N/A".to_string());
table.add_row(vec!["ID".to_string(), item.id.to_string()]);
table.add_row(vec!["Time".to_string(), item.ts.clone()]);
table.add_row(vec!["Size".to_string(), size_str]);
table.add_row(vec!["Compression".to_string(), item.compression.clone()]);
table.add_row(vec!["Tags".to_string(), item.tags.join(", ")]);
for (key, value) in &item.metadata {
table.add_row(vec![format!("Meta: {}", key), value.clone()]);
}
println!("{table}");
let display = DisplayItemInfo {
id: item.id,
timestamp: item.ts.clone(),
path: String::new(),
stream_size: item
.size
.map(|s| format_size(s as u64, settings.human_readable))
.unwrap_or_else(|| "N/A".to_string()),
compression: item.compression.clone(),
file_size: String::new(),
tags: item.tags.clone(),
metadata: item
.metadata
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
};
render_item_info_table(&display, &settings.table_config);
}
}
}

View File

@@ -1,5 +1,7 @@
use crate::client::KeepClient;
use crate::modes::common::{OutputFormat, format_size, settings_output_format};
use crate::modes::common::{
DisplayListItem, OutputFormat, format_size, render_list_table, settings_output_format,
};
use clap::Command;
use log::debug;
@@ -28,31 +30,20 @@ pub fn mode(
println!("{}", serde_yaml::to_string(&items)?);
}
OutputFormat::Table => {
use comfy_table::{Table, presets::UTF8_FULL};
let mut table = Table::new();
table.load_preset(UTF8_FULL);
// Header
let headers = ["ID", "Time", "Size", "Compression", "Tags"];
table.set_header(headers.iter().map(|h| h.to_string()).collect::<Vec<_>>());
for item in &items {
let size_str = item
.size
.map(|s| format_size(s as u64, settings.human_readable))
.unwrap_or_default();
table.add_row(vec![
item.id.to_string(),
item.ts.clone(),
size_str,
item.compression.clone(),
item.tags.join(", "),
]);
}
println!("{table}");
let display_items: Vec<DisplayListItem> = items
.iter()
.map(|item| DisplayListItem {
id: item.id,
time: item.ts.clone(),
size: item
.size
.map(|s| format_size(s as u64, settings.human_readable))
.unwrap_or_default(),
compression: item.compression.clone(),
tags: item.tags.clone(),
})
.collect();
render_list_table(&display_items, &settings.table_config);
}
}

View File

@@ -25,7 +25,8 @@ pub fn mode(
}
OutputFormat::Table => {
// Paths
let mut path_table = crate::modes::common::create_table(true);
let mut path_table =
crate::modes::common::create_table_with_config(&settings.table_config);
path_table.set_header(vec![
Cell::new("Type").add_attribute(Attribute::Bold),
Cell::new("Path").add_attribute(Attribute::Bold),
@@ -46,7 +47,8 @@ pub fn mode(
let mut sorted = configured.clone();
sorted.sort_by(|a, b| a.name.cmp(&b.name));
let mut table = crate::modes::common::create_table(true);
let mut table =
crate::modes::common::create_table_with_config(&settings.table_config);
table.set_header(vec![
Cell::new("Plugin Name").add_attribute(Attribute::Bold),
Cell::new("Enabled").add_attribute(Attribute::Bold),
@@ -68,7 +70,8 @@ pub fn mode(
// Compression
if !status_info.compression.is_empty() {
let mut table = crate::modes::common::create_table(true);
let mut table =
crate::modes::common::create_table_with_config(&settings.table_config);
table.set_header(vec![
Cell::new("Type").add_attribute(Attribute::Bold),
Cell::new("Found").add_attribute(Attribute::Bold),

View File

@@ -337,26 +337,8 @@ pub fn trim_lines_end(s: &str) -> String {
/// let mut table = create_table(true);
/// table.add_row(vec!["Header1", "Header2"]);
/// ```
pub fn create_table(use_styling: bool) -> Table {
let mut table = Table::new();
table.set_content_arrangement(ContentArrangement::Dynamic);
if use_styling {
if std::io::stdout().is_terminal() {
table
.load_preset(comfy_table::presets::UTF8_FULL)
.apply_modifier(comfy_table::modifiers::UTF8_SOLID_INNER_BORDERS);
} else {
table.load_preset(comfy_table::presets::ASCII_FULL);
}
} else {
table.load_preset(comfy_table::presets::NOTHING);
}
if !std::io::stdout().is_terminal() {
table.force_no_tty();
}
table
pub fn create_table(_use_styling: bool) -> Table {
create_table_with_config(&crate::config::TableConfig::default())
}
/// Creates a table configured from application table settings.
@@ -447,3 +429,88 @@ pub fn create_table_with_config(table_config: &crate::config::TableConfig) -> Ta
table
}
/// Display data for a single item's detail view (used by --info).
pub struct DisplayItemInfo {
pub id: i64,
pub timestamp: String,
pub path: String,
pub stream_size: String,
pub compression: String,
pub file_size: String,
pub tags: Vec<String>,
pub metadata: Vec<(String, String)>,
}
/// Display data for a single list row (used by --list).
pub struct DisplayListItem {
pub id: i64,
pub time: String,
pub size: String,
pub compression: String,
pub tags: Vec<String>,
}
/// Renders item detail table. Shared by local and client info modes.
pub fn render_item_info_table(info: &DisplayItemInfo, table_config: &config::TableConfig) {
use comfy_table::{Attribute, Cell};
let mut table = create_table_with_config(table_config);
table.add_row(vec![
Cell::new("ID").add_attribute(Attribute::Bold),
Cell::new(info.id.to_string()),
]);
table.add_row(vec![
Cell::new("Time").add_attribute(Attribute::Bold),
Cell::new(&info.timestamp),
]);
table.add_row(vec![
Cell::new("Size").add_attribute(Attribute::Bold),
Cell::new(&info.stream_size),
]);
table.add_row(vec![
Cell::new("Compression").add_attribute(Attribute::Bold),
Cell::new(&info.compression),
]);
table.add_row(vec![
Cell::new("Tags").add_attribute(Attribute::Bold),
Cell::new(info.tags.join(" ")),
]);
for (key, value) in &info.metadata {
table.add_row(vec![
Cell::new(format!("Meta: {key}")).add_attribute(Attribute::Bold),
Cell::new(value),
]);
}
println!("{}", trim_lines_end(&table.trim_fmt()));
}
/// Renders list table. Shared by local and client list modes.
pub fn render_list_table(items: &[DisplayListItem], table_config: &config::TableConfig) {
use comfy_table::{Attribute, Cell};
let mut table = create_table_with_config(table_config);
table.set_header(vec![
Cell::new("ID").add_attribute(Attribute::Bold),
Cell::new("Time").add_attribute(Attribute::Bold),
Cell::new("Size").add_attribute(Attribute::Bold),
Cell::new("Compression").add_attribute(Attribute::Bold),
Cell::new("Tags").add_attribute(Attribute::Bold),
]);
for item in items {
table.add_row(vec![
item.id.to_string(),
item.time.clone(),
item.size.clone(),
item.compression.clone(),
item.tags.join(" "),
]);
}
println!("{}", trim_lines_end(&table.trim_fmt()));
}

View File

@@ -1,5 +1,5 @@
use crate::config;
use crate::modes::common::{OutputFormat, format_size};
use crate::modes::common::{DisplayItemInfo, OutputFormat, format_size, render_item_info_table};
use crate::services::types::ItemWithMeta;
use anyhow::{Context, Result, anyhow};
use clap::Command;
@@ -9,7 +9,6 @@ use std::path::PathBuf;
use crate::services::item_service::ItemService;
use chrono::prelude::*;
use comfy_table::{Attribute, Cell};
/// Displays detailed information about an item or the last item if no ID/tags specified.
///
@@ -148,73 +147,40 @@ fn show_item(
let item_id = item.id.context("Item missing ID")?;
let item_tags: Vec<String> = item_with_meta.tags.iter().map(|t| t.name.clone()).collect();
let mut table = crate::modes::common::create_table(false);
// Add all the rows
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();
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.to_string());
let path_str = item_path_buf
.to_str()
.ok_or_else(|| anyhow::anyhow!("non-UTF-8 item path"))?
.to_string();
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(),
};
table.add_row(vec![
Cell::new("Stream Size").add_attribute(Attribute::Bold),
Cell::new(&size_str),
]);
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(),
};
table.add_row(vec![
Cell::new("File Size").add_attribute(Attribute::Bold),
Cell::new(&file_size_str),
]);
let tags_str = item_tags.join(" ");
table.add_row(vec![
Cell::new("Tags").add_attribute(Attribute::Bold),
Cell::new(&tags_str),
]);
let metadata: Vec<(String, String)> = item_with_meta
.meta
.iter()
.map(|m| (m.name.clone(), m.value.clone()))
.collect();
// Add meta rows
for meta in item_with_meta.meta {
let meta_name = format!("Meta: {}", &meta.name);
table.add_row(vec![
Cell::new(&meta_name).add_attribute(Attribute::Bold),
Cell::new(&meta.value),
]);
}
let display = DisplayItemInfo {
id: item_id,
timestamp: item.ts.with_timezone(&Local).format("%F %T %Z").to_string(),
path: item_path_buf
.to_str()
.ok_or_else(|| anyhow::anyhow!("non-UTF-8 item path"))?
.to_string(),
stream_size: size_str,
compression: item.compression.clone(),
file_size: file_size_str,
tags: item_tags,
metadata,
};
println!(
"{}",
crate::modes::common::trim_lines_end(&table.trim_fmt())
);
render_item_info_table(&display, &settings.table_config);
Ok(())
}

View File

@@ -14,8 +14,8 @@ use crate::common::status::PathInfo;
use crate::meta_plugin::MetaPluginType;
use crate::meta_plugin::get_meta_plugin;
fn build_path_table(path_info: &PathInfo) -> Table {
let mut path_table = crate::modes::common::create_table(true);
fn build_path_table(path_info: &PathInfo, table_config: &config::TableConfig) -> Table {
let mut path_table = crate::modes::common::create_table_with_config(table_config);
path_table.set_header(vec![
Cell::new("Type").add_attribute(Attribute::Bold),
@@ -29,7 +29,7 @@ fn build_path_table(path_info: &PathInfo) -> Table {
}
fn build_config_table(settings: &config::Settings) -> Table {
let mut config_table = crate::modes::common::create_table(true);
let mut config_table = crate::modes::common::create_table_with_config(&settings.table_config);
config_table.set_header(vec![
Cell::new("Setting").add_attribute(Attribute::Bold),
@@ -52,7 +52,10 @@ fn build_config_table(settings: &config::Settings) -> Table {
config_table
}
fn build_meta_plugins_configured_table(status_info: &StatusInfo) -> Option<Table> {
fn build_meta_plugins_configured_table(
status_info: &StatusInfo,
table_config: &config::TableConfig,
) -> Option<Table> {
let meta_plugins = status_info.configured_meta_plugins.as_ref()?;
if meta_plugins.is_empty() {
return None;
@@ -62,7 +65,7 @@ fn build_meta_plugins_configured_table(status_info: &StatusInfo) -> Option<Table
let mut sorted_meta_plugins = meta_plugins.clone();
sorted_meta_plugins.sort_by(|a, b| a.name.cmp(&b.name));
let mut table = crate::modes::common::create_table(true);
let mut table = crate::modes::common::create_table_with_config(table_config);
table.set_header(vec![
Cell::new("Plugin Name").add_attribute(Attribute::Bold),
@@ -212,7 +215,7 @@ pub fn mode_status(
println!();
println!("PATHS:");
let path_table = build_path_table(&status_info.paths);
let path_table = build_path_table(&status_info.paths, &settings.table_config);
println!(
"{}",
crate::modes::common::trim_lines_end(&path_table.trim_fmt())
@@ -220,7 +223,9 @@ pub fn mode_status(
println!();
// 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, &settings.table_config)
{
println!("META PLUGINS CONFIGURED:");
println!(
"{}",

View File

@@ -60,6 +60,7 @@ use crate::meta_plugin::{MetaPluginType, get_meta_plugin};
fn build_meta_plugin_table(
meta_plugin_info: &std::collections::HashMap<String, MetaPluginInfo>,
table_config: &crate::config::TableConfig,
) -> Table {
// Builds a formatted table displaying meta plugin information.
//
@@ -72,7 +73,7 @@ fn build_meta_plugin_table(
// # Returns
//
// A formatted `comfy_table::Table`.
let mut meta_plugin_table = crate::modes::common::create_table(true);
let mut meta_plugin_table = crate::modes::common::create_table_with_config(table_config);
meta_plugin_table.set_header(vec![
Cell::new("Plugin Name").add_attribute(Attribute::Bold),
@@ -126,7 +127,10 @@ fn build_meta_plugin_table(
meta_plugin_table
}
fn build_compression_table(compression_info: &Vec<CompressionInfo>) -> Table {
fn build_compression_table(
compression_info: &Vec<CompressionInfo>,
table_config: &crate::config::TableConfig,
) -> Table {
// Builds a formatted table displaying compression plugin information.
//
// # Arguments
@@ -136,7 +140,7 @@ fn build_compression_table(compression_info: &Vec<CompressionInfo>) -> Table {
// # Returns
//
// A formatted `comfy_table::Table`.
let mut compression_table = crate::modes::common::create_table(true);
let mut compression_table = crate::modes::common::create_table_with_config(table_config);
compression_table.set_header(vec![
Cell::new("Type").add_attribute(Attribute::Bold),
@@ -167,7 +171,10 @@ fn build_compression_table(compression_info: &Vec<CompressionInfo>) -> Table {
compression_table
}
fn build_filter_plugin_table(filter_plugins: &[crate::common::status::FilterPluginInfo]) -> Table {
fn build_filter_plugin_table(
filter_plugins: &[crate::common::status::FilterPluginInfo],
table_config: &crate::config::TableConfig,
) -> Table {
// Builds a formatted table displaying filter plugin information.
//
// Sorts plugins by name and formats options as YAML sequence.
@@ -179,7 +186,7 @@ fn build_filter_plugin_table(filter_plugins: &[crate::common::status::FilterPlug
// # Returns
//
// A formatted `comfy_table::Table`.
let mut filter_plugin_table = crate::modes::common::create_table(true);
let mut filter_plugin_table = crate::modes::common::create_table_with_config(table_config);
filter_plugin_table.set_header(vec![
Cell::new("Plugin Name").add_attribute(Attribute::Bold),
@@ -304,7 +311,8 @@ pub fn mode_status_plugins(
match output_format {
OutputFormat::Table => {
println!("META PLUGINS:");
let meta_table = build_meta_plugin_table(&status_info.meta_plugins);
let meta_table =
build_meta_plugin_table(&status_info.meta_plugins, &settings.table_config);
println!(
"{}",
crate::modes::common::trim_lines_end(&meta_table.trim_fmt())
@@ -312,7 +320,8 @@ pub fn mode_status_plugins(
println!();
println!("COMPRESSION PLUGINS:");
let compression_table = build_compression_table(&status_info.compression);
let compression_table =
build_compression_table(&status_info.compression, &settings.table_config);
println!(
"{}",
crate::modes::common::trim_lines_end(&compression_table.trim_fmt())
@@ -320,7 +329,8 @@ pub fn mode_status_plugins(
println!();
println!("FILTER PLUGINS:");
let filter_table = build_filter_plugin_table(&status_info.filter_plugins);
let filter_table =
build_filter_plugin_table(&status_info.filter_plugins, &settings.table_config);
println!(
"{}",
crate::modes::common::trim_lines_end(&filter_table.trim_fmt())