105 lines
2.7 KiB
Go
105 lines
2.7 KiB
Go
package luavm
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"reflect"
|
||
|
||
lua "github.com/yuin/gopher-lua"
|
||
"k8s.io/apimachinery/pkg/conversion"
|
||
luajson "layeh.com/gopher-json"
|
||
)
|
||
|
||
// ConvertLuaResultInto convert lua result to obj
|
||
func ConvertLuaResultInto(luaResult lua.LValue, obj interface{}) error {
|
||
t, err := conversion.EnforcePtr(obj)
|
||
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)
|
||
}
|
||
|
||
// for lua an empty object by json encode be [] not {}
|
||
if t.Kind() == reflect.Struct && len(jsonBytes) > 1 && jsonBytes[0] == '[' {
|
||
jsonBytes[0], jsonBytes[len(jsonBytes)-1] = '{', '}'
|
||
}
|
||
|
||
err = json.Unmarshal(jsonBytes, obj)
|
||
if err != nil {
|
||
return fmt.Errorf("can not unmarshal %v to %#v:%v", string(jsonBytes), obj, err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ConvertLuaResultToInt convert lua result to int.
|
||
func ConvertLuaResultToInt(luaResult lua.LValue) (int32, error) {
|
||
if luaResult.Type() != lua.LTNumber {
|
||
return 0, fmt.Errorf("result type %#v is not number", luaResult.Type())
|
||
}
|
||
return int32(luaResult.(lua.LNumber)), nil
|
||
}
|
||
|
||
// ConvertLuaResultToBool convert lua result to bool.
|
||
func ConvertLuaResultToBool(luaResult lua.LValue) (bool, error) {
|
||
if luaResult.Type() != lua.LTBool {
|
||
return false, fmt.Errorf("result type %#v is not bool", luaResult.Type())
|
||
}
|
||
return bool(luaResult.(lua.LBool)), nil
|
||
}
|