use crate::plugin::{Plugin, PluginError, PluginInfo, PluginResult, PluginStatus};
use crate::registry::PluginRegistry;
use parking_lot::RwLock;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
type PluginHandle = Arc<Mutex<Box<dyn Plugin>>>;
pub struct PluginService {
registry: Arc<PluginRegistry>,
configs: Arc<RwLock<HashMap<String, serde_json::Value>>>,
statuses: Arc<RwLock<HashMap<String, PluginStatus>>>,
}
impl PluginService {
pub fn new(registry: Arc<PluginRegistry>) -> Self {
Self {
registry,
configs: Arc::new(RwLock::new(HashMap::new())),
statuses: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn from_global() -> Self {
use std::sync::Arc;
Self::new(Arc::new(PluginRegistry::new()))
}
pub fn with_registry(registry: Arc<PluginRegistry>) -> Self {
Self::new(registry)
}
pub fn get_config(&self, key: &str) -> Option<serde_json::Value> {
let configs = self.configs.read();
configs.get(key).cloned()
}
pub fn set_config(&self, key: &str, config: serde_json::Value) {
let mut configs = self.configs.write();
configs.insert(key.to_string(), config);
}
pub async fn invoke(
&self,
key: &str,
method: &str,
params: serde_json::Value,
) -> PluginResult<serde_json::Value> {
self.check_status(key)?;
self.registry.invoke(key, method, params).await
}
pub fn get_instance(&self, key: &str) -> PluginResult<PluginHandle> {
self.check_status(key)?;
self.registry
.get(key)
.ok_or_else(|| PluginError::NotFound(key.to_string()))
}
pub fn check_status(&self, key: &str) -> PluginResult<()> {
if !self.registry.contains(key) {
return Err(PluginError::NotFound(key.to_string()));
}
let statuses = self.statuses.read();
if let Some(status) = statuses.get(key) {
if *status == PluginStatus::Disabled {
return Err(PluginError::Disabled(key.to_string()));
}
}
Ok(())
}
pub fn set_status(&self, key: &str, status: PluginStatus) {
let mut statuses = self.statuses.write();
statuses.insert(key.to_string(), status);
}
pub fn enable(&self, key: &str) -> PluginResult<()> {
if !self.registry.contains(key) {
return Err(PluginError::NotFound(key.to_string()));
}
self.set_status(key, PluginStatus::Enabled);
Ok(())
}
pub fn disable(&self, key: &str) -> PluginResult<()> {
if !self.registry.contains(key) {
return Err(PluginError::NotFound(key.to_string()));
}
self.set_status(key, PluginStatus::Disabled);
Ok(())
}
pub fn get_info(&self, key: &str) -> PluginResult<PluginInfo> {
let plugin = self.get_instance(key)?;
let plugin = plugin.blocking_lock();
Ok(plugin.info())
}
pub fn list(&self) -> Vec<PluginInfo> {
self.registry.list()
}
pub fn get_by_hook(&self, hook: &str) -> Vec<PluginInfo> {
let plugins = self.registry.get_by_hook(hook);
plugins.iter().map(|p| p.blocking_lock().info()).collect()
}
pub async fn re_init(&self, key: &str) -> PluginResult<()> {
let plugin = self.get_instance(key)?;
let config = self.get_config(key).unwrap_or_default();
let mut plugin = plugin.lock().await;
plugin.init(config).await?;
plugin.ready().await?;
Ok(())
}
pub fn remove(&self, key: &str) -> PluginResult<()> {
self.registry.remove(key);
let mut configs = self.configs.write();
configs.remove(key);
let mut statuses = self.statuses.write();
statuses.remove(key);
Ok(())
}
}
impl Default for PluginService {
fn default() -> Self {
Self::from_global()
}
}
static GLOBAL_PLUGIN_SERVICE: once_cell::sync::Lazy<PluginService> =
once_cell::sync::Lazy::new(PluginService::from_global);
pub fn global_plugin_service() -> &'static PluginService {
&GLOBAL_PLUGIN_SERVICE
}