From f03f56ba4e544c9ea3e46e25a4dbfdeba3637023 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 19 Aug 2021 11:28:25 +0800 Subject: [PATCH] improve gconv.Struct for map attribute converting --- database/gdb/gdb_func.go | 1 + net/ghttp/ghttp_server_router_group.go | 4 +-- os/gcron/gcron_entry.go | 7 ++-- util/gconv/gconv_maptomap.go | 14 +++++--- util/gconv/gconv_scan.go | 45 ++++++++++++++++-------- util/gconv/gconv_struct.go | 9 +++-- util/gconv/gconv_structs.go | 7 ++-- util/gconv/gconv_z_unit_maptomap_test.go | 2 +- util/gconv/gconv_z_unit_struct_test.go | 25 +++++++++++++ 9 files changed, 77 insertions(+), 37 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 0a8a28cab..da566d043 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -548,6 +548,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa break } // Automatically mapping and filtering the struct attribute. + // TODO struct fields in sequence data := DataToMapDeep(in.Where) if in.Table != "" { data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true) diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 0ebebfa15..cf952a81c 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -273,7 +273,7 @@ func (g *RouterGroup) getPrefix() string { return prefix } -// doBindRoutersToServer does really registering for the group. +// doBindRoutersToServer does really register for the group. func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { var ( bindType = item.bindType @@ -289,7 +289,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { if err != nil { g.server.Logger().Fatalf("invalid pattern: %s", pattern) } - // If there'a already a domain, unset the domain field in the pattern. + // If there is already a domain, unset the domain field in the pattern. if g.domain != nil { domain = "" } diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 0c4952faf..0f6ba3549 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -54,9 +54,8 @@ func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...stri } // When you add a scheduled task, you cannot allow it to run. // It cannot start running when added to gtimer. - // It should start running after the entry is added to the entries map, - // to avoid the task from running during adding where the entries - // does not have the entry information, which might cause panic. + // It should start running after the entry is added to the Cron entries map, to avoid the task + // from running during adding where the entries do not have the entry information, which might cause panic. entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped) c.entries.Set(entry.Name, entry) entry.entry.Start() @@ -70,7 +69,7 @@ func (entry *Entry) IsSingleton() bool { // SetSingleton sets the entry running in singleton mode. func (entry *Entry) SetSingleton(enabled bool) { - entry.entry.SetSingleton(true) + entry.entry.SetSingleton(enabled) } // SetTimes sets the times which the entry can run. diff --git a/util/gconv/gconv_maptomap.go b/util/gconv/gconv_maptomap.go index e61aad53e..22524ba9e 100644 --- a/util/gconv/gconv_maptomap.go +++ b/util/gconv/gconv_maptomap.go @@ -22,10 +22,10 @@ func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]str // doMapToMap converts any map type variable `params` to another map type variable `pointer`. // // The parameter `params` can be any type of map, like: -// map[string]string, map[string]struct, , map[string]*struct, etc. +// map[string]string, map[string]struct, map[string]*struct, etc. // // The parameter `pointer` should be type of *map, like: -// map[int]string, map[string]struct, , map[string]*struct, etc. +// map[int]string, map[string]struct, map[string]*struct, etc. // // The optional parameter `mapping` is used for struct attribute to map key mapping, which makes // sense only if the items of original map `params` is type struct. @@ -54,9 +54,13 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s } } var ( - paramsRv reflect.Value - paramsKind reflect.Kind + paramsRv reflect.Value + paramsKind reflect.Kind + keyToAttributeNameMapping map[string]string ) + if len(mapping) > 0 { + keyToAttributeNameMapping = mapping[0] + } if v, ok := params.(reflect.Value); ok { paramsRv = v } else { @@ -113,7 +117,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s e := reflect.New(pointerValueType).Elem() switch pointerValueKind { case reflect.Map, reflect.Struct: - if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil { + if err = doStruct(paramsRv.MapIndex(key).Interface(), e, keyToAttributeNameMapping, ""); err != nil { return err } default: diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 7ad6f240a..bb5af1f47 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -11,28 +11,41 @@ import ( "reflect" ) -// Scan automatically calls MapToMap, MapToMaps, Struct or Structs function according to -// the type of parameter `pointer` to implement the converting. +// Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer` +// with type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting. // -// It calls function MapToMap if `pointer` is type of *map to do the converting. -// It calls function MapToMaps if `pointer` is type of *[]map/*[]*map to do the converting. -// It calls function Struct if `pointer` is type of *struct/**struct to do the converting. -// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the converting. +// It calls function `doMapToMap` internally if `pointer` is type of *map for converting. +// It calls function `doMapToMaps` internally if `pointer` is type of *[]map/*[]*map for converting. +// It calls function `doStruct` internally if `pointer` is type of *struct/**struct for converting. +// It calls function `doStructs` internally if `pointer` is type of *[]struct/*[]*struct for converting. func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { var ( - pointerType = reflect.TypeOf(pointer) - pointerKind = pointerType.Kind() + pointerType reflect.Type + pointerKind reflect.Kind ) + if v, ok := pointer.(reflect.Value); ok { + pointerType = v.Type() + } else { + pointerType = reflect.TypeOf(pointer) + } + if pointerType == nil { + return gerror.NewCode(gerror.CodeInvalidParameter, "parameter pointer should not be nil") + } + pointerKind = pointerType.Kind() if pointerKind != reflect.Ptr { - return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got: %v", pointerKind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got type: %v", pointerKind) } var ( - pointerElem = pointerType.Elem() - pointerElemKind = pointerElem.Kind() + pointerElem = pointerType.Elem() + pointerElemKind = pointerElem.Kind() + keyToAttributeNameMapping map[string]string ) + if len(mapping) > 0 { + keyToAttributeNameMapping = mapping[0] + } switch pointerElemKind { case reflect.Map: - return MapToMap(params, pointer, mapping...) + return doMapToMap(params, pointer, mapping...) case reflect.Array, reflect.Slice: var ( @@ -44,11 +57,13 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) sliceElemKind = sliceElem.Kind() } if sliceElemKind == reflect.Map { - return MapToMaps(params, pointer, mapping...) + return doMapToMaps(params, pointer, mapping...) } - return Structs(params, pointer, mapping...) + return doStructs(params, pointer, keyToAttributeNameMapping, "") + default: - return Struct(params, pointer, mapping...) + + return doStruct(params, pointer, keyToAttributeNameMapping, "") } } diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 07b115daf..381a75d28 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -31,11 +31,7 @@ import ( // in mapping procedure to do the matching. // It ignores the map key, if it does not match. func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - var keyToAttributeNameMapping map[string]string - if len(mapping) > 0 { - keyToAttributeNameMapping = mapping[0] - } - return doStruct(params, pointer, keyToAttributeNameMapping, "") + return Scan(params, pointer, mapping...) } // StructTag acts as Struct but also with support for priority tag feature, which retrieves the @@ -405,6 +401,9 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma // Converting by kind. switch kind { + case reflect.Map: + return doMapToMap(value, structFieldValue, mapping) + case reflect.Struct: // Recursively converting for struct attribute. if err := doStruct(value, structFieldValue, nil, ""); err != nil { diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 68b4be9ca..cf2ac64a0 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -13,12 +13,9 @@ import ( ) // Structs converts any slice to given struct slice. +// Also see Scan, Struct. func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - var keyToAttributeNameMapping map[string]string - if len(mapping) > 0 { - keyToAttributeNameMapping = mapping[0] - } - return doStructs(params, pointer, keyToAttributeNameMapping, "") + return Scan(params, pointer, mapping...) } // StructsTag acts as Structs but also with support for priority tag feature, which retrieves the diff --git a/util/gconv/gconv_z_unit_maptomap_test.go b/util/gconv/gconv_z_unit_maptomap_test.go index cd38f80a3..838d96cd2 100644 --- a/util/gconv/gconv_z_unit_maptomap_test.go +++ b/util/gconv/gconv_z_unit_maptomap_test.go @@ -83,7 +83,7 @@ func Test_MapToMap2(t *testing.T) { gtest.C(t, func(t *gtest.T) { m := make(map[string]User) err := gconv.MapToMap(params, &m) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(m), 1) t.Assert(m["key"].Id, 1) t.Assert(m["key"].Name, "john") diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 94c920a43..d1d12d46d 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -1203,3 +1203,28 @@ func Test_Struct_GVarAttribute(t *testing.T) { }) } + +func Test_Struct_MapAttribute(t *testing.T) { + type NodeStatus struct { + ID int + } + type Nodes map[string]NodeStatus + type Output struct { + Nodes Nodes + } + + gtest.C(t, func(t *gtest.T) { + var ( + out = Output{} + data = g.Map{ + "nodes": g.Map{ + "name": g.Map{ + "id": 10000, + }, + }, + } + ) + err := gconv.Struct(data, &out) + t.AssertNil(err) + }) +}