Skip to content

Commit

Permalink
🚀 chore: Update Conver struct and methods for better flexibility and …
Browse files Browse the repository at this point in the history
…code organization
  • Loading branch information
sohaha committed Jun 27, 2024
1 parent 3b598a2 commit aaa914e
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 60 deletions.
130 changes: 79 additions & 51 deletions ztype/conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import (
)

type Conver struct {
MatchName func(mapKey, fieldName string) bool
ConvHook func(i reflect.Value, o reflect.Type) (reflect.Value, error)
TagName string
ZeroFields bool
Squash bool
Deep bool
Merge bool
MatchName func(mapKey, fieldName string) bool
ConvHook func(name string, i reflect.Value, o reflect.Type) (reflect.Value, bool)
TagName string
IgnoreTagName bool
ZeroFields bool
Squash bool
Deep bool
Merge bool
}

var conv = Conver{TagName: tagName, Squash: true, MatchName: strings.EqualFold}
Expand All @@ -37,7 +38,7 @@ func ValueConv(input interface{}, out reflect.Value, opt ...func(*Conver)) error
if !out.Elem().CanAddr() {
return errors.New("out must be addressable (a pointer)")
}
return o.to("input", input, out, true)
return o.to("", input, out, true)
}

func (d *Conver) to(name string, input interface{}, outVal reflect.Value, deep bool) error {
Expand Down Expand Up @@ -66,13 +67,13 @@ func (d *Conver) to(name string, input interface{}, outVal reflect.Value, deep b
outputKind := zreflect.GetAbbrKind(outVal)

if d.ConvHook != nil {
if i, err := d.ConvHook(inputVal, t); err != nil {
return err
if i, next := d.ConvHook(name, inputVal, t); !next {
outVal.Set(i)
return nil
} else {
input = i.Interface()
}
}

switch outputKind {
case reflect.Bool:
outVal.SetBool(ToBool(input))
Expand Down Expand Up @@ -210,20 +211,21 @@ func (d *Conver) toStructFromMap(name string, dataVal, val reflect.Value) error

squash := d.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous
remain := false
_, opt := zreflect.GetStructTag(fieldType, d.TagName, tagNameLesser)
tagParts := strings.Split(opt, ",")
for _, tag := range tagParts {
if tag == "squash" {
squash = true
break
}

if tag == "remain" {
remain = true
break
if !d.IgnoreTagName {
_, opt := zreflect.GetStructTag(fieldType, d.TagName, tagNameLesser)
tagParts := strings.Split(opt, ",")
for _, tag := range tagParts {
if tag == "squash" {
squash = true
break
}

if tag == "remain" {
remain = true
break
}
}
}

if squash {
if fieldVal.Kind() != reflect.Struct {
return fmt.Errorf("cannot squash non-struct type '%s'", fieldVal.Type())
Expand All @@ -243,7 +245,12 @@ func (d *Conver) toStructFromMap(name string, dataVal, val reflect.Value) error

for _, f := range fields {
field, fieldValue := f.field, f.val
fieldName, _ := zreflect.GetStructTag(field, d.TagName, tagNameLesser)
var fieldName string
if d.IgnoreTagName {
fieldName = field.Name
} else {
fieldName, _ = zreflect.GetStructTag(field, d.TagName, tagNameLesser)
}
rawMapKey := zreflect.ValueOf(fieldName)
rawMapVal := dataVal.MapIndex(rawMapKey)
if !rawMapVal.IsValid() {
Expand Down Expand Up @@ -388,21 +395,37 @@ func (d *Conver) toMapFromStruct(name string, dataVal reflect.Value, val reflect
}

v := dataVal.Field(i)
if !v.Type().AssignableTo(valMap.Type().Elem()) {
return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
vTyp := v.Type()
if !vTyp.AssignableTo(valMap.Type().Elem()) {
return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", vTyp, valMap.Type().Elem())
}

keyName, opt := zreflect.GetStructTag(f, d.TagName, tagNameLesser)
if keyName == "" {
continue
var (
keyName string
opt string
)

if d.IgnoreTagName {
keyName = f.Name
} else {
keyName, opt = zreflect.GetStructTag(f, d.TagName, tagNameLesser)
if keyName == "" {
continue
}
}

squash := d.Squash && v.Kind() == reflect.Struct && f.Anonymous

if !(v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct) {
nv := v.Elem()
for i := 0; i < typ.NumField(); i++ {
f := typ.Field(i)
keyName, _ := zreflect.GetStructTag(f, d.TagName, tagNameLesser)
var keyName string
if d.IgnoreTagName {
keyName = f.Name
} else {
keyName, _ = zreflect.GetStructTag(f, d.TagName, tagNameLesser)
}
if keyName != "" {
v = nv
break
Expand All @@ -421,35 +444,40 @@ func (d *Conver) toMapFromStruct(name string, dataVal reflect.Value, val reflect
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
return fmt.Errorf("cannot squash non-struct type '%s'", vTyp)
}
}
}

switch v.Kind() {
case reflect.Struct:
x := reflect.New(v.Type())
x.Elem().Set(v)
vType := valMap.Type()
vKeyType := vType.Key()
vElemType := vType.Elem()
mType := reflect.MapOf(vKeyType, vElemType)
vMap := reflect.MakeMap(mType)
addrVal := reflect.New(vMap.Type())
reflect.Indirect(addrVal).Set(vMap)

err := d.to(keyName, x.Interface(), reflect.Indirect(addrVal), false)
if err != nil {
return err
}
switch vTyp.String() {
case "time.Time", "ztime.LocalTime":
valMap.SetMapIndex(zreflect.ValueOf(keyName), v)
default:
x := reflect.New(vTyp)
x.Elem().Set(v)
vType := valMap.Type()
vKeyType := vType.Key()
vElemType := vType.Elem()
mType := reflect.MapOf(vKeyType, vElemType)
vMap := reflect.MakeMap(mType)
addrVal := reflect.New(vMap.Type())
reflect.Indirect(addrVal).Set(vMap)

err := d.to(keyName, x.Interface(), reflect.Indirect(addrVal), false)
if err != nil {
return err
}

vMap = reflect.Indirect(addrVal)
if squash {
for _, k := range vMap.MapKeys() {
valMap.SetMapIndex(k, vMap.MapIndex(k))
vMap = reflect.Indirect(addrVal)
if squash {
for _, k := range vMap.MapKeys() {
valMap.SetMapIndex(k, vMap.MapIndex(k))
}
} else {
valMap.SetMapIndex(zreflect.ValueOf(keyName), vMap)
}
} else {
valMap.SetMapIndex(zreflect.ValueOf(keyName), vMap)
}

default:
Expand Down
13 changes: 6 additions & 7 deletions ztype/map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestMapNil(t *testing.T) {
t.Log(err)
tt.EqualTrue(err != nil)

var m2 = &Map{}
m2 := &Map{}
tt.Equal(true, m2.IsEmpty())
err = m.Delete("no")
t.Log(err)
Expand Down Expand Up @@ -138,7 +138,7 @@ func TestToMaps(T *testing.T) {
Key int
Status bool
}
var rawData = make([]u, 2)
rawData := make([]u, 2)
rawData[0] = u{
Name: "666",
Key: 9,
Expand All @@ -156,7 +156,7 @@ func TestToMaps(T *testing.T) {
t.Log(toSliceMapString)
t.Equal(18, toSliceMapString[0].Get("Other").Get("Sex").Int())

var data = make([]map[string]interface{}, 2)
data := make([]map[string]interface{}, 2)
data[0] = map[string]interface{}{"name": "hi"}
data[1] = map[string]interface{}{"name": "golang"}
toSliceMapString = ToMaps(data)
Expand Down Expand Up @@ -192,12 +192,12 @@ func TestConvContainTime(t *testing.T) {
var s S
isTime := zreflect.TypeOf(time.Time{})
err := To(v, &s, func(conver *Conver) {
conver.ConvHook = func(i reflect.Value, o reflect.Type) (reflect.Value, error) {
conver.ConvHook = func(name string, i reflect.Value, o reflect.Type) (reflect.Value, bool) {
t := i.Type()
if t == isTime && t.ConvertibleTo(o) {
return i.Convert(o), nil
return i.Convert(o), true
}
return i, nil
return i, true
}
})
tt.NoError(err)
Expand All @@ -207,7 +207,6 @@ func TestConvContainTime(t *testing.T) {
}

func BenchmarkName(b *testing.B) {

b.Run("toMapString", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
Expand Down
26 changes: 24 additions & 2 deletions ztype/struct.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ztype

import (
"errors"
"fmt"
"reflect"
"strings"

Expand All @@ -26,6 +28,12 @@ const (
typeSliceStruct
)

func NewStructFromValue(v interface{}) (*StruBuilder, error) {
b := NewStruct()
err := b.Merge(v)
return b, err
}

func NewStruct() *StruBuilder {
return &StruBuilder{
typ: typeStruct,
Expand Down Expand Up @@ -62,18 +70,26 @@ func (b *StruBuilder) Copy(v *StruBuilder) *StruBuilder {
return b
}

func (b *StruBuilder) Merge(values ...interface{}) *StruBuilder {
func (b *StruBuilder) String() string {
return ToString(b.Interface())
}

func (b *StruBuilder) Merge(values ...interface{}) error {
for _, value := range values {
valueOf := reflect.Indirect(zreflect.ValueOf(value))
typeOf := valueOf.Type()
if typeOf.Kind() != reflect.Struct {
return errors.New("value must be struct")
}

for i := 0; i < valueOf.NumField(); i++ {
fval := valueOf.Field(i)
ftyp := typeOf.Field(i)
b.AddField(ftyp.Name, fval.Interface(), string(ftyp.Tag))
}
}

return b
return nil
}

// func (b *StruBuilder) AddFunc(name string, fieldType interface{}, tag ...string) *StruBuilder {
Expand Down Expand Up @@ -128,6 +144,12 @@ func (b *StruBuilder) GetField(name string) *StruField {
return b.fields[name]
}

// 全部字段名
func (b *StruBuilder) FieldNames() []string {
fmt.Println(b.fieldKeys)
return b.fieldKeys
}

func (b *StruBuilder) Interface() interface{} {
return b.Value().Interface()
}
Expand Down
Loading

0 comments on commit aaa914e

Please sign in to comment.