feat: Implement registry for meta plugins

Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-09-03 09:33:39 -03:00
parent 21f195d8f6
commit 15496345d9
14 changed files with 219 additions and 48 deletions

View File

@@ -119,3 +119,13 @@ impl MetaPlugin for CwdMetaPlugin {
self.base.options_mut() 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))
});
}

View File

@@ -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))
});
}

View File

@@ -138,3 +138,13 @@ impl MetaPlugin for EnvMetaPlugin {
panic!("options_mut() not implemented 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))
});
}

View File

@@ -225,3 +225,47 @@ impl MetaPlugin for MetaPluginExec {
&mut self.options &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))
});
}

View File

@@ -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))
});
}

View File

@@ -136,3 +136,13 @@ impl MetaPlugin for KeepPidMetaPlugin {
&mut self.options &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))
});
}

View File

@@ -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))
});
}

View File

@@ -244,6 +244,8 @@ pub trait MetaPlugin where Self: 'static {
// Access to outputs mapping with default implementation // Access to outputs mapping with default implementation
fn outputs(&self) -> &std::collections::HashMap<String, serde_yaml::Value> { fn outputs(&self) -> &std::collections::HashMap<String, serde_yaml::Value> {
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::Mutex;
static EMPTY: Lazy<std::collections::HashMap<String, serde_yaml::Value>> = static EMPTY: Lazy<std::collections::HashMap<String, serde_yaml::Value>> =
Lazy::new(|| std::collections::HashMap::new()); Lazy::new(|| std::collections::HashMap::new());
&EMPTY &EMPTY
@@ -279,62 +281,67 @@ pub trait MetaPlugin where Self: 'static {
} }
} }
/// Global registry for meta plugins
static META_PLUGIN_REGISTRY: Lazy<Mutex<HashMap<MetaPluginType, fn(Option<HashMap<String, serde_yaml::Value>>, Option<HashMap<String, serde_yaml::Value>>) -> Box<dyn MetaPlugin>>>> =
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<HashMap<String, serde_yaml::Value>>, Option<HashMap<String, serde_yaml::Value>>) -> Box<dyn MetaPlugin>
) {
META_PLUGIN_REGISTRY.lock().unwrap().insert(meta_plugin_type, constructor);
}
pub fn get_meta_plugin( pub fn get_meta_plugin(
meta_plugin_type: MetaPluginType, meta_plugin_type: MetaPluginType,
options: Option<std::collections::HashMap<String, serde_yaml::Value>>, options: Option<std::collections::HashMap<String, serde_yaml::Value>>,
outputs: Option<std::collections::HashMap<String, serde_yaml::Value>>, outputs: Option<std::collections::HashMap<String, serde_yaml::Value>>,
) -> Box<dyn MetaPlugin> { ) -> Box<dyn MetaPlugin> {
match meta_plugin_type { let registry = META_PLUGIN_REGISTRY.lock().unwrap();
MetaPluginType::MagicFile => Box::new(MagicFileMetaPlugin::new(options, outputs)), if let Some(constructor) = registry.get(&meta_plugin_type) {
MetaPluginType::Cwd => Box::new(CwdMetaPlugin::new(options, outputs)), return constructor(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 { // Fallback for exec plugin which needs special handling
if let Some(command_value) = opts.get("command") { if meta_plugin_type == MetaPluginType::Exec {
if let Some(command_str) = command_value.as_str() { // For exec type, we need to parse the command from options
let parts: Vec<&str> = command_str.split_whitespace().collect(); let mut program_name = String::new();
if !parts.is_empty() { let mut args = Vec::new();
program_name = parts[0].to_string(); let mut meta_name = MetaPluginType::Exec.to_string();
args = parts[1..].iter().map(|s| s.to_string()).collect(); let mut split_whitespace = true;
}
} if let Some(opts) = &options {
} if let Some(command_value) = opts.get("command") {
// Handle other options if needed if let Some(command_str) = command_value.as_str() {
if let Some(split_value) = opts.get("split_whitespace") { let parts: Vec<&str> = command_str.split_whitespace().collect();
if let Some(split_bool) = split_value.as_bool() { if !parts.is_empty() {
split_whitespace = split_bool; program_name = parts[0].to_string();
} args = parts[1..].iter().map(|s| s.to_string()).collect();
}
if let Some(name_value) = opts.get("name") {
if let Some(name_str) = name_value.as_str() {
meta_name = name_str.to_string();
} }
} }
} }
// 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, return Box::new(MetaPluginExec::new(&program_name,
args.iter().map(|s| s.as_str()).collect(), args.iter().map(|s| s.as_str()).collect(),
meta_name, meta_name,
split_whitespace, split_whitespace,
options, options,
outputs)) outputs));
}
MetaPluginType::Env => {
Box::new(EnvMetaPlugin::new(options, outputs))
}
} }
// Fallback for unknown plugins
panic!("Meta plugin {:?} not registered", meta_plugin_type);
} }

View File

@@ -135,3 +135,13 @@ impl MetaPlugin for ReadRateMetaPlugin {
&mut self.options &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))
});
}

View File

@@ -128,3 +128,13 @@ impl MetaPlugin for ReadTimeMetaPlugin {
&mut self.options &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))
});
}

View File

@@ -140,3 +140,13 @@ impl MetaPlugin for ShellMetaPlugin {
&mut self.options &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))
});
}

View File

@@ -140,3 +140,13 @@ impl MetaPlugin for ShellPidMetaPlugin {
&mut self.options &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))
});
}

View File

@@ -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))
});
}

View File

@@ -95,3 +95,13 @@ impl MetaPlugin for UserMetaPlugin {
self.base.options_mut() 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))
});
}