fix error when convert empty lua table

Signed-off-by: yingjinhui <yingjinhui@didiglobal.com>
This commit is contained in:
yingjinhui 2022-11-14 17:30:05 +08:00
parent aeac17b77d
commit 5cc6d813ab
2 changed files with 165 additions and 0 deletions

View File

@ -16,6 +16,60 @@ func ConvertLuaResultInto(luaResult lua.LValue, obj interface{}) error {
if err != nil {
return fmt.Errorf("obj is not pointer")
}
// For example, `GetReplicas` returns requirement with empty:
// {
// nodeClaim: {},
// resourceRequest: {
// cpu: "100m"
// }
// }
// Luajson encodes it to
// {"nodeClaim": [], "resourceRequest": {"cpu": "100m"}}
//
// While go json fails to unmarshal `[]` to ReplicaRequirements.NodeClaim object.
// ReplicaRequirements object.
//
// Here we handle it as follows:
// 1. Walk the object (lua table), delete the key with empty value (`nodeClaim` in this example):
// {
// resourceRequest: {
// cpu: "100m"
// }
// }
// 2. Encode the object with luajson to be:
// {"resourceRequest": {"cpu": "100m"}}
// 4. Finally, unmarshal the new json to object, get
// {
// resourceRequest: {
// cpu: "100m"
// }
// }
isEmptyDic := func(v *lua.LTable) bool {
count := 0
v.ForEach(func(lua.LValue, lua.LValue) {
count++
})
return count == 0
}
var walkValue func(v lua.LValue)
walkValue = func(v lua.LValue) {
if t, ok := v.(*lua.LTable); ok {
t.ForEach(func(key lua.LValue, value lua.LValue) {
if tt, ok := value.(*lua.LTable); ok {
if isEmptyDic(tt) {
// set nil to delete key
t.RawSetH(key, lua.LNil)
} else {
walkValue(value)
}
}
})
}
}
walkValue(luaResult)
jsonBytes, err := luajson.Encode(luaResult)
if err != nil {
return fmt.Errorf("json Encode obj eroor %v", err)

View File

@ -0,0 +1,111 @@
package luavm
import (
"reflect"
"testing"
lua "github.com/yuin/gopher-lua"
)
func TestConvertLuaResultInto(t *testing.T) {
type barStruct struct {
Bar string
}
type fooStruct struct {
Bar barStruct
}
type args struct {
luaResult lua.LValue
obj interface{}
}
tests := []struct {
name string
args args
wantErr bool
want interface{}
}{
{
name: "not a pointer",
args: args{
luaResult: &lua.LTable{},
obj: struct{}{},
},
wantErr: true,
want: struct{}{},
},
{
name: "empty table into struct",
args: args{
luaResult: &lua.LTable{},
obj: &fooStruct{},
},
wantErr: false,
want: &fooStruct{},
},
{
name: "empty table into slice",
args: args{
luaResult: &lua.LTable{},
obj: &[]string{},
},
wantErr: false,
want: &[]string{},
},
{
name: "non-empty table into slice",
args: args{
luaResult: func() lua.LValue {
v := &lua.LTable{}
v.Append(lua.LString("foo"))
v.Append(lua.LString("bar"))
return v
}(),
obj: &[]string{},
},
wantErr: false,
want: &[]string{"foo", "bar"},
},
{
name: "table with empty table into slice",
args: args{
luaResult: func() lua.LValue {
v := &lua.LTable{}
v.RawSetString("Bar", &lua.LTable{})
return v
}(),
obj: &fooStruct{},
},
wantErr: false,
want: &fooStruct{},
},
{
name: "struct is not empty, and convert successfully",
args: args{
luaResult: func() lua.LValue {
bar := &lua.LTable{}
bar.RawSetString("Bar", lua.LString("bar"))
v := &lua.LTable{}
v.RawSetString("Bar", bar)
return v
}(),
obj: &fooStruct{},
},
wantErr: false,
want: &fooStruct{
Bar: barStruct{Bar: "bar"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := ConvertLuaResultInto(tt.args.luaResult, tt.args.obj); (err != nil) != tt.wantErr {
t.Errorf("ConvertLuaResultInto() error = %v, wantErr %v", err, tt.wantErr)
}
if got := tt.args.obj; !reflect.DeepEqual(tt.want, got) {
t.Errorf("ConvertLuaResultInto() got = %v, want %v", got, tt.want)
}
})
}
}