use clap::*; use is_terminal::IsTerminal; use std::path::PathBuf; use std::str::FromStr; 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::{PathInfo, CompressionInfo, MetaPluginInfo}; fn build_path_table(path_info: &PathInfo) -> Table { let mut path_table = Table::new(); if std::io::stdout().is_terminal() { path_table.set_format(get_format_box_chars_no_border_line_separator()); } else { path_table.set_format(*FORMAT_NO_BORDER_LINE_SEPARATOR); } path_table.set_titles(Row::new(vec![ Cell::new("Type").with_style(Attr::Bold), Cell::new("Path").with_style(Attr::Bold), ])); path_table.add_row(Row::new(vec![ Cell::new("Data"), Cell::new(&path_info.data), ])); path_table.add_row(Row::new(vec![ Cell::new("Database"), Cell::new(&path_info.database), ])); path_table } fn build_compression_table(compression_info: &Vec) -> Table { let mut compression_table = Table::new(); if std::io::stdout().is_terminal() { compression_table.set_format(get_format_box_chars_no_border_line_separator()); } else { compression_table.set_format(*FORMAT_NO_BORDER_LINE_SEPARATOR); } compression_table.set_titles(row!( b->"Type", b->"Found", b->"Enabled", b->"Binary", b->"Compress", b->"Decompress")); for info in compression_info { compression_table.add_row(Row::new(vec![ Cell::new(&info.compression_type), match info.found { true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), false => Cell::new("No").with_style(Attr::ForegroundColor(color::RED)), }, match info.default { true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), false => Cell::new("No"), }, match info.binary.as_str() { "" => Cell::new(&info.binary).with_style(Attr::ForegroundColor(color::BRIGHT_BLACK)), _ => Cell::new(&info.binary), }, Cell::new(&info.compress), Cell::new(&info.decompress), ])); } compression_table } fn build_config_table(settings: &config::Settings) -> Table { let mut config_table = Table::new(); if std::io::stdout().is_terminal() { config_table.set_format(get_format_box_chars_no_border_line_separator()); } else { config_table.set_format(*FORMAT_NO_BORDER_LINE_SEPARATOR); } config_table.set_titles(Row::new(vec![ Cell::new("Setting").with_style(Attr::Bold), Cell::new("Value").with_style(Attr::Bold), ])); // Add relevant configuration settings config_table.add_row(Row::new(vec![ Cell::new("Directory"), Cell::new(&settings.dir.to_string_lossy()), ])); config_table.add_row(Row::new(vec![ Cell::new("Human Readable"), Cell::new(&settings.human_readable.to_string()), ])); config_table.add_row(Row::new(vec![ Cell::new("Quiet"), Cell::new(&settings.quiet.to_string()), ])); if let Some(output_format) = &settings.output_format { config_table.add_row(Row::new(vec![ Cell::new("Output Format"), Cell::new(output_format), ])); } if let Some(compression) = settings.compression() { config_table.add_row(Row::new(vec![ Cell::new("Compression"), Cell::new(&compression), ])); } config_table } 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(get_format_box_chars_no_border_line_separator()); 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: &Vec) -> Table { let mut meta_plugin_table = Table::new(); if std::io::stdout().is_terminal() { //meta_plugin_table.set_format(get_format_box_chars_no_border_line_separator()); 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 = meta_plugin_info.clone(); 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( cmd: &mut Command, settings: &config::Settings, data_path: PathBuf, db_path: PathBuf, ) -> Result<(), anyhow::Error> { debug!("STATUS: Starting mode_status function"); let status_service = crate::services::status_service::StatusService::new(); let output_format = crate::modes::common::settings_output_format(settings); debug!("STATUS: About to generate status info"); let status_info = status_service.generate_status(cmd, settings, data_path, db_path); debug!("STATUS: Status info generated successfully"); match output_format { OutputFormat::Table => { 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!("META PLUGINS AVAILABLE:"); build_meta_plugin_table(&status_info.meta_plugins).printstd(); println!(); // Print META PLUGINS CONFIGURED if they exist if let Some(meta_plugins_table) = build_meta_plugins_configured_table(settings) { println!("META PLUGINS CONFIGURED:"); meta_plugins_table.printstd(); println!(); } Ok(()) }, OutputFormat::Json => { // For JSON and YAML, we need to include config info in the status_info // Since we can't modify generate_status_info, we'll need to think of another approach // For now, just print the original status_info println!("{}", serde_json::to_string_pretty(&status_info)?); Ok(()) }, OutputFormat::Yaml => { println!("{}", serde_yaml::to_string(&status_info)?); Ok(()) } } }