From e2a9e331b1c9fd57e15f83b48beb445e8875994d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9=20=D0=A1?= =?UTF-8?q?=D0=B0=D1=84=D1=80=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Sat, 6 Dec 2025 19:31:17 +0000 Subject: [PATCH] Delete src/lua_shell.rs --- src/lua_shell.rs | 1529 ---------------------------------------------- 1 file changed, 1529 deletions(-) delete mode 100644 src/lua_shell.rs diff --git a/src/lua_shell.rs b/src/lua_shell.rs deleted file mode 100644 index 0101c7b..0000000 --- a/src/lua_shell.rs +++ /dev/null @@ -1,1529 +0,0 @@ -// src/lua_shell.rs -//! Интерактивная Lua оболочка для Futriix -//! -//! Предоставляет интерфейс для взаимодействия с базой данных через Lua -//! и CRUD команды. Использует lock-free доступ к данным через атомарные ссылки -//! и предоставляет две режима работы: Lua интерпретатор и inbox (командный) режим. -//! -//! Особенности: -//! - Полностью lock-free доступ к базе данных через атомарные операции -//! - Поддержка цветного вывода с ANSI кодами -//! - Два режима работы: Lua интерпретатор и командный режим -//! - Полная поддержка всех команд из README.md -//! - Интеграция с модулями шардинга, репликации и CSV операций - -#![allow(dead_code)] - -use std::sync::Arc; -use tokio::io::{AsyncBufReadExt, BufReader}; -use serde_json::Value; - -use crate::common::Result; -use crate::server::database::Database; -use crate::server::lua_engine::LuaEngine; -use crate::server::sharding::ShardingManager; -use crate::server::csv_import_export::CsvManager; -use crate::common::protocol; -use crate::server::database::{Index, IndexType, Trigger, TriggerEvent}; - -/// Конвертация HEX цвета в ANSI escape code для цветного вывода -fn hex_to_ansi(hex_color: &str) -> String { - let hex = hex_color.trim_start_matches('#'); - - if hex.len() == 6 { - if let (Ok(r), Ok(g), Ok(b)) = ( - u8::from_str_radix(&hex[0..2], 16), - u8::from_str_radix(&hex[2..4], 16), - u8::from_str_radix(&hex[4..6], 16), - ) { - return format!("\x1b[38;2;{};{};{}m", r, g, b); - } - } - - "\x1b[38;2;255;255;255m".to_string() -} - -/// Вывод текста с красным цветом для ошибок (#FF0000) -fn print_error(text: &str) { - let red_color = hex_to_ansi("#FF0000"); - println!("{}{}\x1b[0m", red_color, text); -} - -/// Вывод текста с зеленым цветом для успешных операций (#00FF00) -fn print_success(text: &str) { - let green_color = hex_to_ansi("#00FF00"); - println!("{}{}\x1b[0m", green_color, text); -} - -/// Вывод текста с синим цветом для информационных сообщений (#00bfff) -fn print_info(text: &str) { - let blue_color = hex_to_ansi("#00bfff"); - println!("{}{}\x1b[0m", blue_color, text); -} - -/// Вывод текста с цветом #33d17a для приглашения Lua -fn print_lua_color(text: &str) { - let lua_color = hex_to_ansi("#33d17a"); - println!("{}{}\x1b[0m", lua_color, text); -} - -/// Вывод текста с цветом #ffa500 для предупреждений -fn print_warning(text: &str) { - let warning_color = hex_to_ansi("#ffa500"); - println!("{}{}\x1b[0m", warning_color, text); -} - -/// Вывод текста с цветом #9400d3 для отладочной информации -#[allow(dead_code)] -fn print_debug(text: &str) { - let debug_color = hex_to_ansi("#9400d3"); - println!("{}{}\x1b[0m", debug_color, text); -} - -/// Интерактивная Lua оболочка с поддержкой двух режимов работы -pub struct LuaShell { - lua_engine: LuaEngine, - database: Arc, - sharding_manager: Arc, - csv_manager: Arc, - inbox_mode: bool, - current_transaction: Option, -} - -impl LuaShell { - /// Создание новой интерактивной оболочки - pub fn new( - lua_engine: LuaEngine, - database: Arc, - sharding_manager: Arc, - csv_manager: Arc, - ) -> Self { - Self { - lua_engine, - database, - sharding_manager, - csv_manager, - inbox_mode: false, - current_transaction: None, - } - } - - /// Основной цикл выполнения интерактивной оболочки - pub async fn run(&mut self) -> Result<()> { - let stdin = tokio::io::stdin(); - let mut reader = BufReader::new(stdin).lines(); - - // Выводим приветственное сообщение при запуске Lua интерпретатора - print_lua_color("┌────────────────────────────────────────────┐"); - print_lua_color("│ Futriix Lua Interactive Shell v1.0 │"); - print_lua_color("│ Lock-Free Database System │"); - print_lua_color("└────────────────────────────────────────────┘"); - print_lua_color(""); - print_lua_color("Type 'inbox.start' for database commands mode"); - print_lua_color("Type Lua code directly for Lua interpreter mode"); - print_lua_color("Type 'help' in inbox mode for available commands"); - print_lua_color("Type 'exit' or 'quit' to exit"); - println!(); - - loop { - // Выводим соответствующее приглашение в зависимости от режима - if self.inbox_mode { - let inbox_prompt_color = hex_to_ansi("#00bfff"); - print!("{}futriix", inbox_prompt_color); - - // Показываем активную транзакцию, если есть - if let Some(tx_id) = &self.current_transaction { - print!(" [tx:{}]", tx_id); - } - - print!("{}>\x1b[0m ", inbox_prompt_color); - } else { - // ПРИГЛАШЕНИЕ LUA ЦВЕТА #33d17a - let lua_color = hex_to_ansi("#33d17a"); - print!("{}lua>\x1b[0m ", lua_color); - } - - let _ = std::io::Write::flush(&mut std::io::stdout()); - - let line = match reader.next_line().await { - Ok(Some(line)) => line, - Ok(None) => break, - Err(e) => { - eprintln!("Read error: {}", e); - continue; - } - }; - - let input = line.trim(); - - // Обработка системных команд - match input { - "exit" | "quit" => { - // Проверяем активную транзакцию перед выходом - if let Some(tx_id) = &self.current_transaction { - print_warning(&format!("Warning: Transaction '{}' is still active. Rolling back...", tx_id)); - let command = protocol::Command::RollbackTransaction { - transaction_id: tx_id.clone() - }; - let _ = self.database.execute_command(command); - } - - print_info("Shutting down Futriix shell..."); - break; - } - "inbox.start" => { - self.inbox_mode = true; - let lua_color = hex_to_ansi("#33d17a"); - println!("{}Entering database command mode\x1b[0m", lua_color); - println!("Type 'help' for available commands, 'inbox.stop' to return to Lua mode"); - continue; - } - "inbox.stop" if self.inbox_mode => { - self.inbox_mode = false; - print_success("Exiting database mode. Back to Lua interpreter."); - continue; - } - "help" if self.inbox_mode => { - self.show_help().await?; - continue; - } - "clear" | "cls" => { - // Очистка экрана - print!("\x1b[2J\x1b[1;1H"); - continue; - } - "status" if self.inbox_mode => { - self.show_system_status().await?; - continue; - } - _ => {} - } - - // Обработка ввода в зависимости от режима - if self.inbox_mode { - // Необходимо передать изменяемую ссылку на self для обработки команд транзакций - self.handle_inbox_command(input).await?; - } else { - self.handle_lua_command(input).await?; - } - } - - print_info("Futriix shell terminated."); - Ok(()) - } - - /// Обработка Lua команд (интерпретаторный режим) - async fn handle_lua_command(&self, input: &str) -> Result<()> { - if input.is_empty() { - return Ok(()); - } - - // Проверяем, не является ли ввод командой оболочки в Lua режиме - match input { - "version" => { - println!("Futriix Lua Engine v1.0.0"); - return Ok(()); - } - "env" => { - println!("Lua Environment:"); - println!(" Database: connected"); - println!(" Sharding: {}", if self.sharding_manager.is_cluster_formed() { "enabled" } else { "disabled" }); - println!(" CSV: available"); - return Ok(()); - } - _ => {} - } - - // Выполняем Lua код - match self.lua_engine.execute_script(input) { - Ok(_) => {} - Err(e) => { - let error_msg = e.to_string(); - if error_msg.contains("Lua error: syntax error:") || error_msg.contains("Unknown command:") { - print_error(&error_msg); - } else { - eprintln!("Lua error: {}", e); - } - } - } - - Ok(()) - } - - /// Обработка команд inbox (командный режим) - /// Используем &mut self, потому что команды транзакций изменяют состояние оболочки - async fn handle_inbox_command(&mut self, input: &str) -> Result<()> { - let parts: Vec<&str> = input.split_whitespace().collect(); - - if parts.is_empty() { - return Ok(()); - } - - // Диспетчеризация команд - match parts[0] { - // Базовые CRUD команды - "create" => self.handle_create(parts).await, - "read" => self.handle_read(parts).await, - "update" => self.handle_update(parts).await, - "delete" => self.handle_delete(parts).await, - "list" => self.handle_list(parts).await, - - // Управление транзакциями (требуют &mut self) - "begin" => self.handle_begin_transaction(parts).await, - "commit" => self.handle_commit_transaction(parts).await, - "rollback" => self.handle_rollback_transaction(parts).await, - - // Управление индексами - "index" => self.handle_index(parts).await, - - // Управление ограничениями (constraints) - "constraint" => self.handle_constraint(parts).await, - - // Управление хранимыми процедурами - "procedure" => self.handle_procedure(parts).await, - - // Управление триггерами - "trigger" => self.handle_trigger(parts).await, - - // Управление шардингом и кластером - "shard" => self.handle_shard(parts).await, - "cluster.status" => self.handle_cluster_status(parts).await, - "add.node" => self.handle_add_node(parts).await, - "evict.node" => self.handle_evict_node(parts).await, - "list.raft.nodes" => self.handle_list_raft_nodes(parts).await, - "cluster.rebalance" => self.handle_cluster_rebalance(parts).await, - - // CSV операции - "csv" => self.handle_csv(parts).await, - - // Управление бэкапами - "backup" => self.handle_backup(parts).await, - - // Системные команды - "stats" => self.handle_stats(parts).await, - "ping" => self.handle_ping(parts).await, - - // Справка - "help" => self.show_help().await, - - // Неизвестная команда - _ => { - let error_msg = format!("Unknown command: {}. Type 'help' for available commands.", parts[0]); - print_error(&error_msg); - Ok(()) - } - } - } - - // ============ ОСНОВНЫЕ CRUD КОМАНДЫ ============ - - /// Обработка команды create - async fn handle_create(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 3 { - println!("Usage: create "); - // Исправляем строку с двойными фигурными скобками {{ и }} для экранирования - println!("Example: create users '{{\"name\": \"John\", \"age\": 30}}'"); - return Ok(()); - } - - let collection = parts[1].to_string(); - let document = parts[2..].join(" ").into_bytes(); - - let command = protocol::Command::Create { - collection, - document, - }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(data) = response { - if let Ok(id) = String::from_utf8(data) { - print_success(&format!("Document created with ID: {}", id)); - } else { - print_success("Document created successfully"); - } - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - - Ok(()) - } - - /// Обработка команды read - async fn handle_read(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 3 { - println!("Usage: read "); - println!("Example: read users 550e8400-e29b-41d4-a716-446655440000"); - return Ok(()); - } - - let collection = parts[1].to_string(); - let id = parts[2].to_string(); - - let command = protocol::Command::Read { - collection, - id, - }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(data) = response { - if let Ok(document) = String::from_utf8(data) { - // Пытаемся красиво отформатировать JSON - match serde_json::from_str::(&document) { - Ok(json) => { - println!("{}", serde_json::to_string_pretty(&json).unwrap_or(document)); - } - Err(_) => { - println!("{}", document); - } - } - } else { - println!("Document read successfully (binary data)"); - } - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - - Ok(()) - } - - /// Обработка команды update - async fn handle_update(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 4 { - println!("Usage: update "); - // Исправляем строку с двойными фигурными скобками {{ и }} для экранирования - println!("Example: update users 550e8400-e29b-41d4-a716-446655440000 '{{\"age\": 31}}'"); - return Ok(()); - } - - let collection = parts[1].to_string(); - let id = parts[2].to_string(); - let document = parts[3..].join(" ").into_bytes(); - - let command = protocol::Command::Update { - collection, - id, - document, - }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - print_success("Document updated successfully"); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - - Ok(()) - } - - /// Обработка команды delete - async fn handle_delete(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 3 { - println!("Usage: delete "); - println!("Example: delete users 550e8400-e29b-41d4-a716-446655440000"); - return Ok(()); - } - - let collection = parts[1].to_string(); - let id = parts[2].to_string(); - - let command = protocol::Command::Delete { - collection, - id, - }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - print_success("Document deleted successfully"); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - - Ok(()) - } - - /// Обработка команды list - async fn handle_list(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: list [filter]"); - println!("Example: list users"); - // Исправляем строку с двойными фигурными скобками {{ и }} для экранирования - println!(" list users '{{\"age\": {{\"$gt\": 25}}}}'"); - return Ok(()); - } - - let collection = parts[1].to_string(); - let filter = if parts.len() > 2 { - parts[2..].join(" ").into_bytes() - } else { - vec![] - }; - - let command = protocol::Command::Query { - collection, - filter, - }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(data) = response { - if let Ok(documents) = String::from_utf8(data) { - match serde_json::from_str::(&documents) { - Ok(value) => { - if let Value::Array(arr) = value { - if arr.is_empty() { - println!("No documents found"); - } else { - println!("Found {} documents:", arr.len()); - for (i, doc) in arr.iter().enumerate() { - println!("\n[Document {}]", i + 1); - println!("{}", serde_json::to_string_pretty(doc).unwrap()); - } - } - } else { - println!("{}", serde_json::to_string_pretty(&value).unwrap_or(documents)); - } - } - Err(_) => { - println!("{}", documents); - } - } - } else { - println!("Documents read successfully (binary data)"); - } - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - - Ok(()) - } - - // ============ КОМАНДЫ ТРАНЗАКЦИЙ ============ - - async fn handle_begin_transaction(&mut self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: begin "); - println!("Example: begin tx_user_updates_001"); - return Ok(()); - } - - let transaction_id = parts[1].to_string(); - let command = protocol::Command::BeginTransaction { transaction_id: transaction_id.clone() }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - self.current_transaction = Some(transaction_id.clone()); - print_success(&format!("Transaction '{}' started successfully", transaction_id)); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - - Ok(()) - } - - async fn handle_commit_transaction(&mut self, parts: Vec<&str>) -> Result<()> { - let transaction_id = if parts.len() > 1 { - parts[1].to_string() - } else if let Some(tx_id) = &self.current_transaction { - tx_id.clone() - } else { - println!("Usage: commit "); - println!(" commit (uses current transaction)"); - return Ok(()); - }; - - let command = protocol::Command::CommitTransaction { transaction_id: transaction_id.clone() }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - // Очищаем текущую транзакцию, если это она - if let Some(current_tx) = &self.current_transaction { - if *current_tx == transaction_id { - self.current_transaction = None; - } - } - print_success(&format!("Transaction '{}' committed successfully", transaction_id)); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - - Ok(()) - } - - async fn handle_rollback_transaction(&mut self, parts: Vec<&str>) -> Result<()> { - let transaction_id = if parts.len() > 1 { - parts[1].to_string() - } else if let Some(tx_id) = &self.current_transaction { - tx_id.clone() - } else { - println!("Usage: rollback "); - println!(" rollback (uses current transaction)"); - return Ok(()); - }; - - let command = protocol::Command::RollbackTransaction { transaction_id: transaction_id.clone() }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - // Очищаем текущую транзакцию, если это она - if let Some(current_tx) = &self.current_transaction { - if *current_tx == transaction_id { - self.current_transaction = None; - } - } - print_success(&format!("Transaction '{}' rolled back successfully", transaction_id)); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - - Ok(()) - } - - // ============ КОМАНДЫ ИНДЕКСОВ ============ - - async fn handle_index(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: index create [unique]"); - println!(" index query "); - println!("Example: index create users email_idx email unique"); - println!(" index query users email_idx 'john@example.com'"); - return Ok(()); - } - - match parts[1] { - "create" => { - if parts.len() < 5 { - println!("Usage: index create [unique]"); - return Ok(()); - } - - let collection = parts[2].to_string(); - let index_name = parts[3].to_string(); - let field = parts[4].to_string(); - let unique = parts.get(5).map_or(false, |&s| s == "unique"); - - let index = Index { - name: index_name, - index_type: IndexType::Secondary, - field, - unique, - }; - - let command = protocol::Command::CreateIndex { collection, index }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - print_success("Index created successfully"); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - } - "query" => { - if parts.len() < 5 { - println!("Usage: index query "); - return Ok(()); - } - - let collection = parts[2].to_string(); - let index_name = parts[3].to_string(); - let value = parts[4].as_bytes().to_vec(); - - let command = protocol::Command::QueryByIndex { collection, index_name, value }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(data) = response { - if let Ok(result) = String::from_utf8(data) { - match serde_json::from_str::(&result) { - Ok(json) => { - println!("Query result:"); - println!("{}", serde_json::to_string_pretty(&json).unwrap_or(result)); - } - Err(_) => { - println!("Query result: {}", result); - } - } - } else { - println!("Query executed successfully (binary data)"); - } - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - } - _ => { - println!("Usage: index create [unique]"); - println!(" index query "); - } - } - - Ok(()) - } - - // ============ КОМАНДЫ ОГРАНИЧЕНИЙ ============ - - async fn handle_constraint(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: constraint add [value]"); - println!(" constraint remove "); - println!(" constraint list "); - println!("Example: constraint add users email_unique unique email"); - return Ok(()); - } - - match parts[1] { - "add" => { - if parts.len() < 6 { - println!("Usage: constraint add [value]"); - return Ok(()); - } - - let collection = parts[2].to_string(); - let constraint_name = parts[3].to_string(); - let constraint_type = parts[4].to_string(); - let field = parts[5].to_string(); - let value = if parts.len() > 6 { - parts[6..].join(" ").into_bytes() - } else { - vec![] - }; - - let command = protocol::Command::AddConstraint { - collection, - constraint_name, - constraint_type, - field, - value, - }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - print_success("Constraint added successfully"); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - } - "remove" => { - if parts.len() < 4 { - println!("Usage: constraint remove "); - return Ok(()); - } - - let collection = parts[2].to_string(); - let constraint_name = parts[3].to_string(); - - let command = protocol::Command::RemoveConstraint { collection, constraint_name }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - print_success("Constraint removed successfully"); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - } - "list" => { - if parts.len() < 3 { - println!("Usage: constraint list "); - return Ok(()); - } - - let collection = parts[2]; - println!("Constraints for collection '{}':", collection); - println!(" - unique(email) - Ensures email addresses are unique"); - println!(" - not_null(name) - Ensures name field is not null"); - println!(" - check(age > 0) - Ensures age is positive"); - println!("(Constraint listing functionality to be implemented)"); - } - _ => { - println!("Usage: constraint add [value]"); - println!(" constraint remove "); - println!(" constraint list "); - } - } - - Ok(()) - } - - // ============ КОМАНДЫ ПРОЦЕДУР ============ - - async fn handle_procedure(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: procedure create "); - println!(" procedure call "); - println!("Example: procedure create hello_world 'print(\"Hello World\")'"); - return Ok(()); - } - - match parts[1] { - "create" => { - if parts.len() < 4 { - println!("Usage: procedure create "); - return Ok(()); - } - - let name = parts[2].to_string(); - let code = parts[3..].join(" ").into_bytes(); - - let command = protocol::Command::CreateProcedure { name, code }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(_) = response { - print_success("Procedure created successfully"); - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - } - "call" => { - if parts.len() < 3 { - println!("Usage: procedure call "); - return Ok(()); - } - - let name = parts[2].to_string(); - let command = protocol::Command::CallProcedure { name }; - - match self.database.execute_command(command) { - Ok(response) => { - if let protocol::Response::Success(data) = response { - if let Ok(result) = String::from_utf8(data) { - println!("Procedure result: {}", result); - } else { - println!("Procedure executed successfully"); - } - } else if let protocol::Response::Error(e) = response { - print_error(&format!("Error: {}", e)); - } - } - Err(e) => { - print_error(&format!("Error: {}", e)); - } - } - } - _ => { - println!("Usage: procedure create "); - println!(" procedure call "); - } - } - - Ok(()) - } - - // ============ КОМАНДЫ ТРИГГЕРОВ ============ - - async fn handle_trigger(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: trigger add "); - println!(" trigger list "); - println!("Events: before_create, after_create, before_update, after_update, before_delete, after_delete"); - println!("Example: trigger add log_creation after_create users 'print(\"User created\")'"); - return Ok(()); - } - - match parts[1] { - "add" => { - if parts.len() < 6 { - println!("Usage: trigger add "); - return Ok(()); - } - - let name = parts[2].to_string(); - let event_str = parts[3]; - let collection = parts[4].to_string(); - let lua_code = parts[5..].join(" "); - - let event = match event_str { - "before_create" => TriggerEvent::BeforeCreate, - "after_create" => TriggerEvent::AfterCreate, - "before_update" => TriggerEvent::BeforeUpdate, - "after_update" => TriggerEvent::AfterUpdate, - "before_delete" => TriggerEvent::BeforeDelete, - "after_delete" => TriggerEvent::AfterDelete, - _ => { - print_error(&format!("Invalid event type: {}. Use: before_create, after_create, before_update, after_update, before_delete, after_delete", event_str)); - return Ok(()); - } - }; - - let trigger = Trigger { - name, - event, - collection, - lua_code, - }; - - match self.database.add_trigger(trigger) { - Ok(_) => { - print_success("Trigger added successfully"); - } - Err(e) => { - print_error(&format!("Error adding trigger: {}", e)); - } - } - } - "list" => { - if parts.len() < 3 { - println!("Usage: trigger list "); - return Ok(()); - } - - let collection = parts[2]; - println!("Triggers for collection '{}':", collection); - println!(" - log_creation (after_create) - Logs when a document is created"); - println!(" - validate_email (before_create) - Validates email format"); - println!(" - update_timestamp (before_update) - Updates the _timestamp field"); - println!("(Trigger listing functionality to be implemented)"); - } - _ => { - println!("Usage: trigger add "); - println!(" trigger list "); - } - } - - Ok(()) - } - - // ============ КОМАНДЫ ШАРДИНГА ============ - - async fn handle_shard(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: shard add
"); - println!(" shard migrate "); - println!(" shard status"); - println!("Example: shard add node1 127.0.0.1:8081 1073741824"); - return Ok(()); - } - - match parts[1] { - "add" => { - if parts.len() < 5 { - println!("Usage: shard add
"); - return Ok(()); - } - - let node_id = parts[2].to_string(); - let address = parts[3].to_string(); - let capacity: u64 = parts[4].parse().unwrap_or(1024 * 1024 * 1024); // 1GB default - - match self.sharding_manager.add_node(node_id, address, capacity) { - Ok(_) => { - print_success("Shard node added successfully"); - } - Err(e) => { - print_error(&format!("Error adding shard node: {}", e)); - } - } - } - "migrate" => { - if parts.len() < 6 { - println!("Usage: shard migrate "); - return Ok(()); - } - - let collection = parts[2].to_string(); - let from_node = parts[3].to_string(); - let to_node = parts[4].to_string(); - let shard_key = parts[5].to_string(); - - match self.sharding_manager.migrate_shard(&collection, &from_node, &to_node, &shard_key) { - Ok(_) => { - print_success(&format!("Shard migration initiated for collection '{}'", collection)); - } - Err(e) => { - print_error(&format!("Error migrating shard: {}", e)); - } - } - } - "status" => { - match self.sharding_manager.get_cluster_status() { - Ok(status) => { - println!("Sharding Status:"); - println!(" Cluster Formed: {}", status.cluster_formed); - println!(" Leader Exists: {}", status.leader_exists); - println!(" Rebalance Needed: {}", status.rebalance_needed); - println!(" Total Nodes: {}", status.nodes.len()); - println!(" Total Capacity: {} bytes", status.total_capacity); - println!(" Total Used: {} bytes", status.total_used); - println!(" Raft Nodes: {}", status.raft_nodes.len()); - - if !status.nodes.is_empty() { - println!(" Nodes:"); - for node in status.nodes { - let usage_percent = if node.capacity > 0 { - (node.used as f64 / node.capacity as f64) * 100.0 - } else { - 0.0 - }; - println!(" - {}: {} ({:.2}% used, {} collections)", - node.node_id, node.address, usage_percent, node.collections.len()); - } - } - } - Err(e) => { - print_error(&format!("Error getting shard status: {}", e)); - } - } - } - _ => { - println!("Usage: shard add
"); - println!(" shard migrate "); - println!(" shard status"); - } - } - - Ok(()) - } - - // ============ КОМАНДЫ КЛАСТЕРА ============ - - async fn handle_cluster_status(&self, _parts: Vec<&str>) -> Result<()> { - match self.sharding_manager.get_cluster_status() { - Ok(status) => { - println!("Cluster Status:"); - println!(" Formed: {}", status.cluster_formed); - println!(" Leader Exists: {}", status.leader_exists); - println!(" Total Capacity: {} bytes", status.total_capacity); - println!(" Total Used: {} bytes", status.total_used); - println!(" Rebalance Needed: {}", status.rebalance_needed); - println!(" Nodes: {}", status.nodes.len()); - - if !status.nodes.is_empty() { - println!(" Node Details:"); - for node in status.nodes { - let usage = if node.capacity > 0 { - (node.used as f64 / node.capacity as f64) * 100.0 - } else { - 0.0 - }; - println!(" - {}: {} ({:.2}% used)", node.node_id, node.address, usage); - } - } - - println!(" Raft Nodes: {}", status.raft_nodes.len()); - if !status.raft_nodes.is_empty() { - println!(" Raft Details:"); - for raft_node in status.raft_nodes { - println!(" - {}: {} (term: {}, state: {})", - raft_node.node_id, raft_node.address, raft_node.term, raft_node.state); - } - } - } - Err(e) => { - println!("Error getting cluster status: {}", e); - } - } - Ok(()) - } - - async fn handle_add_node(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: add.node or add.node "); - return Ok(()); - } - - let node_address = parts[1].to_string(); - let node_id = if parts.len() > 2 { - parts[2].to_string() - } else { - format!("node_{}", uuid::Uuid::new_v4().to_string()[..8].to_string()) - }; - - let capacity = if parts.len() > 3 { - parts[3].parse().unwrap_or(1024 * 1024 * 1024) // 1GB default - } else { - 1024 * 1024 * 1024 - }; - - match self.sharding_manager.add_node(node_id.clone(), node_address.clone(), capacity) { - Ok(_) => { - print_success(&format!("Node '{}' added to cluster at address '{}' with capacity {} bytes", - node_id, node_address, capacity)); - } - Err(e) => { - print_error(&format!("Error adding node: {}", e)); - } - } - - Ok(()) - } - - async fn handle_evict_node(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: evict.node or evict.node "); - return Ok(()); - } - - let node_address = parts[1].to_string(); - - // Находим node_id по адресу - let mut node_id_to_remove = None; - for entry in self.sharding_manager.get_nodes() { - if entry.address == node_address { - node_id_to_remove = Some(entry.node_id.clone()); - break; - } - } - - if let Some(node_id) = node_id_to_remove { - match self.sharding_manager.remove_node(&node_id) { - Ok(_) => { - print_success(&format!("Node '{}' at address '{}' removed from cluster", node_id, node_address)); - } - Err(e) => { - print_error(&format!("Error removing node: {}", e)); - } - } - } else { - print_error(&format!("Node with address '{}' not found in cluster", node_address)); - } - - Ok(()) - } - - async fn handle_list_raft_nodes(&self, _parts: Vec<&str>) -> Result<()> { - let raft_nodes = self.sharding_manager.get_raft_nodes(); - println!("Raft Nodes ({}):", raft_nodes.len()); - for node in raft_nodes { - println!(" - {}: {} (term: {}, state: {:?}, last_heartbeat: {})", - node.node_id, node.address, node.term, node.state, node.last_heartbeat); - } - Ok(()) - } - - async fn handle_cluster_rebalance(&self, _parts: Vec<&str>) -> Result<()> { - match self.sharding_manager.rebalance_cluster() { - Ok(_) => { - print_success("Cluster rebalancing completed successfully"); - } - Err(e) => { - print_error(&format!("Error rebalancing cluster: {}", e)); - } - } - Ok(()) - } - - // ============ CSV КОМАНДЫ ============ - - async fn handle_csv(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: csv import "); - println!(" csv export "); - println!(" csv list"); - println!(" csv progress "); - println!(" csv check "); - return Ok(()); - } - - match parts[1] { - "import" => { - if parts.len() < 4 { - println!("Usage: csv import "); - return Ok(()); - } - - let collection = parts[2].to_string(); - let file_path = parts[3].to_string(); - - // Проверяем существование файла - if !self.csv_manager.file_exists(&file_path) { - print_error(&format!("Error: File '{}' does not exist", file_path)); - return Ok(()); - } - - match self.csv_manager.import_csv(&collection, &file_path) { - Ok(count) => { - print_success(&format!("Successfully imported {} records from '{}' to collection '{}'", - count, file_path, collection)); - } - Err(e) => { - print_error(&format!("Error importing CSV: {}", e)); - } - } - } - "export" => { - if parts.len() < 4 { - println!("Usage: csv export "); - return Ok(()); - } - - let collection = parts[2].to_string(); - let file_path = parts[3].to_string(); - - match self.csv_manager.export_csv(&collection, &file_path) { - Ok(count) => { - print_success(&format!("Successfully exported {} records from collection '{}' to '{}'", - count, collection, file_path)); - } - Err(e) => { - print_error(&format!("Error exporting CSV: {}", e)); - } - } - } - "list" => { - match self.csv_manager.list_csv_files() { - Ok(files) => { - if files.is_empty() { - println!("No CSV files found in import directory"); - } else { - println!("CSV files in import directory:"); - for file in files { - println!(" - {}", file); - } - } - } - Err(e) => { - print_error(&format!("Error listing CSV files: {}", e)); - } - } - } - "progress" => { - if parts.len() < 3 { - println!("Usage: csv progress "); - return Ok(()); - } - - let collection = parts[2].to_string(); - let progress = self.csv_manager.get_import_progress(&collection); - println!("Import progress for collection '{}': {:.2}%", collection, progress); - } - "check" => { - if parts.len() < 3 { - println!("Usage: csv check "); - return Ok(()); - } - - let file_path = parts[2]; - if self.csv_manager.file_exists(file_path) { - println!("File '{}' exists", file_path); - } else { - println!("File '{}' does not exist", file_path); - } - } - _ => { - println!("Usage: csv import "); - println!(" csv export "); - println!(" csv list"); - println!(" csv progress "); - println!(" csv check "); - } - } - - Ok(()) - } - - // ============ КОМАНДЫ БЭКАПА ============ - - async fn handle_backup(&self, parts: Vec<&str>) -> Result<()> { - if parts.len() < 2 { - println!("Usage: backup create [backup_name]"); - println!(" backup restore "); - println!(" backup list"); - return Ok(()); - } - - match parts[1] { - "create" => { - let backup_name = if parts.len() > 2 { - parts[2].to_string() - } else { - format!("backup_{}", chrono::Local::now().format("%Y%m%d_%H%M%S")) - }; - - match self.database.create_backup() { - Ok(backup) => { - let backup_file = format!("./futriix_backups/{}.json", backup_name); - - // Создаем директорию для бэкапов, если не существует - let _ = std::fs::create_dir_all("./futriix_backups"); - - // Сохраняем бэкап в файл - let backup_json = serde_json::to_string_pretty(&backup) - .map_err(|e| crate::common::FutriixError::SerializationError(e.to_string()))?; - - std::fs::write(&backup_file, backup_json) - .map_err(|e| crate::common::FutriixError::IoError(e))?; - - print_success(&format!("Backup created successfully: {} ({} collections)", - backup_file, backup.len())); - } - Err(e) => { - print_error(&format!("Error creating backup: {}", e)); - } - } - } - "restore" => { - if parts.len() < 3 { - println!("Usage: backup restore "); - return Ok(()); - } - - let backup_path = parts[2]; - println!("Restoring from backup '{}'...", backup_path); - - // Загружаем бэкап из файла - let backup_content = std::fs::read_to_string(backup_path) - .map_err(|e| crate::common::FutriixError::IoError(e))?; - - let backup: std::collections::HashMap>> = - serde_json::from_str(&backup_content) - .map_err(|e| crate::common::FutriixError::SerializationError(e.to_string()))?; - - match self.database.restore_from_backup(backup) { - Ok(_) => { - print_success("Backup restored successfully"); - } - Err(e) => { - print_error(&format!("Error restoring backup: {}", e)); - } - } - } - "list" => { - let backup_dir = "./futriix_backups"; - if let Ok(entries) = std::fs::read_dir(backup_dir) { - println!("Available backups:"); - let mut backups = Vec::new(); - for entry in entries.flatten() { - let path = entry.path(); - if path.is_file() { - if let Some(extension) = path.extension() { - if extension == "json" { - if let Some(file_name) = path.file_name() { - backups.push(file_name.to_string_lossy().to_string()); - } - } - } - } - } - - if backups.is_empty() { - println!(" No backups found"); - } else { - for backup in backups { - println!(" - {}", backup); - } - } - } else { - println!("Backup directory not found"); - } - } - _ => { - println!("Usage: backup create [backup_name]"); - println!(" backup restore "); - println!(" backup list"); - } - } - - Ok(()) - } - - // ============ СИСТЕМНЫЕ КОМАНДЫ ============ - - async fn handle_stats(&self, _parts: Vec<&str>) -> Result<()> { - match self.database.get_stats() { - Ok(stats) => { - println!("Database Statistics:"); - for (key, value) in stats { - println!(" - {}: {}", key, value); - } - - // Дополнительная информация о CSV - let active_imports = self.csv_manager.active_imports_count(); - if active_imports > 0 { - println!(" - active_csv_imports: {}", active_imports); - } - - // Информация о кластере - if self.sharding_manager.is_cluster_formed() { - println!(" - cluster_mode: enabled"); - } else { - println!(" - cluster_mode: standalone"); - } - } - Err(e) => { - print_error(&format!("Error getting stats: {}", e)); - } - } - Ok(()) - } - - async fn handle_ping(&self, _parts: Vec<&str>) -> Result<()> { - print_success("pong - Futriix server is running"); - Ok(()) - } - - async fn show_system_status(&self) -> Result<()> { - println!("Futriix System Status:"); - println!(" - Database: ✓ Connected"); - println!(" - Lua Engine: ✓ Running"); - println!(" - Sharding: {}", if self.sharding_manager.is_cluster_formed() { "✓ Cluster formed" } else { "✗ Standalone mode" }); - println!(" - CSV Manager: ✓ Ready"); - println!(" - Active Transaction: {}", - self.current_transaction.as_ref().map_or("None".to_string(), |t| t.clone())); - - // Проверяем доступность системных коллекций - let sys_collections = ["_system", "_users", "_logs", "_procedures", "_triggers"]; - println!(" - System Collections:"); - for coll in &sys_collections { - println!(" - {}: ✓", coll); - } - - Ok(()) - } - - /// Показать справку по командам - async fn show_help(&self) -> Result<()> { - println!("Futriix Database Shell - Available Commands:"); - println!(""); - println!("=== BASIC CRUD OPERATIONS ==="); - println!(" create - Create document"); - // Исправляем строку с двойными фигурными скобками {{ и }} для экранирования - println!(" create - Example: create users '{{\"name\": \"Alice\", \"email\": \"alice@example.com\"}}'"); - println!(" read - Read document"); - println!(" update - Update document"); - println!(" delete - Delete document"); - println!(" list [filter] - List documents"); - println!(""); - println!("=== TRANSACTIONS ==="); - println!(" begin - Start transaction"); - println!(" commit - Commit transaction"); - println!(" rollback - Rollback transaction"); - println!(""); - println!("=== INDEXES ==="); - println!(" index create [unique] - Create index"); - println!(" index query - Query by index"); - println!(""); - println!("=== CONSTRAINTS ==="); - println!(" constraint add [value] - Add constraint"); - println!(" constraint remove - Remove constraint"); - println!(" constraint list - List constraints"); - println!(""); - println!("=== STORED PROCEDURES ==="); - println!(" procedure create - Create stored procedure"); - println!(" procedure call - Call stored procedure"); - println!(""); - println!("=== TRIGGERS ==="); - println!(" trigger add - Add trigger"); - println!(" trigger list - List triggers"); - println!(""); - println!("=== CLUSTER MANAGEMENT ==="); - println!(" cluster.status - Show cluster status"); - println!(" add.node [node_id] [capacity] - Add node to cluster"); - println!(" evict.node - Remove node from cluster"); - println!(" list.raft.nodes - List Raft nodes"); - println!(" cluster.rebalance - Rebalance cluster"); - println!(""); - println!("=== SHARDING ==="); - println!(" shard add - Add shard node"); - println!(" shard migrate - Migrate shard"); - println!(" shard status - Show shard status"); - println!(""); - println!("=== CSV OPERATIONS ==="); - println!(" csv import - Import CSV to collection"); - println!(" csv export - Export collection to CSV"); - println!(" csv list - List CSV files"); - println!(" csv progress - Show import progress"); - println!(" csv check - Check if file exists"); - println!(""); - println!("=== BACKUP ==="); - println!(" backup create [name] - Create backup"); - println!(" backup restore - Restore from backup"); - println!(" backup list - List available backups"); - println!(""); - println!("=== SYSTEM COMMANDS ==="); - println!(" stats - Show database statistics"); - println!(" status - Show system status"); - println!(" ping - Test server connection"); - println!(" inbox.stop - Exit database mode (return to Lua)"); - println!(" clear / cls - Clear screen"); - println!(" help - Show this help"); - println!(" exit / quit - Exit Futriix shell"); - println!(""); - println!("=== LUA MODE COMMANDS ==="); - println!(" inbox.start - Enter database command mode"); - println!(" version - Show version info"); - println!(" env - Show environment info"); - println!(""); - println!("=== EXAMPLES ==="); - // Исправляем строку с двойными фигурными скобками {{ и }} для экранирования - println!(" Create user: create users '{{\"name\": \"Alice\", \"email\": \"alice@example.com\"}}'"); - println!(" List users: list users"); - println!(" Create index: index create users email_idx email unique"); - println!(" Import CSV: csv import users /path/to/users.csv"); - println!(" Backup: backup create"); - - Ok(()) - } -}