use clap::*; use is_terminal::IsTerminal; use std::path::PathBuf; use std::str::FromStr; use log::debug; // Helper function to convert serde_json::Value to serde_yaml::Value fn convert_json_to_yaml_value(value: &serde_json::Value) -> serde_yaml::Value { match value { serde_json::Value::Null => serde_yaml::Value::Null, serde_json::Value::Bool(b) => serde_yaml::Value::Bool(*b), serde_json::Value::Number(n) => { if let Some(i) = n.as_i64() { serde_yaml::Value::Number(serde_yaml::Number::from(i)) } else if let Some(f) = n.as_f64() { serde_yaml::Value::Number(serde_yaml::Number::from(f)) } else { serde_yaml::Value::String(n.to_string()) } } serde_json::Value::String(s) => serde_yaml::Value::String(s.clone()), serde_json::Value::Array(arr) => { let mut yaml_array = Vec::new(); for item in arr { yaml_array.push(convert_json_to_yaml_value(item)); } serde_yaml::Value::Sequence(yaml_array) } serde_json::Value::Object(obj) => { let mut yaml_mapping = serde_yaml::Mapping::new(); for (k, v) in obj { yaml_mapping.insert( serde_yaml::Value::String(k.clone()), convert_json_to_yaml_value(v), ); } serde_yaml::Value::Mapping(yaml_mapping) } } } use crate::modes::common::OutputFormat; use crate::config; use serde_json; use serde_yaml; use comfy_table::{Table, ContentArrangement, Cell, Color, Attribute}; use comfy_table::presets::UTF8_FULL; use comfy_table::modifiers::UTF8_ROUND_CORNERS; use crate::meta_plugin::{MetaPluginType, get_meta_plugin}; use crate::common::status::{MetaPluginInfo, CompressionInfo}; 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 .load_preset(UTF8_FULL) .apply_modifier(UTF8_ROUND_CORNERS); } else { meta_plugin_table.set_content_arrangement(ContentArrangement::Dynamic); } meta_plugin_table.set_header(vec![ Cell::new("Plugin Name").add_attribute(Attribute::Bold), Cell::new("Options").add_attribute(Attribute::Bold), Cell::new("Outputs").add_attribute(Attribute::Bold), ]); // 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(vec![ info.meta_name.clone(), options_str, outputs_display, ]); } meta_plugin_table } fn build_compression_table(compression_info: &Vec) -> Table { let mut compression_table = Table::new(); if std::io::stdout().is_terminal() { compression_table .load_preset(UTF8_FULL) .apply_modifier(UTF8_ROUND_CORNERS); } else { compression_table.set_content_arrangement(ContentArrangement::Dynamic); } compression_table.set_header(vec![ Cell::new("Type").add_attribute(Attribute::Bold), Cell::new("Found").add_attribute(Attribute::Bold), Cell::new("Enabled").add_attribute(Attribute::Bold), Cell::new("Binary").add_attribute(Attribute::Bold), Cell::new("Compress").add_attribute(Attribute::Bold), Cell::new("Decompress").add_attribute(Attribute::Bold), ]); for info in compression_info { compression_table.add_row(vec![ info.compression_type.clone(), match info.found { true => Cell::new("Yes").fg(Color::Green), false => Cell::new("No").fg(Color::Red), }, match info.default { true => Cell::new("Yes").fg(Color::Green), false => Cell::new("No"), }, match info.binary.as_str() { "" => Cell::new(&info.binary).fg(Color::DarkGrey), _ => Cell::new(&info.binary), }, info.compress.clone(), info.decompress.clone(), ]); } compression_table } fn build_filter_plugin_table(filter_plugins: &Vec) -> Table { let mut filter_plugin_table = Table::new(); if std::io::stdout().is_terminal() { filter_plugin_table .load_preset(UTF8_FULL) .apply_modifier(UTF8_ROUND_CORNERS); } else { filter_plugin_table.set_content_arrangement(ContentArrangement::Dynamic); } filter_plugin_table.set_header(vec![ Cell::new("Plugin Name").add_attribute(Attribute::Bold), Cell::new("Options").add_attribute(Attribute::Bold), Cell::new("Description").add_attribute(Attribute::Bold), ]); // Sort plugins by name let mut sorted_plugins: Vec<_> = filter_plugins.iter().collect(); sorted_plugins.sort_by(|a, b| a.name.cmp(&b.name)); for plugin_info in sorted_plugins { // Format options as YAML string let options_str = if plugin_info.options.is_empty() { "{}".to_string() } else { // Convert options to a proper structure for display let mut options_list = Vec::new(); for opt in &plugin_info.options { let mut opt_map = serde_yaml::Mapping::new(); opt_map.insert( serde_yaml::Value::String("name".to_string()), serde_yaml::Value::String(opt.name.clone()), ); if let Some(default) = &opt.default { // Convert serde_json::Value to serde_yaml::Value let yaml_value = match default { serde_json::Value::Null => serde_yaml::Value::Null, serde_json::Value::Bool(b) => serde_yaml::Value::Bool(*b), serde_json::Value::Number(n) => { if let Some(i) = n.as_i64() { serde_yaml::Value::Number(serde_yaml::Number::from(i)) } else if let Some(f) = n.as_f64() { serde_yaml::Value::Number(serde_yaml::Number::from(f)) } else { serde_yaml::Value::String(default.to_string()) } } serde_json::Value::String(s) => serde_yaml::Value::String(s.clone()), serde_json::Value::Array(arr) => { // Convert each element in the array let mut yaml_array = Vec::new(); for item in arr { yaml_array.push(convert_json_to_yaml_value(item)); } serde_yaml::Value::Sequence(yaml_array) } serde_json::Value::Object(obj) => { // Convert each key-value pair in the object let mut yaml_mapping = serde_yaml::Mapping::new(); for (k, v) in obj { yaml_mapping.insert( serde_yaml::Value::String(k.clone()), convert_json_to_yaml_value(v), ); } serde_yaml::Value::Mapping(yaml_mapping) } }; opt_map.insert( serde_yaml::Value::String("default".to_string()), yaml_value, ); } else { opt_map.insert( serde_yaml::Value::String("default".to_string()), serde_yaml::Value::Null, ); } opt_map.insert( serde_yaml::Value::String("required".to_string()), serde_yaml::Value::Bool(opt.required), ); options_list.push(serde_yaml::Value::Mapping(opt_map)); } serde_yaml::to_string(&serde_yaml::Value::Sequence(options_list)) .unwrap_or_else(|_| "Unable to serialize options".to_string()) .trim() .to_string() }; filter_plugin_table.add_row(vec![ plugin_info.name.clone(), options_str, plugin_info.description.clone(), ]); } // If no filter plugins are available, add a row indicating that if filter_plugins.is_empty() { filter_plugin_table.add_row(vec![ "No filter plugins available", "{}", "", ]); } filter_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:"); build_meta_plugin_table(&status_info.meta_plugins).printstd(); println!(); println!("COMPRESSION PLUGINS:"); build_compression_table(&status_info.compression).printstd(); println!(); println!("FILTER PLUGINS:"); build_filter_plugin_table(&status_info.filter_plugins).printstd(); println!(); Ok(()) }, OutputFormat::Json => { // Create a subset for plugins only using status_info let plugins_info = serde_json::json!({ "meta_plugins_available": status_info.meta_plugins, "meta_plugins_configured": status_info.configured_meta_plugins, "filter_plugins": status_info.filter_plugins }); println!("{}", serde_json::to_string_pretty(&plugins_info)?); Ok(()) }, OutputFormat::Yaml => { // Create a proper structure for plugins info using status_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) = &status_info.configured_meta_plugins { plugins_mapping.insert( serde_yaml::Value::String("meta_plugins_configured".to_string()), serde_yaml::to_value(configured_plugins)?, ); } // Add filter plugins plugins_mapping.insert( serde_yaml::Value::String("filter_plugins".to_string()), serde_yaml::to_value(&status_info.filter_plugins)?, ); println!("{}", serde_yaml::to_string(&plugins_mapping)?); Ok(()) } } }