use clap::*; use is_terminal::IsTerminal; use std::path::PathBuf; use strum::IntoEnumIterator; use crate::compression_engine; use crate::compression_engine::COMPRESSION_PROGRAMS; use crate::compression_engine::CompressionType; use crate::compression_engine::program::CompressionEngineProgram; use crate::modes::common::{get_format_box_chars_no_border_line_separator, get_output_format, OutputFormat}; use prettytable::color; use serde::{Deserialize, Serialize}; use serde_json; use serde_yaml; use prettytable::row; use prettytable::{Attr, Cell, Row, Table}; use prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR; use crate::meta_plugin; use crate::meta_plugin::MetaPluginType; fn build_path_table(data_path: PathBuf, db_path: PathBuf) -> 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( &data_path .into_os_string() .into_string() .expect("Unable to convert data path to string"), ), ])); path_table.add_row(Row::new(vec![ Cell::new("Database"), Cell::new( &db_path .into_os_string() .into_string() .expect("Unable to convert DB path to string"), ), ])); path_table } fn build_compression_table() -> 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->"Default", b->"Binary", b->"Compress", b->"Decompress")); let default_type = compression_engine::default_compression_type(); for compression_type in CompressionType::iter() { let compression_program: CompressionEngineProgram = match &COMPRESSION_PROGRAMS[compression_type.clone()] { Some(compression_program) => compression_program.clone(), None => CompressionEngineProgram { program: "".to_string(), compress: Vec::new(), decompress: Vec::new(), supported: true, }, }; let is_default = compression_type == default_type; compression_table.add_row(Row::new(vec![ Cell::new(&compression_type.to_string()), match compression_program.supported { true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), false => Cell::new("No").with_style(Attr::ForegroundColor(color::RED)), }, match is_default { true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), false => Cell::new("No"), }, match compression_program.program.is_empty() { true => { Cell::new("").with_style(Attr::ForegroundColor(color::BRIGHT_BLACK)) } false => Cell::new(&compression_program.program), }, Cell::new(&compression_program.compress.join(" ")), Cell::new(&compression_program.decompress.join(" ")), ])); } compression_table } fn build_meta_plugin_table(enabled_meta_plugins: &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()); } else { meta_plugin_table.set_format(*FORMAT_NO_BORDER_LINE_SEPARATOR); } meta_plugin_table.set_titles(row!( b->"Meta Name", b->"Found", b->"Enabled", b->"Binary", b->"Args")); for meta_plugin_type in MetaPluginType::iter() { let mut meta_plugin = meta_plugin::get_meta_plugin(meta_plugin_type.clone()); let is_supported = meta_plugin.is_supported(); let is_enabled = enabled_meta_plugins.contains(&meta_plugin_type); // Determine what implementation will actually be used let (binary_display, args_display) = if !is_supported { ("".to_string(), "".to_string()) } else { match meta_plugin_type { // For internal plugins, always show as internal MetaPluginType::DigestSha256 | MetaPluginType::ReadTime | MetaPluginType::ReadRate | MetaPluginType::Cwd | MetaPluginType::Uid | MetaPluginType::User | MetaPluginType::Gid | MetaPluginType::Group | MetaPluginType::Shell | MetaPluginType::ShellPid | MetaPluginType::KeepPid | MetaPluginType::Hostname | MetaPluginType::FullHostname => { ("".to_string(), "".to_string()) }, // For program-based plugins, show program info _ => { // Get program info from the meta plugin itself if let Some((program, args)) = meta_plugin.program_info() { (program.to_string(), args.join(" ")) } else { ("".to_string(), "".to_string()) } } } }; meta_plugin_table.add_row(Row::new(vec![ Cell::new(&meta_plugin.meta_name()), match is_supported { true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), false => Cell::new("No").with_style(Attr::ForegroundColor(color::RED)), }, match is_enabled { true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), false => Cell::new("No"), }, match binary_display.as_str() { "" => Cell::new(&binary_display).with_style(Attr::ForegroundColor(color::BRIGHT_BLACK)), "" => Cell::new(&binary_display).with_style(Attr::ForegroundColor(color::RED)), _ => Cell::new(&binary_display), }, Cell::new(&args_display), ])); } meta_plugin_table } fn show_status_structured( data_path: PathBuf, db_path: PathBuf, enabled_meta_plugins: &Vec, output_format: OutputFormat, ) -> Result<(), anyhow::Error> { let path_info = PathInfo { data: data_path.into_os_string().into_string().expect("Unable to convert data path to string"), database: db_path.into_os_string().into_string().expect("Unable to convert DB path to string"), }; let default_type = compression_engine::default_compression_type(); let mut compression_info = Vec::new(); for compression_type in CompressionType::iter() { let compression_program: CompressionEngineProgram = match &COMPRESSION_PROGRAMS[compression_type.clone()] { Some(compression_program) => compression_program.clone(), None => CompressionEngineProgram { program: "".to_string(), compress: Vec::new(), decompress: Vec::new(), supported: true, }, }; let is_default = compression_type == default_type; let binary = if compression_program.program.is_empty() { "".to_string() } else { compression_program.program }; compression_info.push(CompressionInfo { compression_type: compression_type.to_string(), found: compression_program.supported, default: is_default, binary, compress: compression_program.compress.join(" "), decompress: compression_program.decompress.join(" "), }); } let mut meta_plugin_info = Vec::new(); for meta_plugin_type in MetaPluginType::iter() { let mut meta_plugin = meta_plugin::get_meta_plugin(meta_plugin_type.clone()); let is_supported = meta_plugin.is_supported(); let is_enabled = enabled_meta_plugins.contains(&meta_plugin_type); let (binary_display, args_display) = if !is_supported { ("".to_string(), "".to_string()) } else { match meta_plugin_type { MetaPluginType::DigestSha256 | MetaPluginType::ReadTime | MetaPluginType::ReadRate | MetaPluginType::Cwd | MetaPluginType::Uid | MetaPluginType::User | MetaPluginType::Gid | MetaPluginType::Group | MetaPluginType::Shell | MetaPluginType::ShellPid | MetaPluginType::KeepPid | MetaPluginType::Hostname | MetaPluginType::FullHostname => { ("".to_string(), "".to_string()) }, _ => { if let Some((program, args)) = meta_plugin.program_info() { (program.to_string(), args.join(" ")) } else { ("".to_string(), "".to_string()) } } } }; meta_plugin_info.push(MetaPluginInfo { meta_name: meta_plugin.meta_name(), found: is_supported, enabled: is_enabled, binary: binary_display, args: args_display, }); } let status_info = StatusInfo { paths: path_info, compression: compression_info, meta_plugins: meta_plugin_info, }; match output_format { OutputFormat::Json => { println!("{}", serde_json::to_string_pretty(&status_info)?); } OutputFormat::Yaml => { println!("{}", serde_yaml::to_string(&status_info)?); } OutputFormat::Table => unreachable!(), } Ok(()) } #[derive(Serialize, Deserialize)] pub struct StatusInfo { pub paths: PathInfo, pub compression: Vec, pub meta_plugins: Vec, } #[derive(Serialize, Deserialize)] pub struct PathInfo { pub data: String, pub database: String, } #[derive(Serialize, Deserialize)] pub struct CompressionInfo { #[serde(rename = "type")] pub compression_type: String, pub found: bool, pub default: bool, pub binary: String, pub compress: String, pub decompress: String, } #[derive(Serialize, Deserialize)] pub struct MetaPluginInfo { pub meta_name: String, pub found: bool, pub enabled: bool, pub binary: String, pub args: String, } pub fn mode_status( _cmd: &mut Command, args: &crate::Args, data_path: PathBuf, db_path: PathBuf, ) -> Result<(), anyhow::Error> { // Determine which meta plugins would be enabled for a save operation let mut meta_plugin_types: Vec = crate::modes::common::cmd_args_meta_plugin_types(_cmd, &args); // Add digest type if specified let digest_type = crate::modes::common::cmd_args_digest_type(_cmd, &args); let digest_meta_plugin_type = match digest_type { crate::meta_plugin::MetaPluginType::DigestSha256 => Some(MetaPluginType::DigestSha256), crate::meta_plugin::MetaPluginType::DigestMd5 => Some(MetaPluginType::DigestMd5), _ => None, }; if let Some(digest_plugin_type) = digest_meta_plugin_type { if !meta_plugin_types.contains(&digest_plugin_type) { meta_plugin_types.push(digest_plugin_type); } } let output_format = get_output_format(args); if output_format != OutputFormat::Table { return show_status_structured(data_path, db_path, &meta_plugin_types, output_format); } println!("PATHS:"); build_path_table(data_path, db_path).printstd(); println!(); println!("COMPRESSION:"); build_compression_table().printstd(); println!(); println!("META PLUGINS:"); build_meta_plugin_table(&meta_plugin_types).printstd(); Ok(()) }