Delete src/plugins/bad-mod.rs
This commit is contained in:
parent
de119b9b15
commit
c5ec1d432b
@ -1,340 +0,0 @@
|
|||||||
//! Система плагинов для flusql с lock-free архитектурой
|
|
||||||
//!
|
|
||||||
//! Основные возможности:
|
|
||||||
//! - Lock-free обработка событий через каналы
|
|
||||||
//! - Песочница для плагинов Lua
|
|
||||||
//! - Поддержка хуков и событий
|
|
||||||
//! - Горячая перезагрузка плагинов
|
|
||||||
//! - Безопасная обработка ошибок
|
|
||||||
//! - Асинхронное выполнение задач
|
|
||||||
|
|
||||||
mod traits;
|
|
||||||
mod channel;
|
|
||||||
mod sandbox;
|
|
||||||
mod worker;
|
|
||||||
|
|
||||||
pub use traits::{
|
|
||||||
PluginData, PluginState, EventHandler, HookHandler,
|
|
||||||
PluginEvent, EventType, PluginHook, PluginManagerTrait
|
|
||||||
};
|
|
||||||
pub use channel::{
|
|
||||||
PluginMessage, PluginChannels, HookResponse, LoadPluginResponse,
|
|
||||||
UnloadPluginResponse, ListPluginsResponse, GetPluginResponse, PluginInfo
|
|
||||||
};
|
|
||||||
pub use sandbox::LuaSandbox;
|
|
||||||
pub use worker::PluginWorker;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use mlua::Lua;
|
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
/// Ошибки плагинов
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum PluginError {
|
|
||||||
#[error("IO error: {0}")]
|
|
||||||
IoError(#[from] std::io::Error),
|
|
||||||
|
|
||||||
#[error("Lua error: {0}")]
|
|
||||||
LuaError(String),
|
|
||||||
|
|
||||||
#[error("Serialization error: {0}")]
|
|
||||||
SerializationError(#[from] serde_json::Error),
|
|
||||||
|
|
||||||
#[error("Channel error: {0}")]
|
|
||||||
ChannelError(String),
|
|
||||||
|
|
||||||
#[error("Plugin not found: {0}")]
|
|
||||||
PluginNotFound(String),
|
|
||||||
|
|
||||||
#[error("Plugin already loaded: {0}")]
|
|
||||||
PluginAlreadyLoaded(String),
|
|
||||||
|
|
||||||
#[error("Invalid plugin: {0}")]
|
|
||||||
InvalidPlugin(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Реализация данных плагина
|
|
||||||
pub struct Plugin {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub version: String,
|
|
||||||
pub description: String,
|
|
||||||
pub author: String,
|
|
||||||
pub path: String,
|
|
||||||
pub state: PluginState,
|
|
||||||
pub hooks: Vec<PluginHook>,
|
|
||||||
pub lua_sandbox: Option<Arc<RwLock<LuaSandbox>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PluginData for Plugin {
|
|
||||||
fn id(&self) -> &str {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
fn version(&self) -> &str {
|
|
||||||
&self.version
|
|
||||||
}
|
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
&self.description
|
|
||||||
}
|
|
||||||
|
|
||||||
fn author(&self) -> &str {
|
|
||||||
&self.author
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path(&self) -> &str {
|
|
||||||
&self.path
|
|
||||||
}
|
|
||||||
|
|
||||||
fn state(&self) -> PluginState {
|
|
||||||
self.state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Менеджер плагинов с lock-free архитектурой
|
|
||||||
pub struct PluginManager {
|
|
||||||
plugins: Arc<RwLock<Vec<Arc<Plugin>>>>,
|
|
||||||
channels: PluginChannels,
|
|
||||||
worker: PluginWorker,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PluginManager {
|
|
||||||
/// Создать новый менеджер плагинов
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let channels = PluginChannels::new();
|
|
||||||
let plugins = Arc::new(RwLock::new(Vec::new()));
|
|
||||||
|
|
||||||
let worker = PluginWorker::new(
|
|
||||||
channels.clone(),
|
|
||||||
Arc::clone(&plugins),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Запустить worker в фоновом потоке
|
|
||||||
worker.start();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
plugins,
|
|
||||||
channels,
|
|
||||||
worker,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Отправить событие (lock-free, через канал)
|
|
||||||
pub async fn emit_event(&self, event: PluginEvent) -> Result<(), PluginError> {
|
|
||||||
self.channels.send(PluginMessage::Event(event))
|
|
||||||
.map_err(|e| PluginError::ChannelError(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Выполнить хук (lock-free, через канал)
|
|
||||||
pub async fn execute_hook(&self, hook_name: &str, data: Value) -> Result<Value, PluginError> {
|
|
||||||
let (sender, receiver) = crossbeam::channel::bounded(1);
|
|
||||||
|
|
||||||
self.channels.send(PluginMessage::HookRequest {
|
|
||||||
hook_name: hook_name.to_string(),
|
|
||||||
data,
|
|
||||||
response_sender: sender,
|
|
||||||
}).map_err(|e| PluginError::ChannelError(e))?;
|
|
||||||
|
|
||||||
match receiver.recv() {
|
|
||||||
Ok(HookResponse::Success(result)) => Ok(result),
|
|
||||||
Ok(HookResponse::Error(err)) => Err(PluginError::LuaError(err)),
|
|
||||||
Ok(HookResponse::NoHandler) => Err(PluginError::InvalidPlugin("No handler found".to_string())),
|
|
||||||
Err(e) => Err(PluginError::ChannelError(format!("Failed to receive hook response: {}", e))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Загрузить плагин (lock-free, через канал)
|
|
||||||
pub async fn load_plugin(&self, path: &str) -> Result<String, PluginError> {
|
|
||||||
let (sender, receiver) = crossbeam::channel::bounded(1);
|
|
||||||
|
|
||||||
self.channels.send(PluginMessage::LoadPlugin {
|
|
||||||
path: path.to_string(),
|
|
||||||
response_sender: sender,
|
|
||||||
}).map_err(|e| PluginError::ChannelError(e))?;
|
|
||||||
|
|
||||||
match receiver.recv() {
|
|
||||||
Ok(LoadPluginResponse::Success(plugin_id)) => Ok(plugin_id),
|
|
||||||
Ok(LoadPluginResponse::Error(err)) => Err(PluginError::InvalidPlugin(err)),
|
|
||||||
Err(e) => Err(PluginError::ChannelError(format!("Failed to receive load response: {}", e))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Выгрузить плагин (lock-free, через канал)
|
|
||||||
pub async fn unload_plugin(&self, plugin_id: &str) -> Result<(), PluginError> {
|
|
||||||
let (sender, receiver) = crossbeam::channel::bounded(1);
|
|
||||||
|
|
||||||
self.channels.send(PluginMessage::UnloadPlugin {
|
|
||||||
plugin_id: plugin_id.to_string(),
|
|
||||||
response_sender: sender,
|
|
||||||
}).map_err(|e| PluginError::ChannelError(e))?;
|
|
||||||
|
|
||||||
match receiver.recv() {
|
|
||||||
Ok(UnloadPluginResponse::Success) => Ok(()),
|
|
||||||
Ok(UnloadPluginResponse::Error(err)) => Err(PluginError::InvalidPlugin(err)),
|
|
||||||
Err(e) => Err(PluginError::ChannelError(format!("Failed to receive unload response: {}", e))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Получить список плагинов (lock-free, через канал)
|
|
||||||
pub async fn list_plugins(&self) -> Result<Vec<PluginInfo>, PluginError> {
|
|
||||||
let (sender, receiver) = crossbeam::channel::bounded(1);
|
|
||||||
|
|
||||||
self.channels.send(PluginMessage::ListPlugins {
|
|
||||||
response_sender: sender,
|
|
||||||
}).map_err(|e| PluginError::ChannelError(e))?;
|
|
||||||
|
|
||||||
match receiver.recv() {
|
|
||||||
Ok(response) => Ok(response.plugins),
|
|
||||||
Err(e) => Err(PluginError::ChannelError(format!("Failed to receive list response: {}", e))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Получить информацию о плагине (lock-free, через канал)
|
|
||||||
pub async fn get_plugin(&self, plugin_id: &str) -> Result<Option<PluginInfo>, PluginError> {
|
|
||||||
let (sender, receiver) = crossbeam::channel::bounded(1);
|
|
||||||
|
|
||||||
self.channels.send(PluginMessage::GetPlugin {
|
|
||||||
plugin_id: plugin_id.to_string(),
|
|
||||||
response_sender: sender,
|
|
||||||
}).map_err(|e| PluginError::ChannelError(e))?;
|
|
||||||
|
|
||||||
match receiver.recv() {
|
|
||||||
Ok(GetPluginResponse::Found(info)) => Ok(Some(info)),
|
|
||||||
Ok(GetPluginResponse::NotFound) => Ok(None),
|
|
||||||
Ok(GetPluginResponse::Error(err)) => Err(PluginError::InvalidPlugin(err)),
|
|
||||||
Err(e) => Err(PluginError::ChannelError(format!("Failed to receive get response: {}", e))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Загрузить все плагины из директории
|
|
||||||
pub async fn load_all_plugins(&self) -> Result<Vec<String>, PluginError> {
|
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
let plugins_dir = Path::new("./plugins");
|
|
||||||
if !plugins_dir.exists() {
|
|
||||||
fs::create_dir_all(plugins_dir)
|
|
||||||
.map_err(PluginError::IoError)?;
|
|
||||||
return Ok(Vec::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut loaded = Vec::new();
|
|
||||||
|
|
||||||
for entry in fs::read_dir(plugins_dir)
|
|
||||||
.map_err(PluginError::IoError)?
|
|
||||||
{
|
|
||||||
let entry = entry.map_err(PluginError::IoError)?;
|
|
||||||
let path = entry.path();
|
|
||||||
|
|
||||||
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("lua") {
|
|
||||||
if let Ok(plugin_id) = self.load_plugin(path.to_str().unwrap()).await {
|
|
||||||
loaded.push(plugin_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(loaded)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Остановить менеджер плагинов
|
|
||||||
pub fn shutdown(&self) {
|
|
||||||
self.channels.send(PluginMessage::Shutdown)
|
|
||||||
.unwrap_or_else(|e| log::error!("Failed to send shutdown message: {}", e));
|
|
||||||
|
|
||||||
// Дожидаемся остановки worker
|
|
||||||
self.worker.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PluginManagerTrait for PluginManager {
|
|
||||||
fn load_all_plugins(&mut self) -> Result<Vec<Arc<dyn PluginData>>, String> {
|
|
||||||
// Для синхронного вызова используем tokio runtime
|
|
||||||
let rt = tokio::runtime::Runtime::new()
|
|
||||||
.map_err(|e| format!("Failed to create runtime: {}", e))?;
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
self.load_all_plugins().await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let plugins = self.plugins.read();
|
|
||||||
let result: Vec<Arc<dyn PluginData>> = plugins
|
|
||||||
.iter()
|
|
||||||
.map(|p| p.clone() as Arc<dyn PluginData>)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_plugin(&mut self, path: &str) -> Result<Arc<dyn PluginData>, String> {
|
|
||||||
let rt = tokio::runtime::Runtime::new()
|
|
||||||
.map_err(|e| format!("Failed to create runtime: {}", e))?;
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
let plugin_id = self.load_plugin(path).await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let plugins = self.plugins.read();
|
|
||||||
plugins.iter()
|
|
||||||
.find(|p| p.id == plugin_id)
|
|
||||||
.map(|p| p.clone() as Arc<dyn PluginData>)
|
|
||||||
.ok_or_else(|| format!("Plugin not found after loading: {}", plugin_id))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unload_plugin(&mut self, plugin_id: &str) -> Result<(), String> {
|
|
||||||
let rt = tokio::runtime::Runtime::new()
|
|
||||||
.map_err(|e| format!("Failed to create runtime: {}", e))?;
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
self.unload_plugin(plugin_id).await
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_plugins(&self) -> Vec<Arc<dyn PluginData>> {
|
|
||||||
let plugins = self.plugins.read();
|
|
||||||
plugins.iter()
|
|
||||||
.map(|p| p.clone() as Arc<dyn PluginData>)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_plugin(&self, plugin_id: &str) -> Option<Arc<dyn PluginData>> {
|
|
||||||
let plugins = self.plugins.read();
|
|
||||||
plugins.iter()
|
|
||||||
.find(|p| p.id == plugin_id)
|
|
||||||
.map(|p| p.clone() as Arc<dyn PluginData>)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_event(&self, event: PluginEvent) -> Result<(), String> {
|
|
||||||
let rt = tokio::runtime::Runtime::new()
|
|
||||||
.map_err(|e| format!("Failed to create runtime: {}", e))?;
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
self.emit_event(event).await
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_hook(&self, hook_name: &str, data: Value) -> Result<Value, String> {
|
|
||||||
let rt = tokio::runtime::Runtime::new()
|
|
||||||
.map_err(|e| format!("Failed to create runtime: {}", e))?;
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
self.execute_hook(hook_name, data).await
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for PluginManager {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user