package plugins import ( "fmt" lua "github.com/yuin/gopher-lua" ) // pushError pushes an error message as a Lua error (using lua.Errorf-style). // Returns 0 (no return values — the Lua function will error). func pushError(L *lua.LState, err error) int { L.RaiseError("%s", err.Error()) return 0 // unreachable, but satisfies signature } // pushResult pushes a single value to Lua stack and returns 1. func pushResult(L *lua.LState, val lua.LValue) int { L.Push(val) return 1 } // pushOK pushes true (success) to Lua stack. func pushOK(L *lua.LState) int { L.Push(lua.LBool(true)) return 1 } // checkOptString gets an optional string argument at position. func checkOptString(L *lua.LState, pos int, defaultVal string) string { if L.GetTop() >= pos { return L.CheckString(pos) } return defaultVal } // checkOptInt gets an optional int argument at position. func checkOptInt(L *lua.LState, pos int, defaultVal int) int { if L.GetTop() >= pos { return L.CheckInt(pos) } return defaultVal } // checkOptTable gets an optional table argument at position. func checkOptTable(L *lua.LState, pos int) *lua.LTable { if L.GetTop() >= pos && L.Get(pos).Type() == lua.LTTable { return L.CheckTable(pos) } return nil } // tableToMap converts a Lua table to map[string]interface{}. // Only handles string keys and basic value types. func tableToMap(tbl *lua.LTable) map[string]interface{} { m := make(map[string]interface{}) tbl.ForEach(func(key lua.LValue, val lua.LValue) { k := lua.LVAsString(key) m[k] = luaValueToGo(val) }) return m } // luaValueToGo converts a Lua value to a Go interface{}. func luaValueToGo(v lua.LValue) interface{} { if v == lua.LNil { return nil } switch val := v.(type) { case lua.LBool: return bool(val) case lua.LString: return string(val) case lua.LNumber: return float64(val) case *lua.LTable: // Detect if it's an array or map if val.MaxN() > 0 { arr := make([]interface{}, 0, val.MaxN()) for i := 1; i <= val.MaxN(); i++ { arr = append(arr, luaValueToGo(val.RawGetInt(i))) } return arr } // Check if table has any keys at all — empty tables are ambiguous. // DB queries return empty tables when no rows match; the frontend // expects a valid array [] (with .length), not {} (where .length is undefined). hasKeys := false val.ForEach(func(k lua.LValue, v lua.LValue) { hasKeys = true }) if !hasKeys { return make([]interface{}, 0) } m := make(map[string]interface{}) val.ForEach(func(k lua.LValue, v lua.LValue) { m[fmt.Sprintf("%v", k)] = luaValueToGo(v) }) return m default: return fmt.Sprintf("%v", v) } }