Browse Source

优化serder

pull/1/head
Sydonian 2 years ago
parent
commit
9368ebd936
5 changed files with 278 additions and 105 deletions
  1. +1
    -1
      pkg/distlock/service/internal/lease_actor.go
  2. +2
    -2
      pkg/distlock/service/service.go
  3. +138
    -0
      utils/serder/any_to_any.go
  4. +6
    -79
      utils/serder/serder.go
  5. +131
    -23
      utils/serder/serder_test.go

+ 1
- 1
pkg/distlock/service/internal/lease_actor.go View File

@@ -115,7 +115,7 @@ func (a *LeaseActor) Serve() error {


err := a.mainActor.Release(reqID) err := a.mainActor.Release(reqID)
if err != nil { if err != nil {
logger.Std.Warnf("releasing lock request: %w", err)
logger.Std.Warnf("releasing lock request: %s", err.Error())
} }
} }
} }


+ 2
- 2
pkg/distlock/service/service.go View File

@@ -92,7 +92,7 @@ func (svc *Service) Acquire(req distlock.LockRequest, opts ...AcquireOption) (st
// TODO 不影响结果,但考虑打日志 // TODO 不影响结果,但考虑打日志
err := svc.leaseActor.Add(reqID, time.Duration(opt.LeaseTimeSec)*time.Second) err := svc.leaseActor.Add(reqID, time.Duration(opt.LeaseTimeSec)*time.Second)
if err != nil { if err != nil {
logger.Std.Warnf("adding lease: %w", err)
logger.Std.Warnf("adding lease: %s", err.Error())
} }
} }


@@ -111,7 +111,7 @@ func (svc *Service) Release(reqID string) error {
// TODO 不影响结果,但考虑打日志 // TODO 不影响结果,但考虑打日志
err2 := svc.leaseActor.Remove(reqID) err2 := svc.leaseActor.Remove(reqID)
if err2 != nil { if err2 != nil {
logger.Std.Warnf("removing lease: %w", err2)
logger.Std.Warnf("removing lease: %s", err2.Error())
} }


return err return err


+ 138
- 0
utils/serder/any_to_any.go View File

@@ -0,0 +1,138 @@
package serder

import (
"reflect"

mp "github.com/mitchellh/mapstructure"
myreflect "gitlink.org.cn/cloudream/common/utils/reflect"
)

type Converter func(srcType reflect.Type, dstType reflect.Type, data interface{}) (interface{}, error)

type AnyToAnyOption struct {
NoFromAny bool // 不判断目的字段是否实现了FromAny接口
NoToAny bool // 不判断源字段是否实现了ToAny接口
Converters []Converter // 字段类型转换函数
}

type FromAny interface {
FromAny(val any) (ok bool, err error)
}

type ToAny interface {
ToAny(typ reflect.Type) (val any, ok bool, err error)
}

// AnyToAny 相同结构的任意类型对象之间的转换
func AnyToAny(src any, dst any, opts ...AnyToAnyOption) error {
var opt AnyToAnyOption
if len(opts) > 0 {
opt = opts[0]
}

var hooks []mp.DecodeHookFunc
if !opt.NoToAny {
hooks = append(hooks, toAny)
}

if !opt.NoFromAny {
hooks = append(hooks, fromAny)
}

for _, c := range opt.Converters {
hooks = append(hooks, c)
}

config := &mp.DecoderConfig{
TagName: "json",
Squash: true,
WeaklyTypedInput: true,
Result: dst,
DecodeHook: mp.ComposeDecodeHookFunc(hooks...),
}

decoder, err := mp.NewDecoder(config)
if err != nil {
return err
}

return decoder.Decode(src)
}

// fromAny 如果目的字段实现的FromAny接口,那么通过此接口实现字段类型转换
func fromAny(srcType reflect.Type, targetType reflect.Type, data interface{}) (interface{}, error) {
if myreflect.TypeOfValue(data) == targetType {
return data, nil
}

if targetType.Implements(myreflect.TypeOf[FromAny]()) {
// 非pointer receiver的FromAny没有意义,因为修改不了receiver的内容,所以这里只支持指针类型
if targetType.Kind() == reflect.Pointer {
val := reflect.New(targetType.Elem())
anyIf := val.Interface().(FromAny)
ok, err := anyIf.FromAny(data)
if err != nil {
return nil, err
}
if !ok {
return data, nil
}

return val.Interface(), nil
}

} else if reflect.PointerTo(targetType).Implements(myreflect.TypeOf[FromAny]()) {
val := reflect.New(targetType)
anyIf := val.Interface().(FromAny)
ok, err := anyIf.FromAny(data)
if err != nil {
return nil, err
}
if !ok {
return data, nil
}

return val.Interface(), nil
}

return data, nil
}

// 如果源字段实现了ToAny接口,那么通过此接口实现字段类型转换
func toAny(srcType reflect.Type, targetType reflect.Type, data interface{}) (interface{}, error) {
dataType := myreflect.TypeOfValue(data)
if dataType == targetType {
return data, nil
}

if dataType.Implements(myreflect.TypeOf[ToAny]()) {
anyIf := data.(ToAny)
dstVal, ok, err := anyIf.ToAny(targetType)
if err != nil {
return nil, err
}
if !ok {
return data, nil
}

return dstVal, nil
} else if reflect.PointerTo(dataType).Implements(myreflect.TypeOf[ToAny]()) {
dataVal := reflect.ValueOf(data)

dataPtrVal := reflect.New(dataType)
dataPtrVal.Elem().Set(dataVal)

anyIf := dataPtrVal.Interface().(ToAny)
dstVal, ok, err := anyIf.ToAny(targetType)
if err != nil {
return nil, err
}
if !ok {
return data, nil
}

return dstVal, nil
}

return data, nil
}

+ 6
- 79
utils/serder/serder.go View File

@@ -4,10 +4,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"time"

mp "github.com/mitchellh/mapstructure"
myreflect "gitlink.org.cn/cloudream/common/utils/reflect"
) )


func ObjectToJSON(obj any) ([]byte, error) { func ObjectToJSON(obj any) ([]byte, error) {
@@ -28,84 +24,14 @@ type TypedSerderOption struct {
TypeFieldName string TypeFieldName string
} }


type FromAny interface {
FromAny(val any) (ok bool, err error)
}

func parseTimeHook(srcType reflect.Type, targetType reflect.Type, data interface{}) (interface{}, error) {
if targetType != reflect.TypeOf(time.Time{}) {
return data, nil
}

switch srcType.Kind() {
case reflect.String:
return time.Parse(time.RFC3339, data.(string))
case reflect.Float64:
return time.Unix(0, int64(data.(float64))*int64(time.Millisecond)), nil
case reflect.Int64:
return time.Unix(0, data.(int64)*int64(time.Millisecond)), nil
default:
return data, nil
}
}

// fromAny 如果目的字段实现的FromAny接口,那么通过此接口实现字段类型转换
func fromAny(srcType reflect.Type, targetType reflect.Type, data interface{}) (interface{}, error) {
if reflect.PointerTo(targetType).Implements(myreflect.TypeOf[FromAny]()) {
val := reflect.New(targetType)
anyIf := val.Interface().(FromAny)
ok, err := anyIf.FromAny(data)
if err != nil {
return nil, err
}
if !ok {
return data, nil
}

return val.Interface(), nil
}

return data, nil
}

// AnyToAny 相同结构的任意类型对象之间的转换
func AnyToAny(src any, dst any) error {
config := &mp.DecoderConfig{
TagName: "json",
Squash: true,
WeaklyTypedInput: true,
Result: dst,
DecodeHook: mp.ComposeDecodeHookFunc(fromAny, parseTimeHook),
}

decoder, err := mp.NewDecoder(config)
if err != nil {
return err
}

return decoder.Decode(src)
}

func MapToObject(m map[string]any, obj any) error { func MapToObject(m map[string]any, obj any) error {
return AnyToAny(m, obj) return AnyToAny(m, obj)
} }


func ObjectToMap(obj any) (map[string]any, error) { func ObjectToMap(obj any) (map[string]any, error) {
var retMap map[string]any
config := &mp.DecoderConfig{
TagName: "json",
Squash: true,
WeaklyTypedInput: true,
Result: &retMap,
}

decoder, err := mp.NewDecoder(config)
if err != nil {
return nil, err
}

err = decoder.Decode(obj)
return retMap, err
var m map[string]any
err := AnyToAny(obj, &m)
return m, err
} }


func TypedMapToObject(m map[string]any, opt TypedSerderOption) (any, error) { func TypedMapToObject(m map[string]any, opt TypedSerderOption) (any, error) {
@@ -128,7 +54,7 @@ func TypedMapToObject(m map[string]any, opt TypedSerderOption) (any, error) {
val := reflect.New(typ) val := reflect.New(typ)


valPtr := val.Interface() valPtr := val.Interface()
err = MapToObject(m, valPtr)
err = AnyToAny(m, valPtr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -137,7 +63,8 @@ func TypedMapToObject(m map[string]any, opt TypedSerderOption) (any, error) {
} }


func ObjectToTypedMap(obj any, opt TypedSerderOption) (map[string]any, error) { func ObjectToTypedMap(obj any, opt TypedSerderOption) (map[string]any, error) {
mp, err := ObjectToMap(obj)
var mp map[string]any
err := AnyToAny(obj, &mp)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 131
- 23
utils/serder/serder_test.go View File

@@ -1,18 +1,19 @@
package serder package serder


import ( import (
"fmt"
"reflect"
"testing" "testing"
"time"


. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
myreflect "gitlink.org.cn/cloudream/common/utils/reflect" myreflect "gitlink.org.cn/cloudream/common/utils/reflect"
) )


type SpecialString struct {
type FromAnyString struct {
Str string Str string
} }


func (a *SpecialString) FromAny(val any) (bool, error) {
func (a *FromAnyString) FromAny(val any) (bool, error) {
if str, ok := val.(string); ok { if str, ok := val.(string); ok {
a.Str = "@" + str a.Str = "@" + str
return true, nil return true, nil
@@ -21,6 +22,61 @@ func (a *SpecialString) FromAny(val any) (bool, error) {
return false, nil return false, nil
} }


type ToAnyString struct {
Str string
}

func (a *ToAnyString) ToAny(typ reflect.Type) (val any, ok bool, err error) {
if typ == myreflect.TypeOf[map[string]any]() {
return map[string]any{
"str": "@" + a.Str,
}, true, nil
}

return nil, false, nil
}

type FromAnySt struct {
Value string
}

func (a *FromAnySt) FromAny(val any) (bool, error) {
if st, ok := val.(ToAnySt); ok {
a.Value = "From:" + st.Value
return true, nil
}

return false, nil
}

type ToAnySt struct {
Value string
}

func (a *ToAnySt) ToAny(typ reflect.Type) (val any, ok bool, err error) {
if typ == myreflect.TypeOf[FromAnySt]() {
return FromAnySt{
Value: "To:" + a.Value,
}, true, nil
}

return nil, false, nil
}

type DirToAnySt struct {
Value string
}

func (a DirToAnySt) ToAny(typ reflect.Type) (val any, ok bool, err error) {
if typ == myreflect.TypeOf[FromAnySt]() {
return FromAnySt{
Value: "DirTo:" + a.Value,
}, true, nil
}

return nil, false, nil
}

func Test_MapToObject(t *testing.T) { func Test_MapToObject(t *testing.T) {
Convey("包含用字符串保存的int数据", t, func() { Convey("包含用字符串保存的int数据", t, func() {
type Struct struct { type Struct struct {
@@ -37,7 +93,7 @@ func Test_MapToObject(t *testing.T) {


var st Struct var st Struct


err := MapToObject(mp, &st)
err := AnyToAny(mp, &st)
So(err, ShouldBeNil) So(err, ShouldBeNil)


So(st.A, ShouldEqual, "a") So(st.A, ShouldEqual, "a")
@@ -45,35 +101,25 @@ func Test_MapToObject(t *testing.T) {
So(st.C, ShouldEqual, 1234) So(st.C, ShouldEqual, 1234)
}) })


Convey("包含Time,先从结构体转为JSON,再从JSON转为Map,最后变回结构体", t, func() {
Convey("只有FromAny", t, func() {
type Struct struct { type Struct struct {
Time time.Time
NilTime *time.Time
Special FromAnyString `json:"str"`
} }


var st = Struct{
Time: time.Now(),
NilTime: nil,
mp := map[string]any{
"str": "test",
} }


data, err := ObjectToJSON(st)
So(err, ShouldBeNil)

var mp map[string]any
err = JSONToObject(data, &mp)
So(err, ShouldBeNil)

var st2 Struct
err = MapToObject(mp, &st2)
var ret Struct
err := AnyToAny(mp, &ret)
So(err, ShouldBeNil) So(err, ShouldBeNil)


So(st.Time, ShouldEqual, st2.Time)
So(st.NilTime, ShouldEqual, st2.NilTime)
So(ret.Special.Str, ShouldEqual, "@test")
}) })


Convey("使用FromAny", t, func() {
Convey("字段类型直接实现了FromAny", t, func() {
type Struct struct { type Struct struct {
Special SpecialString `json:"str"`
Special *FromAnyString `json:"str"`
} }


mp := map[string]any{ mp := map[string]any{
@@ -86,6 +132,68 @@ func Test_MapToObject(t *testing.T) {


So(ret.Special.Str, ShouldEqual, "@test") So(ret.Special.Str, ShouldEqual, "@test")
}) })

Convey("只有ToAny", t, func() {
st := struct {
Special ToAnyString `json:"str"`
}{
Special: ToAnyString{
Str: "test",
},
}

ret := map[string]any{}

err := AnyToAny(st, &ret)
So(err, ShouldBeNil)

So(ret["str"].(map[string]any)["str"], ShouldEqual, "@test")
})

Convey("优先使用ToAny", t, func() {
st1 := ToAnySt{
Value: "test",
}

st2 := FromAnySt{}

err := AnyToAny(st1, &st2)
So(err, ShouldBeNil)

So(st2.Value, ShouldEqual, "To:test")
})

Convey("使用Convertor", t, func() {
type Struct1 struct {
Value string
}

type Struct2 struct {
Value string
}

st1 := Struct1{
Value: "test",
}

st2 := Struct2{}

err := AnyToAny(st1, &st2, AnyToAnyOption{
Converters: []Converter{func(srcType reflect.Type, dstType reflect.Type, data interface{}) (interface{}, error) {
if srcType == myreflect.TypeOf[Struct1]() && dstType == myreflect.TypeOf[Struct2]() {
s1 := data.(Struct1)
return Struct2{
Value: "@" + s1.Value,
}, nil
}

return nil, fmt.Errorf("should not arrive here!")
}},
})
So(err, ShouldBeNil)

So(st2.Value, ShouldEqual, "@test")
})
} }


func Test_TypedMapToObject(t *testing.T) { func Test_TypedMapToObject(t *testing.T) {


Loading…
Cancel
Save