summaryrefslogtreecommitdiff
path: root/lua.go
diff options
context:
space:
mode:
authorunwox <me@unwox.com>2024-09-14 15:51:23 +0600
committerunwox <me@unwox.com>2024-09-14 15:51:23 +0600
commit986522e417406956e83362e08adbd6a18ec904a5 (patch)
tree598fd241569c845acd6a604cdf88942e146237de /lua.go
parenta6412f9fc48f410ba4e3b39938d8d3f6a5597066 (diff)
better handling of args coming from lua with l.Scan
Diffstat (limited to 'lua.go')
-rw-r--r--lua.go72
1 files changed, 72 insertions, 0 deletions
diff --git a/lua.go b/lua.go
index 875aac0..8401d37 100644
--- a/lua.go
+++ b/lua.go
@@ -22,7 +22,9 @@ import "C"
import (
"errors"
"fmt"
+ "reflect"
"runtime/cgo"
+ "slices"
"unsafe"
)
@@ -231,6 +233,76 @@ func (l *Lua) LoadString(code string) error {
return nil
}
+// Scan scans values from the Lua stack into vars according to their types.
+func (l *Lua) Scan(vars ...any) error {
+ slices.Reverse(vars)
+ for i, v := range vars {
+ t := reflect.TypeOf(v)
+ // unwrap pointers
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ tk := t.Kind()
+ if t.Name() == "LuaRef" {
+ if l.IsFunction(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be function",
+ len(vars)-i,
+ )
+ }
+ *v.(*LuaRef) = l.PopToRef()
+ } else if tk == reflect.String {
+ if l.IsString(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be string",
+ len(vars)-i,
+ )
+ }
+ *v.(*string) = l.ToString(-1)
+ l.Pop(1)
+ } else if tk == reflect.Int {
+ if l.IsNumber(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be number",
+ len(vars)-i,
+ )
+ }
+ *v.(*int) = l.ToInt(-1)
+ l.Pop(1)
+ } else if tk == reflect.Map && t.Key().Kind() == reflect.String {
+ // TODO: should be possible to use maps with any types
+ // of value, not only strings.
+ vm, _ := v.(*map[string]string)
+ l.PushNil()
+ for l.Next() {
+ if !l.IsString(-1) {
+ return fmt.Errorf(
+ "map arg #%d must only have string values",
+ len(vars)-i,
+ )
+ }
+ if !l.IsString(-2) {
+ return fmt.Errorf(
+ "map arg #%d must only have string keys",
+ len(vars)-i,
+ )
+ }
+ iv := l.ToString(-1)
+ l.Pop(1)
+ // We must not pop the item key from the stack
+ // because otherwise C.lua_next won't work
+ // properly.
+ k := l.ToString(-1)
+ (*vm)[k] = iv
+ }
+ l.Pop(1)
+ } else {
+ return fmt.Errorf("unknown var type: %s", t)
+ }
+ }
+ return nil
+}
+
// RestoreStackFunc remembers the Lua stack size and then restores it when a
// returned function is called. It's a helper function to avoid stack leakage.
func (l *Lua) RestoreStackFunc() func () {