From c135122ca178974e18deb0052b7018ca5d9fef3a Mon Sep 17 00:00:00 2001 From: john Date: Mon, 13 Jul 2020 23:13:50 +0800 Subject: [PATCH] improve package gconv for detailed handling of interface attributes --- util/gconv/gconv_map.go | 9 +++-- util/gconv/gconv_scan.go | 6 ++-- util/gconv/gconv_struct.go | 21 +++++++---- util/gconv/gconv_structs.go | 10 +++--- util/gconv/gconv_z_unit_struct_test.go | 50 ++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 21 deletions(-) diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index d4a648387..c1f67df2f 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -7,7 +7,6 @@ package gconv import ( - "errors" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "reflect" @@ -325,7 +324,7 @@ func doMapToMap(params interface{}, pointer interface{}, deep bool, mapping ...m paramsKind = paramsRv.Kind() } if paramsKind != reflect.Map { - return errors.New("params should be type of map") + return gerror.New("params should be type of map") } // Empty params map, no need continue. if paramsRv.Len() == 0 { @@ -343,7 +342,7 @@ func doMapToMap(params interface{}, pointer interface{}, deep bool, mapping ...m pointerKind = pointerRv.Kind() } if pointerKind != reflect.Map { - return errors.New("pointer should be type of *map") + return gerror.New("pointer should be type of *map") } defer func() { // Catch the panic, especially the reflect operation panics. @@ -434,7 +433,7 @@ func doMapToMaps(params interface{}, pointer interface{}, deep bool, mapping ... paramsKind = paramsRv.Kind() } if paramsKind != reflect.Map { - return errors.New("params should be type of map") + return gerror.New("params should be type of map") } // Empty params map, no need continue. if paramsRv.Len() == 0 { @@ -449,7 +448,7 @@ func doMapToMaps(params interface{}, pointer interface{}, deep bool, mapping ... pointerKind = pointerRv.Kind() } if pointerKind != reflect.Map { - return errors.New("pointer should be type of *map/**map") + return gerror.New("pointer should be type of *map/**map") } defer func() { // Catch the panic, especially the reflect operation panics. diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 43194c67e..fc90da51c 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -7,7 +7,7 @@ package gconv import ( - "fmt" + "github.com/gogf/gf/errors/gerror" "reflect" ) @@ -19,7 +19,7 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) t := reflect.TypeOf(pointer) k := t.Kind() if k != reflect.Ptr { - return fmt.Errorf("params should be type of pointer, but got: %v", k) + return gerror.Newf("params should be type of pointer, but got: %v", k) } switch t.Elem().Kind() { case reflect.Array, reflect.Slice: @@ -37,7 +37,7 @@ func ScanDeep(params interface{}, pointer interface{}, mapping ...map[string]str t := reflect.TypeOf(pointer) k := t.Kind() if k != reflect.Ptr { - return fmt.Errorf("params should be type of pointer, but got: %v", k) + return gerror.Newf("params should be type of pointer, but got: %v", k) } switch t.Elem().Kind() { case reflect.Array, reflect.Slice: diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 81eeb3aeb..5e39805a5 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -7,7 +7,6 @@ package gconv import ( - "errors" "fmt" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/empty" @@ -50,10 +49,10 @@ func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]s // doStruct is the core internal converting function for any data to struct recursively or not. func doStruct(params interface{}, pointer interface{}, recursive bool, mapping ...map[string]string) (err error) { if params == nil { - return errors.New("params cannot be nil") + return gerror.New("params cannot be nil") } if pointer == nil { - return errors.New("object pointer cannot be nil") + return gerror.New("object pointer cannot be nil") } defer func() { // Catch the panic, especially the reflect operation panics. @@ -65,7 +64,7 @@ func doStruct(params interface{}, pointer interface{}, recursive bool, mapping . // paramsMap is the map[string]interface{} type variable for params. paramsMap := MapDeep(params) if paramsMap == nil { - return fmt.Errorf("invalid params: %v", params) + return gerror.Newf("invalid params: %v", params) } // UnmarshalValue. @@ -81,15 +80,23 @@ func doStruct(params interface{}, pointer interface{}, recursive bool, mapping . if !ok { rv := reflect.ValueOf(pointer) if kind := rv.Kind(); kind != reflect.Ptr { - return fmt.Errorf("object pointer should be type of '*struct', but got '%v'", kind) + return gerror.Newf("object pointer should be type of '*struct', but got '%v'", kind) } // Using IsNil on reflect.Ptr variable is OK. if !rv.IsValid() || rv.IsNil() { - return errors.New("object pointer cannot be nil") + return gerror.New("object pointer cannot be nil") } elem = rv.Elem() } + // Check if an invalid interface. + if elem.Kind() == reflect.Interface { + elem = elem.Elem() + if !elem.IsValid() { + return gerror.New("interface type converting is not supported") + } + } + // It automatically creates struct object if necessary. // For example, if is **User, then is *User, which is a pointer to User. if elem.Kind() == reflect.Ptr { @@ -360,7 +367,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, re default: defer func() { if e := recover(); e != nil { - err = errors.New( + err = gerror.New( fmt.Sprintf(`cannot convert value "%+v" to type "%s"`, value, structFieldValue.Type().String(), diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index f127942ee..3395680f3 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -7,8 +7,6 @@ package gconv import ( - "errors" - "fmt" "github.com/gogf/gf/errors/gerror" "reflect" ) @@ -32,10 +30,10 @@ func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string] // it will create the struct/pointer internally. func doStructs(params interface{}, pointer interface{}, deep bool, mapping ...map[string]string) (err error) { if params == nil { - return errors.New("params cannot be nil") + return gerror.New("params cannot be nil") } if pointer == nil { - return errors.New("object pointer cannot be nil") + return gerror.New("object pointer cannot be nil") } defer func() { // Catch the panic, especially the reflect operation panics. @@ -47,7 +45,7 @@ func doStructs(params interface{}, pointer interface{}, deep bool, mapping ...ma if !ok { pointerRv = reflect.ValueOf(pointer) if kind := pointerRv.Kind(); kind != reflect.Ptr { - return fmt.Errorf("pointer should be type of pointer, but got: %v", kind) + return gerror.Newf("pointer should be type of pointer, but got: %v", kind) } } params = Maps(params) @@ -101,6 +99,6 @@ func doStructs(params interface{}, pointer interface{}, deep bool, mapping ...ma pointerRv.Elem().Set(array) return nil default: - return fmt.Errorf("params should be type of slice, but got: %v", reflectKind) + return gerror.Newf("params should be type of slice, but got: %v", reflectKind) } } diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 48c729faa..c5d882ee2 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -910,3 +910,53 @@ func Test_Struct_UnmarshalValue(t *testing.T) { t.AssertNE(err, nil) }) } + +type T struct { + Name string +} + +func (t *T) Test() string { + return t.Name +} + +type TestInterface interface { + Test() string +} + +type TestStruct struct { + TestInterface +} + +func Test_Struct_WithInterfaceAttr(t *testing.T) { + // Implemented interface attribute. + gtest.C(t, func(t *gtest.T) { + v1 := TestStruct{ + TestInterface: &T{"john"}, + } + v2 := g.Map{} + err := gconv.StructDeep(v2, &v1) + t.Assert(err, nil) + t.Assert(v1.Test(), "john") + }) + // Implemented interface attribute. + gtest.C(t, func(t *gtest.T) { + v1 := TestStruct{ + TestInterface: &T{"john"}, + } + v2 := g.Map{ + "name": "test", + } + err := gconv.StructDeep(v2, &v1) + t.Assert(err, nil) + t.Assert(v1.Test(), "test") + }) + // No implemented interface attribute. + gtest.C(t, func(t *gtest.T) { + v1 := TestStruct{} + v2 := g.Map{ + "name": "test", + } + err := gconv.StructDeep(v2, &v1) + t.AssertNE(err, nil) + }) +}