From fdc5f1d744066b2ceb75618122b6b792aaaa6265 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Sat, 14 Mar 2026 20:01:58 -0300 Subject: [PATCH] fix: client --list uses list_format from config like local mode Move apply_color/apply_table_attribute to common.rs for sharing. Add render_list_table_with_format() that takes ColumnConfig slice and pre-computed row values. Client list now renders columns based on settings.list_format, showing empty for columns where server data is unavailable (e.g. text_line_count, token_count). --- src/modes/client/list.rs | 44 +++++++++++----- src/modes/common.rs | 106 +++++++++++++++++++++++++++++++-------- src/modes/list.rs | 86 +------------------------------ 3 files changed, 120 insertions(+), 116 deletions(-) diff --git a/src/modes/client/list.rs b/src/modes/client/list.rs index 274cec4..717326a 100644 --- a/src/modes/client/list.rs +++ b/src/modes/client/list.rs @@ -1,9 +1,10 @@ use crate::client::KeepClient; use crate::modes::common::{ - DisplayListItem, OutputFormat, format_size, render_list_table, settings_output_format, + ColumnType, OutputFormat, format_size, render_list_table_with_format, settings_output_format, }; use clap::Command; use log::debug; +use std::str::FromStr; pub fn mode( client: &KeepClient, @@ -30,20 +31,39 @@ pub fn mode( println!("{}", serde_yaml::to_string(&items)?); } OutputFormat::Table => { - let display_items: Vec = items + let rows: Vec> = 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(), + .map(|item| { + let mut row = Vec::new(); + for column in &settings.list_format { + let col_type = ColumnType::from_str(&column.name).ok(); + let cell = match col_type { + Some(ColumnType::Id) => item.id.to_string(), + Some(ColumnType::Time) => item.ts.clone(), + Some(ColumnType::Size) => item + .size + .map(|s| format_size(s as u64, settings.human_readable)) + .unwrap_or_default(), + Some(ColumnType::Compression) => item.compression.clone(), + Some(ColumnType::Tags) => item.tags.join(" "), + Some(ColumnType::Meta) => { + let meta_key = column.name.strip_prefix("meta:"); + match meta_key { + Some(key) => { + item.metadata.get(key).cloned().unwrap_or_default() + } + None => String::new(), + } + } + _ => String::new(), + }; + row.push(cell); + } + row }) .collect(); - render_list_table(&display_items, &settings.table_config); + + render_list_table_with_format(&settings.list_format, &rows, &settings.table_config); } } diff --git a/src/modes/common.rs b/src/modes/common.rs index 3aee15b..4260834 100644 --- a/src/modes/common.rs +++ b/src/modes/common.rs @@ -17,7 +17,7 @@ use crate::config; use crate::meta_plugin::MetaPluginType; use clap::Command; use clap::error::ErrorKind; -use comfy_table::{ContentArrangement, Table}; +use comfy_table::{Attribute, Cell, ContentArrangement, Table}; use log::debug; use regex::Regex; use std::collections::HashMap; @@ -488,29 +488,95 @@ pub fn render_item_info_table(info: &DisplayItemInfo, table_config: &config::Tab 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}; - +/// Renders list table with column format from config. Shared by local and client list modes. +pub fn render_list_table_with_format( + columns: &[config::ColumnConfig], + rows: &[Vec], + table_config: &config::TableConfig, +) { 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), - ]); + let header_cells: Vec = columns + .iter() + .map(|col| Cell::new(&col.label).add_attribute(Attribute::Bold)) + .collect(); + table.set_header(header_cells); - for item in items { - table.add_row(vec![ - item.id.to_string(), - item.time.clone(), - item.size.clone(), - item.compression.clone(), - item.tags.join(" "), - ]); + for row in rows { + let cells: Vec = row + .iter() + .enumerate() + .map(|(i, val)| { + let mut cell = Cell::new(val); + if let Some(col) = columns.get(i) { + if let Some(ref fg) = col.fg_color { + cell = apply_color(cell, fg, true); + } + if let Some(ref bg) = col.bg_color { + cell = apply_color(cell, bg, false); + } + for attr in &col.attributes { + cell = apply_table_attribute(cell, attr); + } + } + cell + }) + .collect(); + table.add_row(cells); } println!("{}", trim_lines_end(&table.trim_fmt())); } + +/// Applies config TableColor to a comfy-table Cell. +pub fn apply_color(mut cell: Cell, color: &config::TableColor, is_foreground: bool) -> Cell { + use comfy_table::Color; + + let comfy_color = match color { + config::TableColor::Black => Color::Black, + config::TableColor::Red => Color::Red, + config::TableColor::Green => Color::Green, + config::TableColor::Yellow => Color::Yellow, + config::TableColor::Blue => Color::Blue, + config::TableColor::Magenta => Color::Magenta, + config::TableColor::Cyan => Color::Cyan, + config::TableColor::White => Color::White, + config::TableColor::Gray => Color::Grey, + config::TableColor::DarkRed => Color::DarkRed, + config::TableColor::DarkGreen => Color::DarkGreen, + config::TableColor::DarkYellow => Color::DarkYellow, + config::TableColor::DarkBlue => Color::DarkBlue, + config::TableColor::DarkMagenta => Color::DarkMagenta, + config::TableColor::DarkCyan => Color::DarkCyan, + config::TableColor::Rgb(r, g, b) => Color::Rgb { + r: *r, + g: *g, + b: *b, + }, + }; + + if is_foreground { + cell = cell.fg(comfy_color); + } else { + cell = cell.bg(comfy_color); + } + + cell +} + +/// Applies config TableAttribute to a comfy-table Cell. +pub fn apply_table_attribute(mut cell: Cell, attribute: &config::TableAttribute) -> Cell { + match attribute { + config::TableAttribute::Bold => cell = cell.add_attribute(Attribute::Bold), + config::TableAttribute::Dim => cell = cell.add_attribute(Attribute::Dim), + config::TableAttribute::Italic => cell = cell.add_attribute(Attribute::Italic), + config::TableAttribute::Underlined => cell = cell.add_attribute(Attribute::Underlined), + config::TableAttribute::SlowBlink => cell = cell.add_attribute(Attribute::SlowBlink), + config::TableAttribute::RapidBlink => cell = cell.add_attribute(Attribute::RapidBlink), + config::TableAttribute::Reverse => cell = cell.add_attribute(Attribute::Reverse), + config::TableAttribute::Hidden => cell = cell.add_attribute(Attribute::Hidden), + config::TableAttribute::CrossedOut => cell = cell.add_attribute(Attribute::CrossedOut), + } + + cell +} diff --git a/src/modes/list.rs b/src/modes/list.rs index 15b0e33..15245f9 100644 --- a/src/modes/list.rs +++ b/src/modes/list.rs @@ -5,7 +5,7 @@ /// including table, JSON, and YAML. use crate::config; use crate::modes::common::ColumnType; -use crate::modes::common::{OutputFormat, format_size}; +use crate::modes::common::{OutputFormat, apply_color, apply_table_attribute, format_size}; use crate::services::item_service::ItemService; use crate::services::types::ItemWithMeta; use anyhow::{Context, Result}; @@ -63,88 +63,6 @@ struct ListItem { meta: std::collections::HashMap, } -// Helper function to apply color to a cell. -/// -/// This function converts the configuration color to a comfy-table Color and -/// applies it to the cell as foreground or background color. -/// -/// # Arguments -/// -/// * `cell` - The cell to modify. -/// * `color` - The color from configuration to apply. -/// * `is_foreground` - True for foreground color, false for background. -/// -/// # Returns -/// -/// The modified cell with color applied. -fn apply_color(mut cell: Cell, color: &crate::config::TableColor, is_foreground: bool) -> Cell { - use crate::config::TableColor::*; - use comfy_table::Color; - - let comfy_color = match color { - Black => Color::Black, - Red => Color::Red, - Green => Color::Green, - Yellow => Color::Yellow, - Blue => Color::Blue, - Magenta => Color::Magenta, - Cyan => Color::Cyan, - White => Color::White, - Gray => Color::Grey, - DarkRed => Color::DarkRed, - DarkGreen => Color::DarkGreen, - DarkYellow => Color::DarkYellow, - DarkBlue => Color::DarkBlue, - DarkMagenta => Color::DarkMagenta, - DarkCyan => Color::DarkCyan, - Rgb(r, g, b) => Color::Rgb { - r: *r, - g: *g, - b: *b, - }, - }; - - if is_foreground { - cell = cell.fg(comfy_color); - } else { - cell = cell.bg(comfy_color); - } - - cell -} - -// Helper function to apply attribute to a cell. -/// -/// This function applies a single table attribute to the cell based on the -/// configuration attribute type. -/// -/// # Arguments -/// -/// * `cell` - The cell to modify. -/// * `attribute` - The attribute from configuration to apply. -/// -/// # Returns -/// -/// The modified cell with attribute applied. -fn apply_attribute(mut cell: Cell, attribute: &crate::config::TableAttribute) -> Cell { - use crate::config::TableAttribute::*; - use comfy_table::Attribute; - - match attribute { - Bold => cell = cell.add_attribute(Attribute::Bold), - Dim => cell = cell.add_attribute(Attribute::Dim), - Italic => cell = cell.add_attribute(Attribute::Italic), - Underlined => cell = cell.add_attribute(Attribute::Underlined), - SlowBlink => cell = cell.add_attribute(Attribute::SlowBlink), - RapidBlink => cell = cell.add_attribute(Attribute::RapidBlink), - Reverse => cell = cell.add_attribute(Attribute::Reverse), - Hidden => cell = cell.add_attribute(Attribute::Hidden), - CrossedOut => cell = cell.add_attribute(Attribute::CrossedOut), - } - - cell -} - /// Main list mode function. /// /// This function handles the listing of items based on tags, applying formatting @@ -287,7 +205,7 @@ pub fn mode_list( } for attribute in &column.attributes { - cell = apply_attribute(cell, attribute); + cell = apply_table_attribute(cell, attribute); } // Apply padding if specified