use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use crate::errors::NoosResult;
use crate::kernel::events::EventBus;
pub struct KernelServices {
pub events: Arc<EventBus>,
}
#[async_trait]
pub trait SemanticPlugin: Send + Sync {
fn id(&self) -> &str;
fn name(&self) -> &str;
fn version(&self) -> &str;
fn description(&self) -> &str;
async fn initialize(&self, services: &KernelServices) -> NoosResult<()>;
async fn destroy(&self) -> NoosResult<()>;
fn capabilities(&self) -> Vec<PluginCapability> {
vec![]
}
}
#[derive(Debug, Clone)]
pub enum PluginCapability {
Primitive { id: String, name: String },
Composition { id: String, name: String },
Dimension { id: String, label: String },
Theory { id: String, name: String },
}
pub struct PluginRegistry {
plugins: Vec<Arc<dyn SemanticPlugin>>,
capabilities: Vec<(String, PluginCapability)>, plugin_map: HashMap<String, usize>, }
impl PluginRegistry {
pub fn new() -> Self {
Self {
plugins: Vec::new(),
capabilities: Vec::new(),
plugin_map: HashMap::new(),
}
}
pub fn register(&mut self, plugin: Arc<dyn SemanticPlugin>) {
let id = plugin.id().to_string();
let index = self.plugins.len();
for cap in plugin.capabilities() {
self.capabilities.push((id.clone(), cap));
}
self.plugin_map.insert(id, index);
self.plugins.push(plugin);
}
pub fn get(&self, id: &str) -> Option<&Arc<dyn SemanticPlugin>> {
self.plugin_map.get(id).map(|&i| &self.plugins[i])
}
pub fn all(&self) -> &[Arc<dyn SemanticPlugin>] {
&self.plugins
}
pub fn find_primitives(&self) -> Vec<(&str, &str)> {
self.capabilities
.iter()
.filter_map(|(_plugin_id, cap)| match cap {
PluginCapability::Primitive { id, name } => Some((id.as_str(), name.as_str())),
_ => None,
})
.collect()
}
pub fn find_compositions(&self) -> Vec<(&str, &str)> {
self.capabilities
.iter()
.filter_map(|(_plugin_id, cap)| match cap {
PluginCapability::Composition { id, name } => Some((id.as_str(), name.as_str())),
_ => None,
})
.collect()
}
pub fn find_dimensions(&self) -> Vec<(&str, &str)> {
self.capabilities
.iter()
.filter_map(|(_plugin_id, cap)| match cap {
PluginCapability::Dimension { id, label } => Some((id.as_str(), label.as_str())),
_ => None,
})
.collect()
}
pub fn count(&self) -> usize {
self.plugins.len()
}
}
impl Default for PluginRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestPlugin {
id: String,
}
#[async_trait]
impl SemanticPlugin for TestPlugin {
fn id(&self) -> &str { &self.id }
fn name(&self) -> &str { "Test Plugin" }
fn version(&self) -> &str { "0.1.0" }
fn description(&self) -> &str { "A test plugin" }
async fn initialize(&self, _services: &KernelServices) -> NoosResult<()> { Ok(()) }
async fn destroy(&self) -> NoosResult<()> { Ok(()) }
fn capabilities(&self) -> Vec<PluginCapability> {
vec![PluginCapability::Theory {
id: "test-theory".into(),
name: "Test Theory".into(),
}]
}
}
#[test]
fn register_and_lookup() {
let mut registry = PluginRegistry::new();
let plugin = Arc::new(TestPlugin { id: "test".into() });
registry.register(plugin);
assert_eq!(registry.count(), 1);
assert!(registry.get("test").is_some());
assert!(registry.get("nonexistent").is_none());
}
#[test]
fn capability_indexing() {
let mut registry = PluginRegistry::new();
registry.register(Arc::new(TestPlugin { id: "p1".into() }));
let dims = registry.find_dimensions();
assert!(dims.is_empty());
let primitives = registry.find_primitives();
assert!(primitives.is_empty()); }
}