use log::debug; use serde::{Deserialize, Serialize}; pub mod program; pub mod digest; pub mod magic; pub mod binary; pub mod text; pub mod read_time; pub mod read_rate; pub mod hostname; pub mod cwd; pub mod user; pub mod shell; pub mod shell_pid; pub mod keep_pid; use crate::meta_plugin::program::MetaPluginProgram; use crate::meta_plugin::digest::DigestMetaPlugin; use crate::meta_plugin::read_time::ReadTimeMetaPlugin; use crate::meta_plugin::read_rate::ReadRateMetaPlugin; use crate::meta_plugin::magic::MagicFileMetaPlugin; use crate::meta_plugin::binary::BinaryMetaPlugin; use crate::meta_plugin::text::TextMetaPlugin; use crate::meta_plugin::hostname::HostnameMetaPlugin; use crate::meta_plugin::cwd::CwdMetaPlugin; use crate::meta_plugin::user::UserMetaPlugin; use crate::meta_plugin::shell::ShellMetaPlugin; use crate::meta_plugin::shell_pid::ShellPidMetaPlugin; use crate::meta_plugin::keep_pid::KeepPidMetaPlugin; /// Represents metadata to be stored #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MetaData { pub name: String, pub value: String, } /// Response from meta plugin operations #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct MetaPluginResponse { pub metadata: Vec, pub is_finalized: bool, } /// Base implementation for meta plugins to reduce boilerplate #[derive(Debug, Clone, Default)] pub struct BaseMetaPlugin { pub outputs: std::collections::HashMap, pub options: std::collections::HashMap, pub meta_name: String, pub is_finalized: bool, } impl BaseMetaPlugin { pub fn new() -> Self { Self::default() } pub fn outputs(&self) -> &std::collections::HashMap { &self.outputs } pub fn outputs_mut(&mut self) -> &mut std::collections::HashMap { &mut self.outputs } pub fn options(&self) -> &std::collections::HashMap { &self.options } pub fn options_mut(&mut self) -> &mut std::collections::HashMap { &mut self.options } /// Helper function to initialize plugin options and outputs pub fn initialize_plugin( &mut self, default_outputs: &[&str], options: Option>, outputs: Option>, ) { // Set default outputs for output_name in default_outputs { self.outputs.insert(output_name.to_string(), serde_yaml::Value::String(output_name.to_string())); } // Apply provided options and outputs if let Some(opts) = options { self.options.extend(opts); } if let Some(outs) = outputs { self.outputs.extend(outs); } } } impl MetaPlugin for BaseMetaPlugin { fn meta_name(&self) -> String { self.meta_name.clone() } fn outputs(&self) -> &std::collections::HashMap { &self.outputs } fn outputs_mut(&mut self) -> &mut std::collections::HashMap { &mut self.outputs } fn options(&self) -> &std::collections::HashMap { &self.options } fn options_mut(&mut self) -> &mut std::collections::HashMap { &mut self.options } } #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString)] #[strum(serialize_all = "snake_case", ascii_case_insensitive)] pub enum MetaPluginType { FileMagic, FileMime, FileEncoding, MagicFile, LineCount, WordCount, Cwd, Binary, Text, User, Shell, ShellPid, KeepPid, Digest, ReadTime, ReadRate, Hostname, } /// Central function to handle metadata output with name mapping /// outputs: HashMap where key is internal name, value is either custom name or "false" to disable pub fn process_metadata_outputs(internal_name: &str, value: String, outputs: &std::collections::HashMap) -> Option { // Check if this output is disabled if let Some(mapping) = outputs.get(internal_name) { if let Some(false_val) = mapping.as_bool() { if !false_val { debug!("META: Skipping disabled output: {}", internal_name); return None; } } if let Some(custom_name) = mapping.as_str() { debug!("META: Processing metadata: internal_name={}, custom_name={}, value={}", internal_name, custom_name, value); return Some(MetaData { name: custom_name.to_string(), value, }); } } // Default: use internal name as output name debug!("META: Processing metadata: name={}, value={}", internal_name, value); Some(MetaData { name: internal_name.to_string(), value, }) } pub trait MetaPlugin where Self: 'static { fn is_supported(&self) -> bool { true } fn is_internal(&self) -> bool { true } // Check if the plugin is already finalized fn is_finalized(&self) -> bool { false } // Set the finalized state (only for plugins that can track this) fn set_finalized(&mut self, _finalized: bool) {} // Update the meta plugin with new data fn update(&mut self, _data: &[u8]) -> MetaPluginResponse { // Default implementation does nothing MetaPluginResponse { metadata: Vec::new(), is_finalized: false, } } fn finalize(&mut self) -> MetaPluginResponse { // Default implementation does nothing MetaPluginResponse { metadata: Vec::new(), is_finalized: true, } } fn meta_name(&self) -> String; // Get program information for display in status fn program_info(&self) -> Option<(&str, Vec<&str>)> { None } // Initialize the plugin fn initialize(&mut self) -> MetaPluginResponse { // Default implementation does nothing MetaPluginResponse { metadata: Vec::new(), is_finalized: false, } } // Access to outputs mapping with default implementation fn outputs(&self) -> &std::collections::HashMap { use once_cell::sync::Lazy; static EMPTY: Lazy> = Lazy::new(|| std::collections::HashMap::new()); &EMPTY } fn outputs_mut(&mut self) -> &mut std::collections::HashMap { panic!("outputs_mut() not implemented for this plugin") } // Access to options mapping with default implementation fn options(&self) -> &std::collections::HashMap { use once_cell::sync::Lazy; static EMPTY: Lazy> = Lazy::new(|| std::collections::HashMap::new()); &EMPTY } fn options_mut(&mut self) -> &mut std::collections::HashMap { panic!("options_mut() not implemented for this plugin") } // Get the default output names this plugin can produce fn default_outputs(&self) -> Vec { // Default implementation returns empty - plugins should override this Vec::new() } // Method to downcast to concrete type (for checking finalization state) fn as_any_mut(&mut self) -> &mut dyn std::any::Any where Self: Sized { self } } pub fn get_meta_plugin(meta_plugin_type: MetaPluginType) -> Box { // For now, use new_simple() which passes None for options and outputs // The calling code should be updated to use a different function that accepts options and outputs match meta_plugin_type { MetaPluginType::FileMagic => Box::new(MetaPluginProgram::new_simple("file", vec!["-bE", "-"], "file_magic".to_string(), true)), MetaPluginType::FileMime => Box::new(MetaPluginProgram::new_simple("file", vec!["-b", "--mime-type", "-"], "file_mime".to_string(), true)), MetaPluginType::FileEncoding => Box::new(MetaPluginProgram::new_simple("file", vec!["-b", "--mime-encoding", "-"], "file_encoding".to_string(), true)), MetaPluginType::MagicFile => Box::new(MagicFileMetaPlugin::new_simple()), MetaPluginType::LineCount => Box::new(MetaPluginProgram::new_simple("wc", vec!["-l"], "line_count".to_string(), true)), MetaPluginType::WordCount => Box::new(MetaPluginProgram::new_simple("wc", vec!["-w"], "word_count".to_string(), true)), MetaPluginType::Cwd => Box::new(CwdMetaPlugin::new_simple()), MetaPluginType::Binary => Box::new(BinaryMetaPlugin::new_simple()), MetaPluginType::Text => Box::new(TextMetaPlugin::new_simple()), MetaPluginType::User => Box::new(UserMetaPlugin::new_simple()), MetaPluginType::Shell => Box::new(ShellMetaPlugin::new_simple()), MetaPluginType::ShellPid => Box::new(ShellPidMetaPlugin::new_simple()), MetaPluginType::KeepPid => Box::new(KeepPidMetaPlugin::new_simple()), MetaPluginType::Digest => Box::new(DigestMetaPlugin::new_simple()), MetaPluginType::ReadTime => Box::new(ReadTimeMetaPlugin::new_simple()), MetaPluginType::ReadRate => Box::new(ReadRateMetaPlugin::new_simple()), MetaPluginType::Hostname => Box::new(HostnameMetaPlugin::new_simple()), } } // Add a new function to create plugins with options and outputs pub fn get_meta_plugin_with_config( meta_plugin_type: MetaPluginType, options: Option>, outputs: Option>, ) -> Box { match meta_plugin_type { MetaPluginType::FileMagic => Box::new(MetaPluginProgram::new("file", vec!["-bE", "-"], "file_magic".to_string(), true, options, outputs)), MetaPluginType::FileMime => Box::new(MetaPluginProgram::new("file", vec!["-b", "--mime-type", "-"], "file_mime".to_string(), true, options, outputs)), MetaPluginType::FileEncoding => Box::new(MetaPluginProgram::new("file", vec!["-b", "--mime-encoding", "-"], "file_encoding".to_string(), true, options, outputs)), MetaPluginType::MagicFile => Box::new(MagicFileMetaPlugin::new(options, outputs)), MetaPluginType::LineCount => Box::new(MetaPluginProgram::new("wc", vec!["-l"], "line_count".to_string(), true, options, outputs)), MetaPluginType::WordCount => Box::new(MetaPluginProgram::new("wc", vec!["-w"], "word_count".to_string(), true, options, outputs)), MetaPluginType::Cwd => Box::new(CwdMetaPlugin::new(options, outputs)), MetaPluginType::Binary => Box::new(BinaryMetaPlugin::new(options, outputs)), MetaPluginType::Text => Box::new(TextMetaPlugin::new(options, outputs)), MetaPluginType::User => Box::new(UserMetaPlugin::new(options, outputs)), MetaPluginType::Shell => Box::new(ShellMetaPlugin::new(options, outputs)), MetaPluginType::ShellPid => Box::new(ShellPidMetaPlugin::new(options, outputs)), MetaPluginType::KeepPid => Box::new(KeepPidMetaPlugin::new(options, outputs)), MetaPluginType::Digest => Box::new(DigestMetaPlugin::new(options, outputs)), MetaPluginType::ReadTime => Box::new(ReadTimeMetaPlugin::new(options, outputs)), MetaPluginType::ReadRate => Box::new(ReadRateMetaPlugin::new(options, outputs)), MetaPluginType::Hostname => Box::new(HostnameMetaPlugin::new(options, outputs)), } }