use crate::config::types::PluginConfig;
use crate::plugin::Plugin;
use std::sync::Arc;
use tracing::debug;
pub trait PluginFactory: Send + Sync {
fn create(&self, config: &PluginConfig) -> crate::Result<Arc<dyn Plugin>>;
fn plugin_type(&self) -> &'static str;
fn aliases(&self) -> &'static [&'static str];
}
pub trait ExecPluginFactory: Send + Sync {
fn create(&self, prefix: &str, exec_str: &str) -> crate::Result<Arc<dyn Plugin>>;
fn plugin_type(&self) -> &'static str;
fn aliases(&self) -> &'static [&'static str];
}
#[linkme::distributed_slice]
pub static PLUGIN_FACTORIES_SLICE: [fn() -> Arc<dyn PluginFactory>];
#[linkme::distributed_slice]
pub static EXEC_PLUGIN_FACTORIES_SLICE: [fn() -> Arc<dyn ExecPluginFactory>];
pub fn get_plugin_factory(plugin_type: &str) -> Option<Arc<dyn PluginFactory>> {
for factory_constructor in PLUGIN_FACTORIES_SLICE {
let factory = factory_constructor();
if factory.plugin_type() == plugin_type {
return Some(factory);
}
for alias in factory.aliases() {
if *alias == plugin_type {
return Some(factory);
}
}
}
None
}
pub fn get_exec_plugin_factory(plugin_type: &str) -> Option<Arc<dyn ExecPluginFactory>> {
for factory_constructor in EXEC_PLUGIN_FACTORIES_SLICE {
let factory = factory_constructor();
if factory.plugin_type() == plugin_type {
return Some(factory);
}
for alias in factory.aliases() {
if *alias == plugin_type {
return Some(factory);
}
}
}
None
}
pub fn get_all_plugin_types() -> Vec<String> {
let mut types: Vec<String> = PLUGIN_FACTORIES_SLICE
.iter()
.flat_map(|factory_constructor| {
let factory = factory_constructor();
let mut names = vec![factory.plugin_type().to_string()];
names.extend(factory.aliases().iter().map(|s| s.to_string()));
names
})
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
types.sort();
types
}
pub fn get_all_exec_plugin_types() -> Vec<String> {
let mut types: Vec<String> = EXEC_PLUGIN_FACTORIES_SLICE
.iter()
.flat_map(|factory_constructor| {
let factory = factory_constructor();
let mut names = vec![factory.plugin_type().to_string()];
names.extend(factory.aliases().iter().map(|s| s.to_string()));
names
})
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
types.sort();
types
}
pub fn init() {
initialize_all_plugin_factories();
initialize_all_exec_plugin_factories();
}
pub fn initialize_all_plugin_factories() {
use once_cell::sync::OnceCell;
static INIT: OnceCell<()> = OnceCell::new();
INIT.get_or_init(|| {
let _ = PLUGIN_FACTORIES_SLICE.len();
let types = get_all_plugin_types();
debug!("Initialized {} plugin factories: {:?}", types.len(), types);
});
}
fn initialize_all_exec_plugin_factories() {
use once_cell::sync::OnceCell;
static EXEC_INIT: OnceCell<()> = OnceCell::new();
EXEC_INIT.get_or_init(|| {
let _ = EXEC_PLUGIN_FACTORIES_SLICE.len();
let types = get_all_exec_plugin_types();
debug!(
"Initialized {} exec plugin factories: {:?}",
types.len(),
types
);
});
}
#[macro_export]
#[deprecated(since = "0.2.61", note = "Use #[derive(RegisterPlugin)] instead.")]
macro_rules! register_plugin_builder {
($plugin_type:ty) => {
compile_error!(
"register_plugin_builder! is deprecated. Use #[derive(RegisterPlugin)] instead."
);
};
}
#[macro_export]
#[deprecated(since = "0.2.61", note = "Use #[derive(RegisterExecPlugin)] instead.")]
macro_rules! register_exec_plugin_builder {
($plugin_type:ty) => {
compile_error!("register_exec_plugin_builder! is deprecated. Use #[derive(RegisterExecPlugin)] instead.");
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::types::PluginConfig;
use crate::plugin::Context;
use crate::{RegisterExecPlugin, RegisterPlugin};
use async_trait::async_trait;
use std::sync::Arc;
#[test]
fn test_derive_register_plugin_discovery() {
#[derive(Debug, RegisterPlugin)]
struct MyMacroPlugin;
#[async_trait]
impl crate::plugin::traits::Plugin for MyMacroPlugin {
async fn execute(&self, _ctx: &mut Context) -> crate::Result<()> {
Ok(())
}
fn name(&self) -> &str {
"my_macro_plugin"
}
fn init(_config: &PluginConfig) -> crate::Result<Arc<dyn crate::plugin::Plugin>> {
Ok(Arc::new(MyMacroPlugin))
}
}
initialize_all_plugin_factories();
let types = get_all_plugin_types();
assert!(
types.iter().any(|t| t == "my_macro"),
"Expected derived factory name 'my_macro' present"
);
assert!(
get_plugin_factory("my_macro").is_some(),
"Factory for 'my_macro' should be found"
);
let factory = get_plugin_factory("my_macro").unwrap();
let plugin = factory
.create(&PluginConfig::new("my_macro".to_string()))
.unwrap();
assert_eq!(plugin.name(), "my_macro_plugin");
}
#[test]
fn test_derive_register_exec_plugin_discovery() {
#[derive(Debug, RegisterExecPlugin)]
struct MyExecPlugin;
impl crate::plugin::traits::ExecPlugin for MyExecPlugin {
fn quick_setup(
_prefix: &str,
_exec_str: &str,
) -> crate::Result<Arc<dyn crate::plugin::Plugin>> {
Ok(Arc::new(MyExecPlugin))
}
}
#[async_trait]
impl crate::plugin::traits::Plugin for MyExecPlugin {
async fn execute(&self, _ctx: &mut Context) -> crate::Result<()> {
Ok(())
}
fn name(&self) -> &str {
"my_exec_plugin"
}
fn init(_config: &PluginConfig) -> crate::Result<Arc<dyn crate::plugin::Plugin>> {
Err(crate::Error::Config("not supported".to_string()))
}
fn aliases() -> &'static [&'static str] {
&[]
}
}
initialize_all_exec_plugin_factories();
let exec_types = get_all_exec_plugin_types();
assert!(
exec_types.iter().any(|t| t == "my_exec"),
"Expected exec factory name 'my_exec' present"
);
assert!(
get_exec_plugin_factory("my_exec").is_some(),
"Exec factory for 'my_exec' should be found"
);
let exec_factory = get_exec_plugin_factory("my_exec").unwrap();
let plugin = exec_factory.create("my_exec", "arg").unwrap();
assert_eq!(plugin.name(), "my_exec_plugin");
}
#[test]
fn test_alias_matching_plugin_factory() {
#[derive(Debug, RegisterPlugin)]
struct MyAliasPlugin;
#[async_trait]
impl crate::plugin::traits::Plugin for MyAliasPlugin {
async fn execute(&self, _ctx: &mut Context) -> crate::Result<()> {
Ok(())
}
fn name(&self) -> &str {
"my_alias_plugin"
}
fn init(_config: &PluginConfig) -> crate::Result<Arc<dyn crate::plugin::Plugin>> {
Ok(Arc::new(MyAliasPlugin))
}
fn aliases() -> &'static [&'static str] {
&["alias_one", "other"]
}
}
initialize_all_plugin_factories();
assert!(get_plugin_factory("my_alias").is_some());
assert!(get_plugin_factory("alias_one").is_some());
assert!(get_plugin_factory("other").is_some());
let f1 = get_plugin_factory("alias_one").unwrap();
let inst = f1
.create(&PluginConfig::new("alias_one".to_string()))
.unwrap();
assert_eq!(inst.name(), "my_alias_plugin");
}
#[test]
fn test_alias_matching_exec_plugin_factory() {
#[derive(Debug, RegisterExecPlugin)]
struct MyAliasExecPlugin;
impl crate::plugin::traits::ExecPlugin for MyAliasExecPlugin {
fn quick_setup(
_prefix: &str,
_exec_str: &str,
) -> crate::Result<Arc<dyn crate::plugin::Plugin>> {
Ok(Arc::new(MyAliasExecPlugin))
}
}
#[async_trait]
impl crate::plugin::traits::Plugin for MyAliasExecPlugin {
async fn execute(&self, _ctx: &mut Context) -> crate::Result<()> {
Ok(())
}
fn name(&self) -> &str {
"my_alias_exec_plugin"
}
fn init(_config: &PluginConfig) -> crate::Result<Arc<dyn crate::plugin::Plugin>> {
Err(crate::Error::Config("not supported".to_string()))
}
fn aliases() -> &'static [&'static str] {
&["alias_exec"]
}
}
initialize_all_exec_plugin_factories();
assert!(get_exec_plugin_factory("my_alias_exec").is_some());
assert!(get_exec_plugin_factory("alias_exec").is_some());
let f = get_exec_plugin_factory("alias_exec").unwrap();
let p = f.create("alias_exec", "x").unwrap();
assert_eq!(p.name(), "my_alias_exec_plugin");
}
}