diff --git a/src/meta_plugin/mod.rs b/src/meta_plugin/mod.rs index ac77f63..e200ecc 100644 --- a/src/meta_plugin/mod.rs +++ b/src/meta_plugin/mod.rs @@ -1 +1,301 @@ +use log::debug; +use serde::{Deserialize, Serialize}; + +pub mod program; +pub mod digest; pub mod system; +pub mod magic; +pub mod binary; +pub mod text; +pub mod read_time; +pub mod read_rate; +pub mod hostname; + +use crate::meta_plugin::program::MetaPluginProgram; +use crate::meta_plugin::digest::DigestSha256MetaPlugin; +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::system::CwdMetaPlugin; +use crate::meta_plugin::system::UserMetaPlugin; +use crate::meta_plugin::system::ShellMetaPlugin; +use crate::meta_plugin::system::ShellPidMetaPlugin; +use crate::meta_plugin::system::KeepPidMetaPlugin; +use crate::meta_plugin::hostname::HostnameMetaPlugin; + +/// 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(ascii_case_insensitive)] +pub enum MetaPluginType { + FileMagic, + FileMime, + FileEncoding, + MagicFile, + LineCount, + WordCount, + Cwd, + Binary, + Text, + User, + Shell, + ShellPid, + KeepPid, + DigestSha256, + DigestMd5, + 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() + } + + // Get the default options for this plugin + fn default_options(&self) -> std::collections::HashMap { + // Default implementation returns empty - plugins should override this + std::collections::HashMap::new() + } + + // Configure plugin with options (excluding outputs) + fn configure_options(&mut self, _options: &std::collections::HashMap) -> anyhow::Result<()> { + // Default implementation does nothing - plugins can override this + Ok(()) + } + + // Configure plugin outputs mapping + fn configure_outputs(&mut self, outputs: &std::collections::HashMap) -> anyhow::Result<()> { + for (key, value) in outputs { + self.outputs_mut().insert(key.clone(), value.clone()); + } + Ok(()) + } + + // Configure both options and outputs + fn configure(&mut self, options: &std::collections::HashMap, outputs: &std::collections::HashMap) -> anyhow::Result<()> { + self.configure_options(options)?; + self.configure_outputs(outputs)?; + Ok(()) + } + + // 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 { + 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::DigestSha256 => Box::new(DigestSha256MetaPlugin::new_simple()), + MetaPluginType::DigestMd5 => Box::new(MetaPluginProgram::new_simple("md5sum", vec![], "digest_md5".to_string(), true)), + MetaPluginType::ReadTime => Box::new(ReadTimeMetaPlugin::new_simple()), + MetaPluginType::ReadRate => Box::new(ReadRateMetaPlugin::new_simple()), + MetaPluginType::Hostname => Box::new(HostnameMetaPlugin::new_simple()), + } +}