From 15496345d9740bcfee57d9fec145e02395a32857 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Wed, 3 Sep 2025 09:33:39 -0300 Subject: [PATCH] feat: Implement registry for meta plugins Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) --- src/meta_plugin/cwd.rs | 10 ++++ src/meta_plugin/digest.rs | 10 ++++ src/meta_plugin/env.rs | 10 ++++ src/meta_plugin/exec.rs | 44 +++++++++++++++ src/meta_plugin/hostname.rs | 10 ++++ src/meta_plugin/keep_pid.rs | 10 ++++ src/meta_plugin/magic.rs | 10 ++++ src/meta_plugin/mod.rs | 103 +++++++++++++++++++---------------- src/meta_plugin/read_rate.rs | 10 ++++ src/meta_plugin/read_time.rs | 10 ++++ src/meta_plugin/shell.rs | 10 ++++ src/meta_plugin/shell_pid.rs | 10 ++++ src/meta_plugin/text.rs | 10 ++++ src/meta_plugin/user.rs | 10 ++++ 14 files changed, 219 insertions(+), 48 deletions(-) diff --git a/src/meta_plugin/cwd.rs b/src/meta_plugin/cwd.rs index d54e0fe..6eaa7c3 100644 --- a/src/meta_plugin/cwd.rs +++ b/src/meta_plugin/cwd.rs @@ -119,3 +119,13 @@ impl MetaPlugin for CwdMetaPlugin { self.base.options_mut() } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_cwd_plugin() { + register_meta_plugin(MetaPluginType::Cwd, |options, outputs| { + Box::new(CwdMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/digest.rs b/src/meta_plugin/digest.rs index 09e58b9..685580e 100644 --- a/src/meta_plugin/digest.rs +++ b/src/meta_plugin/digest.rs @@ -245,3 +245,13 @@ impl MetaPlugin for DigestMetaPlugin { } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_digest_plugin() { + register_meta_plugin(MetaPluginType::Digest, |options, outputs| { + Box::new(DigestMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/env.rs b/src/meta_plugin/env.rs index 6d87ce7..154935d 100644 --- a/src/meta_plugin/env.rs +++ b/src/meta_plugin/env.rs @@ -138,3 +138,13 @@ impl MetaPlugin for EnvMetaPlugin { panic!("options_mut() not implemented for EnvMetaPlugin") } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_env_plugin() { + register_meta_plugin(MetaPluginType::Env, |options, outputs| { + Box::new(EnvMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/exec.rs b/src/meta_plugin/exec.rs index 01228bf..d7425ab 100644 --- a/src/meta_plugin/exec.rs +++ b/src/meta_plugin/exec.rs @@ -225,3 +225,47 @@ impl MetaPlugin for MetaPluginExec { &mut self.options } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_exec_plugin() { + register_meta_plugin(MetaPluginType::Exec, |options, outputs| { + // For exec type, we need to parse the command from options + let mut program_name = String::new(); + let mut args = Vec::new(); + let mut meta_name = MetaPluginType::Exec.to_string(); + let mut split_whitespace = true; + + if let Some(opts) = &options { + if let Some(command_value) = opts.get("command") { + if let Some(command_str) = command_value.as_str() { + let parts: Vec<&str> = command_str.split_whitespace().collect(); + if !parts.is_empty() { + program_name = parts[0].to_string(); + args = parts[1..].iter().map(|s| s.to_string()).collect(); + } + } + } + // Handle other options if needed + if let Some(split_value) = opts.get("split_whitespace") { + if let Some(split_bool) = split_value.as_bool() { + split_whitespace = split_bool; + } + } + if let Some(name_value) = opts.get("name") { + if let Some(name_str) = name_value.as_str() { + meta_name = name_str.to_string(); + } + } + } + + Box::new(MetaPluginExec::new(&program_name, + args.iter().map(|s| s.as_str()).collect(), + meta_name, + split_whitespace, + options, + outputs)) + }); +} diff --git a/src/meta_plugin/hostname.rs b/src/meta_plugin/hostname.rs index 2f24986..0db2600 100644 --- a/src/meta_plugin/hostname.rs +++ b/src/meta_plugin/hostname.rs @@ -356,3 +356,13 @@ impl MetaPlugin for HostnameMetaPlugin { } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_hostname_plugin() { + register_meta_plugin(MetaPluginType::Hostname, |options, outputs| { + Box::new(HostnameMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/keep_pid.rs b/src/meta_plugin/keep_pid.rs index 772f4cc..28613af 100644 --- a/src/meta_plugin/keep_pid.rs +++ b/src/meta_plugin/keep_pid.rs @@ -136,3 +136,13 @@ impl MetaPlugin for KeepPidMetaPlugin { &mut self.options } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_keep_pid_plugin() { + register_meta_plugin(MetaPluginType::KeepPid, |options, outputs| { + Box::new(KeepPidMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/magic.rs b/src/meta_plugin/magic.rs index 964e8f3..ba3d8ed 100644 --- a/src/meta_plugin/magic.rs +++ b/src/meta_plugin/magic.rs @@ -234,3 +234,13 @@ impl MetaPlugin for MagicFileMetaPlugin { } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_magic_file_plugin() { + register_meta_plugin(MetaPluginType::MagicFile, |options, outputs| { + Box::new(MagicFileMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/mod.rs b/src/meta_plugin/mod.rs index 50a7dcd..2835055 100644 --- a/src/meta_plugin/mod.rs +++ b/src/meta_plugin/mod.rs @@ -244,6 +244,8 @@ pub trait MetaPlugin where Self: 'static { // Access to outputs mapping with default implementation fn outputs(&self) -> &std::collections::HashMap { use once_cell::sync::Lazy; + use std::collections::HashMap; + use std::sync::Mutex; static EMPTY: Lazy> = Lazy::new(|| std::collections::HashMap::new()); &EMPTY @@ -279,62 +281,67 @@ pub trait MetaPlugin where Self: 'static { } } +/// Global registry for meta plugins +static META_PLUGIN_REGISTRY: Lazy>, Option>) -> Box>>> = + Lazy::new(|| Mutex::new(HashMap::new())); + +/// Register a meta plugin with the global registry +pub fn register_meta_plugin( + meta_plugin_type: MetaPluginType, + constructor: fn(Option>, Option>) -> Box +) { + META_PLUGIN_REGISTRY.lock().unwrap().insert(meta_plugin_type, constructor); +} + pub fn get_meta_plugin( meta_plugin_type: MetaPluginType, options: Option>, outputs: Option>, ) -> Box { - match meta_plugin_type { - MetaPluginType::MagicFile => Box::new(MagicFileMetaPlugin::new(options, outputs)), - MetaPluginType::Cwd => Box::new(CwdMetaPlugin::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)), - MetaPluginType::Exec => { - // For exec type, we need to parse the command from options - let mut program_name = String::new(); - let mut args = Vec::new(); - let mut meta_name = MetaPluginType::Exec.to_string(); - let mut split_whitespace = true; - - if let Some(opts) = &options { - if let Some(command_value) = opts.get("command") { - if let Some(command_str) = command_value.as_str() { - let parts: Vec<&str> = command_str.split_whitespace().collect(); - if !parts.is_empty() { - program_name = parts[0].to_string(); - args = parts[1..].iter().map(|s| s.to_string()).collect(); - } - } - } - // Handle other options if needed - if let Some(split_value) = opts.get("split_whitespace") { - if let Some(split_bool) = split_value.as_bool() { - split_whitespace = split_bool; - } - } - if let Some(name_value) = opts.get("name") { - if let Some(name_str) = name_value.as_str() { - meta_name = name_str.to_string(); + let registry = META_PLUGIN_REGISTRY.lock().unwrap(); + if let Some(constructor) = registry.get(&meta_plugin_type) { + return constructor(options, outputs); + } + + // Fallback for exec plugin which needs special handling + if meta_plugin_type == MetaPluginType::Exec { + // For exec type, we need to parse the command from options + let mut program_name = String::new(); + let mut args = Vec::new(); + let mut meta_name = MetaPluginType::Exec.to_string(); + let mut split_whitespace = true; + + if let Some(opts) = &options { + if let Some(command_value) = opts.get("command") { + if let Some(command_str) = command_value.as_str() { + let parts: Vec<&str> = command_str.split_whitespace().collect(); + if !parts.is_empty() { + program_name = parts[0].to_string(); + args = parts[1..].iter().map(|s| s.to_string()).collect(); } } } - - Box::new(MetaPluginExec::new(&program_name, - args.iter().map(|s| s.as_str()).collect(), - meta_name, - split_whitespace, - options, - outputs)) - } - MetaPluginType::Env => { - Box::new(EnvMetaPlugin::new(options, outputs)) + // Handle other options if needed + if let Some(split_value) = opts.get("split_whitespace") { + if let Some(split_bool) = split_value.as_bool() { + split_whitespace = split_bool; + } + } + if let Some(name_value) = opts.get("name") { + if let Some(name_str) = name_value.as_str() { + meta_name = name_str.to_string(); + } + } } + + return Box::new(MetaPluginExec::new(&program_name, + args.iter().map(|s| s.as_str()).collect(), + meta_name, + split_whitespace, + options, + outputs)); } + + // Fallback for unknown plugins + panic!("Meta plugin {:?} not registered", meta_plugin_type); } diff --git a/src/meta_plugin/read_rate.rs b/src/meta_plugin/read_rate.rs index b857520..00ab673 100644 --- a/src/meta_plugin/read_rate.rs +++ b/src/meta_plugin/read_rate.rs @@ -135,3 +135,13 @@ impl MetaPlugin for ReadRateMetaPlugin { &mut self.options } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_read_rate_plugin() { + register_meta_plugin(MetaPluginType::ReadRate, |options, outputs| { + Box::new(ReadRateMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/read_time.rs b/src/meta_plugin/read_time.rs index eddffe1..1149e25 100644 --- a/src/meta_plugin/read_time.rs +++ b/src/meta_plugin/read_time.rs @@ -128,3 +128,13 @@ impl MetaPlugin for ReadTimeMetaPlugin { &mut self.options } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_read_time_plugin() { + register_meta_plugin(MetaPluginType::ReadTime, |options, outputs| { + Box::new(ReadTimeMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/shell.rs b/src/meta_plugin/shell.rs index fbd9ac2..e97f285 100644 --- a/src/meta_plugin/shell.rs +++ b/src/meta_plugin/shell.rs @@ -140,3 +140,13 @@ impl MetaPlugin for ShellMetaPlugin { &mut self.options } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// 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)) + }); +} diff --git a/src/meta_plugin/shell_pid.rs b/src/meta_plugin/shell_pid.rs index 21297e0..7751747 100644 --- a/src/meta_plugin/shell_pid.rs +++ b/src/meta_plugin/shell_pid.rs @@ -140,3 +140,13 @@ impl MetaPlugin for ShellPidMetaPlugin { &mut self.options } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_shell_pid_plugin() { + register_meta_plugin(MetaPluginType::ShellPid, |options, outputs| { + Box::new(ShellPidMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/text.rs b/src/meta_plugin/text.rs index 816eb5a..9cd9c46 100644 --- a/src/meta_plugin/text.rs +++ b/src/meta_plugin/text.rs @@ -639,3 +639,13 @@ impl MetaPlugin for TextMetaPlugin { } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_text_plugin() { + register_meta_plugin(MetaPluginType::Text, |options, outputs| { + Box::new(TextMetaPlugin::new(options, outputs)) + }); +} diff --git a/src/meta_plugin/user.rs b/src/meta_plugin/user.rs index 0159700..9166371 100644 --- a/src/meta_plugin/user.rs +++ b/src/meta_plugin/user.rs @@ -95,3 +95,13 @@ impl MetaPlugin for UserMetaPlugin { self.base.options_mut() } } +use crate::meta_plugin::register_meta_plugin; +use crate::meta_plugin::MetaPluginType; + +// Register the plugin at module initialization time +#[ctor::ctor] +fn register_user_plugin() { + register_meta_plugin(MetaPluginType::User, |options, outputs| { + Box::new(UserMetaPlugin::new(options, outputs)) + }); +}