// /futriis/internal/engine/engine.go // Пакет engine реализует ядро СУБД, координирующее все операции package engine import ( "fmt" "strings" "futriis/internal/cluster" "futriis/internal/lua" "futriis/internal/replication" "futriis/internal/storage" "futriis/internal/transaction" "futriis/pkg/config" "futriis/pkg/utils" ) // Engine представляет ядро СУБД type Engine struct { storage *storage.Storage clusterMgr *cluster.ClusterManager txMgr *transaction.TransactionManager luaMgr *lua.PluginManager aofMgr *replication.AOFManager cfg *config.Config } // NewEngine создаёт новый экземпляр ядра СУБД func NewEngine() *Engine { cfg := config.Get() // Создаём AOF менеджер aofMgr, _ := replication.NewAOFManager(cfg.Node.AOFFile, cfg.Node.AOFEnabled) // Воспроизводим AOF если нужно if aofMgr != nil && cfg.Node.AOFEnabled { // TODO: восстановление состояния из AOF } return &Engine{ storage: storage.NewStorage(), clusterMgr: cluster.NewClusterManager(cfg), txMgr: transaction.NewTransactionManager(), luaMgr: lua.NewPluginManager(&cfg.Lua), aofMgr: aofMgr, cfg: cfg, } } // Execute выполняет команду и возвращает результат func (e *Engine) Execute(input string) (string, error) { // Разбираем ввод parts := strings.Fields(input) if len(parts) == 0 { return "", nil } command := strings.ToLower(parts[0]) args := parts[1:] // Записываем команду в AOF if e.aofMgr != nil { argsInterface := make([]interface{}, len(args)) for i, v := range args { argsInterface[i] = v } e.aofMgr.Append(command, argsInterface) } // Обработка команд switch command { case "help": return e.help(), nil case "exit", "quit": return "exit", nil case "create": return e.handleCreate(args) case "delete": return e.handleDelete(args) case "update": return e.handleUpdate(args) case "list": return e.handleList(args) case "show": return e.handleShow(args) case "begin": return e.handleBegin() case "commit": return e.handleCommit() case "rollback": return e.handleRollback() case "cluster.status": return e.handleClusterStatus() case "add.node": return e.handleAddNode(args) case "evict.node": return e.handleEvictNode(args) case "lua": return e.handleLua(args) default: return "", fmt.Errorf("неизвестная команда: %s", command) } } // handleCreate обрабатывает команды создания func (e *Engine) handleCreate(args []string) (string, error) { if len(args) < 2 { return "", fmt.Errorf("недостаточно аргументов для команды create") } switch args[0] { case "tapple": if len(args) < 2 { return "", fmt.Errorf("не указано имя таппла") } return e.createTapple(args[1]) case "slice": if len(args) < 3 { return "", fmt.Errorf("недостаточно аргументов для создания слайса") } return e.createSlice(args[1], args[2]) case "tuple": if len(args) < 4 { return "", fmt.Errorf("недостаточно аргументов для создания кортежа") } return e.createTuple(args[1], args[2], args[3], args[4:]) default: return "", fmt.Errorf("неизвестный тип создания: %s", args[0]) } } // handleDelete обрабатывает команды удаления func (e *Engine) handleDelete(args []string) (string, error) { if len(args) < 2 { return "", fmt.Errorf("недостаточно аргументов для команды delete") } switch args[0] { case "tapple": if len(args) < 2 { return "", fmt.Errorf("не указано имя таппла") } return e.deleteTapple(args[1]) case "slice": if len(args) < 3 { return "", fmt.Errorf("недостаточно аргументов для удаления слайса") } return e.deleteSlice(args[1], args[2]) case "tuple": if len(args) < 4 { return "", fmt.Errorf("недостаточно аргументов для удаления кортежа") } return e.deleteTuple(args[1], args[2], args[3]) default: return "", fmt.Errorf("неизвестный тип удаления: %s", args[0]) } } // handleUpdate обрабатывает команды обновления func (e *Engine) handleUpdate(args []string) (string, error) { if len(args) < 4 || args[0] != "tuple" { return "", fmt.Errorf("неверная команда update") } return e.updateTuple(args[1], args[2], args[3], args[4:]) } // handleList обрабатывает команды списка func (e *Engine) handleList(args []string) (string, error) { if len(args) < 1 { return "", fmt.Errorf("недостаточно аргументов для команды list") } switch args[0] { case "tapples": return e.listTapples() case "slices": if len(args) < 2 { return "", fmt.Errorf("не указано имя таппла") } return e.listSlices(args[1]) default: return "", fmt.Errorf("неизвестный тип списка: %s", args[0]) } } // handleShow обрабатывает команды показа func (e *Engine) handleShow(args []string) (string, error) { if len(args) < 2 || args[0] != "tuples" { return "", fmt.Errorf("неверная команда show") } if len(args) < 3 { return "", fmt.Errorf("недостаточно аргументов для show tuples") } return e.showTuples(args[1], args[2]) } // handleBegin начинает транзакцию func (e *Engine) handleBegin() (string, error) { id, err := e.txMgr.Begin() if err != nil { return "", err } return utils.ColorGreen + "Транзакция начата. ID: " + id + utils.ColorReset, nil } // handleCommit фиксирует транзакцию func (e *Engine) handleCommit() (string, error) { err := e.txMgr.Commit() if err != nil { return "", err } return utils.ColorGreen + "Транзакция зафиксирована" + utils.ColorReset, nil } // handleRollback откатывает транзакцию func (e *Engine) handleRollback() (string, error) { err := e.txMgr.Rollback() if err != nil { return "", err } return utils.ColorGreen + "Транзакция отменена" + utils.ColorReset, nil } // handleClusterStatus показывает статус кластера func (e *Engine) handleClusterStatus() (string, error) { status := e.clusterMgr.GetClusterStatus() result := utils.ColorCyan + "Статус кластера:" + utils.ColorReset + "\n" result += fmt.Sprintf(" Всего узлов: %d\n", status["total_nodes"]) result += fmt.Sprintf(" Активных узлов: %d\n", status["active_nodes"]) result += fmt.Sprintf(" Координатор: %v\n", status["coordinator"]) nodes, _ := status["nodes"].([]map[string]interface{}) if len(nodes) > 0 { result += utils.ColorCyan + "\nУзлы кластера:" + utils.ColorReset + "\n" for _, node := range nodes { result += fmt.Sprintf(" %s (%s) - %s, last seen: %s\n", node["id"], node["address"], node["state"], node["last_seen"]) } } return result, nil } // handleAddNode добавляет узел в кластер func (e *Engine) handleAddNode(args []string) (string, error) { if len(args) < 1 { return "", fmt.Errorf("укажите адрес узла") } address := args[0] err := e.clusterMgr.AddNode(address) if err != nil { return "", err } return utils.ColorGreen + "Узел " + address + " добавлен в кластер" + utils.ColorReset, nil } // handleEvictNode удаляет узел из кластера func (e *Engine) handleEvictNode(args []string) (string, error) { if len(args) < 1 { return "", fmt.Errorf("укажите ID узла или адрес") } nodeID := args[0] err := e.clusterMgr.RemoveNode(nodeID) if err != nil { return "", err } return utils.ColorGreen + "Узел " + nodeID + " удален из кластера" + utils.ColorReset, nil } // handleLua выполняет Lua скрипт func (e *Engine) handleLua(args []string) (string, error) { if len(args) < 1 { return "", fmt.Errorf("укажите имя плагина") } pluginName := args[0] err := e.luaMgr.ExecutePlugin(pluginName) if err != nil { return "", err } return utils.ColorGreen + "Плагин выполнен" + utils.ColorReset, nil } // Методы для работы с тапплами func (e *Engine) createTapple(name string) (string, error) { tapple, err := e.storage.GetTappleManager().CreateTapple(name) if err != nil { return "", err } return utils.ColorGreen + "Таппл '" + tapple.Name + "' успешно создан" + utils.ColorReset, nil } func (e *Engine) deleteTapple(name string) (string, error) { err := e.storage.GetTappleManager().DeleteTapple(name) if err != nil { return "", err } return utils.ColorGreen + "Таппл '" + name + "' успешно удален" + utils.ColorReset, nil } func (e *Engine) listTapples() (string, error) { tapples := e.storage.GetTappleManager().ListTapples() if len(tapples) == 0 { return utils.ColorYellow + "Тапплы не найдены" + utils.ColorReset, nil } result := utils.ColorCyan + "Список тапплов:" + utils.ColorReset + "\n" for _, t := range tapples { result += " " + utils.ColorGreen + t + utils.ColorReset + "\n" } return result, nil } // Методы для работы со слайсами func (e *Engine) createSlice(tappleName, sliceName string) (string, error) { tapple, err := e.storage.GetTappleManager().GetTapple(tappleName) if err != nil { return "", err } slice, err := e.storage.GetTappleManager().GetSliceManager().CreateSlice(tapple, sliceName) if err != nil { return "", err } return utils.ColorGreen + "Слайс '" + slice.Name + "' в таппле '" + tappleName + "' успешно создан" + utils.ColorReset, nil } func (e *Engine) deleteSlice(tappleName, sliceName string) (string, error) { tapple, err := e.storage.GetTappleManager().GetTapple(tappleName) if err != nil { return "", err } err = e.storage.GetTappleManager().GetSliceManager().DeleteSlice(tapple, sliceName) if err != nil { return "", err } return utils.ColorGreen + "Слайс '" + sliceName + "' в таппле '" + tappleName + "' успешно удален" + utils.ColorReset, nil } func (e *Engine) listSlices(tappleName string) (string, error) { tapple, err := e.storage.GetTappleManager().GetTapple(tappleName) if err != nil { return "", err } slices := e.storage.GetTappleManager().GetSliceManager().ListSlices(tapple) if len(slices) == 0 { return utils.ColorYellow + "Слайсы в таппле '" + tappleName + "' не найдены" + utils.ColorReset, nil } result := utils.ColorCyan + "Список слайсов в таппле '" + tappleName + "':" + utils.ColorReset + "\n" for _, s := range slices { result += " " + utils.ColorGreen + s + utils.ColorReset + "\n" } return result, nil } // Методы для работы с кортежами func (e *Engine) createTuple(tappleName, sliceName, tupleID string, fieldsArgs []string) (string, error) { tapple, err := e.storage.GetTappleManager().GetTapple(tappleName) if err != nil { return "", err } slice, err := e.storage.GetTappleManager().GetSliceManager().GetSlice(tapple, sliceName) if err != nil { return "", err } // Парсим поля fields := make(map[string]interface{}) for _, arg := range fieldsArgs { parts := strings.SplitN(arg, "=", 2) if len(parts) == 2 { fields[parts[0]] = parts[1] } } tuple, err := e.storage.GetTappleManager().GetSliceManager().GetTupleManager().CreateTuple(slice, tupleID, fields) if err != nil { return "", err } return utils.ColorGreen + "Кортеж '" + tuple.ID + "' в слайсе '" + sliceName + "' успешно создан" + utils.ColorReset, nil } func (e *Engine) deleteTuple(tappleName, sliceName, tupleID string) (string, error) { tapple, err := e.storage.GetTappleManager().GetTapple(tappleName) if err != nil { return "", err } slice, err := e.storage.GetTappleManager().GetSliceManager().GetSlice(tapple, sliceName) if err != nil { return "", err } err = e.storage.GetTappleManager().GetSliceManager().GetTupleManager().DeleteTuple(slice, tupleID) if err != nil { return "", err } return utils.ColorGreen + "Кортеж '" + tupleID + "' в слайсе '" + sliceName + "' успешно удален" + utils.ColorReset, nil } func (e *Engine) updateTuple(tappleName, sliceName, tupleID string, fieldsArgs []string) (string, error) { tapple, err := e.storage.GetTappleManager().GetTapple(tappleName) if err != nil { return "", err } slice, err := e.storage.GetTappleManager().GetSliceManager().GetSlice(tapple, sliceName) if err != nil { return "", err } // Парсим поля fields := make(map[string]interface{}) for _, arg := range fieldsArgs { parts := strings.SplitN(arg, "=", 2) if len(parts) == 2 { fields[parts[0]] = parts[1] } } tuple, err := e.storage.GetTappleManager().GetSliceManager().GetTupleManager().UpdateTuple(slice, tupleID, fields) if err != nil { return "", err } return utils.ColorGreen + "Кортеж '" + tuple.ID + "' в слайсе '" + sliceName + "' успешно обновлен" + utils.ColorReset, nil } // showTuples показывает все кортежи в слайсе func (e *Engine) showTuples(tappleName, sliceName string) (string, error) { tapple, err := e.storage.GetTappleManager().GetTapple(tappleName) if err != nil { return "", err } slice, err := e.storage.GetTappleManager().GetSliceManager().GetSlice(tapple, sliceName) if err != nil { return "", err } slice.RLock() defer slice.RUnlock() if len(slice.Tuples) == 0 { return utils.ColorYellow + "Кортежи в слайсе '" + sliceName + "' не найдены" + utils.ColorReset, nil } result := utils.ColorCyan + "Список кортежей в слайсе '" + sliceName + "':" + utils.ColorReset + "\n" for id, tuple := range slice.Tuples { result += " " + utils.ColorGreen + "ID: " + id + utils.ColorReset + "\n" for k, v := range tuple.Fields { // ИСПРАВЛЕНО: заменено ColorPrompt на ColorPromptCode result += " " + utils.ColorYellow + k + utils.ColorReset + ": " + utils.ColorPromptCode + fmt.Sprint(v) + utils.ColorReset + "\n" } result += "\n" } return result, nil } // help возвращает справку по командам func (e *Engine) help() string { help := utils.ColorCyan + "Доступные команды:" + utils.ColorReset + "\n" help += "\n" + utils.ColorYellow + "Основные команды:" + utils.ColorReset + "\n" help += " " + utils.ColorGreen + "create tapple " + utils.ColorReset + " - создать новый таппл (базу данных)\n" help += " " + utils.ColorGreen + "create slice " + utils.ColorReset + " - создать новый слайс (таблицу)\n" help += " " + utils.ColorGreen + "create tuple [key=value...]" + utils.ColorReset + " - создать новый кортеж (запись)\n" help += " " + utils.ColorGreen + "delete tapple " + utils.ColorReset + " - удалить таппл\n" help += " " + utils.ColorGreen + "delete slice " + utils.ColorReset + " - удалить слайс\n" help += " " + utils.ColorGreen + "delete tuple " + utils.ColorReset + " - удалить кортеж\n" help += " " + utils.ColorGreen + "update tuple [key=value...]" + utils.ColorReset + " - обновить кортеж\n" help += " " + utils.ColorGreen + "list tapples" + utils.ColorReset + " - показать все тапплы\n" help += " " + utils.ColorGreen + "list slices " + utils.ColorReset + " - показать все слайсы в таппле\n" help += " " + utils.ColorGreen + "show tuples " + utils.ColorReset + " - показать все кортежи в слайсе\n" help += "\n" + utils.ColorYellow + "Транзакции:" + utils.ColorReset + "\n" help += " " + utils.ColorGreen + "begin" + utils.ColorReset + " - начать транзакцию\n" help += " " + utils.ColorGreen + "commit" + utils.ColorReset + " - зафиксировать транзакцию\n" help += " " + utils.ColorGreen + "rollback" + utils.ColorReset + " - отменить транзакцию\n" help += "\n" + utils.ColorYellow + "Управление кластером:" + utils.ColorReset + "\n" help += " " + utils.ColorGreen + "cluster.status" + utils.ColorReset + " - показать статус кластера\n" help += " " + utils.ColorGreen + "add.node
" + utils.ColorReset + " - добавить узел в кластер\n" help += " " + utils.ColorGreen + "evict.node " + utils.ColorReset + " - удалить узел из кластера\n" help += "\n" + utils.ColorYellow + "Lua плагины:" + utils.ColorReset + "\n" help += " " + utils.ColorGreen + "lua " + utils.ColorReset + " - выполнить Lua плагин\n" help += "\n" + utils.ColorYellow + "Прочее:" + utils.ColorReset + "\n" help += " " + utils.ColorGreen + "exit/quit" + utils.ColorReset + " - выйти из СУБД\n" return help }