use std::env; use crate::meta_plugin::{MetaPlugin, MetaPluginType}; #[derive(Debug, Clone, Default)] /// Meta plugin for capturing shell environment information. /// /// This plugin retrieves the current shell from the SHELL environment variable /// and provides it as metadata. It runs once during initialization and does not /// process input data. pub struct ShellMetaPlugin { is_finalized: bool, outputs: std::collections::HashMap, options: std::collections::HashMap, } impl ShellMetaPlugin { /// Creates a new ShellMetaPlugin instance. /// /// Initializes with default outputs and options, overridden by provided values. /// Defaults to "shell" as the output key. /// /// # Arguments /// /// * `_options` - Optional configuration options (unused currently). /// * `outputs` - Optional output mappings to override defaults. /// /// # Returns /// /// * `ShellMetaPlugin` - A new instance with processed options and outputs. /// /// # Examples /// /// ``` /// let plugin = ShellMetaPlugin::new(None, None); /// ``` pub fn new( _options: Option>, outputs: Option>, ) -> ShellMetaPlugin { // Start with default options let mut final_options = std::collections::HashMap::new(); if let Some(opts) = _options { for (key, value) in opts { final_options.insert(key, value); } } // Start with default outputs let mut final_outputs = std::collections::HashMap::new(); let default_outputs = Self::default().default_outputs(); for output_name in default_outputs { final_outputs.insert(output_name.clone(), serde_yaml::Value::String(output_name)); } if let Some(outs) = outputs { for (key, value) in outs { final_outputs.insert(key, value); } } ShellMetaPlugin { is_finalized: false, outputs: final_outputs, options: final_options, } } } impl MetaPlugin for ShellMetaPlugin { /// Checks if the plugin has been finalized. /// /// # Returns /// /// * `bool` - True if finalized, false otherwise. fn is_finalized(&self) -> bool { self.is_finalized } /// Sets the finalized state of the plugin. /// /// # Arguments /// /// * `finalized` - The new finalized state. fn set_finalized(&mut self, finalized: bool) { self.is_finalized = finalized; } /// Finalizes the plugin without processing data. /// /// For this plugin, finalization is handled in `initialize`, so this returns empty metadata. /// /// # Returns /// /// * `MetaPluginResponse` - Response with no metadata and finalized state. fn finalize(&mut self) -> crate::meta_plugin::MetaPluginResponse { // If already finalized, don't process again if self.is_finalized { return crate::meta_plugin::MetaPluginResponse { metadata: Vec::new(), is_finalized: true, }; } // Mark as finalized self.is_finalized = true; crate::meta_plugin::MetaPluginResponse { metadata: Vec::new(), is_finalized: true, } } /// Updates the plugin with data (not used for shell). /// /// Shell plugin doesn't process data streams; returns empty response unless not finalized. /// /// # Arguments /// /// * `_data` - Byte slice of input data (ignored). /// /// # Returns /// /// * `MetaPluginResponse` - Empty metadata response. fn update(&mut self, _data: &[u8]) -> crate::meta_plugin::MetaPluginResponse { // If already finalized, don't process more data if self.is_finalized { return crate::meta_plugin::MetaPluginResponse { metadata: Vec::new(), is_finalized: true, }; } crate::meta_plugin::MetaPluginResponse { metadata: Vec::new(), is_finalized: false, } } /// Returns the type of this meta plugin. /// /// # Returns /// /// * `MetaPluginType::Shell` - The shell plugin type. fn meta_type(&self) -> MetaPluginType { MetaPluginType::Shell } /// Initializes the plugin and extracts shell metadata. /// /// Retrieves the SHELL environment variable and adds it to metadata. /// Marks the plugin as finalized after one run. /// /// # Returns /// /// * `MetaPluginResponse` - Response with shell metadata and finalized state. /// /// # Examples /// /// ``` /// let mut plugin = ShellMetaPlugin::new(None, None); /// let response = plugin.initialize(); /// assert!(response.is_finalized); /// ``` fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse { // If already finalized, don't process again if self.is_finalized { return crate::meta_plugin::MetaPluginResponse { metadata: Vec::new(), is_finalized: true, }; } let mut metadata = Vec::new(); let shell = match env::var("SHELL") { Ok(shell) => shell, Err(_) => "unknown".to_string(), }; // Use process_metadata_outputs to handle output mapping if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs( "shell", serde_yaml::Value::String(shell), &self.outputs ) { metadata.push(meta_data); } // Mark as finalized since this plugin only needs to run once self.is_finalized = true; crate::meta_plugin::MetaPluginResponse { metadata, is_finalized: true, } } /// Returns a reference to the plugin's outputs. /// /// # Returns /// /// * `&HashMap` - The outputs map. fn outputs(&self) -> &std::collections::HashMap { &self.outputs } /// Returns a mutable reference to the plugin's outputs. /// /// # Returns /// /// * `&mut HashMap` - Mutable outputs map. fn outputs_mut(&mut self) -> &mut std::collections::HashMap { &mut self.outputs } /// Returns a reference to the plugin's options. /// /// # Returns /// /// * `&HashMap` - The options map. fn options(&self) -> &std::collections::HashMap { &self.options } /// Returns a mutable reference to the plugin's options. /// /// # Returns /// /// * `&mut HashMap` - Mutable options map. fn options_mut(&mut self) -> &mut std::collections::HashMap { &mut self.options } } /// Registers the shell meta plugin with the global registry. /// /// This constructor function is called at module load time using ctor crate. /// It creates the plugin with provided options and outputs. use crate::meta_plugin::register_meta_plugin; // Register the plugin at module initialization time #[ctor::ctor] fn register_shell_plugin() { register_meta_plugin(MetaPluginType::Shell, |options, outputs| { Box::new(ShellMetaPlugin::new(options, outputs)) }); }