From 20e406d5c842bdd1c74024c26cf4365971adf399 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Wed, 3 Sep 2025 08:49:10 -0300 Subject: [PATCH] refactor: Move status_plugins functionality to its own module Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) --- src/main.rs | 4 +- src/modes/mod.rs | 1 + src/modes/status.rs | 73 ++------- src/modes/status_plugins.rs | 290 ++++++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+), 63 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4e202bf..3fa23bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -201,8 +201,8 @@ fn main() -> Result<(), Error> { KeepModes::List => modes::list::mode_list(&mut cmd, &settings, ids, tags, &mut conn, data_path), KeepModes::Delete => modes::delete::mode_delete(&mut cmd, &settings, &settings, ids, tags, &mut conn, data_path), KeepModes::Info => modes::info::mode_info(&mut cmd, &settings, ids, tags, &mut conn, data_path), - KeepModes::Status => modes::status::mode_status(&mut cmd, &settings, data_path, db_path, false), - KeepModes::StatusPlugins => modes::status::mode_status(&mut cmd, &settings, data_path, db_path, true), + KeepModes::Status => modes::status::mode_status(&mut cmd, &settings, data_path, db_path), + KeepModes::StatusPlugins => modes::status_plugins::mode_status_plugins(&mut cmd, &settings, data_path, db_path), KeepModes::Server => modes::server::mode_server(&mut cmd, &settings, &mut conn, data_path), KeepModes::GenerateConfig => modes::generate_config::mode_generate_config(&mut cmd, &settings), KeepModes::Unknown => unreachable!(), diff --git a/src/modes/mod.rs b/src/modes/mod.rs index f9f30a9..5fac735 100644 --- a/src/modes/mod.rs +++ b/src/modes/mod.rs @@ -8,3 +8,4 @@ pub mod list; pub mod save; pub mod server; pub mod status; +pub mod status_plugins; diff --git a/src/modes/status.rs b/src/modes/status.rs index 71546f0..9aa6e52 100644 --- a/src/modes/status.rs +++ b/src/modes/status.rs @@ -343,7 +343,6 @@ pub fn mode_status( settings: &config::Settings, data_path: PathBuf, db_path: PathBuf, - plugins_only: bool, ) -> Result<(), anyhow::Error> { debug!("STATUS: Starting mode_status function"); @@ -355,73 +354,25 @@ pub fn mode_status( match output_format { OutputFormat::Table => { - if plugins_only { - println!("META PLUGINS AVAILABLE:"); - build_meta_plugin_table(&status_info.meta_plugins).printstd(); - println!(); + println!("CONFIG:"); + build_config_table(settings).printstd(); + println!(); + + println!("PATHS:"); + build_path_table(&status_info.paths).printstd(); + println!(); - // Always try to print META PLUGINS CONFIGURED section - if let Some(meta_plugins_table) = build_meta_plugins_configured_table(settings) { - println!("META PLUGINS CONFIGURED:"); - meta_plugins_table.printstd(); - println!(); - } else { - println!("META PLUGINS CONFIGURED:"); - println!("No plugins configured"); - println!(); - } - } else { - println!("CONFIG:"); - build_config_table(settings).printstd(); - println!(); - - println!("PATHS:"); - build_path_table(&status_info.paths).printstd(); - println!(); - - println!("COMPRESSION:"); - build_compression_table(&status_info.compression).printstd(); - println!(); - } + println!("COMPRESSION:"); + build_compression_table(&status_info.compression).printstd(); + println!(); Ok(()) }, OutputFormat::Json => { - if plugins_only { - // Create a subset for plugins only - let plugins_info = serde_json::json!({ - "meta_plugins_available": status_info.meta_plugins, - "meta_plugins_configured": settings.meta_plugins - }); - println!("{}", serde_json::to_string_pretty(&plugins_info)?); - } else { - println!("{}", serde_json::to_string_pretty(&status_info)?); - } + println!("{}", serde_json::to_string_pretty(&status_info)?); Ok(()) }, OutputFormat::Yaml => { - if plugins_only { - // Create a proper structure for plugins info - use serde_yaml::Mapping; - let mut plugins_mapping = Mapping::new(); - - // Add available plugins - plugins_mapping.insert( - serde_yaml::Value::String("meta_plugins_available".to_string()), - serde_yaml::to_value(&status_info.meta_plugins)?, - ); - - // Add configured plugins if they exist - if let Some(configured_plugins) = &settings.meta_plugins { - plugins_mapping.insert( - serde_yaml::Value::String("meta_plugins_configured".to_string()), - serde_yaml::to_value(configured_plugins)?, - ); - } - - println!("{}", serde_yaml::to_string(&plugins_mapping)?); - } else { - println!("{}", serde_yaml::to_string(&status_info)?); - } + println!("{}", serde_yaml::to_string(&status_info)?); Ok(()) } } diff --git a/src/modes/status_plugins.rs b/src/modes/status_plugins.rs index e69de29..bb8330e 100644 --- a/src/modes/status_plugins.rs +++ b/src/modes/status_plugins.rs @@ -0,0 +1,290 @@ +use clap::*; +use is_terminal::IsTerminal; +use std::path::PathBuf; +use log::debug; + +use crate::modes::common::{get_format_box_chars_no_border_line_separator, OutputFormat}; +use crate::config; +use prettytable::color; +use serde_json; +use serde_yaml; +use prettytable::row; +use prettytable::{Attr, Cell, Row, Table}; +use prettytable::format::consts::{FORMAT_BOX_CHARS, FORMAT_NO_BORDER_LINE_SEPARATOR}; + +use crate::meta_plugin::{MetaPluginType, get_meta_plugin}; +use crate::common::status::{MetaPluginInfo}; + +fn build_meta_plugins_configured_table(settings: &config::Settings) -> Option { + let meta_plugins = settings.meta_plugins.as_ref()?; + if meta_plugins.is_empty() { + return None; + } + + // Sort meta plugins by name + let mut sorted_meta_plugins = meta_plugins.clone(); + sorted_meta_plugins.sort_by(|a, b| a.name.cmp(&b.name)); + + let mut table = Table::new(); + if std::io::stdout().is_terminal() { + table.set_format(*FORMAT_BOX_CHARS); + } else { + table.set_format(*FORMAT_NO_BORDER_LINE_SEPARATOR); + } + + table.set_titles(Row::new(vec![ + Cell::new("Plugin Name").with_style(Attr::Bold), + Cell::new("Options").with_style(Attr::Bold), + Cell::new("Outputs").with_style(Attr::Bold), + ])); + + for plugin_config in sorted_meta_plugins { + // Create the plugin to get its default options + let meta_plugin_type = match MetaPluginType::from_str(&plugin_config.name) { + Ok(plugin_type) => plugin_type, + Err(_) => continue, + }; + + // First, create a default plugin to get its default options + let default_plugin = get_meta_plugin( + meta_plugin_type.clone(), + None, + None, + ); + + // Start with the default options + let mut effective_options = default_plugin.options().clone(); + + // Merge with the configured options + for (key, value) in &plugin_config.options { + effective_options.insert(key.clone(), value.clone()); + } + + // Convert outputs from HashMap to HashMap + let outputs_converted: std::collections::HashMap = plugin_config.outputs + .iter() + .map(|(k, v)| (k.clone(), serde_yaml::Value::String(v.clone()))) + .collect(); + + // Create the actual plugin with merged options - the constructor will handle setting up outputs + let actual_plugin = get_meta_plugin( + meta_plugin_type.clone(), + Some(effective_options.clone()), + Some(outputs_converted), + ); + + // Get the default plugin to see its default options + let default_plugin = get_meta_plugin( + meta_plugin_type.clone(), + None, + None, + ); + + // Start with the default options + let mut all_options = default_plugin.options().clone(); + // Merge with the configured options + for (key, value) in &effective_options { + all_options.insert(key.clone(), value.clone()); + } + + // Sort options by key and convert to a YAML string + let mut sorted_options: Vec<_> = all_options.iter().collect(); + sorted_options.sort_by(|a, b| a.0.cmp(b.0)); + let sorted_options_map: std::collections::BTreeMap<_, _> = sorted_options.into_iter().collect(); + + let options_str = if sorted_options_map.is_empty() { + "{}".to_string() + } else { + serde_yaml::to_string(&sorted_options_map) + .unwrap_or_else(|_| "Unable to serialize options".to_string()) + .trim() + .to_string() + }; + + // Show only non-null outputs from the plugin + // Collect and sort outputs by their string representation + let mut enabled_output_pairs = Vec::new(); + for (key, value) in actual_plugin.outputs() { + // Skip null values (disabled outputs) + if value.is_null() { + continue; + } + + // Convert serde_yaml::Value to a string representation + let value_str = match value { + serde_yaml::Value::String(s) => s.clone(), + serde_yaml::Value::Number(n) => n.to_string(), + serde_yaml::Value::Bool(b) => b.to_string(), + serde_yaml::Value::Null => "null".to_string(), + serde_yaml::Value::Sequence(_) => { + serde_yaml::to_string(value).unwrap_or_else(|_| "[]".to_string()) + } + serde_yaml::Value::Mapping(_) => { + serde_yaml::to_string(value).unwrap_or_else(|_| "{}".to_string()) + } + serde_yaml::Value::Tagged(_) => { + serde_yaml::to_string(value).unwrap_or_else(|_| "tagged".to_string()) + } + }; + // Trim any extra whitespace from the serialized values + let value_str = value_str.trim().to_string(); + if key == &value_str { + enabled_output_pairs.push((key.clone(), key.clone())); + } else { + enabled_output_pairs.push((key.clone(), format!("{}->{}", key, value_str))); + } + } + + // Sort outputs by their display value (second element of the tuple) + enabled_output_pairs.sort_by(|a, b| a.1.cmp(&b.1)); + + // Join each output on a new line + let outputs_str = if enabled_output_pairs.is_empty() { + "{}".to_string() + } else { + enabled_output_pairs.into_iter() + .map(|(_, display)| display) + .collect::>() + .join("\n") + }; + + table.add_row(Row::new(vec![ + Cell::new(&plugin_config.name), + Cell::new(&options_str), + Cell::new(&outputs_str), + ])); + } + + Some(table) +} + +fn build_meta_plugin_table(meta_plugin_info: &std::collections::HashMap) -> Table { + let mut meta_plugin_table = Table::new(); + if std::io::stdout().is_terminal() { + meta_plugin_table.set_format(*FORMAT_BOX_CHARS); + } else { + meta_plugin_table.set_format(*FORMAT_NO_BORDER_LINE_SEPARATOR); + } + + meta_plugin_table.set_titles(row!( + b->"Plugin Name", + b->"Options", + b->"Outputs")); + + // Sort meta plugin info by plugin name + let mut sorted_meta_plugin_info: Vec<&MetaPluginInfo> = meta_plugin_info.values().collect(); + sorted_meta_plugin_info.sort_by(|a, b| a.meta_name.cmp(&b.meta_name)); + + for info in sorted_meta_plugin_info { + // Get default options for the meta plugin + let meta_plugin_type = match MetaPluginType::from_str(&info.meta_name) { + Ok(plugin_type) => plugin_type, + Err(_) => continue, + }; + + // Create a default plugin to get its default options + let default_plugin = get_meta_plugin( + meta_plugin_type.clone(), + None, + None, + ); + + // Get and sort options + let mut options: Vec<_> = default_plugin.options().iter().collect(); + options.sort_by(|a, b| a.0.cmp(b.0)); + + // Format options as YAML string, each on a new line + let options_str = if options.is_empty() { + "{}".to_string() + } else { + let options_map: std::collections::BTreeMap<_, _> = options.into_iter().collect(); + serde_yaml::to_string(&options_map) + .unwrap_or_else(|_| "Unable to serialize options".to_string()) + .trim() + .to_string() + }; + + // Get and sort output keys + let mut output_keys: Vec = info.outputs.keys().map(|k| k.to_string()).collect(); + output_keys.sort(); + let outputs_display = if output_keys.is_empty() { + "{}".to_string() + } else { + output_keys.join("\n") + }; + + meta_plugin_table.add_row(Row::new(vec![ + Cell::new(&info.meta_name), + Cell::new(&options_str), + Cell::new(&outputs_display), + ])); + } + + meta_plugin_table +} + +pub fn mode_status_plugins( + cmd: &mut Command, + settings: &config::Settings, + data_path: PathBuf, + db_path: PathBuf, +) -> Result<(), anyhow::Error> { + debug!("STATUS_PLUGINS: Starting mode_status_plugins function"); + + let status_service = crate::services::status_service::StatusService::new(); + let output_format = crate::modes::common::settings_output_format(settings); + debug!("STATUS_PLUGINS: About to generate status info"); + let status_info = status_service.generate_status(cmd, settings, data_path, db_path); + debug!("STATUS_PLUGINS: Status info generated successfully"); + + match output_format { + OutputFormat::Table => { + println!("META PLUGINS AVAILABLE:"); + build_meta_plugin_table(&status_info.meta_plugins).printstd(); + println!(); + + // Always try to print META PLUGINS CONFIGURED section + if let Some(meta_plugins_table) = build_meta_plugins_configured_table(settings) { + println!("META PLUGINS CONFIGURED:"); + meta_plugins_table.printstd(); + println!(); + } else { + println!("META PLUGINS CONFIGURED:"); + println!("No plugins configured"); + println!(); + } + Ok(()) + }, + OutputFormat::Json => { + // Create a subset for plugins only + let plugins_info = serde_json::json!({ + "meta_plugins_available": status_info.meta_plugins, + "meta_plugins_configured": settings.meta_plugins + }); + println!("{}", serde_json::to_string_pretty(&plugins_info)?); + Ok(()) + }, + OutputFormat::Yaml => { + // Create a proper structure for plugins info + use serde_yaml::Mapping; + let mut plugins_mapping = Mapping::new(); + + // Add available plugins + plugins_mapping.insert( + serde_yaml::Value::String("meta_plugins_available".to_string()), + serde_yaml::to_value(&status_info.meta_plugins)?, + ); + + // Add configured plugins if they exist + if let Some(configured_plugins) = &settings.meta_plugins { + plugins_mapping.insert( + serde_yaml::Value::String("meta_plugins_configured".to_string()), + serde_yaml::to_value(configured_plugins)?, + ); + } + + println!("{}", serde_yaml::to_string(&plugins_mapping)?); + Ok(()) + } + } +}