Skip to content

Commit f29ff4e

Browse files
committed
embed gopher-json package
We kept a fork, but the code is unmaintained anyway, and this saves a dependency.
1 parent e79bddc commit f29ff4e

File tree

7 files changed

+326
-9
lines changed

7 files changed

+326
-9
lines changed

cmd_scripting.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import (
99
"strings"
1010
"sync"
1111

12-
luajson "github.com/alicebob/gopher-json"
1312
lua "github.com/yuin/gopher-lua"
1413
"github.com/yuin/gopher-lua/parse"
1514

15+
luajson "github.com/alicebob/miniredis/v2/gopher-json"
1616
"github.com/alicebob/miniredis/v2/server"
1717
)
1818

go.mod

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
module github.com/alicebob/miniredis/v2
22

3-
require (
4-
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302
5-
github.com/yuin/gopher-lua v1.1.1
6-
)
3+
require github.com/yuin/gopher-lua v1.1.1
74

85
go 1.17

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
2-
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
3-
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
4-
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
51
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
62
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
73
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=

gopher-json/LICENSE

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
This is free and unencumbered software released into the public domain.
2+
3+
Anyone is free to copy, modify, publish, use, compile, sell, or
4+
distribute this software, either in source code form or as a compiled
5+
binary, for any purpose, commercial or non-commercial, and by any
6+
means.
7+
8+
In jurisdictions that recognize copyright laws, the author or authors
9+
of this software dedicate any and all copyright interest in the
10+
software to the public domain. We make this dedication for the benefit
11+
of the public at large and to the detriment of our heirs and
12+
successors. We intend this dedication to be an overt act of
13+
relinquishment in perpetuity of all present and future rights to this
14+
software under copyright law.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
23+
24+
For more information, please refer to <http://unlicense.org/>

gopher-json/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Copied from https://github.com/layeh/gopher-json and https://github.com/alicebob/gopher-json

gopher-json/json.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package json
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
7+
"github.com/yuin/gopher-lua"
8+
)
9+
10+
// Preload adds json to the given Lua state's package.preload table. After it
11+
// has been preloaded, it can be loaded using require:
12+
//
13+
// local json = require("json")
14+
func Preload(L *lua.LState) {
15+
L.PreloadModule("json", Loader)
16+
}
17+
18+
// Loader is the module loader function.
19+
func Loader(L *lua.LState) int {
20+
t := L.NewTable()
21+
L.SetFuncs(t, api)
22+
L.Push(t)
23+
return 1
24+
}
25+
26+
var api = map[string]lua.LGFunction{
27+
"decode": apiDecode,
28+
"encode": apiEncode,
29+
}
30+
31+
func apiDecode(L *lua.LState) int {
32+
if L.GetTop() != 1 {
33+
L.Error(lua.LString("bad argument #1 to decode"), 1)
34+
return 0
35+
}
36+
str := L.CheckString(1)
37+
38+
value, err := Decode(L, []byte(str))
39+
if err != nil {
40+
L.Push(lua.LNil)
41+
L.Push(lua.LString(err.Error()))
42+
return 2
43+
}
44+
L.Push(value)
45+
return 1
46+
}
47+
48+
func apiEncode(L *lua.LState) int {
49+
if L.GetTop() != 1 {
50+
L.Error(lua.LString("bad argument #1 to encode"), 1)
51+
return 0
52+
}
53+
value := L.CheckAny(1)
54+
55+
data, err := Encode(value)
56+
if err != nil {
57+
L.Push(lua.LNil)
58+
L.Push(lua.LString(err.Error()))
59+
return 2
60+
}
61+
L.Push(lua.LString(string(data)))
62+
return 1
63+
}
64+
65+
var (
66+
errNested = errors.New("cannot encode recursively nested tables to JSON")
67+
errSparseArray = errors.New("cannot encode sparse array")
68+
errInvalidKeys = errors.New("cannot encode mixed or invalid key types")
69+
)
70+
71+
type invalidTypeError lua.LValueType
72+
73+
func (i invalidTypeError) Error() string {
74+
return `cannot encode ` + lua.LValueType(i).String() + ` to JSON`
75+
}
76+
77+
// Encode returns the JSON encoding of value.
78+
func Encode(value lua.LValue) ([]byte, error) {
79+
return json.Marshal(jsonValue{
80+
LValue: value,
81+
visited: make(map[*lua.LTable]bool),
82+
})
83+
}
84+
85+
type jsonValue struct {
86+
lua.LValue
87+
visited map[*lua.LTable]bool
88+
}
89+
90+
func (j jsonValue) MarshalJSON() (data []byte, err error) {
91+
switch converted := j.LValue.(type) {
92+
case lua.LBool:
93+
data, err = json.Marshal(bool(converted))
94+
case lua.LNumber:
95+
data, err = json.Marshal(float64(converted))
96+
case *lua.LNilType:
97+
data = []byte(`null`)
98+
case lua.LString:
99+
data, err = json.Marshal(string(converted))
100+
case *lua.LTable:
101+
if j.visited[converted] {
102+
return nil, errNested
103+
}
104+
j.visited[converted] = true
105+
106+
key, value := converted.Next(lua.LNil)
107+
108+
switch key.Type() {
109+
case lua.LTNil: // empty table
110+
data = []byte(`[]`)
111+
case lua.LTNumber:
112+
arr := make([]jsonValue, 0, converted.Len())
113+
expectedKey := lua.LNumber(1)
114+
for key != lua.LNil {
115+
if key.Type() != lua.LTNumber {
116+
err = errInvalidKeys
117+
return
118+
}
119+
if expectedKey != key {
120+
err = errSparseArray
121+
return
122+
}
123+
arr = append(arr, jsonValue{value, j.visited})
124+
expectedKey++
125+
key, value = converted.Next(key)
126+
}
127+
data, err = json.Marshal(arr)
128+
case lua.LTString:
129+
obj := make(map[string]jsonValue)
130+
for key != lua.LNil {
131+
if key.Type() != lua.LTString {
132+
err = errInvalidKeys
133+
return
134+
}
135+
obj[key.String()] = jsonValue{value, j.visited}
136+
key, value = converted.Next(key)
137+
}
138+
data, err = json.Marshal(obj)
139+
default:
140+
err = errInvalidKeys
141+
}
142+
default:
143+
err = invalidTypeError(j.LValue.Type())
144+
}
145+
return
146+
}
147+
148+
// Decode converts the JSON encoded data to Lua values.
149+
func Decode(L *lua.LState, data []byte) (lua.LValue, error) {
150+
var value interface{}
151+
err := json.Unmarshal(data, &value)
152+
if err != nil {
153+
return nil, err
154+
}
155+
return DecodeValue(L, value), nil
156+
}
157+
158+
// DecodeValue converts the value to a Lua value.
159+
//
160+
// This function only converts values that the encoding/json package decodes to.
161+
// All other values will return lua.LNil.
162+
func DecodeValue(L *lua.LState, value interface{}) lua.LValue {
163+
switch converted := value.(type) {
164+
case bool:
165+
return lua.LBool(converted)
166+
case float64:
167+
return lua.LNumber(converted)
168+
case string:
169+
return lua.LString(converted)
170+
case json.Number:
171+
return lua.LString(converted)
172+
case []interface{}:
173+
arr := L.CreateTable(len(converted), 0)
174+
for _, item := range converted {
175+
arr.Append(DecodeValue(L, item))
176+
}
177+
return arr
178+
case map[string]interface{}:
179+
tbl := L.CreateTable(0, len(converted))
180+
for key, item := range converted {
181+
tbl.RawSetH(lua.LString(key), DecodeValue(L, item))
182+
}
183+
return tbl
184+
case nil:
185+
return lua.LNil
186+
}
187+
188+
return lua.LNil
189+
}

gopher-json/json_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package json
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
lua "github.com/yuin/gopher-lua"
8+
)
9+
10+
func TestSimple(t *testing.T) {
11+
const str = `
12+
local json = require("json")
13+
assert(type(json) == "table")
14+
assert(type(json.decode) == "function")
15+
assert(type(json.encode) == "function")
16+
17+
assert(json.encode(true) == "true")
18+
assert(json.encode(1) == "1")
19+
assert(json.encode(-10) == "-10")
20+
assert(json.encode(nil) == "null")
21+
assert(json.encode({}) == "[]")
22+
assert(json.encode({1, 2, 3}) == "[1,2,3]")
23+
24+
local _, err = json.encode({1, 2, [10] = 3})
25+
assert(string.find(err, "sparse array"))
26+
27+
local _, err = json.encode({1, 2, 3, name = "Tim"})
28+
assert(string.find(err, "mixed or invalid key types"))
29+
30+
local _, err = json.encode({name = "Tim", [false] = 123})
31+
assert(string.find(err, "mixed or invalid key types"))
32+
33+
local obj = {"a",1,"b",2,"c",3}
34+
local jsonStr = json.encode(obj)
35+
local jsonObj = json.decode(jsonStr)
36+
for i = 1, #obj do
37+
assert(obj[i] == jsonObj[i])
38+
end
39+
40+
local obj = {name="Tim",number=12345}
41+
local jsonStr = json.encode(obj)
42+
local jsonObj = json.decode(jsonStr)
43+
assert(obj.name == jsonObj.name)
44+
assert(obj.number == jsonObj.number)
45+
46+
assert(json.decode("null") == nil)
47+
48+
local status, err = pcall(function() json.decode() end)
49+
50+
assert(err == "<string>:38: bad argument #1 to decode", err)
51+
local status, err = pcall(function() json.decode(1,2) end)
52+
assert(err == "<string>:40: bad argument #1 to decode", err)
53+
local status, err = pcall(function() json.encode() end)
54+
assert(err == "<string>:42: bad argument #1 to encode", err)
55+
local status, err = pcall(function() json.encode(1,2) end)
56+
assert(err == "<string>:44: bad argument #1 to encode", err)
57+
58+
assert(json.decode(json.encode({person={name = "tim",}})).person.name == "tim")
59+
60+
local obj = {
61+
abc = 123,
62+
def = nil,
63+
}
64+
local obj2 = {
65+
obj = obj,
66+
}
67+
obj.obj2 = obj2
68+
assert(json.encode(obj) == nil)
69+
70+
local a = {}
71+
for i=1, 5 do
72+
a[i] = i
73+
end
74+
assert(json.encode(a) == "[1,2,3,4,5]")
75+
`
76+
s := lua.NewState()
77+
defer s.Close()
78+
79+
t.Skip("broken")
80+
Preload(s)
81+
if err := s.DoString(str); err != nil {
82+
t.Error(err)
83+
}
84+
}
85+
86+
func TestCustomRequire(t *testing.T) {
87+
const str = `
88+
local j = require("JSON")
89+
assert(type(j) == "table")
90+
assert(type(j.decode) == "function")
91+
assert(type(j.encode) == "function")
92+
`
93+
s := lua.NewState()
94+
defer s.Close()
95+
96+
s.PreloadModule("JSON", Loader)
97+
if err := s.DoString(str); err != nil {
98+
t.Error(err)
99+
}
100+
}
101+
102+
func TestDecodeValue_jsonNumber(t *testing.T) {
103+
s := lua.NewState()
104+
defer s.Close()
105+
106+
v := DecodeValue(s, json.Number("124.11"))
107+
if v.Type() != lua.LTString || v.String() != "124.11" {
108+
t.Fatalf("expecting LString, got %T", v)
109+
}
110+
}

0 commit comments

Comments
 (0)