From 5b34abf19797b654e5fbccab7c8599d161d2ff88 Mon Sep 17 00:00:00 2001 From: thedae Date: Thu, 21 Nov 2024 12:31:06 +0100 Subject: [PATCH 1/8] Rearrange decorators --- binder.go | 4 + decorator/custom_error.go | 24 +++ decorator/decorator.go | 14 ++ {proxy => decorator}/http.go | 22 +- {proxy => decorator}/http_test.go | 9 +- {proxy => decorator}/json.go | 32 +-- decorator/lualist.go | 141 +++++++++++++ decorator/luanil.go | 11 + decorator/luatable.go | 170 +++++++++++++++ errors.go | 22 +- proxy/proxy.go | 25 ++- proxy/proxy_test.go | 10 +- proxy/response.go | 338 +----------------------------- router/gin/lua.go | 3 +- router/mux/lua.go | 3 +- types.go | 87 +++++++- 16 files changed, 501 insertions(+), 414 deletions(-) create mode 100644 decorator/custom_error.go create mode 100644 decorator/decorator.go rename {proxy => decorator}/http.go (86%) rename {proxy => decorator}/http_test.go (88%) rename {proxy => decorator}/json.go (72%) create mode 100644 decorator/lualist.go create mode 100644 decorator/luanil.go create mode 100644 decorator/luatable.go diff --git a/binder.go b/binder.go index a711078..7cb395e 100644 --- a/binder.go +++ b/binder.go @@ -6,6 +6,10 @@ import ( "github.com/krakendio/binder" ) +type Binder = binder.Binder +type Context = binder.Context +type Handler = binder.Handler + type BinderWrapper struct { binder *binder.Binder sourceMap *SourceMap diff --git a/decorator/custom_error.go b/decorator/custom_error.go new file mode 100644 index 0000000..b6349e2 --- /dev/null +++ b/decorator/custom_error.go @@ -0,0 +1,24 @@ +package decorator + +import ( + "fmt" + + "github.com/krakendio/binder" +) + +const separator = " || " + +func RegisterErrors(b *binder.Binder) { + b.Func("custom_error", func(c *binder.Context) error { + switch c.Top() { + case 0: + return ErrNeedsArguments + case 1: + return fmt.Errorf("%s%s%d", c.Arg(1).String(), separator, -1) + case 2: + return fmt.Errorf("%s%s%d", c.Arg(1).String(), separator, int(c.Arg(2).Number())) + default: + return fmt.Errorf("%s%s%d%s%s", c.Arg(1).String(), separator, int(c.Arg(2).Number()), separator, c.Arg(3).String()) + } + }) +} diff --git a/decorator/decorator.go b/decorator/decorator.go new file mode 100644 index 0000000..cf0a81b --- /dev/null +++ b/decorator/decorator.go @@ -0,0 +1,14 @@ +package decorator + +import ( + "errors" + + "github.com/krakendio/binder" +) + +type Decorator func(*binder.Binder) + +var ( + ErrNeedsArguments = errors.New("need arguments") + ErrResponseExpected = errors.New("response expected") +) diff --git a/proxy/http.go b/decorator/http.go similarity index 86% rename from proxy/http.go rename to decorator/http.go index e432e57..7ef7a7f 100644 --- a/proxy/http.go +++ b/decorator/http.go @@ -1,4 +1,4 @@ -package proxy +package decorator import ( "bytes" @@ -9,12 +9,12 @@ import ( "sync" "github.com/krakendio/binder" - lua "github.com/yuin/gopher-lua" + lua "github.com/krakendio/krakend-lua/v2" "github.com/luraproject/lura/v2/transport/http/server" ) -func registerHTTPRequest(ctx context.Context, b *binder.Binder) { +func RegisterHTTPRequest(ctx context.Context, b *binder.Binder) { t := b.Table("http_response") t.Static("new", newHttpResponse(ctx)) @@ -50,10 +50,10 @@ func newHttpResponse(ctx context.Context) func(*binder.Context) error { } if c.Top() == 4 { - headers, ok := c.Arg(4).Any().(*lua.LTable) + headers, ok := c.Arg(4).Any().(*lua.NativeTable) if ok { - headers.ForEach(func(key, value lua.LValue) { + headers.ForEach(func(key, value lua.NativeValue) { req.Header.Add(key.String(), value.String()) }) } @@ -65,7 +65,7 @@ func newHttpResponse(ctx context.Context) func(*binder.Context) error { return err } if resp == nil { - return errResponseExpected + return ErrResponseExpected } pushHTTPResponse(c, resp) return nil @@ -118,7 +118,7 @@ func pushHTTPResponse(c *binder.Context, r *http.Response) { func httpStatus(c *binder.Context) error { resp, ok := c.Arg(1).Data().(*httpResponse) if !ok { - return errResponseExpected + return ErrResponseExpected } c.Push().Number(float64(resp.r.StatusCode)) @@ -128,10 +128,10 @@ func httpStatus(c *binder.Context) error { func httpHeaders(c *binder.Context) error { resp, ok := c.Arg(1).Data().(*httpResponse) if !ok { - return errResponseExpected + return ErrResponseExpected } if c.Top() != 2 { - return errNeedsArguments + return ErrNeedsArguments } c.Push().String(resp.Header(c.Arg(2).String())) @@ -141,7 +141,7 @@ func httpHeaders(c *binder.Context) error { func httpBody(c *binder.Context) error { resp, ok := c.Arg(1).Data().(*httpResponse) if !ok { - return errResponseExpected + return ErrResponseExpected } c.Push().String(resp.Body()) @@ -151,7 +151,7 @@ func httpBody(c *binder.Context) error { func httpClose(c *binder.Context) error { resp, ok := c.Arg(1).Data().(*httpResponse) if !ok { - return errResponseExpected + return ErrResponseExpected } if resp == nil { return nil diff --git a/proxy/http_test.go b/decorator/http_test.go similarity index 88% rename from proxy/http_test.go rename to decorator/http_test.go index 262a2ec..561886a 100644 --- a/proxy/http_test.go +++ b/decorator/http_test.go @@ -1,4 +1,4 @@ -package proxy +package decorator import ( "context" @@ -9,7 +9,6 @@ import ( "net/http/httptest" "github.com/krakendio/binder" - lua "github.com/krakendio/krakend-lua/v2" ) func Example_RegisterBackendModule() { @@ -27,16 +26,16 @@ func Example_RegisterBackendModule() { })) defer ts.Close() - bindr := lua.NewBinderWrapper(binder.Options{ + bindr := binder.New(binder.Options{ SkipOpenLibs: true, IncludeGoStackTrace: true, }) - registerHTTPRequest(context.Background(), bindr.GetBinder()) + RegisterHTTPRequest(context.Background(), bindr) code := fmt.Sprintf("local url = '%s'\n%s", ts.URL, sampleLuaCode) - if err := bindr.WithCode("test-code", code); err != nil { + if err := bindr.DoString(code); err != nil { fmt.Println(err.Error()) } diff --git a/proxy/json.go b/decorator/json.go similarity index 72% rename from proxy/json.go rename to decorator/json.go index 2c03b24..844e4db 100644 --- a/proxy/json.go +++ b/decorator/json.go @@ -1,13 +1,13 @@ -package proxy +package decorator import ( "encoding/json" "github.com/krakendio/binder" - lua "github.com/yuin/gopher-lua" + lua "github.com/krakendio/krakend-lua/v2" ) -func registerJson(b *binder.Binder) { +func RegisterJson(b *binder.Binder) { tab := b.Table("json") tab.Static("unmarshal", fromJson) tab.Static("marshal", toJson) @@ -15,7 +15,7 @@ func registerJson(b *binder.Binder) { func fromJson(c *binder.Context) error { if c.Top() != 1 { - return errNeedsArguments + return ErrNeedsArguments } data := new(interface{}) err := json.Unmarshal([]byte(c.Arg(1).String()), data) @@ -36,38 +36,38 @@ func fromJson(c *binder.Context) error { case bool: c.Push().Bool(v) case []interface{}: - c.Push().Data(&List{Data: v}, "luaList") + c.Push().Data(&lua.List{Data: v}, "luaList") case map[string]interface{}: - c.Push().Data(&Table{Data: v}, "luaTable") + c.Push().Data(&lua.Table{Data: v}, "luaTable") } return nil } func toJson(c *binder.Context) error { if c.Top() != 1 { - return errNeedsArguments + return ErrNeedsArguments } switch t := c.Arg(1).Any().(type) { - case lua.LString: + case lua.NativeString: return marshal(c, c.Arg(1).String()) - case lua.LNumber: + case lua.NativeNumber: return marshal(c, c.Arg(1).Number()) - case lua.LBool: + case lua.NativeBool: return marshal(c, c.Arg(1).Bool()) - case *lua.LTable: + case *lua.NativeTable: res := map[string]interface{}{} - t.ForEach(func(k, v lua.LValue) { - parseToTable(k, v, res) + t.ForEach(func(k, v lua.NativeValue) { + lua.ParseToTable(k, v, res) }) return marshal(c, res) - case *lua.LUserData: + case *lua.NativeUserData: if t.Value == nil { return marshal(c, nil) } else { switch v := t.Value.(type) { - case *Table: + case *lua.Table: return marshal(c, v.Data) - case *List: + case *lua.List: return marshal(c, v.Data) } } diff --git a/decorator/lualist.go b/decorator/lualist.go new file mode 100644 index 0000000..4074681 --- /dev/null +++ b/decorator/lualist.go @@ -0,0 +1,141 @@ +package decorator + +import ( + "encoding/json" + + "github.com/krakendio/binder" + lua "github.com/krakendio/krakend-lua/v2" +) + +func RegisterLuaList(b *binder.Binder) { + list := b.Table("luaList") + list.Static("new", func(c *binder.Context) error { + c.Push().Data(&lua.List{Data: []interface{}{}}, "luaList") + return nil + }) + list.Dynamic("get", listGet) + list.Dynamic("set", listSet) + list.Dynamic("len", listLen) + list.Dynamic("del", listDel) +} + +func listLen(c *binder.Context) error { + list, ok := c.Arg(1).Data().(*lua.List) + if !ok { + return ErrResponseExpected + } + c.Push().Number(float64(len(list.Data))) + return nil +} + +func listGet(c *binder.Context) error { + if c.Top() != 2 { + return ErrNeedsArguments + } + tab, ok := c.Arg(1).Data().(*lua.List) + if !ok { + return ErrResponseExpected + } + index := int(c.Arg(2).Number()) + if index < 0 || index >= len(tab.Data) { + return nil + } + if tab.Data[index] == nil { + c.Push().Data(nil, "luaNil") + return nil + } + + switch t := tab.Data[index].(type) { + case string: + c.Push().String(t) + case json.Number: + n, _ := t.Float64() + c.Push().Number(n) + case int: + c.Push().Number(float64(t)) + case float64: + c.Push().Number(t) + case bool: + c.Push().Bool(t) + case []interface{}: + c.Push().Data(&lua.List{Data: t}, "luaList") + case map[string]interface{}: + c.Push().Data(&lua.Table{Data: t}, "luaTable") + } + + return nil +} + +func listSet(c *binder.Context) error { + if c.Top() != 3 { + return ErrNeedsArguments + } + tab, ok := c.Arg(1).Data().(*lua.List) + if !ok { + return ErrResponseExpected + } + key := int(c.Arg(2).Number()) + if key < 0 { + return nil + } + if key >= len(tab.Data) { + if cap(tab.Data) > key { + for i := len(tab.Data); i < key; i++ { + tab.Data = append(tab.Data, nil) + } + } else { + newData := make([]interface{}, key+1) + copy(newData, tab.Data) + tab.Data = newData + } + } + switch t := c.Arg(3).Any().(type) { + case lua.NativeString: + tab.Data[key] = c.Arg(3).String() + case lua.NativeNumber: + tab.Data[key] = c.Arg(3).Number() + case lua.NativeBool: + tab.Data[key] = c.Arg(3).Bool() + case *lua.NativeTable: + res := map[string]interface{}{} + t.ForEach(func(k, v lua.NativeValue) { + lua.ParseToTable(k, v, res) + }) + tab.Data[key] = res + case *lua.NativeUserData: + if t.Value == nil { + tab.Data[key] = nil + } else { + switch v := t.Value.(type) { + case *lua.Table: + tab.Data[key] = v.Data + case *lua.List: + tab.Data[key] = v.Data + } + } + } + + return nil +} + +func listDel(c *binder.Context) error { + if c.Top() != 2 { + return ErrNeedsArguments + } + tab, ok := c.Arg(1).Data().(*lua.List) + if !ok { + return ErrResponseExpected + } + key := int(c.Arg(2).Number()) + if key < 0 || key >= len(tab.Data) { + return nil + } + + last := len(tab.Data) - 1 + if key < last { + copy(tab.Data[key:], tab.Data[key+1:]) + } + tab.Data[last] = nil + tab.Data = tab.Data[:last] + return nil +} diff --git a/decorator/luanil.go b/decorator/luanil.go new file mode 100644 index 0000000..0596c9b --- /dev/null +++ b/decorator/luanil.go @@ -0,0 +1,11 @@ +package decorator + +import "github.com/krakendio/binder" + +func RegisterNil(b *binder.Binder) { + tab := b.Table("luaNil") + tab.Static("new", func(c *binder.Context) error { + c.Push().Data(nil, "luaNil") + return nil + }) +} diff --git a/decorator/luatable.go b/decorator/luatable.go new file mode 100644 index 0000000..284087d --- /dev/null +++ b/decorator/luatable.go @@ -0,0 +1,170 @@ +package decorator + +import ( + "encoding/json" + "errors" + "fmt" + "sort" + + "github.com/krakendio/binder" + lua "github.com/krakendio/krakend-lua/v2" + "github.com/luraproject/lura/v2/transport/http/client" +) + +func RegisterLuaTable(b *binder.Binder) { + tab := b.Table("luaTable") + tab.Static("new", func(c *binder.Context) error { + c.Push().Data(&lua.Table{Data: map[string]interface{}{}}, "luaTable") + return nil + }) + tab.Dynamic("get", tableGet) + tab.Dynamic("set", tableSet) + tab.Dynamic("len", tableLen) + tab.Dynamic("del", tableDel) + tab.Dynamic("keys", tableKeys) + tab.Dynamic("keyExists", tableKeyExists) +} + +func tableGet(c *binder.Context) error { + if c.Top() != 2 { + return ErrNeedsArguments + } + tab, ok := c.Arg(1).Data().(*lua.Table) + if !ok { + return ErrResponseExpected + } + data, ok := tab.Data[c.Arg(2).String()] + if !ok { + return nil + } + if data == nil { + c.Push().Data(nil, "luaNil") + return nil + } + + switch t := data.(type) { + case string: + c.Push().String(t) + case json.Number: + n, _ := t.Float64() + c.Push().Number(n) + case int: + c.Push().Number(float64(t)) + case float64: + c.Push().Number(t) + case bool: + c.Push().Bool(t) + case []interface{}: + c.Push().Data(&lua.List{Data: t}, "luaList") + case map[string]interface{}: + c.Push().Data(&lua.Table{Data: t}, "luaTable") + case client.HTTPResponseError: + c.Push().Data(&lua.Table{Data: clientErrorToMap(t)}, "luaTable") + case client.NamedHTTPResponseError: + d := clientErrorToMap(t.HTTPResponseError) + d["name"] = t.Name() + c.Push().Data(&lua.Table{Data: d}, "luaTable") + default: + return errors.New(fmt.Sprintf("unknown type (%T) %v", t, t)) + } + + return nil +} + +func tableSet(c *binder.Context) error { + if c.Top() != 3 { + return ErrNeedsArguments + } + tab, ok := c.Arg(1).Data().(*lua.Table) + if !ok { + return ErrResponseExpected + } + key := c.Arg(2).String() + switch t := c.Arg(3).Any().(type) { + case lua.NativeString: + tab.Data[key] = c.Arg(3).String() + case lua.NativeNumber: + tab.Data[key] = c.Arg(3).Number() + case lua.NativeBool: + tab.Data[key] = c.Arg(3).Bool() + case *lua.NativeTable: + res := map[string]interface{}{} + t.ForEach(func(k, v lua.NativeValue) { + lua.ParseToTable(k, v, res) + }) + tab.Data[key] = res + case *lua.NativeUserData: + if t.Value == nil { + tab.Data[key] = nil + } else { + switch v := t.Value.(type) { + case *lua.Table: + tab.Data[key] = v.Data + case *lua.List: + tab.Data[key] = v.Data + } + } + } + + return nil +} + +func tableKeys(c *binder.Context) error { + tab, ok := c.Arg(1).Data().(*lua.Table) + if !ok { + return ErrResponseExpected + } + var l []string + for k := range tab.Data { + l = append(l, k) + } + sort.Strings(l) + keys := make([]interface{}, len(l)) + for k, v := range l { + keys[k] = v + } + c.Push().Data(&lua.List{Data: keys}, "luaList") + return nil +} + +func tableKeyExists(c *binder.Context) error { + if c.Top() != 2 { + return ErrNeedsArguments + } + tab, ok := c.Arg(1).Data().(*lua.Table) + if !ok { + return ErrResponseExpected + } + _, ok = tab.Data[c.Arg(2).String()] + c.Push().Bool(ok) + return nil +} + +func tableLen(c *binder.Context) error { + tab, ok := c.Arg(1).Data().(*lua.Table) + if !ok { + return ErrResponseExpected + } + c.Push().Number(float64(len(tab.Data))) + return nil +} + +func tableDel(c *binder.Context) error { + if c.Top() != 2 { + return ErrNeedsArguments + } + tab, ok := c.Arg(1).Data().(*lua.Table) + if !ok { + return ErrResponseExpected + } + delete(tab.Data, c.Arg(2).String()) + return nil +} + +func clientErrorToMap(err client.HTTPResponseError) map[string]interface{} { + return map[string]interface{}{ + "http_status_code": err.StatusCode(), + "http_body": err.Error(), + "http_body_encoding": err.Encoding(), + } +} diff --git a/errors.go b/errors.go index f127101..e9ab5b0 100644 --- a/errors.go +++ b/errors.go @@ -1,7 +1,6 @@ package lua import ( - "errors" "fmt" "strconv" "strings" @@ -29,8 +28,6 @@ func (e ErrUnknownSource) Error() string { return "lua: unable to load required source " + string(e) } -var errNeedsArguments = errors.New("need arguments") - type ErrInternal string func (e ErrInternal) Error() string { @@ -59,8 +56,6 @@ func (e ErrInternalHTTPWithContentType) Encoding() string { return e.contentType } -const separator = " || " - func ToError(e error, source *SourceMap) error { if e == nil { return nil @@ -73,7 +68,7 @@ func ToError(e error, source *SourceMap) error { originalMsg := binderError.Error() msgSplitIndex := strings.Index(originalMsg, ":") - errMsgParts := strings.Split(originalMsg[msgSplitIndex+2:], separator) + errMsgParts := strings.Split(originalMsg[msgSplitIndex+2:], " || ") if len(errMsgParts) == 0 { return binderError @@ -114,18 +109,3 @@ func ToError(e error, source *SourceMap) error { contentType: errMsgParts[2], } } - -func RegisterErrors(b *binder.Binder) { - b.Func("custom_error", func(c *binder.Context) error { - switch c.Top() { - case 0: - return errNeedsArguments - case 1: - return fmt.Errorf("%s%s%d", c.Arg(1).String(), separator, -1) - case 2: - return fmt.Errorf("%s%s%d", c.Arg(1).String(), separator, int(c.Arg(2).Number())) - default: - return fmt.Errorf("%s%s%d%s%s", c.Arg(1).String(), separator, int(c.Arg(2).Number()), separator, c.Arg(3).String()) - } - }) -} diff --git a/proxy/proxy.go b/proxy/proxy.go index aa471b2..34ca7a1 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -6,6 +6,7 @@ import ( "github.com/krakendio/binder" lua "github.com/krakendio/krakend-lua/v2" + "github.com/krakendio/krakend-lua/v2/decorator" "github.com/luraproject/lura/v2/config" "github.com/luraproject/lura/v2/logging" "github.com/luraproject/lura/v2/proxy" @@ -56,12 +57,12 @@ func BackendFactory(l logging.Logger, bf proxy.BackendFactory) proxy.BackendFact } type registerer struct { - decorators []func(*binder.Binder) + decorators []decorator.Decorator } -var localRegisterer = registerer{decorators: []func(*binder.Binder){}} +var localRegisterer = registerer{decorators: []decorator.Decorator{}} -func RegisterDecorator(f func(*binder.Binder)) { +func RegisterDecorator(f decorator.Decorator) { localRegisterer.decorators = append(localRegisterer.decorators, f) } @@ -73,9 +74,16 @@ func New(cfg lua.Config, next proxy.Proxy) proxy.Proxy { }) defer b.GetBinder().Close() - lua.RegisterErrors(b.GetBinder()) - lua.RegisterNil(b.GetBinder()) - registerHTTPRequest(ctx, b.GetBinder()) + decorator.RegisterErrors(b.GetBinder()) + decorator.RegisterNil(b.GetBinder()) + decorator.RegisterLuaTable(b.GetBinder()) + decorator.RegisterLuaList(b.GetBinder()) + decorator.RegisterHTTPRequest(ctx, b.GetBinder()) + decorator.RegisterJson(b.GetBinder()) + for _, f := range localRegisterer.decorators { + f(b.GetBinder()) + } + registerRequestTable(req, b.GetBinder()) if err := b.WithConfig(&cfg); err != nil { @@ -96,11 +104,6 @@ func New(cfg lua.Config, next proxy.Proxy) proxy.Proxy { } registerResponseTable(resp, b.GetBinder()) - registerJson(b.GetBinder()) - - for _, f := range localRegisterer.decorators { - f(b.GetBinder()) - } if err := b.WithCode("post-script", cfg.PostCode); err != nil { return nil, err diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index e1652ba..e3f83b8 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -648,11 +648,11 @@ func TestProxyFactory(t *testing.T) { "bar": 120, "collection_size": 3, "foobar": true, - "ids": { - "1": 1, - "2": 42, - "3": 0 - }, + "ids": [ + 1, + 42, + 0 + ], "supu": { "original": true, "tupu": "some" diff --git a/proxy/response.go b/proxy/response.go index 2d4b21e..ac15598 100644 --- a/proxy/response.go +++ b/proxy/response.go @@ -2,41 +2,15 @@ package proxy import ( "bytes" - "encoding/json" "errors" - "fmt" "io" - "sort" "github.com/krakendio/binder" + lua "github.com/krakendio/krakend-lua/v2" "github.com/luraproject/lura/v2/proxy" - "github.com/luraproject/lura/v2/transport/http/client" - lua "github.com/yuin/gopher-lua" ) func registerResponseTable(resp *proxy.Response, b *binder.Binder) { - tab := b.Table("luaTable") - tab.Static("new", func(c *binder.Context) error { - c.Push().Data(&Table{Data: map[string]interface{}{}}, "luaTable") - return nil - }) - tab.Dynamic("get", tableGet) - tab.Dynamic("set", tableSet) - tab.Dynamic("len", tableLen) - tab.Dynamic("del", tableDel) - tab.Dynamic("keys", tableKeys) - tab.Dynamic("keyExists", tableKeyExists) - - list := b.Table("luaList") - list.Static("new", func(c *binder.Context) error { - c.Push().Data(&List{Data: []interface{}{}}, "luaList") - return nil - }) - list.Dynamic("get", listGet) - list.Dynamic("set", listSet) - list.Dynamic("len", listLen) - list.Dynamic("del", listDel) - r := &response{resp} if r.Metadata.Headers == nil { r.Metadata.Headers = map[string][]string{} @@ -143,315 +117,7 @@ func (*response) data(c *binder.Context) error { if !ok { return errResponseExpected } - c.Push().Data(&Table{Data: resp.Data}, "luaTable") - - return nil -} - -func tableKeyExists(c *binder.Context) error { - if c.Top() != 2 { - return errNeedsArguments - } - tab, ok := c.Arg(1).Data().(*Table) - if !ok { - return errResponseExpected - } - _, ok = tab.Data[c.Arg(2).String()] - c.Push().Bool(ok) - return nil -} - -func tableKeys(c *binder.Context) error { - tab, ok := c.Arg(1).Data().(*Table) - if !ok { - return errResponseExpected - } - var l []string - for k := range tab.Data { - l = append(l, k) - } - sort.Strings(l) - keys := make([]interface{}, len(l)) - for k, v := range l { - keys[k] = v - } - c.Push().Data(&List{Data: keys}, "luaList") - return nil -} - -func tableLen(c *binder.Context) error { - tab, ok := c.Arg(1).Data().(*Table) - if !ok { - return errResponseExpected - } - c.Push().Number(float64(len(tab.Data))) - return nil -} - -func listLen(c *binder.Context) error { - list, ok := c.Arg(1).Data().(*List) - if !ok { - return errResponseExpected - } - c.Push().Number(float64(len(list.Data))) - return nil -} - -func tableGet(c *binder.Context) error { - if c.Top() != 2 { - return errNeedsArguments - } - tab, ok := c.Arg(1).Data().(*Table) - if !ok { - return errResponseExpected - } - data, ok := tab.Data[c.Arg(2).String()] - if !ok { - return nil - } - if data == nil { - c.Push().Data(nil, "luaNil") - return nil - } - - switch t := data.(type) { - case string: - c.Push().String(t) - case json.Number: - n, _ := t.Float64() - c.Push().Number(n) - case int: - c.Push().Number(float64(t)) - case float64: - c.Push().Number(t) - case bool: - c.Push().Bool(t) - case []interface{}: - c.Push().Data(&List{Data: t}, "luaList") - case map[string]interface{}: - c.Push().Data(&Table{Data: t}, "luaTable") - case client.HTTPResponseError: - c.Push().Data(&Table{Data: clientErrorToMap(t)}, "luaTable") - case client.NamedHTTPResponseError: - d := clientErrorToMap(t.HTTPResponseError) - d["name"] = t.Name() - c.Push().Data(&Table{Data: d}, "luaTable") - default: - return fmt.Errorf("unknown type (%T) %v", t, t) - } - - return nil -} - -func listGet(c *binder.Context) error { - if c.Top() != 2 { - return errNeedsArguments - } - tab, ok := c.Arg(1).Data().(*List) - if !ok { - return errResponseExpected - } - index := int(c.Arg(2).Number()) - if index < 0 || index >= len(tab.Data) { - return nil - } - if tab.Data[index] == nil { - c.Push().Data(nil, "luaNil") - return nil - } - - switch t := tab.Data[index].(type) { - case string: - c.Push().String(t) - case json.Number: - n, _ := t.Float64() - c.Push().Number(n) - case int: - c.Push().Number(float64(t)) - case float64: - c.Push().Number(t) - case bool: - c.Push().Bool(t) - case []interface{}: - c.Push().Data(&List{Data: t}, "luaList") - case map[string]interface{}: - c.Push().Data(&Table{Data: t}, "luaTable") - } + c.Push().Data(&lua.Table{Data: resp.Data}, "luaTable") return nil } - -func tableSet(c *binder.Context) error { - if c.Top() != 3 { - return errNeedsArguments - } - tab, ok := c.Arg(1).Data().(*Table) - if !ok { - return errResponseExpected - } - key := c.Arg(2).String() - switch t := c.Arg(3).Any().(type) { - case lua.LString: - tab.Data[key] = c.Arg(3).String() - case lua.LNumber: - tab.Data[key] = c.Arg(3).Number() - case lua.LBool: - tab.Data[key] = c.Arg(3).Bool() - case *lua.LTable: - res := map[string]interface{}{} - t.ForEach(func(k, v lua.LValue) { - parseToTable(k, v, res) - }) - tab.Data[key] = res - case *lua.LUserData: - if t.Value == nil { - tab.Data[key] = nil - } else { - switch v := t.Value.(type) { - case *Table: - tab.Data[key] = v.Data - case *List: - tab.Data[key] = v.Data - } - } - } - - return nil -} - -func listSet(c *binder.Context) error { - if c.Top() != 3 { - return errNeedsArguments - } - tab, ok := c.Arg(1).Data().(*List) - if !ok { - return errResponseExpected - } - key := int(c.Arg(2).Number()) - if key < 0 { - return nil - } - if key >= len(tab.Data) { - if cap(tab.Data) > key { - for i := len(tab.Data); i < key; i++ { - tab.Data = append(tab.Data, nil) - } - } else { - newData := make([]interface{}, key+1) - copy(newData, tab.Data) - tab.Data = newData - } - } - switch t := c.Arg(3).Any().(type) { - case lua.LString: - tab.Data[key] = c.Arg(3).String() - case lua.LNumber: - tab.Data[key] = c.Arg(3).Number() - case lua.LBool: - tab.Data[key] = c.Arg(3).Bool() - case *lua.LTable: - res := map[string]interface{}{} - t.ForEach(func(k, v lua.LValue) { - parseToTable(k, v, res) - }) - tab.Data[key] = res - case *lua.LUserData: - if t.Value == nil { - tab.Data[key] = nil - } else { - switch v := t.Value.(type) { - case *Table: - tab.Data[key] = v.Data - case *List: - tab.Data[key] = v.Data - } - } - } - - return nil -} - -func tableDel(c *binder.Context) error { - if c.Top() != 2 { - return errNeedsArguments - } - tab, ok := c.Arg(1).Data().(*Table) - if !ok { - return errResponseExpected - } - delete(tab.Data, c.Arg(2).String()) - return nil -} - -func listDel(c *binder.Context) error { - if c.Top() != 2 { - return errNeedsArguments - } - tab, ok := c.Arg(1).Data().(*List) - if !ok { - return errResponseExpected - } - key := int(c.Arg(2).Number()) - if key < 0 || key >= len(tab.Data) { - return nil - } - - last := len(tab.Data) - 1 - if key < last { - copy(tab.Data[key:], tab.Data[key+1:]) - } - tab.Data[last] = nil - tab.Data = tab.Data[:last] - return nil -} - -type Table struct { - Data map[string]interface{} -} - -type List struct { - Data []interface{} -} - -func parseToTable(k, v lua.LValue, acc map[string]interface{}) { - - switch v.Type() { - case lua.LTString: - acc[k.String()] = v.String() - case lua.LTBool: - acc[k.String()] = lua.LVAsBool(v) - case lua.LTNumber: - f := float64(v.(lua.LNumber)) - if f == float64(int64(v.(lua.LNumber))) { - acc[k.String()] = int(v.(lua.LNumber)) - } else { - acc[k.String()] = f - } - case lua.LTUserData: - userV := v.(*lua.LUserData) - if userV.Value == nil { - acc[k.String()] = nil - } else { - switch v := userV.Value.(type) { - case *Table: - acc[k.String()] = v.Data - case *List: - acc[k.String()] = v.Data - } - } - case lua.LTTable: - res := map[string]interface{}{} - v.(*lua.LTable).ForEach(func(k, v lua.LValue) { - parseToTable(k, v, res) - }) - acc[k.String()] = res - } -} - -func clientErrorToMap(err client.HTTPResponseError) map[string]interface{} { - return map[string]interface{}{ - "http_status_code": err.StatusCode(), - "http_body": err.Error(), - "http_body_encoding": err.Encoding(), - } -} diff --git a/router/gin/lua.go b/router/gin/lua.go index 75c0abb..48df4c6 100644 --- a/router/gin/lua.go +++ b/router/gin/lua.go @@ -10,6 +10,7 @@ import ( "github.com/gin-gonic/gin" "github.com/krakendio/binder" lua "github.com/krakendio/krakend-lua/v2" + "github.com/krakendio/krakend-lua/v2/decorator" "github.com/krakendio/krakend-lua/v2/router" "github.com/luraproject/lura/v2/config" "github.com/luraproject/lura/v2/logging" @@ -89,7 +90,7 @@ func process(c *gin.Context, cfg *lua.Config) error { }) defer b.GetBinder().Close() - lua.RegisterErrors(b.GetBinder()) + decorator.RegisterErrors(b.GetBinder()) registerCtxTable(c, b.GetBinder()) if err := b.WithConfig(cfg); err != nil { diff --git a/router/mux/lua.go b/router/mux/lua.go index 828c34d..86c9173 100644 --- a/router/mux/lua.go +++ b/router/mux/lua.go @@ -10,6 +10,7 @@ import ( "github.com/krakendio/binder" lua "github.com/krakendio/krakend-lua/v2" + "github.com/krakendio/krakend-lua/v2/decorator" "github.com/krakendio/krakend-lua/v2/router" "github.com/luraproject/lura/v2/config" "github.com/luraproject/lura/v2/logging" @@ -100,7 +101,7 @@ func process(r *http.Request, pe mux.ParamExtractor, cfg *lua.Config) error { IncludeGoStackTrace: true, }) - lua.RegisterErrors(b.GetBinder()) + decorator.RegisterErrors(b.GetBinder()) registerRequestTable(r, pe, b.GetBinder()) if err := b.WithConfig(cfg); err != nil { diff --git a/types.go b/types.go index 6f53f5a..b133d6d 100644 --- a/types.go +++ b/types.go @@ -1,13 +1,86 @@ package lua import ( - "github.com/krakendio/binder" + "errors" + "sort" + "strconv" + + glua "github.com/yuin/gopher-lua" ) -func RegisterNil(b *binder.Binder) { - tab := b.Table("luaNil") - tab.Static("new", func(c *binder.Context) error { - c.Push().Data(nil, "luaNil") - return nil - }) +type Table struct { + Data map[string]interface{} +} + +type List struct { + Data []interface{} +} + +type NativeValue = glua.LValue +type NativeNumber = glua.LNumber +type NativeString = glua.LString +type NativeBool = glua.LBool +type NativeUserData = glua.LUserData +type NativeTable = glua.LTable + +func ParseToTable(k, v NativeValue, acc map[string]interface{}) { + switch v.Type() { + case glua.LTString: + acc[k.String()] = v.String() + case glua.LTBool: + acc[k.String()] = glua.LVAsBool(v) + case glua.LTNumber: + f := float64(v.(NativeNumber)) + if f == float64(int64(v.(NativeNumber))) { + acc[k.String()] = int(v.(NativeNumber)) + } else { + acc[k.String()] = f + } + case glua.LTUserData: + userV := v.(*NativeUserData) + if userV.Value == nil { + acc[k.String()] = nil + } else { + switch v := userV.Value.(type) { + case *Table: + acc[k.String()] = v.Data + case *List: + acc[k.String()] = v.Data + } + } + case glua.LTTable: + res := map[string]interface{}{} + v.(*NativeTable).ForEach(func(k, v NativeValue) { + ParseToTable(k, v, res) + }) + // Check if all the keys are integers and convert to array + acc[k.String()] = res + t, err := tryConvertToArray(res) + if err == nil { + acc[k.String()] = t + } + } +} + +func tryConvertToArray(input map[string]interface{}) ([]interface{}, error) { + keys := make([]int, 0, len(input)) + values := map[int]interface{}{} + + for k, v := range input { + ik, err := strconv.Atoi(k) + if err != nil { + return nil, errors.New("non-integer key, cannot convert") + } + keys = append(keys, ik) + values[ik] = v + } + + sort.Ints(keys) + + result := []interface{}{} + for _, k := range keys { + result = append(result, values[k]) + } + + return result, nil } From 58372c7b716ba47593c92f9f2717bd331f1b3cd7 Mon Sep 17 00:00:00 2001 From: thedae Date: Thu, 21 Nov 2024 12:41:39 +0100 Subject: [PATCH 2/8] Deepsource fixes --- decorator/http_test.go | 2 +- decorator/luatable.go | 3 +-- types.go | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/decorator/http_test.go b/decorator/http_test.go index 561886a..9eb5a53 100644 --- a/decorator/http_test.go +++ b/decorator/http_test.go @@ -11,7 +11,7 @@ import ( "github.com/krakendio/binder" ) -func Example_RegisterBackendModule() { +func ExampleRegisterHTTPRequest() { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { headers, _ := json.Marshal(r.Header) fmt.Println(string(headers)) diff --git a/decorator/luatable.go b/decorator/luatable.go index 284087d..1407321 100644 --- a/decorator/luatable.go +++ b/decorator/luatable.go @@ -2,7 +2,6 @@ package decorator import ( "encoding/json" - "errors" "fmt" "sort" @@ -65,7 +64,7 @@ func tableGet(c *binder.Context) error { d["name"] = t.Name() c.Push().Data(&lua.Table{Data: d}, "luaTable") default: - return errors.New(fmt.Sprintf("unknown type (%T) %v", t, t)) + return fmt.Errorf("unknown type (%T) %v", t, t) } return nil diff --git a/types.go b/types.go index b133d6d..4cff7e4 100644 --- a/types.go +++ b/types.go @@ -77,7 +77,7 @@ func tryConvertToArray(input map[string]interface{}) ([]interface{}, error) { sort.Ints(keys) - result := []interface{}{} + var result []interface{} for _, k := range keys { result = append(result, values[k]) } From d711c2ce534d653f13812531d3945f17c4a6486d Mon Sep 17 00:00:00 2001 From: thedae Date: Thu, 21 Nov 2024 15:42:50 +0100 Subject: [PATCH 3/8] Export proxy request and response --- proxy/request.go | 32 ++++++++++++++++---------------- proxy/response.go | 24 ++++++++++++------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/proxy/request.go b/proxy/request.go index 585e889..fbd18ab 100644 --- a/proxy/request.go +++ b/proxy/request.go @@ -12,7 +12,7 @@ import ( ) func registerRequestTable(req *proxy.Request, b *binder.Binder) { - r := &request{req} + r := &ProxyRequest{req} t := b.Table("request") @@ -30,14 +30,14 @@ func registerRequestTable(req *proxy.Request, b *binder.Binder) { t.Dynamic("body", r.body) } -type request struct { +type ProxyRequest struct { *proxy.Request } var errRequestExpected = errors.New("request expected") -func (*request) method(c *binder.Context) error { - req, ok := c.Arg(1).Data().(*request) +func (*ProxyRequest) method(c *binder.Context) error { + req, ok := c.Arg(1).Data().(*ProxyRequest) if !ok { return errRequestExpected } @@ -51,8 +51,8 @@ func (*request) method(c *binder.Context) error { return nil } -func (*request) path(c *binder.Context) error { - req, ok := c.Arg(1).Data().(*request) +func (*ProxyRequest) path(c *binder.Context) error { + req, ok := c.Arg(1).Data().(*ProxyRequest) if !ok { return errRequestExpected } @@ -66,8 +66,8 @@ func (*request) path(c *binder.Context) error { return nil } -func (*request) query(c *binder.Context) error { - req, ok := c.Arg(1).Data().(*request) +func (*ProxyRequest) query(c *binder.Context) error { + req, ok := c.Arg(1).Data().(*ProxyRequest) if !ok { return errRequestExpected } @@ -81,8 +81,8 @@ func (*request) query(c *binder.Context) error { return nil } -func (*request) url(c *binder.Context) error { - req, ok := c.Arg(1).Data().(*request) +func (*ProxyRequest) url(c *binder.Context) error { + req, ok := c.Arg(1).Data().(*ProxyRequest) if !ok { return errRequestExpected } @@ -101,8 +101,8 @@ func (*request) url(c *binder.Context) error { return nil } -func (*request) params(c *binder.Context) error { - req, ok := c.Arg(1).Data().(*request) +func (*ProxyRequest) params(c *binder.Context) error { + req, ok := c.Arg(1).Data().(*ProxyRequest) if !ok { return errRequestExpected } @@ -118,8 +118,8 @@ func (*request) params(c *binder.Context) error { return nil } -func (*request) headers(c *binder.Context) error { - req, ok := c.Arg(1).Data().(*request) +func (*ProxyRequest) headers(c *binder.Context) error { + req, ok := c.Arg(1).Data().(*ProxyRequest) if !ok { return errRequestExpected } @@ -142,8 +142,8 @@ func (*request) headers(c *binder.Context) error { return nil } -func (*request) body(c *binder.Context) error { - req, ok := c.Arg(1).Data().(*request) +func (*ProxyRequest) body(c *binder.Context) error { + req, ok := c.Arg(1).Data().(*ProxyRequest) if !ok { return errRequestExpected } diff --git a/proxy/response.go b/proxy/response.go index ac15598..6a2e64e 100644 --- a/proxy/response.go +++ b/proxy/response.go @@ -11,7 +11,7 @@ import ( ) func registerResponseTable(resp *proxy.Response, b *binder.Binder) { - r := &response{resp} + r := &ProxyResponse{resp} if r.Metadata.Headers == nil { r.Metadata.Headers = map[string][]string{} } @@ -33,14 +33,14 @@ func registerResponseTable(resp *proxy.Response, b *binder.Binder) { t.Dynamic("body", r.body) } -type response struct { +type ProxyResponse struct { *proxy.Response } var errResponseExpected = errors.New("response expected") -func (*response) isComplete(c *binder.Context) error { - resp, ok := c.Arg(1).Data().(*response) +func (*ProxyResponse) isComplete(c *binder.Context) error { + resp, ok := c.Arg(1).Data().(*ProxyResponse) if !ok { return errResponseExpected } @@ -54,8 +54,8 @@ func (*response) isComplete(c *binder.Context) error { return nil } -func (*response) statusCode(c *binder.Context) error { - resp, ok := c.Arg(1).Data().(*response) +func (*ProxyResponse) statusCode(c *binder.Context) error { + resp, ok := c.Arg(1).Data().(*ProxyResponse) if !ok { return errResponseExpected } @@ -69,8 +69,8 @@ func (*response) statusCode(c *binder.Context) error { return nil } -func (*response) headers(c *binder.Context) error { - resp, ok := c.Arg(1).Data().(*response) +func (*ProxyResponse) headers(c *binder.Context) error { + resp, ok := c.Arg(1).Data().(*ProxyResponse) if !ok { return errResponseExpected } @@ -91,8 +91,8 @@ func (*response) headers(c *binder.Context) error { return nil } -func (*response) body(c *binder.Context) error { - resp, ok := c.Arg(1).Data().(*response) +func (*ProxyResponse) body(c *binder.Context) error { + resp, ok := c.Arg(1).Data().(*ProxyResponse) if !ok { return errResponseExpected } @@ -112,8 +112,8 @@ func (*response) body(c *binder.Context) error { return nil } -func (*response) data(c *binder.Context) error { - resp, ok := c.Arg(1).Data().(*response) +func (*ProxyResponse) data(c *binder.Context) error { + resp, ok := c.Arg(1).Data().(*ProxyResponse) if !ok { return errResponseExpected } From 522aee6cd86ed85e2719d674f3b19c38b973926d Mon Sep 17 00:00:00 2001 From: thedae Date: Fri, 22 Nov 2024 09:15:35 +0100 Subject: [PATCH 4/8] Removing json decorator --- decorator/json.go | 86 --------------------------------------------- proxy/proxy.go | 1 - proxy/proxy_test.go | 2 -- 3 files changed, 89 deletions(-) delete mode 100644 decorator/json.go diff --git a/decorator/json.go b/decorator/json.go deleted file mode 100644 index 844e4db..0000000 --- a/decorator/json.go +++ /dev/null @@ -1,86 +0,0 @@ -package decorator - -import ( - "encoding/json" - - "github.com/krakendio/binder" - lua "github.com/krakendio/krakend-lua/v2" -) - -func RegisterJson(b *binder.Binder) { - tab := b.Table("json") - tab.Static("unmarshal", fromJson) - tab.Static("marshal", toJson) -} - -func fromJson(c *binder.Context) error { - if c.Top() != 1 { - return ErrNeedsArguments - } - data := new(interface{}) - err := json.Unmarshal([]byte(c.Arg(1).String()), data) - if err != nil { - return err - } - - switch v := (*data).(type) { - case string: - c.Push().String(v) - case json.Number: - n, _ := v.Float64() - c.Push().Number(n) - case int: - c.Push().Number(float64(v)) - case float64: - c.Push().Number(v) - case bool: - c.Push().Bool(v) - case []interface{}: - c.Push().Data(&lua.List{Data: v}, "luaList") - case map[string]interface{}: - c.Push().Data(&lua.Table{Data: v}, "luaTable") - } - return nil -} - -func toJson(c *binder.Context) error { - if c.Top() != 1 { - return ErrNeedsArguments - } - switch t := c.Arg(1).Any().(type) { - case lua.NativeString: - return marshal(c, c.Arg(1).String()) - case lua.NativeNumber: - return marshal(c, c.Arg(1).Number()) - case lua.NativeBool: - return marshal(c, c.Arg(1).Bool()) - case *lua.NativeTable: - res := map[string]interface{}{} - t.ForEach(func(k, v lua.NativeValue) { - lua.ParseToTable(k, v, res) - }) - return marshal(c, res) - case *lua.NativeUserData: - if t.Value == nil { - return marshal(c, nil) - } else { - switch v := t.Value.(type) { - case *lua.Table: - return marshal(c, v.Data) - case *lua.List: - return marshal(c, v.Data) - } - } - } - - return nil -} - -func marshal(c *binder.Context, v interface{}) error { - b, err := json.MarshalIndent(v, "", "\t") - if err != nil { - return err - } - c.Push().String(string(b)) - return nil -} diff --git a/proxy/proxy.go b/proxy/proxy.go index 34ca7a1..2c426c0 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -79,7 +79,6 @@ func New(cfg lua.Config, next proxy.Proxy) proxy.Proxy { decorator.RegisterLuaTable(b.GetBinder()) decorator.RegisterLuaList(b.GetBinder()) decorator.RegisterHTTPRequest(ctx, b.GetBinder()) - decorator.RegisterJson(b.GetBinder()) for _, f := range localRegisterer.decorators { f(b.GetBinder()) } diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index e3f83b8..704ae48 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -556,7 +556,6 @@ func TestProxyFactory(t *testing.T) { responseData:set("bar", bar) responseData:set("keys", responseData:keys()) responseData:del("to_be_removed") - responseData:set("json", json.marshal(responseData:keys())) if responseData:keyExists("to_be_removed") then @@ -635,7 +634,6 @@ func TestProxyFactory(t *testing.T) { } ], "foo": "some_new_value", - "json": "[\n\t\"bar\",\n\t\"collection\",\n\t\"foo\",\n\t\"keys\",\n\t\"more\",\n\t\"ok\"\n]", "keys": [ "bar", "collection", From 3c00d872eb99008d0c3a700b031fc27fcbedd480 Mon Sep 17 00:00:00 2001 From: thedae Date: Fri, 22 Nov 2024 18:54:01 +0100 Subject: [PATCH 5/8] Fix response header setter --- proxy/response.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/response.go b/proxy/response.go index 6a2e64e..1201b64 100644 --- a/proxy/response.go +++ b/proxy/response.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "io" + "net/http" "github.com/krakendio/binder" lua "github.com/krakendio/krakend-lua/v2" @@ -85,7 +86,7 @@ func (*ProxyResponse) headers(c *binder.Context) error { c.Push().String(headers[0]) } case 3: - resp.Metadata.Headers[c.Arg(2).String()] = []string{c.Arg(3).String()} + resp.Metadata.Headers[http.CanonicalHeaderKey(c.Arg(2).String())] = []string{c.Arg(3).String()} } return nil From 0230f5c5863e8681910a8f02f3d4ce84fb842665 Mon Sep 17 00:00:00 2001 From: thedae Date: Fri, 22 Nov 2024 20:34:30 +0100 Subject: [PATCH 6/8] Also try to parse arrays at first level --- types.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/types.go b/types.go index 4cff7e4..e94f860 100644 --- a/types.go +++ b/types.go @@ -62,6 +62,20 @@ func ParseToTable(k, v NativeValue, acc map[string]interface{}) { } } +func MapNativeTable(t *NativeTable) (map[string]interface{}, []interface{}) { + res := map[string]interface{}{} + t.ForEach(func(k, v NativeValue) { + ParseToTable(k, v, res) + }) + + // Check if all the keys are integers and convert to array + at, err := tryConvertToArray(res) + if err == nil { + return nil, at + } + return res, nil +} + func tryConvertToArray(input map[string]interface{}) ([]interface{}, error) { keys := make([]int, 0, len(input)) values := map[int]interface{}{} From 59455709227fe7dc5025d9edfb840a8f103af9e0 Mon Sep 17 00:00:00 2001 From: thedae Date: Tue, 3 Dec 2024 10:14:56 +0100 Subject: [PATCH 7/8] Improve native table mapper --- types.go | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/types.go b/types.go index e94f860..5687b6a 100644 --- a/types.go +++ b/types.go @@ -53,30 +53,49 @@ func ParseToTable(k, v NativeValue, acc map[string]interface{}) { v.(*NativeTable).ForEach(func(k, v NativeValue) { ParseToTable(k, v, res) }) - // Check if all the keys are integers and convert to array + // Check if all the keys are integers and convert to slice acc[k.String()] = res - t, err := tryConvertToArray(res) + t, err := tryConvertToSlice(res) if err == nil { acc[k.String()] = t } } } -func MapNativeTable(t *NativeTable) (map[string]interface{}, []interface{}) { +type MappedNativeTable struct { + data interface{} + IsSlice bool +} + +func (m *MappedNativeTable) GetData() map[string]interface{} { + return m.data.(map[string]interface{}) +} + +func (m *MappedNativeTable) GetDataAsSlice() []interface{} { + return m.data.([]interface{}) +} + +func MapNativeTable(t *NativeTable) *MappedNativeTable { res := map[string]interface{}{} t.ForEach(func(k, v NativeValue) { ParseToTable(k, v, res) }) - // Check if all the keys are integers and convert to array - at, err := tryConvertToArray(res) + // Check if all the keys are integers and convert to slice + at, err := tryConvertToSlice(res) if err == nil { - return nil, at + return &MappedNativeTable{ + data: at, + IsSlice: true, + } + } + return &MappedNativeTable{ + data: res, + IsSlice: false, } - return res, nil } -func tryConvertToArray(input map[string]interface{}) ([]interface{}, error) { +func tryConvertToSlice(input map[string]interface{}) ([]interface{}, error) { keys := make([]int, 0, len(input)) values := map[int]interface{}{} From b3332aac1ecc118a6d9309c21b404e0c0b7cfe02 Mon Sep 17 00:00:00 2001 From: thedae Date: Tue, 3 Dec 2024 11:16:07 +0100 Subject: [PATCH 8/8] Simplify native table mapper --- types.go | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/types.go b/types.go index 5687b6a..94f8ddc 100644 --- a/types.go +++ b/types.go @@ -62,20 +62,7 @@ func ParseToTable(k, v NativeValue, acc map[string]interface{}) { } } -type MappedNativeTable struct { - data interface{} - IsSlice bool -} - -func (m *MappedNativeTable) GetData() map[string]interface{} { - return m.data.(map[string]interface{}) -} - -func (m *MappedNativeTable) GetDataAsSlice() []interface{} { - return m.data.([]interface{}) -} - -func MapNativeTable(t *NativeTable) *MappedNativeTable { +func MapNativeTable(t *NativeTable) (interface{}, bool) { res := map[string]interface{}{} t.ForEach(func(k, v NativeValue) { ParseToTable(k, v, res) @@ -84,15 +71,9 @@ func MapNativeTable(t *NativeTable) *MappedNativeTable { // Check if all the keys are integers and convert to slice at, err := tryConvertToSlice(res) if err == nil { - return &MappedNativeTable{ - data: at, - IsSlice: true, - } - } - return &MappedNativeTable{ - data: res, - IsSlice: false, + return at, true } + return res, false } func tryConvertToSlice(input map[string]interface{}) ([]interface{}, error) {