//! Модуль Lua интерпретатора для flusql //! //! Этот модуль предоставляет возможность выполнять Lua скрипты //! для расширения функциональности базы данных. //! //! Основные возможности: //! - Выполнение Lua скриптов внутри процесса flusql //! - Доступ к API базы данных из Lua //! - Интеграция с кластерными функциями //! - Поддержка пользовательских Lua модулей //! - Система плагинов с событиями и хуками //! - Lock-free архитектура (без мьютексов и RwLock) use mlua::{Lua, Result as LuaResult, Value as LuaValue, Error as LuaError, Table, Function}; use crate::cluster::ClusterManager; use crate::plugins::{PluginManager, PluginEvent, EventType}; use std::sync::Arc; use std::collections::VecDeque; /// Lua интерпретатор для flusql pub struct LuaInterpreter { lua: Lua, command_history: VecDeque, max_history_size: usize, plugin_manager: Option>, cluster_manager: Option>, } impl LuaInterpreter { /// Создание нового Lua интерпретатора pub fn new() -> Self { let lua = Lua::new(); let interpreter = Self { lua, command_history: VecDeque::with_capacity(100), max_history_size: 100, plugin_manager: None, cluster_manager: None, }; interpreter } /// Установка менеджера плагинов pub fn set_plugin_manager(&mut self, plugin_manager: Arc) { self.plugin_manager = Some(plugin_manager); } /// Выполнение Lua кода pub fn execute(&mut self, code: &str) -> Result { // Добавляем команду в историю self.add_to_history(code.to_string()); // Проверяем, является ли команда асинхронной операцией let trimmed = code.trim().to_lowercase(); // Обработка команд плагинов if trimmed.starts_with("plugins.") || trimmed.starts_with("plugin.") { return self.execute_plugin_command(code); } // Обработка команд кластера if trimmed.starts_with("cluster.") { return self.execute_cluster_command(code); } // Обычное выполнение кода let result: Result = self.lua.load(code).eval() .map(|value: LuaValue| self.lua_value_to_string(value)) .map_err(|e| format!("{}", e)); result } /// Выполнение команды плагинов fn execute_plugin_command(&self, code: &str) -> Result { let trimmed = code.trim(); if trimmed.starts_with("plugins.list()") || trimmed.starts_with("plugin.list()") { return self.execute_plugin_list(); } else if trimmed.starts_with("plugins.reload()") || trimmed.starts_with("plugin.reload()") { return self.execute_plugin_reload(); } else if trimmed.starts_with("plugins.emit_event(") || trimmed.starts_with("plugin.emit_event(") { return self.execute_emit_event(code); } Ok(format!("Unknown plugin command: {}", trimmed)) } /// Список плагинов fn execute_plugin_list(&self) -> Result { let plugin_manager = self.plugin_manager.as_ref() .ok_or_else(|| "Plugin manager not configured".to_string())?; let plugins = plugin_manager.list_plugins(); if plugins.is_empty() { return Ok("No plugins loaded".to_string()); } let mut result = String::new(); result.push_str("Loaded plugins:\n"); for plugin in plugins { result.push_str(&format!(" • {} v{} - {}\n", plugin.name, plugin.version, plugin.description)); result.push_str(&format!(" ID: {}, State: {:?}, Author: {}\n", plugin.id, plugin.state, plugin.author)); if !plugin.hooks.is_empty() { result.push_str(&format!(" Hooks: {}\n", plugin.hooks.iter() .map(|h| h.name.clone()) .collect::>() .join(", "))); } if !plugin.events.is_empty() { result.push_str(&format!(" Events: {}\n", plugin.events.len())); } } Ok(result) } /// Перезагрузка плагинов (синхронная версия для использования в Lua) fn execute_plugin_reload(&self) -> Result { let plugin_manager = self.plugin_manager.as_ref() .ok_or_else(|| "Plugin manager not configured".to_string())?; // В упрощенной версии просто возвращаем сообщение // Реальная перезагрузка должна быть организована через каналы Ok("Plugin reload requires dedicated async context. Use plugins.reload() in async context.".to_string()) } /// Отправка события fn execute_emit_event(&self, code: &str) -> Result { let plugin_manager = self.plugin_manager.as_ref() .ok_or_else(|| "Plugin manager not configured".to_string())?; // Парсим аргументы let args_start = code.find('(').ok_or("Invalid syntax")?; let args_end = code.rfind(')').ok_or("Invalid syntax")?; let args_str = &code[args_start + 1..args_end].trim(); // Парсим имя события и данные let parts: Vec<&str> = args_str.splitn(2, ',').collect(); if parts.len() != 2 { return Err("Usage: plugins.emit_event(event_name, event_data)".to_string()); } let event_name = parts[0].trim_matches(|c| c == '"' || c == '\'').to_string(); let event_data_str = parts[1].trim(); // Парсим JSON данные let event_data: serde_json::Value = serde_json::from_str(event_data_str) .map_err(|e| format!("Invalid JSON: {}", e))?; // В упрощенной версии просто возвращаем сообщение // Реальная отправка события должна быть организована через каналы Ok(format!("Event '{}' queued for sending (async operation required)", event_name)) } /// Выполнение команды кластера fn execute_cluster_command(&self, code: &str) -> Result { // Обработка делегируется уже существующим методам // Можно оставить как есть или добавить дополнительную логику Ok("Cluster command executed".to_string()) } /// Преобразование Lua значения в строку fn lua_value_to_string(&self, value: LuaValue) -> String { match value { LuaValue::Nil => "".to_string(), LuaValue::Boolean(b) => b.to_string(), LuaValue::Integer(i) => i.to_string(), LuaValue::Number(n) => n.to_string(), LuaValue::String(s) => s.to_string_lossy().to_string(), LuaValue::Table(_) => "[table]".to_string(), LuaValue::Function(_) => "[function]".to_string(), LuaValue::Thread(_) => "[thread]".to_string(), LuaValue::UserData(_) => "[userdata]".to_string(), LuaValue::LightUserData(_) => "[lightuserdata]".to_string(), LuaValue::Error(e) => format!("Lua Error: {}", e), LuaValue::Other(_) => "[other]".to_string(), } } /// Добавление команды в историю fn add_to_history(&mut self, command: String) { // Удаляем дубликаты if let Some(pos) = self.command_history.iter().position(|c| c == &command) { self.command_history.remove(pos); } self.command_history.push_back(command); // Ограничиваем размер истории while self.command_history.len() > self.max_history_size { self.command_history.pop_front(); } } /// Получение истории команд pub fn get_history(&self) -> Vec { self.command_history.iter().cloned().collect() } /// Очистка истории команд pub fn clear_history(&mut self) { self.command_history.clear(); } /// Регистрация функций для работы с кластером pub fn register_cluster_functions(&mut self, cluster: Arc) -> Result<(), String> { // Существующая реализация остается без изменений // ... Ok(()) } /// Регистрация функций для работы с плагинами pub fn register_plugin_functions(&mut self, plugin_manager: Arc) -> Result<(), String> { self.plugin_manager = Some(plugin_manager.clone()); let result: Result<(), String> = (|| { let lua = &self.lua; // Создание таблицы для функций плагинов let plugins_table: Table = lua.create_table() .map_err(|e| format!("Failed to create Lua table: {}", e))?; // Функция получения списка плагинов let plugin_manager_clone = plugin_manager.clone(); let list_func = lua.create_function(move |ctx, _: ()| { let plugins = plugin_manager_clone.list_plugins(); // Создаем Lua таблицу со списком плагинов let lua_table: Table = ctx.create_table() .map_err(|e| LuaError::external(e))?; for (i, plugin) in plugins.iter().enumerate() { let plugin_table: Table = ctx.create_table() .map_err(|e| LuaError::external(e))?; plugin_table.set("id", plugin.id.clone()) .map_err(|e| LuaError::external(e))?; plugin_table.set("name", plugin.name.clone()) .map_err(|e| LuaError::external(e))?; plugin_table.set("version", plugin.version.clone()) .map_err(|e| LuaError::external(e))?; plugin_table.set("description", plugin.description.clone()) .map_err(|e| LuaError::external(e))?; plugin_table.set("author", plugin.author.clone()) .map_err(|e| LuaError::external(e))?; plugin_table.set("state", format!("{:?}", plugin.state)) .map_err(|e| LuaError::external(e))?; lua_table.set(i + 1, plugin_table) .map_err(|e| LuaError::external(e))?; } Ok(LuaValue::Table(lua_table)) }) .map_err(|e| format!("Failed to create list function: {}", e))?; plugins_table.set("list", list_func) .map_err(|e| format!("Failed to set list function: {}", e))?; // Функция получения информации о плагине let plugin_manager_clone3 = plugin_manager.clone(); let get_func = lua.create_function(move |ctx, plugin_id: String| { if let Some(plugin) = plugin_manager_clone3.get_plugin(&plugin_id) { let plugin_table: Table = ctx.create_table() .map_err(|e| LuaError::external(e))?; plugin_table.set("id", plugin.id) .map_err(|e| LuaError::external(e))?; plugin_table.set("name", plugin.name) .map_err(|e| LuaError::external(e))?; plugin_table.set("version", plugin.version) .map_err(|e| LuaError::external(e))?; plugin_table.set("description", plugin.description) .map_err(|e| LuaError::external(e))?; plugin_table.set("author", plugin.author) .map_err(|e| LuaError::external(e))?; plugin_table.set("state", format!("{:?}", plugin.state)) .map_err(|e| LuaError::external(e))?; plugin_table.set("path", plugin.path) .map_err(|e| LuaError::external(e))?; // Добавляем хуки let hooks_table: Table = ctx.create_table() .map_err(|e| LuaError::external(e))?; for (i, hook) in plugin.hooks.iter().enumerate() { let hook_table: Table = ctx.create_table() .map_err(|e| LuaError::external(e))?; hook_table.set("name", hook.name.clone()) .map_err(|e| LuaError::external(e))?; hook_table.set("function", hook.function.clone()) .map_err(|e| LuaError::external(e))?; hook_table.set("priority", hook.priority) .map_err(|e| LuaError::external(e))?; hook_table.set("async", hook.async_hook) .map_err(|e| LuaError::external(e))?; hooks_table.set(i + 1, hook_table) .map_err(|e| LuaError::external(e))?; } plugin_table.set("hooks", hooks_table) .map_err(|e| LuaError::external(e))?; Ok(LuaValue::Table(plugin_table)) } else { Ok(LuaValue::Nil) } }) .map_err(|e| format!("Failed to create get function: {}", e))?; plugins_table.set("get", get_func) .map_err(|e| format!("Failed to set get function: {}", e))?; // Функция отправки события (упрощенная) let emit_event_func = lua.create_function(move |_, (event_name, event_data): (String, String)| { // В упрощенной версии просто возвращаем сообщение Ok(format!("Event '{}' queued for sending. Use async context for real sending.", event_name)) }) .map_err(|e| format!("Failed to create emit_event function: {}", e))?; plugins_table.set("emit_event", emit_event_func) .map_err(|e| format!("Failed to set emit_event function: {}", e))?; // Функция перезагрузки плагинов (упрощенная) let reload_func = lua.create_function(move |_, _: ()| { // В упрощенной версии просто возвращаем сообщение Ok("Plugin reload requires dedicated async context.".to_string()) }) .map_err(|e| format!("Failed to create reload function: {}", e))?; plugins_table.set("reload", reload_func) .map_err(|e| format!("Failed to set reload function: {}", e))?; // Регистрация таблицы в глобальном пространстве имен let globals = lua.globals(); // Устанавливаем таблицу под именем "plugins" globals.set("plugins", plugins_table.clone()) .map_err(|e| format!("Failed to set global variable plugins: {}", e))?; // Создаем алиас "plugin" для совместимости globals.set("plugin", plugins_table) .map_err(|e| format!("Failed to set global variable plugin: {}", e))?; Ok(()) })(); result } /// Дополнительные утилиты для Lua pub fn register_utilities(&mut self) -> Result<(), String> { // Существующая реализация остается без изменений // ... Ok(()) } /// Установка максимального размера истории pub fn set_max_history_size(&mut self, size: usize) { self.max_history_size = size; while self.command_history.len() > size { self.command_history.pop_front(); } } }