use super::registry::PluginRegistry;
#[cfg(feature = "dynamic_loading")]
use super::Plugin;
use crate::error::{Result, SklearsError};
#[cfg(feature = "dynamic_loading")]
use std::collections::HashMap;
use std::sync::Arc;
#[allow(dead_code)]
#[derive(Debug)]
pub struct PluginLoader {
#[cfg(feature = "dynamic_loading")]
libraries: HashMap<String, libloading::Library>,
registry: Arc<PluginRegistry>,
}
impl PluginLoader {
pub fn new(registry: Arc<PluginRegistry>) -> Self {
Self {
#[cfg(feature = "dynamic_loading")]
libraries: HashMap::new(),
registry,
}
}
#[cfg(feature = "dynamic_loading")]
pub fn load_from_library(&mut self, library_path: &str, plugin_id: &str) -> Result<()> {
unsafe {
let lib = libloading::Library::new(library_path).map_err(|e| {
SklearsError::InvalidOperation(format!(
"Failed to load library '{}': {}",
library_path, e
))
})?;
let create_plugin: libloading::Symbol<fn() -> Box<dyn Plugin>> =
lib.get(b"create_plugin").map_err(|e| {
SklearsError::InvalidOperation(format!(
"Failed to get create_plugin symbol from '{}': {}",
library_path, e
))
})?;
let plugin = create_plugin();
let _plugin_metadata = plugin.metadata();
if plugin.id() != plugin_id {
return Err(SklearsError::InvalidOperation(format!(
"Plugin ID mismatch: expected '{}', got '{}'",
plugin_id,
plugin.id()
)));
}
self.registry.register(plugin_id, plugin).map_err(|e| {
SklearsError::InvalidOperation(format!(
"Failed to register plugin '{}': {}",
plugin_id, e
))
})?;
self.libraries.insert(plugin_id.to_string(), lib);
Ok(())
}
}
#[cfg(feature = "dynamic_loading")]
pub fn unload_library(&mut self, plugin_id: &str) -> Result<()> {
self.registry.unregister(plugin_id).map_err(|e| {
SklearsError::InvalidOperation(format!(
"Failed to unregister plugin '{}': {}",
plugin_id, e
))
})?;
if self.libraries.remove(plugin_id).is_none() {
return Err(SklearsError::InvalidOperation(format!(
"Library for plugin '{}' was not found",
plugin_id
)));
}
Ok(())
}
#[cfg(feature = "dynamic_loading")]
pub fn load_from_directory(&mut self, directory: &str) -> Result<Vec<String>> {
use std::fs;
let entries = fs::read_dir(directory).map_err(|e| {
SklearsError::InvalidOperation(format!(
"Failed to read directory '{}': {}",
directory, e
))
})?;
let mut loaded_plugins = Vec::new();
for entry in entries {
let entry = entry.map_err(|e| {
SklearsError::InvalidOperation(format!("Failed to read directory entry: {}", e))
})?;
let path = entry.path();
if path.is_file() {
if let Some(extension) = path.extension() {
let ext_str = extension.to_string_lossy().to_lowercase();
if ext_str == "so" || ext_str == "dll" || ext_str == "dylib" {
if let Some(plugin_id) = path.file_stem().and_then(|s| s.to_str()) {
match self.load_from_library(
path.to_str().expect("load_from_library should succeed"),
plugin_id,
) {
Ok(()) => {
loaded_plugins.push(plugin_id.to_string());
println!("Successfully loaded plugin: {}", plugin_id);
}
Err(e) => {
eprintln!(
"Failed to load plugin '{}' from '{}': {}",
plugin_id,
path.display(),
e
);
}
}
} else {
eprintln!(
"Could not determine plugin ID from filename: {}",
path.display()
);
}
}
}
}
}
Ok(loaded_plugins)
}
#[cfg(feature = "dynamic_loading")]
pub fn get_loaded_libraries(&self) -> Vec<String> {
self.libraries.keys().cloned().collect()
}
#[cfg(feature = "dynamic_loading")]
pub fn is_library_loaded(&self, plugin_id: &str) -> bool {
self.libraries.contains_key(plugin_id)
}
pub fn registry(&self) -> &Arc<PluginRegistry> {
&self.registry
}
#[cfg(feature = "dynamic_loading")]
pub fn unload_all(&mut self) -> Result<()> {
let plugin_ids: Vec<String> = self.libraries.keys().cloned().collect();
for plugin_id in plugin_ids.into_iter() {
if let Err(e) = self.unload_library(&plugin_id) {
eprintln!("Failed to unload plugin '{}': {}", plugin_id, e);
}
}
Ok(())
}
#[cfg(feature = "dynamic_loading")]
pub fn get_statistics(&self) -> (usize, Vec<String>) {
let plugin_ids = self.get_loaded_libraries();
(plugin_ids.len(), plugin_ids)
}
}
#[cfg(not(feature = "dynamic_loading"))]
impl PluginLoader {
pub fn load_from_library(&mut self, _library_path: &str, _plugin_id: &str) -> Result<()> {
Err(SklearsError::InvalidOperation(
"Dynamic loading is not enabled. Rebuild with the 'dynamic_loading' feature to use this functionality.".to_string()
))
}
pub fn unload_library(&mut self, _plugin_id: &str) -> Result<()> {
Err(SklearsError::InvalidOperation(
"Dynamic loading is not enabled. Rebuild with the 'dynamic_loading' feature to use this functionality.".to_string()
))
}
pub fn load_from_directory(&mut self, _directory: &str) -> Result<Vec<String>> {
Err(SklearsError::InvalidOperation(
"Dynamic loading is not enabled. Rebuild with the 'dynamic_loading' feature to use this functionality.".to_string()
))
}
}