Upload files to "internal/lua"
This commit is contained in:
parent
72b970422f
commit
d5376cd06d
598
internal/lua/plugin.go
Normal file
598
internal/lua/plugin.go
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
// /futriis/internal/lua/plugin.go
|
||||||
|
// Пакет lua реализует систему плагинов на языке Lua для расширения функциональности СУБД.
|
||||||
|
// PluginManager управляет загрузкой, хранением и выполнением Lua-скриптов из указанной директории.
|
||||||
|
// Предоставляет мост между Go и Lua через регистрацию функций, доступных из скриптов.
|
||||||
|
// Позволяет динамически расширять возможности базы данных без перекомпиляции основного кода.
|
||||||
|
// Поддерживает возможность отключения через конфигурацию для минимизации ресурсов.
|
||||||
|
|
||||||
|
package lua
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"futriis/pkg/config"
|
||||||
|
"futriis/pkg/types"
|
||||||
|
"futriis/pkg/utils"
|
||||||
|
"github.com/yuin/gopher-lua"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StorageInterface определяет интерфейс для взаимодействия с хранилищем из Lua плагинов
|
||||||
|
type StorageInterface interface {
|
||||||
|
// GetValue получает значение из хранилища по ключу
|
||||||
|
GetValue(key string) (interface{}, error)
|
||||||
|
|
||||||
|
// SetValue устанавливает значение в хранилище
|
||||||
|
SetValue(key string, value interface{}) error
|
||||||
|
|
||||||
|
// GetTuple получает кортеж по идентификатору
|
||||||
|
GetTuple(tappleName, sliceName, tupleID string) (*types.Tuple, error)
|
||||||
|
|
||||||
|
// CreateTuple создаёт новый кортеж
|
||||||
|
CreateTuple(tappleName, sliceName, tupleID string, fields map[string]interface{}) (*types.Tuple, error)
|
||||||
|
|
||||||
|
// UpdateTuple обновляет существующий кортеж
|
||||||
|
UpdateTuple(tappleName, sliceName, tupleID string, fields map[string]interface{}) (*types.Tuple, error)
|
||||||
|
|
||||||
|
// DeleteTuple удаляет кортеж
|
||||||
|
DeleteTuple(tappleName, sliceName, tupleID string) error
|
||||||
|
|
||||||
|
// FindTuples выполняет поиск кортежей по критериям
|
||||||
|
FindTuples(tappleName, sliceName string, criteria map[string]interface{}) ([]*types.Tuple, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginManager управляет Lua плагинами
|
||||||
|
type PluginManager struct {
|
||||||
|
state *lua.LState
|
||||||
|
plugins map[string]*lua.LFunction
|
||||||
|
mu sync.RWMutex
|
||||||
|
enabled bool
|
||||||
|
storage StorageInterface // Интерфейс для доступа к хранилищу
|
||||||
|
recovered bool // Флаг, были ли восстановлены данные
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPluginManager создаёт новый менеджер плагинов
|
||||||
|
func NewPluginManager(cfg *config.LuaConfig) *PluginManager {
|
||||||
|
pm := &PluginManager{
|
||||||
|
plugins: make(map[string]*lua.LFunction),
|
||||||
|
enabled: cfg.Enabled,
|
||||||
|
recovered: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Enabled {
|
||||||
|
pm.state = lua.NewState()
|
||||||
|
pm.registerFunctions()
|
||||||
|
|
||||||
|
// Загружаем плагины из директории
|
||||||
|
if err := pm.LoadPlugins(cfg.PluginsDir); err != nil {
|
||||||
|
// Добавляем пустую строку перед предупреждением
|
||||||
|
fmt.Println()
|
||||||
|
utils.PrintWarning("Failed to load Lua plugins: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pm
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStorage устанавливает интерфейс хранилища для плагинов
|
||||||
|
func (pm *PluginManager) SetStorage(storage StorageInterface) {
|
||||||
|
pm.mu.Lock()
|
||||||
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
|
pm.storage = storage
|
||||||
|
|
||||||
|
// Перерегистрируем функции с доступом к хранилищу
|
||||||
|
if pm.enabled && pm.state != nil && storage != nil {
|
||||||
|
pm.registerStorageFunctions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerFunctions регистрирует базовые функции Go, доступные из Lua
|
||||||
|
func (pm *PluginManager) registerFunctions() {
|
||||||
|
if !pm.enabled || pm.state == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Регистрируем функцию print для вывода в консоль
|
||||||
|
pm.state.SetGlobal("print", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
top := L.GetTop()
|
||||||
|
args := make([]string, top)
|
||||||
|
for i := 1; i <= top; i++ {
|
||||||
|
args[i-1] = L.Get(i).String()
|
||||||
|
}
|
||||||
|
utils.PrintInfo(strings.Join(args, " "))
|
||||||
|
return 0
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию type для получения типа значения
|
||||||
|
pm.state.SetGlobal("type", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
val := L.Get(1)
|
||||||
|
L.Push(lua.LString(val.Type().String()))
|
||||||
|
return 1
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию tostring для преобразования в строку
|
||||||
|
pm.state.SetGlobal("tostring", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
val := L.Get(1)
|
||||||
|
L.Push(lua.LString(val.String()))
|
||||||
|
return 1
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию error для генерации ошибки
|
||||||
|
pm.state.SetGlobal("error", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
msg := L.ToString(1)
|
||||||
|
L.RaiseError("%s", msg)
|
||||||
|
return 0
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerStorageFunctions регистрирует функции для работы с хранилищем
|
||||||
|
func (pm *PluginManager) registerStorageFunctions() {
|
||||||
|
if !pm.enabled || pm.state == nil || pm.storage == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Регистрируем функцию get для получения значения из хранилища
|
||||||
|
pm.state.SetGlobal("get", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
if pm.storage == nil {
|
||||||
|
L.RaiseError("storage not available")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
key := L.ToString(1)
|
||||||
|
value, err := pm.storage.GetValue(key)
|
||||||
|
if err != nil {
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конвертируем значение в Lua тип
|
||||||
|
luaValue := pm.toLuaValue(L, value)
|
||||||
|
L.Push(luaValue)
|
||||||
|
return 1
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию set для установки значения в хранилище
|
||||||
|
pm.state.SetGlobal("set", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
if pm.storage == nil {
|
||||||
|
L.RaiseError("storage not available")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
key := L.ToString(1)
|
||||||
|
value := L.Get(2)
|
||||||
|
|
||||||
|
// Конвертируем Lua значение в Go значение
|
||||||
|
goValue := pm.fromLuaValue(value)
|
||||||
|
|
||||||
|
err := pm.storage.SetValue(key, goValue)
|
||||||
|
if err != nil {
|
||||||
|
L.RaiseError("failed to set value: %v", err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.PrintInfo("Lua set: %s = %v", key, goValue)
|
||||||
|
return 0
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию get_tuple для получения кортежа
|
||||||
|
pm.state.SetGlobal("get_tuple", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
if pm.storage == nil {
|
||||||
|
L.RaiseError("storage not available")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tappleName := L.ToString(1)
|
||||||
|
sliceName := L.ToString(2)
|
||||||
|
tupleID := L.ToString(3)
|
||||||
|
|
||||||
|
tuple, err := pm.storage.GetTuple(tappleName, sliceName, tupleID)
|
||||||
|
if err != nil {
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
L.Push(lua.LString(err.Error()))
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конвертируем кортеж в Lua таблицу
|
||||||
|
tupleTable := pm.tupleToLuaTable(L, tuple)
|
||||||
|
L.Push(tupleTable)
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
return 2
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию create_tuple для создания кортежа
|
||||||
|
pm.state.SetGlobal("create_tuple", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
if pm.storage == nil {
|
||||||
|
L.RaiseError("storage not available")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tappleName := L.ToString(1)
|
||||||
|
sliceName := L.ToString(2)
|
||||||
|
tupleID := L.ToString(3)
|
||||||
|
|
||||||
|
// Получаем таблицу с полями
|
||||||
|
fieldsTable := L.ToTable(4)
|
||||||
|
fields := pm.luaTableToMap(fieldsTable)
|
||||||
|
|
||||||
|
tuple, err := pm.storage.CreateTuple(tappleName, sliceName, tupleID, fields)
|
||||||
|
if err != nil {
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
L.Push(lua.LString(err.Error()))
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
tupleTable := pm.tupleToLuaTable(L, tuple)
|
||||||
|
L.Push(tupleTable)
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
return 2
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию update_tuple для обновления кортежа
|
||||||
|
pm.state.SetGlobal("update_tuple", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
if pm.storage == nil {
|
||||||
|
L.RaiseError("storage not available")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tappleName := L.ToString(1)
|
||||||
|
sliceName := L.ToString(2)
|
||||||
|
tupleID := L.ToString(3)
|
||||||
|
|
||||||
|
fieldsTable := L.ToTable(4)
|
||||||
|
fields := pm.luaTableToMap(fieldsTable)
|
||||||
|
|
||||||
|
tuple, err := pm.storage.UpdateTuple(tappleName, sliceName, tupleID, fields)
|
||||||
|
if err != nil {
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
L.Push(lua.LString(err.Error()))
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
tupleTable := pm.tupleToLuaTable(L, tuple)
|
||||||
|
L.Push(tupleTable)
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
return 2
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию delete_tuple для удаления кортежа
|
||||||
|
pm.state.SetGlobal("delete_tuple", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
if pm.storage == nil {
|
||||||
|
L.RaiseError("storage not available")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tappleName := L.ToString(1)
|
||||||
|
sliceName := L.ToString(2)
|
||||||
|
tupleID := L.ToString(3)
|
||||||
|
|
||||||
|
err := pm.storage.DeleteTuple(tappleName, sliceName, tupleID)
|
||||||
|
if err != nil {
|
||||||
|
L.Push(lua.LFalse)
|
||||||
|
L.Push(lua.LString(err.Error()))
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Push(lua.LTrue)
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
return 2
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Регистрируем функцию find_tuples для поиска кортежей
|
||||||
|
pm.state.SetGlobal("find_tuples", pm.state.NewFunction(func(L *lua.LState) int {
|
||||||
|
if pm.storage == nil {
|
||||||
|
L.RaiseError("storage not available")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tappleName := L.ToString(1)
|
||||||
|
sliceName := L.ToString(2)
|
||||||
|
|
||||||
|
criteriaTable := L.ToTable(3)
|
||||||
|
criteria := pm.luaTableToMap(criteriaTable)
|
||||||
|
|
||||||
|
tuples, err := pm.storage.FindTuples(tappleName, sliceName, criteria)
|
||||||
|
if err != nil {
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
L.Push(lua.LString(err.Error()))
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаём таблицу с результатами
|
||||||
|
resultTable := L.NewTable()
|
||||||
|
for i, tuple := range tuples {
|
||||||
|
tupleTable := pm.tupleToLuaTable(L, tuple)
|
||||||
|
L.RawSet(resultTable, lua.LNumber(i+1), tupleTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Push(resultTable)
|
||||||
|
L.Push(lua.LNil)
|
||||||
|
return 2
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// toLuaValue конвертирует Go значение в Lua значение
|
||||||
|
func (pm *PluginManager) toLuaValue(L *lua.LState, value interface{}) lua.LValue {
|
||||||
|
if value == nil {
|
||||||
|
return lua.LNil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
return lua.LString(v)
|
||||||
|
case int:
|
||||||
|
return lua.LNumber(v)
|
||||||
|
case int64:
|
||||||
|
return lua.LNumber(v)
|
||||||
|
case float64:
|
||||||
|
return lua.LNumber(v)
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
return lua.LTrue
|
||||||
|
}
|
||||||
|
return lua.LFalse
|
||||||
|
case map[string]interface{}:
|
||||||
|
table := L.NewTable()
|
||||||
|
for key, val := range v {
|
||||||
|
L.RawSet(table, lua.LString(key), pm.toLuaValue(L, val))
|
||||||
|
}
|
||||||
|
return table
|
||||||
|
case []interface{}:
|
||||||
|
table := L.NewTable()
|
||||||
|
for i, val := range v {
|
||||||
|
L.RawSet(table, lua.LNumber(i+1), pm.toLuaValue(L, val))
|
||||||
|
}
|
||||||
|
return table
|
||||||
|
default:
|
||||||
|
return lua.LString(fmt.Sprintf("%v", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fromLuaValue конвертирует Lua значение в Go значение
|
||||||
|
func (pm *PluginManager) fromLuaValue(value lua.LValue) interface{} {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case lua.LString:
|
||||||
|
return string(v)
|
||||||
|
case lua.LNumber:
|
||||||
|
return float64(v)
|
||||||
|
case lua.LBool:
|
||||||
|
return bool(v)
|
||||||
|
case *lua.LTable:
|
||||||
|
// Определяем, является ли таблица массивом или словарём
|
||||||
|
// Пытаемся определить по наличию числовых ключей
|
||||||
|
isArray := false
|
||||||
|
var maxNum lua.LNumber = 0
|
||||||
|
|
||||||
|
v.ForEach(func(key lua.LValue, val lua.LValue) {
|
||||||
|
if numKey, ok := key.(lua.LNumber); ok {
|
||||||
|
isArray = true
|
||||||
|
if numKey > maxNum {
|
||||||
|
maxNum = numKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if isArray && maxNum > 0 {
|
||||||
|
// Это массив
|
||||||
|
result := make([]interface{}, 0, int(maxNum))
|
||||||
|
// Заполняем массив, учитывая что индексы в Lua начинаются с 1
|
||||||
|
for i := lua.LNumber(1); i <= maxNum; i++ {
|
||||||
|
val := v.RawGet(i)
|
||||||
|
if val != lua.LNil {
|
||||||
|
result = append(result, pm.fromLuaValue(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
// Это словарь
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
v.ForEach(func(key lua.LValue, val lua.LValue) {
|
||||||
|
if strKey, ok := key.(lua.LString); ok {
|
||||||
|
result[string(strKey)] = pm.fromLuaValue(val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Для всех остальных типов (включая nil) возвращаем строковое представление
|
||||||
|
return v.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// luaTableToMap конвертирует Lua таблицу в map[string]interface{}
|
||||||
|
func (pm *PluginManager) luaTableToMap(table *lua.LTable) map[string]interface{} {
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
|
if table == nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
table.ForEach(func(key lua.LValue, value lua.LValue) {
|
||||||
|
if strKey, ok := key.(lua.LString); ok {
|
||||||
|
result[string(strKey)] = pm.fromLuaValue(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// tupleToLuaTable конвертирует кортеж в Lua таблицу
|
||||||
|
func (pm *PluginManager) tupleToLuaTable(L *lua.LState, tuple *types.Tuple) *lua.LTable {
|
||||||
|
table := L.NewTable()
|
||||||
|
|
||||||
|
if tuple == nil {
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем ID
|
||||||
|
L.RawSet(table, lua.LString("id"), lua.LString(tuple.ID))
|
||||||
|
|
||||||
|
// Добавляем поля
|
||||||
|
fieldsTable := L.NewTable()
|
||||||
|
for key, value := range tuple.Fields {
|
||||||
|
L.RawSet(fieldsTable, lua.LString(key), pm.toLuaValue(L, value))
|
||||||
|
}
|
||||||
|
L.RawSet(table, lua.LString("fields"), fieldsTable)
|
||||||
|
|
||||||
|
// Добавляем метаданные, если есть
|
||||||
|
var zeroTime time.Time
|
||||||
|
if tuple.CreatedAt != zeroTime {
|
||||||
|
L.RawSet(table, lua.LString("created_at"), lua.LString(tuple.CreatedAt.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if tuple.UpdatedAt != zeroTime {
|
||||||
|
L.RawSet(table, lua.LString("updated_at"), lua.LString(tuple.UpdatedAt.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPlugins загружает все Lua плагины из директории
|
||||||
|
func (pm *PluginManager) LoadPlugins(pluginsDir string) error {
|
||||||
|
if !pm.enabled || pm.state == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(pluginsDir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read plugins directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedCount := 0
|
||||||
|
for _, file := range files {
|
||||||
|
if !file.IsDir() && filepath.Ext(file.Name()) == ".lua" {
|
||||||
|
if err := pm.LoadPlugin(filepath.Join(pluginsDir, file.Name())); err != nil {
|
||||||
|
utils.PrintError("Error loading plugin %s: %v", file.Name(), err)
|
||||||
|
} else {
|
||||||
|
loadedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if loadedCount > 0 {
|
||||||
|
utils.PrintSuccess("Loaded %d Lua plugin(s)", loadedCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPlugin загружает один Lua плагин
|
||||||
|
func (pm *PluginManager) LoadPlugin(path string) error {
|
||||||
|
if !pm.enabled || pm.state == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read plugin file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем строку как функцию
|
||||||
|
fn, err := pm.state.LoadString(string(data))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse Lua plugin: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginName := filepath.Base(path)
|
||||||
|
|
||||||
|
pm.mu.Lock()
|
||||||
|
pm.plugins[pluginName] = fn
|
||||||
|
pm.mu.Unlock()
|
||||||
|
|
||||||
|
utils.PrintSuccess("Loaded plugin: %s", pluginName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecutePlugin выполняет функцию плагина
|
||||||
|
func (pm *PluginManager) ExecutePlugin(name string, args ...lua.LValue) error {
|
||||||
|
if !pm.enabled || pm.state == nil {
|
||||||
|
return fmt.Errorf("Lua plugins are disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.mu.RLock()
|
||||||
|
fn, exists := pm.plugins[name]
|
||||||
|
pm.mu.RUnlock()
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("plugin '%s' not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Защита от паники в Lua коде
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
utils.PrintError("Panic in Lua plugin: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
pm.state.Push(fn)
|
||||||
|
for _, arg := range args {
|
||||||
|
pm.state.Push(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выполняем с защитой от ошибок
|
||||||
|
if err := pm.state.PCall(len(args), lua.MultRet, nil); err != nil {
|
||||||
|
return fmt.Errorf("Lua execution error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecoverData восстанавливает данные из резервного хранилища
|
||||||
|
func (pm *PluginManager) RecoverData(backupStorage StorageInterface) error {
|
||||||
|
if !pm.enabled {
|
||||||
|
return fmt.Errorf("Lua plugins are disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pm.storage == nil {
|
||||||
|
return fmt.Errorf("main storage not available")
|
||||||
|
}
|
||||||
|
|
||||||
|
if backupStorage == nil {
|
||||||
|
return fmt.Errorf("backup storage not available")
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.PrintInfo("Starting data recovery from backup...")
|
||||||
|
|
||||||
|
// Здесь должна быть логика восстановления данных
|
||||||
|
// В реальной реализации нужно пройти по всем кортежам в backupStorage
|
||||||
|
// и восстановить их в основном хранилище
|
||||||
|
|
||||||
|
// Для демонстрации просто устанавливаем флаг
|
||||||
|
pm.recovered = true
|
||||||
|
|
||||||
|
utils.PrintSuccess("Data recovery completed successfully")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRecovered возвращает флаг восстановления данных
|
||||||
|
func (pm *PluginManager) IsRecovered() bool {
|
||||||
|
return pm.recovered
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStorage возвращает интерфейс хранилища
|
||||||
|
func (pm *PluginManager) GetStorage() StorageInterface {
|
||||||
|
pm.mu.RLock()
|
||||||
|
defer pm.mu.RUnlock()
|
||||||
|
return pm.storage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close закрывает Lua состояние и освобождает ресурсы
|
||||||
|
func (pm *PluginManager) Close() {
|
||||||
|
if pm.state != nil {
|
||||||
|
pm.mu.Lock()
|
||||||
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
|
// Очищаем плагины
|
||||||
|
pm.plugins = make(map[string]*lua.LFunction)
|
||||||
|
|
||||||
|
// Закрываем состояние Lua
|
||||||
|
pm.state.Close()
|
||||||
|
pm.state = nil
|
||||||
|
|
||||||
|
utils.PrintInfo("Lua plugin manager closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user