use std::path::Path;
use tokio::sync::broadcast;
use crate::{
error::ClawDBResult,
plugins::{events::ClawEvent, interface::ClawPlugin},
};
pub struct PluginManager {
plugins: Vec<Box<dyn ClawPlugin>>,
event_tx: broadcast::Sender<ClawEvent>,
}
impl PluginManager {
pub fn new() -> (Self, broadcast::Receiver<ClawEvent>) {
let (event_tx, event_rx) = broadcast::channel(256);
(
Self {
plugins: Vec::new(),
event_tx,
},
event_rx,
)
}
pub fn load_from_dir(&mut self, dir: &Path) -> ClawDBResult<usize> {
if !dir.exists() {
return Ok(0);
}
let mut loaded = 0usize;
for entry in std::fs::read_dir(dir)? {
let path = entry?.path();
if !is_dynamic_library(&path) {
continue;
}
unsafe {
let library = libloading::Library::new(&path).map_err(|error| {
crate::error::ClawDBError::ComponentInit("plugin", error.to_string())
})?;
let constructor: libloading::Symbol<unsafe fn() -> Box<dyn ClawPlugin>> =
library.get(b"clawdb_create_plugin").map_err(|error| {
crate::error::ClawDBError::ComponentInit("plugin", error.to_string())
})?;
let plugin = constructor();
self.plugins.push(plugin);
std::mem::forget(library);
loaded += 1;
}
}
Ok(loaded)
}
pub fn emit(&self, event: ClawEvent) {
let _ = self.event_tx.send(event);
}
pub async fn dispatch(&mut self, event: &ClawEvent) {
for plugin in &mut self.plugins {
let _ = plugin.on_event(event).await;
}
}
}
fn is_dynamic_library(path: &Path) -> bool {
matches!(
path.extension().and_then(|value| value.to_str()),
Some("so") | Some("dylib") | Some("dll")
)
}