Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions fixtures/array.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
local json = require("json")

function main()
return {
["empty"] = json.array(),
["empty_map"] = json.array({}),
["array"] = json.array({1, 2, 3}),
["table"] = json.array({["apple"] = 5}),
["str"] = json.array("hello"),
["int"] = json.array(12),
["bool"] = json.array(true),
["float"] = json.array(12.34)
}
end
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.20
require (
github.com/cheekybits/genny v1.0.0
github.com/stretchr/testify v1.8.0
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
github.com/yuin/gopher-lua v1.1.1
layeh.com/gopher-luar v1.0.10
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ=
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
70 changes: 58 additions & 12 deletions json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,77 @@ func Loader(L *lua.LState) int {
var api = map[string]lua.LGFunction{
"decode": apiDecode,
"encode": apiEncode,
"array": apiArray,
}

func apiDecode(L *lua.LState) int {
str := L.CheckString(1)
func apiDecode(state *lua.LState) int {
str := state.CheckString(1)

value, err := Decode(L, []byte(str))
value, err := Decode(state, []byte(str))
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
state.Push(lua.LNil)
state.Push(lua.LString(err.Error()))
return 2
}
L.Push(value)
state.Push(value)
return 1
}

func apiEncode(L *lua.LState) int {
value := L.CheckAny(1)
func apiEncode(state *lua.LState) int {
value := state.CheckAny(1)

data, err := Encode(value)
if err != nil {
L.Push(lua.LNil)
L.RaiseError(err.Error())
state.Push(lua.LNil)
state.RaiseError(err.Error())
return 1
}
L.Push(lua.LString(string(data)))
state.Push(lua.LString(string(data)))
return 1
}

// --------------------------------------------------------------------

// EmptyArray is a marker for an empty array.
var EmptyArray = &lua.LUserData{Value: []any(nil)}

// apiArray creates an array from the arguments.
func apiArray(state *lua.LState) int {
switch state.GetTop() {
case 0:
state.Push(EmptyArray)
return 1
case 1:
// If it's not a table, or empty return a marker
table, ok := state.CheckAny(1).(*lua.LTable)
switch {
case ok && table.Len() > 0: // array
state.Push(table)
return 1
case ok: // check if it's an empty map
k, v := table.Next(lua.LNil)
if k == lua.LNil && v == lua.LNil {
state.Push(EmptyArray)
return 1
}
}

// Otherwise, return an array
fallthrough
default:
table := state.CreateTable(state.GetTop(), 0)
for i := 1; i <= state.GetTop(); i++ {
table.RawSetInt(i, state.Get(i))
}

// Return the table
state.Push(table)
return 1
}
}

// --------------------------------------------------------------------

type invalidTypeError lua.LValueType

func (i invalidTypeError) Error() string {
Expand Down Expand Up @@ -87,7 +128,12 @@ func (j jsonValue) MarshalJSON() (data []byte, err error) {
case *lua.LNilType:
data = []byte(`null`)
case *lua.LUserData:
data, err = json.Marshal(converted.Value)
switch {
case converted == EmptyArray:
data = []byte(`[]`)
default:
data, err = json.Marshal(converted.Value)
}
case lua.LString:
data, err = json.Marshal(string(converted))
case *lua.LTable:
Expand Down
30 changes: 29 additions & 1 deletion json/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ package json

import (
"encoding/json"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/yuin/gopher-lua"
lua "github.com/yuin/gopher-lua"
)

func TestSimple(t *testing.T) {
Expand Down Expand Up @@ -94,3 +95,30 @@ func TestDecodeValue_jsonNumber(t *testing.T) {
t.Fatalf("expecting LString, got %T", v)
}
}

func TestArrayCoerce(t *testing.T) {
const require = `local json = require("json")`
tests := [][2]string{
{"", "[]"},
{"{}", "[]"},
{"{1, 2}", "[1,2]"},
{`"hello"`, `[\"hello\"]`},
{`1.2`, `[1.2]`},
{`true`, `[true]`},
}

for _, tc := range tests {
t.Run(tc[0], func(t *testing.T) {
s := lua.NewState()
defer s.Close()

script := fmt.Sprintf("%s\n"+
"print(json.array(%s))\n"+
"assert(json.encode(json.array(%s)) == \"%s\")",
require, tc[0], tc[0], tc[1])

s.PreloadModule("json", Loader)
assert.NoError(t, s.DoString(script))
})
}
}
22 changes: 22 additions & 0 deletions script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package lua

import (
"context"
"encoding/json"
"os"
"testing"

Expand Down Expand Up @@ -289,3 +290,24 @@ func TestNewScript(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 10, s.Concurrency())
}

func TestEmptyArray(t *testing.T) {
s, err := newScript("fixtures/array.lua")
assert.NoError(t, err)

out, err := s.Run(context.Background())
b, err := json.Marshal(out)
assert.NoError(t, err)

assert.JSONEq(t, `{
"empty": [],
"empty_map": [],
"array": [1, 2, 3],
"table": [{"apple": 5}],
"str": ["hello"],
"int": [12],
"bool": [true],
"float": [12.34]
}`, string(b))

}