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

View File

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

View File

@@ -25,7 +25,8 @@ pub fn mode(
} }
OutputFormat::Table => { OutputFormat::Table => {
// Paths // 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![ path_table.set_header(vec![
Cell::new("Type").add_attribute(Attribute::Bold), Cell::new("Type").add_attribute(Attribute::Bold),
Cell::new("Path").add_attribute(Attribute::Bold), Cell::new("Path").add_attribute(Attribute::Bold),
@@ -46,7 +47,8 @@ pub fn mode(
let mut sorted = configured.clone(); let mut sorted = configured.clone();
sorted.sort_by(|a, b| a.name.cmp(&b.name)); 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![ table.set_header(vec![
Cell::new("Plugin Name").add_attribute(Attribute::Bold), Cell::new("Plugin Name").add_attribute(Attribute::Bold),
Cell::new("Enabled").add_attribute(Attribute::Bold), Cell::new("Enabled").add_attribute(Attribute::Bold),
@@ -68,7 +70,8 @@ pub fn mode(
// Compression // Compression
if !status_info.compression.is_empty() { 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![ table.set_header(vec![
Cell::new("Type").add_attribute(Attribute::Bold), Cell::new("Type").add_attribute(Attribute::Bold),
Cell::new("Found").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); /// let mut table = create_table(true);
/// table.add_row(vec!["Header1", "Header2"]); /// table.add_row(vec!["Header1", "Header2"]);
/// ``` /// ```
pub fn create_table(use_styling: bool) -> Table { pub fn create_table(_use_styling: bool) -> Table {
let mut table = Table::new(); create_table_with_config(&crate::config::TableConfig::default())
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
} }
/// Creates a table configured from application table settings. /// 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 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::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 crate::services::types::ItemWithMeta;
use anyhow::{Context, Result, anyhow}; use anyhow::{Context, Result, anyhow};
use clap::Command; use clap::Command;
@@ -9,7 +9,6 @@ use std::path::PathBuf;
use crate::services::item_service::ItemService; use crate::services::item_service::ItemService;
use chrono::prelude::*; use chrono::prelude::*;
use comfy_table::{Attribute, Cell};
/// Displays detailed information about an item or the last item if no ID/tags specified. /// 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_id = item.id.context("Item missing ID")?;
let item_tags: Vec<String> = item_with_meta.tags.iter().map(|t| t.name.clone()).collect(); 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(); let mut item_path_buf = data_path.clone();
item_path_buf.push(item_id.to_string()); 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 { 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(),
}; };
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() { 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(),
}; };
table.add_row(vec![
Cell::new("File Size").add_attribute(Attribute::Bold),
Cell::new(&file_size_str),
]);
let tags_str = item_tags.join(" "); let metadata: Vec<(String, String)> = item_with_meta
table.add_row(vec![ .meta
Cell::new("Tags").add_attribute(Attribute::Bold), .iter()
Cell::new(&tags_str), .map(|m| (m.name.clone(), m.value.clone()))
]); .collect();
// Add meta rows let display = DisplayItemInfo {
for meta in item_with_meta.meta { id: item_id,
let meta_name = format!("Meta: {}", &meta.name); timestamp: item.ts.with_timezone(&Local).format("%F %T %Z").to_string(),
table.add_row(vec![ path: item_path_buf
Cell::new(&meta_name).add_attribute(Attribute::Bold), .to_str()
Cell::new(&meta.value), .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!( render_item_info_table(&display, &settings.table_config);
"{}",
crate::modes::common::trim_lines_end(&table.trim_fmt())
);
Ok(()) Ok(())
} }

View File

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

View File

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