From 986522e417406956e83362e08adbd6a18ec904a5 Mon Sep 17 00:00:00 2001 From: unwox Date: Sat, 14 Sep 2024 15:51:23 +0600 Subject: better handling of args coming from lua with l.Scan --- lua.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 35 +++++++++++++------------------- 2 files changed, 86 insertions(+), 21 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 () { diff --git a/main.go b/main.go index 96e8a38..bde394e 100644 --- a/main.go +++ b/main.go @@ -37,9 +37,14 @@ func main() { // define luna.router module routeModule := make(map[string]any) routeModule["route"] = func (l *Lua) int { - fn := l.PopToRef() - route := l.ToString(-1) - l.Pop(1) + var route string + var fn LuaRef + err := l.Scan(&route, &fn) + if err != nil { + fmt.Println("error:", err) + // FIXME: handle + return 0 + } // find corresponding worker for the lua context var wrk *Worker for _, wrk = range wrks { @@ -84,26 +89,14 @@ func main() { // define luna.http module httpModule := make(map[string]any) httpModule["request"] = func (l *Lua) int { - body := l.ToString(-1) - url := l.ToString(-3) - method := l.ToString(-4) - l.Pop(1) + var method, url, body string headers := make(map[string]string) - l.PushNil() - for l.Next() { - if !l.IsString(-2) || !l.IsString(-2) { - l.Pop(1) - continue - } - v := 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) - headers[k] = v + err := l.Scan(&method, &url, &headers, &body) + if err != nil { + // FIXME: handle + fmt.Println("error:", err) + return 0 } - l.Pop(2) req, err := http.NewRequest(method, url, strings.NewReader(body)) if err != nil { -- cgit v1.2.3