103 lines
2.6 KiB
Go
103 lines
2.6 KiB
Go
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)
|
|
}
|
|
}
|