Skip to content

Commit f53bc7b

Browse files
johri21kelindar
andauthored
add a special marker for arrays which are empty (#17)
Co-authored-by: kelindar <roman.atachiants@careem.com>
1 parent b7c8b66 commit f53bc7b

6 files changed

Lines changed: 126 additions & 16 deletions

File tree

fixtures/array.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
local json = require("json")
2+
3+
function main()
4+
return {
5+
["empty"] = json.array(),
6+
["empty_map"] = json.array({}),
7+
["array"] = json.array({1, 2, 3}),
8+
["table"] = json.array({["apple"] = 5}),
9+
["str"] = json.array("hello"),
10+
["int"] = json.array(12),
11+
["bool"] = json.array(true),
12+
["float"] = json.array(12.34)
13+
}
14+
end

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.20
55
require (
66
github.com/cheekybits/genny v1.0.0
77
github.com/stretchr/testify v1.8.0
8-
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
8+
github.com/yuin/gopher-lua v1.1.1
99
layeh.com/gopher-luar v1.0.10
1010
)
1111

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
1414
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
1515
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
1616
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
17-
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ=
18-
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
17+
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
18+
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
1919
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
2020
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2121
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

json/json.go

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,77 @@ func Loader(L *lua.LState) int {
2929
var api = map[string]lua.LGFunction{
3030
"decode": apiDecode,
3131
"encode": apiEncode,
32+
"array": apiArray,
3233
}
3334

34-
func apiDecode(L *lua.LState) int {
35-
str := L.CheckString(1)
35+
func apiDecode(state *lua.LState) int {
36+
str := state.CheckString(1)
3637

37-
value, err := Decode(L, []byte(str))
38+
value, err := Decode(state, []byte(str))
3839
if err != nil {
39-
L.Push(lua.LNil)
40-
L.Push(lua.LString(err.Error()))
40+
state.Push(lua.LNil)
41+
state.Push(lua.LString(err.Error()))
4142
return 2
4243
}
43-
L.Push(value)
44+
state.Push(value)
4445
return 1
4546
}
4647

47-
func apiEncode(L *lua.LState) int {
48-
value := L.CheckAny(1)
48+
func apiEncode(state *lua.LState) int {
49+
value := state.CheckAny(1)
4950

5051
data, err := Encode(value)
5152
if err != nil {
52-
L.Push(lua.LNil)
53-
L.RaiseError(err.Error())
53+
state.Push(lua.LNil)
54+
state.RaiseError(err.Error())
5455
return 1
5556
}
56-
L.Push(lua.LString(string(data)))
57+
state.Push(lua.LString(string(data)))
5758
return 1
5859
}
5960

6061
// --------------------------------------------------------------------
6162

63+
// EmptyArray is a marker for an empty array.
64+
var EmptyArray = &lua.LUserData{Value: []any(nil)}
65+
66+
// apiArray creates an array from the arguments.
67+
func apiArray(state *lua.LState) int {
68+
switch state.GetTop() {
69+
case 0:
70+
state.Push(EmptyArray)
71+
return 1
72+
case 1:
73+
// If it's not a table, or empty return a marker
74+
table, ok := state.CheckAny(1).(*lua.LTable)
75+
switch {
76+
case ok && table.Len() > 0: // array
77+
state.Push(table)
78+
return 1
79+
case ok: // check if it's an empty map
80+
k, v := table.Next(lua.LNil)
81+
if k == lua.LNil && v == lua.LNil {
82+
state.Push(EmptyArray)
83+
return 1
84+
}
85+
}
86+
87+
// Otherwise, return an array
88+
fallthrough
89+
default:
90+
table := state.CreateTable(state.GetTop(), 0)
91+
for i := 1; i <= state.GetTop(); i++ {
92+
table.RawSetInt(i, state.Get(i))
93+
}
94+
95+
// Return the table
96+
state.Push(table)
97+
return 1
98+
}
99+
}
100+
101+
// --------------------------------------------------------------------
102+
62103
type invalidTypeError lua.LValueType
63104

64105
func (i invalidTypeError) Error() string {
@@ -87,7 +128,12 @@ func (j jsonValue) MarshalJSON() (data []byte, err error) {
87128
case *lua.LNilType:
88129
data = []byte(`null`)
89130
case *lua.LUserData:
90-
data, err = json.Marshal(converted.Value)
131+
switch {
132+
case converted == EmptyArray:
133+
data = []byte(`[]`)
134+
default:
135+
data, err = json.Marshal(converted.Value)
136+
}
91137
case lua.LString:
92138
data, err = json.Marshal(string(converted))
93139
case *lua.LTable:

json/json_test.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ package json
77

88
import (
99
"encoding/json"
10+
"fmt"
1011
"testing"
1112

1213
"github.com/stretchr/testify/assert"
13-
"github.com/yuin/gopher-lua"
14+
lua "github.com/yuin/gopher-lua"
1415
)
1516

1617
func TestSimple(t *testing.T) {
@@ -94,3 +95,30 @@ func TestDecodeValue_jsonNumber(t *testing.T) {
9495
t.Fatalf("expecting LString, got %T", v)
9596
}
9697
}
98+
99+
func TestArrayCoerce(t *testing.T) {
100+
const require = `local json = require("json")`
101+
tests := [][2]string{
102+
{"", "[]"},
103+
{"{}", "[]"},
104+
{"{1, 2}", "[1,2]"},
105+
{`"hello"`, `[\"hello\"]`},
106+
{`1.2`, `[1.2]`},
107+
{`true`, `[true]`},
108+
}
109+
110+
for _, tc := range tests {
111+
t.Run(tc[0], func(t *testing.T) {
112+
s := lua.NewState()
113+
defer s.Close()
114+
115+
script := fmt.Sprintf("%s\n"+
116+
"print(json.array(%s))\n"+
117+
"assert(json.encode(json.array(%s)) == \"%s\")",
118+
require, tc[0], tc[0], tc[1])
119+
120+
s.PreloadModule("json", Loader)
121+
assert.NoError(t, s.DoString(script))
122+
})
123+
}
124+
}

script_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package lua
55

66
import (
77
"context"
8+
"encoding/json"
89
"os"
910
"testing"
1011

@@ -289,3 +290,24 @@ func TestNewScript(t *testing.T) {
289290
assert.NoError(t, err)
290291
assert.Equal(t, 10, s.Concurrency())
291292
}
293+
294+
func TestEmptyArray(t *testing.T) {
295+
s, err := newScript("fixtures/array.lua")
296+
assert.NoError(t, err)
297+
298+
out, err := s.Run(context.Background())
299+
b, err := json.Marshal(out)
300+
assert.NoError(t, err)
301+
302+
assert.JSONEq(t, `{
303+
"empty": [],
304+
"empty_map": [],
305+
"array": [1, 2, 3],
306+
"table": [{"apple": 5}],
307+
"str": ["hello"],
308+
"int": [12],
309+
"bool": [true],
310+
"float": [12.34]
311+
}`, string(b))
312+
313+
}

0 commit comments

Comments
 (0)