// src/server/lua_engine.rs //! Встроенный интерпретатор Lua (на основе rlua) use rlua::{Lua, RluaCompat}; use std::sync::Arc; use std::fs; use std::path::Path; use crate::common::Result; use crate::common::protocol; use crate::server::database::{Trigger, TriggerEvent, Index, IndexType}; /// Движок Lua для выполнения скриптов #[derive(Clone)] pub struct LuaEngine { lua: Arc, } impl LuaEngine { pub fn new() -> Result { let lua = Lua::new(); // Настройка Lua окружения lua.load(r#" function futriix_log(message) print("LUA: " .. message) end function futriix_error(message) print("LUA ERROR: " .. message) end "#).exec()?; Ok(Self { lua: Arc::new(lua) }) } /// Выполнение Lua скрипта из файла #[allow(dead_code)] pub fn execute_script_file(&self, file_path: &str) -> Result<()> { let script_content = fs::read_to_string(file_path) .map_err(|e| crate::common::FutriixError::LuaError(format!("Failed to read script file {}: {}", file_path, e)))?; self.execute_script(&script_content) } /// Выполнение Lua скрипта из строки pub fn execute_script(&self, script: &str) -> Result<()> { let lua = self.lua.clone(); lua.load(script).exec()?; Ok(()) } /// Выполнение всех скриптов из директории #[allow(dead_code)] pub fn execute_scripts_from_dir(&self, dir_path: &str, script_names: &[String]) -> Result<()> { let path = Path::new(dir_path); if !path.exists() { println!("Lua scripts directory does not exist: {}", path.display()); return Ok(()); } if !path.is_dir() { return Err(crate::common::FutriixError::LuaError( format!("Lua scripts path is not a directory: {}", path.display()) )); } for script_name in script_names { let script_path = path.join(script_name); if script_path.exists() && script_path.is_file() { println!("Executing Lua script: {}", script_path.display()); match self.execute_script_file(script_path.to_str().unwrap()) { Ok(_) => println!("✓ Script executed successfully: {}", script_name), Err(e) => eprintln!("✗ Failed to execute script {}: {}", script_name, e), } } else { println!("Script not found: {}", script_path.display()); } } Ok(()) } /// Регистрация функций базы данных в Lua pub fn register_db_functions( &self, db: Arc, sharding_manager: Arc ) -> Result<()> { let lua = self.lua.clone(); // Создаем таблицу для функций БД let futriix_db = lua.create_table()?; // Базовые CRUD функции let db_clone = db.clone(); futriix_db.set("create", lua.create_function(move |_, (collection, data): (String, String)| { let command = protocol::Command::Create { collection, document: data.into_bytes(), }; match db_clone.execute_command(command) { Ok(_) => Ok(()), Err(e) => Err(rlua::Error::RuntimeError(e.to_string())), } })?)?; let db_clone = db.clone(); futriix_db.set("read", lua.create_function(move |_, (collection, id): (String, String)| { let command = protocol::Command::Read { collection, id, }; match db_clone.execute_command(command) { Ok(response) => { match response { protocol::Response::Success(data) => { Ok(String::from_utf8_lossy(&data).to_string()) } protocol::Response::Error(e) => { Err(rlua::Error::RuntimeError(e)) } } } Err(e) => Err(rlua::Error::RuntimeError(e.to_string())), } })?)?; // Добавляем таблицу в глобальное пространство имен lua.globals().set("futriix_db", futriix_db)?; Ok(()) } /// Получение директории Lua скриптов fn lua_scripts_dir(&self) -> &'static str { "lua_scripts" } }