use crate::errors::{Result, TrustformersError};
use crate::plugins::{Plugin, PluginInfo};
use std::collections::HashMap;
use std::path::Path;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub struct PluginLoader {
library_cache: Arc<Mutex<HashMap<String, LibraryHandle>>>,
static_plugins: Arc<Mutex<HashMap<String, StaticPluginFactory>>>,
cache_hits: Arc<Mutex<u64>>,
cache_misses: Arc<Mutex<u64>>,
#[allow(dead_code)]
config: LoaderConfig,
}
impl PluginLoader {
pub fn new() -> Self {
Self {
library_cache: Arc::new(Mutex::new(HashMap::new())),
static_plugins: Arc::new(Mutex::new(HashMap::new())),
cache_hits: Arc::new(Mutex::new(0)),
cache_misses: Arc::new(Mutex::new(0)),
config: LoaderConfig::default(),
}
}
pub fn with_config(config: LoaderConfig) -> Self {
Self {
library_cache: Arc::new(Mutex::new(HashMap::new())),
static_plugins: Arc::new(Mutex::new(HashMap::new())),
cache_hits: Arc::new(Mutex::new(0)),
cache_misses: Arc::new(Mutex::new(0)),
config,
}
}
pub fn load_plugin_info<P: AsRef<Path>>(&self, path: P) -> Result<PluginInfo> {
let path = path.as_ref();
let metadata_path = path.with_extension("json");
if metadata_path.exists() {
return self.load_metadata_file(&metadata_path);
}
self.load_embedded_metadata(path)
}
pub fn load_plugin(&self, info: &PluginInfo) -> Result<Box<dyn Plugin>> {
if let Ok(static_plugins) = self.static_plugins.lock() {
if let Some(factory) = static_plugins.get(info.name()) {
return factory();
}
}
self.load_dynamic_plugin(info)
}
pub fn register_static_plugin(&self, name: &str, factory: StaticPluginFactory) -> Result<()> {
let mut static_plugins = self
.static_plugins
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
static_plugins.insert(name.to_string(), factory);
Ok(())
}
pub fn unload_library(&self, name: &str) -> Result<()> {
let mut cache = self
.library_cache
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
cache.remove(name);
Ok(())
}
pub fn clear_cache(&self) -> Result<()> {
let mut cache = self
.library_cache
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
cache.clear();
Ok(())
}
pub fn stats(&self) -> Result<LoaderStats> {
let cache = self
.library_cache
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
let static_plugins = self
.static_plugins
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
let cache_hits = self
.cache_hits
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
let cache_misses = self
.cache_misses
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
Ok(LoaderStats {
cached_libraries: cache.len(),
static_plugins: static_plugins.len(),
cache_hits: *cache_hits,
cache_misses: *cache_misses,
})
}
fn load_metadata_file<P: AsRef<Path>>(&self, path: P) -> Result<PluginInfo> {
let content = std::fs::read_to_string(path)
.map_err(|e| TrustformersError::io_error(format!("Failed to read metadata: {}", e)))?;
serde_json::from_str(&content)
.map_err(|e| TrustformersError::serialization_error(format!("Invalid metadata: {}", e)))
}
fn load_embedded_metadata<P: AsRef<Path>>(&self, path: P) -> Result<PluginInfo> {
let path = path.as_ref();
let name = path.file_stem().and_then(|s| s.to_str()).ok_or_else(|| {
TrustformersError::plugin_error("Invalid plugin filename".to_string())
})?;
Ok(PluginInfo::new(
name,
"1.0.0",
"Dynamically loaded plugin",
&[],
))
}
fn load_dynamic_plugin(&self, info: &PluginInfo) -> Result<Box<dyn Plugin>> {
{
let cache = self
.library_cache
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
if let Some(handle) = cache.get(info.name()) {
if let Ok(mut hits) = self.cache_hits.lock() {
*hits += 1;
}
return handle.create_plugin();
}
}
if let Ok(mut misses) = self.cache_misses.lock() {
*misses += 1;
}
let handle = LibraryHandle::load(info)?;
let plugin = handle.create_plugin()?;
{
let mut cache = self
.library_cache
.lock()
.map_err(|_| TrustformersError::lock_error("Failed to acquire lock".to_string()))?;
cache.insert(info.name().to_string(), handle);
}
Ok(plugin)
}
}
impl Default for PluginLoader {
fn default() -> Self {
Self::new()
}
}
pub type StaticPluginFactory = fn() -> Result<Box<dyn Plugin>>;
#[derive(Debug)]
struct LibraryHandle {
#[allow(dead_code)]
name: String,
_entry_point: String,
}
impl LibraryHandle {
fn load(info: &PluginInfo) -> Result<Self> {
Ok(Self {
name: info.name().to_string(),
_entry_point: info.entry_point().to_string(),
})
}
fn create_plugin(&self) -> Result<Box<dyn Plugin>> {
Err(TrustformersError::plugin_error(
"Dynamic plugin loading not implemented in this example".to_string(),
))
}
}
#[derive(Debug, Clone)]
pub struct LoaderConfig {
pub cache_enabled: bool,
pub max_cached_libraries: usize,
pub load_timeout_secs: u64,
pub lazy_loading: bool,
pub symbol_prefix: String,
}
impl Default for LoaderConfig {
fn default() -> Self {
Self {
cache_enabled: true,
max_cached_libraries: 50,
load_timeout_secs: 30,
lazy_loading: true,
symbol_prefix: "create_plugin".to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct LoaderStats {
pub cached_libraries: usize,
pub static_plugins: usize,
pub cache_hits: u64,
pub cache_misses: u64,
}
#[derive(Debug, Clone)]
pub enum LoadError {
LibraryNotFound(String),
SymbolNotFound(String),
InitializationFailed(String),
InvalidFormat(String),
VersionMismatch(String),
DependencyNotSatisfied(String),
}
impl std::fmt::Display for LoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LoadError::LibraryNotFound(path) => write!(f, "Library not found: {}", path),
LoadError::SymbolNotFound(symbol) => write!(f, "Symbol not found: {}", symbol),
LoadError::InitializationFailed(msg) => write!(f, "Initialization failed: {}", msg),
LoadError::InvalidFormat(msg) => write!(f, "Invalid format: {}", msg),
LoadError::VersionMismatch(msg) => write!(f, "Version mismatch: {}", msg),
LoadError::DependencyNotSatisfied(dep) => {
write!(f, "Dependency not satisfied: {}", dep)
},
}
}
}
impl std::error::Error for LoadError {}
#[macro_export]
macro_rules! register_static_plugin {
($plugin_type:ty, $name:expr) => {
pub fn register_plugin() -> $crate::errors::Result<Box<dyn $crate::plugins::Plugin>> {
Ok(Box::new(<$plugin_type>::default()))
}
#[cfg(feature = "static-plugins")]
#[ctor::ctor]
fn register() {
use $crate::plugins::PluginLoader;
let loader = PluginLoader::new();
let _ = loader.register_static_plugin($name, register_plugin);
}
};
}