package main import ( "reflect" "slices" "fmt" "runtime/cgo" ) // 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 t.String() == "cgo.Handle" { if l.IsNumber(-1) == false { return fmt.Errorf( "passed arg #%d must be Go handler", len(vars)-i, ) } *v.(*cgo.Handle) = cgo.Handle(uintptr(l.ToInt(-1))) l.Pop(1) } 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, ) } 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) (*vm)[k] = v } l.Pop(1) } else if tk == reflect.Slice && t.Elem().Kind() == reflect.Interface { if !l.IsTable(-1) { return fmt.Errorf( "passed arg #%d must be a table", len(vars)-i, ) } va, _ := v.(*[]any) // Check if table contains element "n" with // a count of elements. Useful when an array may // contain nils. l.GetTableItem("n") n := 0 if l.IsNumber(-1) { n = l.ToInt(-1) } l.Pop(1) scanAndAppend := func () error { var v any = nil if l.IsString(-1) { v = l.ToString(-1) } else if l.IsNumber(-1) { v = l.ToInt(-1) } else if l.IsBoolean(-1) { v = l.ToBoolean(-1) } else if l.IsNil(-1) { v = nil } else { return fmt.Errorf("unknown value in array") } l.Pop(1) (*va) = append((*va), v) return nil } if n > 0 { for i := 0; i < n; i++ { l.RawGetI(-1, i + 1) if err := scanAndAppend(); err != nil { return err } } } else { l.PushNil() for l.Next() { if err := scanAndAppend(); err != nil { return err } } } l.Pop(1) } else { return fmt.Errorf("unknown var type: %s", t) } } return nil }