use super::core_traits::Plugin;
use super::types_config::{PluginCategory, PluginMetadata};
use crate::error::{Result, SklearsError};
use std::any::TypeId;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
#[derive(Debug)]
pub struct PluginRegistry {
plugins: Arc<RwLock<HashMap<String, Box<dyn Plugin>>>>,
metadata_cache: Arc<RwLock<HashMap<String, PluginMetadata>>>,
category_index: Arc<RwLock<HashMap<PluginCategory, Vec<String>>>>,
type_index: Arc<RwLock<HashMap<TypeId, Vec<String>>>>,
}
impl PluginRegistry {
pub fn new() -> Self {
Self {
plugins: Arc::new(RwLock::new(HashMap::new())),
metadata_cache: Arc::new(RwLock::new(HashMap::new())),
category_index: Arc::new(RwLock::new(HashMap::new())),
type_index: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn register(&self, id: &str, plugin: Box<dyn Plugin>) -> Result<()> {
let metadata = plugin.metadata();
self.validate_plugin(&metadata)?;
self.update_indices(id, &metadata)?;
{
let mut plugins = self.plugins.write().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire plugin registry lock".to_string())
})?;
plugins.insert(id.to_string(), plugin);
}
{
let mut cache = self.metadata_cache.write().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire metadata cache lock".to_string())
})?;
cache.insert(id.to_string(), metadata);
}
Ok(())
}
pub fn unregister(&self, id: &str) -> Result<()> {
let mut plugin = {
let mut plugins = self.plugins.write().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire plugin registry lock".to_string())
})?;
plugins.remove(id)
};
if let Some(ref mut plugin) = plugin {
plugin.cleanup()?;
}
let metadata = {
let mut cache = self.metadata_cache.write().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire metadata cache lock".to_string())
})?;
cache.remove(id)
};
if let Some(metadata) = metadata {
self.remove_from_indices(id, &metadata)?;
}
Ok(())
}
pub fn get_plugin(&self, id: &str) -> Result<Arc<RwLock<Box<dyn Plugin>>>> {
let plugins = self.plugins.read().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire plugin registry lock".to_string())
})?;
if plugins.contains_key(id) {
Err(SklearsError::InvalidOperation(
"Plugin access needs proper lifetime management".to_string(),
))
} else {
Err(SklearsError::InvalidOperation(format!(
"Plugin '{id}' not found"
)))
}
}
pub fn list_plugins(&self) -> Result<Vec<String>> {
let plugins = self.plugins.read().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire plugin registry lock".to_string())
})?;
Ok(plugins.keys().cloned().collect())
}
pub fn get_plugins_by_category(&self, category: &PluginCategory) -> Result<Vec<String>> {
let index = self.category_index.read().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire category index lock".to_string())
})?;
Ok(index.get(category).cloned().unwrap_or_default())
}
pub fn get_compatible_plugins(&self, type_id: TypeId) -> Result<Vec<String>> {
let index = self.type_index.read().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire type index lock".to_string())
})?;
Ok(index.get(&type_id).cloned().unwrap_or_default())
}
pub fn get_metadata(&self, id: &str) -> Result<PluginMetadata> {
let cache = self.metadata_cache.read().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire metadata cache lock".to_string())
})?;
cache
.get(id)
.cloned()
.ok_or_else(|| SklearsError::InvalidOperation(format!("Plugin '{id}' not found")))
}
pub fn search_plugins(&self, query: &str) -> Result<Vec<String>> {
let cache = self.metadata_cache.read().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire metadata cache lock".to_string())
})?;
let query_lower = query.to_lowercase();
let matches: Vec<String> = cache
.iter()
.filter(|(_, metadata)| {
metadata.name.to_lowercase().contains(&query_lower)
|| metadata.description.to_lowercase().contains(&query_lower)
})
.map(|(id, _)| id.clone())
.collect();
Ok(matches)
}
pub fn get_statistics(&self) -> Result<HashMap<String, usize>> {
let plugins = self.plugins.read().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire plugin registry lock".to_string())
})?;
let category_index = self.category_index.read().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire category index lock".to_string())
})?;
let mut stats = HashMap::new();
stats.insert("total_plugins".to_string(), plugins.len());
stats.insert("categories".to_string(), category_index.len());
Ok(stats)
}
fn validate_plugin(&self, metadata: &PluginMetadata) -> Result<()> {
if metadata.name.is_empty() {
return Err(SklearsError::InvalidOperation(
"Plugin name cannot be empty".to_string(),
));
}
if metadata.version.is_empty() {
return Err(SklearsError::InvalidOperation(
"Plugin version cannot be empty".to_string(),
));
}
if metadata.author.is_empty() {
return Err(SklearsError::InvalidOperation(
"Plugin author cannot be empty".to_string(),
));
}
if !metadata.version.chars().any(|c| c.is_ascii_digit()) {
return Err(SklearsError::InvalidOperation(
"Plugin version must contain at least one digit".to_string(),
));
}
Ok(())
}
fn update_indices(&self, id: &str, metadata: &PluginMetadata) -> Result<()> {
{
let mut index = self.category_index.write().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire category index lock".to_string())
})?;
index
.entry(metadata.category.clone())
.or_insert_with(Vec::new)
.push(id.to_string());
}
{
let mut index = self.type_index.write().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire type index lock".to_string())
})?;
for type_id in &metadata.supported_types {
index
.entry(*type_id)
.or_insert_with(Vec::new)
.push(id.to_string());
}
}
Ok(())
}
fn remove_from_indices(&self, id: &str, metadata: &PluginMetadata) -> Result<()> {
{
let mut index = self.category_index.write().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire category index lock".to_string())
})?;
if let Some(plugins) = index.get_mut(&metadata.category) {
plugins.retain(|plugin_id| plugin_id != id);
if plugins.is_empty() {
index.remove(&metadata.category);
}
}
}
{
let mut index = self.type_index.write().map_err(|_| {
SklearsError::InvalidOperation("Failed to acquire type index lock".to_string())
})?;
for type_id in &metadata.supported_types {
if let Some(plugins) = index.get_mut(type_id) {
plugins.retain(|plugin_id| plugin_id != id);
if plugins.is_empty() {
index.remove(type_id);
}
}
}
}
Ok(())
}
pub fn is_registered(&self, id: &str) -> bool {
if let Ok(plugins) = self.plugins.read() {
plugins.contains_key(id)
} else {
false
}
}
pub fn plugin_count(&self) -> usize {
if let Ok(plugins) = self.plugins.read() {
plugins.len()
} else {
0
}
}
}
impl Default for PluginRegistry {
fn default() -> Self {
Self::new()
}
}
impl Clone for PluginRegistry {
fn clone(&self) -> Self {
Self::new()
}
}