From ea5d52cb2f0b8484ed2c3a84f1621ccf374c8477 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 22 Nov 2023 21:05:39 +0800 Subject: [PATCH] fix issue #3147 (#3161) --- cmd/gf/internal/cmd/cmd.go | 2 + os/gcmd/gcmd.go | 2 + os/gcmd/gcmd_command.go | 11 +- os/gcmd/gcmd_command_object.go | 18 ++- os/gcmd/gcmd_command_run.go | 8 +- os/gcmd/gcmd_z_unit_feature_object1_test.go | 25 ++- util/gconv/gconv_maptomaps.go | 6 +- util/gconv/gconv_scan.go | 10 +- util/gconv/gconv_struct.go | 165 ++++++++++++-------- util/gconv/gconv_structs.go | 12 +- util/gconv/gconv_z_unit_scan_test.go | 27 ++++ 11 files changed, 188 insertions(+), 98 deletions(-) diff --git a/cmd/gf/internal/cmd/cmd.go b/cmd/gf/internal/cmd/cmd.go index 7d20aedda3d..bd5e8922e07 100644 --- a/cmd/gf/internal/cmd/cmd.go +++ b/cmd/gf/internal/cmd/cmd.go @@ -55,6 +55,7 @@ func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error) _, err = Version.Index(ctx, cVersionInput{}) return } + answer := "n" // No argument or option, do installation checks. if data, isInstalled := service.Install.IsInstalled(); !isInstalled { @@ -71,6 +72,7 @@ func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error) gcmd.Scan("press `Enter` to exit...") return } + // Print help content. gcmd.CommandFromCtx(ctx).Print() return diff --git a/os/gcmd/gcmd.go b/os/gcmd/gcmd.go index 139b60c828e..41b7eff03a2 100644 --- a/os/gcmd/gcmd.go +++ b/os/gcmd/gcmd.go @@ -28,6 +28,8 @@ const ( helpOptionNameShort = "h" maxLineChars = 120 tracingInstrumentName = "github.com/gogf/gf/v2/os/gcmd.Command" + tagNameName = "name" + tagNameShort = "short" ) // Init does custom initialization. diff --git a/os/gcmd/gcmd_command.go b/os/gcmd/gcmd_command.go index d3cfc2b78a9..557a7531c82 100644 --- a/os/gcmd/gcmd_command.go +++ b/os/gcmd/gcmd_command.go @@ -42,12 +42,11 @@ type FuncWithValue func(ctx context.Context, parser *Parser) (out interface{}, e // Argument is the command value that are used by certain command. type Argument struct { - Name string // Option name. - FieldName string // Option field name. - Short string // Option short. - Brief string // Brief info about this Option, which is used in help info. - IsArg bool // IsArg marks this argument taking value from command line argument instead of option. - Orphan bool // Whether this Option having or having no value bound to it. + Name string // Option name. + Short string // Option short. + Brief string // Brief info about this Option, which is used in help info. + IsArg bool // IsArg marks this argument taking value from command line argument instead of option. + Orphan bool // Whether this Option having or having no value bound to it. } var ( diff --git a/os/gcmd/gcmd_command_object.go b/os/gcmd/gcmd_command_object.go index ab9d3d7cb0c..68c8610ad11 100644 --- a/os/gcmd/gcmd_command_object.go +++ b/os/gcmd/gcmd_command_object.go @@ -353,9 +353,6 @@ func newArgumentsFromInput(object interface{}) (args []Argument, err error) { } if arg.Name == "" { arg.Name = field.Name() - } else if arg.Name != field.Name() { - arg.FieldName = field.Name() - nameSet.Add(arg.FieldName) } if arg.Name == helpOptionName { return nil, gerror.Newf( @@ -411,14 +408,25 @@ func mergeDefaultStructValue(data map[string]interface{}, pointer interface{}) e foundValue interface{} ) for _, field := range tagFields { + var ( + nameValue = field.Tag(tagNameName) + shortValue = field.Tag(tagNameShort) + ) + // If it already has value, it then ignores the default value. + if value, ok := data[nameValue]; ok { + data[field.Name()] = value + continue + } + if value, ok := data[shortValue]; ok { + data[field.Name()] = value + continue + } foundKey, foundValue = gutil.MapPossibleItemByKey(data, field.Name()) if foundKey == "" { data[field.Name()] = field.TagValue } else { if utils.IsEmpty(foundValue) { data[foundKey] = field.TagValue - } else { - data[field.Name()] = foundValue } } } diff --git a/os/gcmd/gcmd_command_run.go b/os/gcmd/gcmd_command_run.go index 7aa61f95df4..c3f6fd53cb6 100644 --- a/os/gcmd/gcmd_command_run.go +++ b/os/gcmd/gcmd_command_run.go @@ -171,12 +171,10 @@ func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error) if arg.IsArg { continue } - optionKey = arg.Name - if arg.FieldName != "" { - optionKey += fmt.Sprintf(`,%s`, arg.FieldName) - } if arg.Short != "" { - optionKey += fmt.Sprintf(`,%s`, arg.Short) + optionKey = fmt.Sprintf(`%s,%s`, arg.Name, arg.Short) + } else { + optionKey = arg.Name } supportedOptions[optionKey] = !arg.Orphan } diff --git a/os/gcmd/gcmd_z_unit_feature_object1_test.go b/os/gcmd/gcmd_z_unit_feature_object1_test.go index a2845b327d5..c804e7c68cc 100644 --- a/os/gcmd/gcmd_z_unit_feature_object1_test.go +++ b/os/gcmd/gcmd_z_unit_feature_object1_test.go @@ -30,12 +30,14 @@ type TestCmdObjectEnvInput struct { type TestCmdObjectEnvOutput struct{} type TestCmdObjectTestInput struct { - g.Meta `name:"test" usage:"root test" brief:"root test command" dc:"root test command description" ad:"root test command ad"` - Name string `name:"yourname" v:"required" short:"n" orphan:"false" brief:"name for test command" d:"tom"` + g.Meta `name:"test" usage:"root test" brief:"root test command" dc:"root test command description" ad:"root test command ad"` + Name string `name:"yourname" v:"required" short:"n" orphan:"false" brief:"name for test command" d:"tom"` + Version bool `name:"version" short:"v" orphan:"true" brief:"show version"` } type TestCmdObjectTestOutput struct { - Content string + Name string + Version bool } func (TestCmdObject) Env(ctx context.Context, in TestCmdObjectEnvInput) (out *TestCmdObjectEnvOutput, err error) { @@ -44,7 +46,8 @@ func (TestCmdObject) Env(ctx context.Context, in TestCmdObjectEnvInput) (out *Te func (TestCmdObject) Test(ctx context.Context, in TestCmdObjectTestInput) (out *TestCmdObjectTestOutput, err error) { out = &TestCmdObjectTestOutput{ - Content: in.Name, + Name: in.Name, + Version: in.Version, } return } @@ -93,19 +96,25 @@ func Test_Command_NewFromObject_RunWithValue(t *testing.T) { os.Args = []string{"root", "test", "-n=john"} value, err := cmd.RunWithValueError(ctx) t.AssertNil(err) - t.Assert(value, `{"Content":"john"}`) + t.Assert(value, `{"Name":"john","Version":false}`) // test name tag name os.Args = []string{"root", "test", "-yourname=hailaz"} value1, err1 := cmd.RunWithValueError(ctx) t.AssertNil(err1) - t.Assert(value1, `{"Content":"hailaz"}`) + t.Assert(value1, `{"Name":"hailaz","Version":false}`) // test default tag value os.Args = []string{"root", "test"} value2, err2 := cmd.RunWithValueError(ctx) t.AssertNil(err2) - t.Assert(value2, `{"Content":"tom"}`) + t.Assert(value2, `{"Name":"tom","Version":false}`) + + // test name tag and orphan tag true + os.Args = []string{"root", "test", "-v"} + value3, err3 := cmd.RunWithValueError(ctx) + t.AssertNil(err3) + t.Assert(value3, `{"Name":"tom","Version":true}`) }) } @@ -123,7 +132,7 @@ func Test_Command_AddObject(t *testing.T) { os.Args = []string{"start", "root", "test", "-n=john"} value, err := command.RunWithValueError(ctx) t.AssertNil(err) - t.Assert(value, `{"Content":"john"}`) + t.Assert(value, `{"Name":"john","Version":false}`) }) } diff --git a/util/gconv/gconv_maptomaps.go b/util/gconv/gconv_maptomaps.go index 4a474ef8c61..63d19759d57 100644 --- a/util/gconv/gconv_maptomaps.go +++ b/util/gconv/gconv_maptomaps.go @@ -28,7 +28,7 @@ func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]st // // The optional parameter `mapping` is used for struct attribute to map key mapping, which makes // sense only if the item of `params` is type struct. -func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { +func doMapToMaps(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { // If given `params` is JSON, it then uses json.Unmarshal doing the converting. switch r := params.(type) { case []byte: @@ -124,13 +124,13 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] var item reflect.Value if pointerElemType.Kind() == reflect.Ptr { item = reflect.New(pointerElemType.Elem()) - if err = MapToMap(paramsRv.Index(i).Interface(), item, mapping...); err != nil { + if err = MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap...); err != nil { return err } pointerSlice.Index(i).Set(item) } else { item = reflect.New(pointerElemType) - if err = MapToMap(paramsRv.Index(i).Interface(), item, mapping...); err != nil { + if err = MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap...); err != nil { return err } pointerSlice.Index(i).Set(item.Elem()) diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 6187d079de2..4f286ad8963 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -23,7 +23,7 @@ import ( // 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) { +func Scan(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { var ( pointerType reflect.Type pointerKind reflect.Kind @@ -82,12 +82,12 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) pointerElemKind = pointerElem.Kind() keyToAttributeNameMapping map[string]string ) - if len(mapping) > 0 { - keyToAttributeNameMapping = mapping[0] + if len(paramKeyToAttrMap) > 0 { + keyToAttributeNameMapping = paramKeyToAttrMap[0] } switch pointerElemKind { case reflect.Map: - return doMapToMap(params, pointer, mapping...) + return doMapToMap(params, pointer, paramKeyToAttrMap...) case reflect.Array, reflect.Slice: var ( @@ -99,7 +99,7 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) sliceElemKind = sliceElem.Kind() } if sliceElemKind == reflect.Map { - return doMapToMaps(params, pointer, mapping...) + return doMapToMaps(params, pointer, paramKeyToAttrMap...) } return doStructs(params, pointer, keyToAttributeNameMapping, "") diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index b3554ba5506..44b26851594 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -31,8 +31,8 @@ import ( // It will automatically convert the first letter of the key to uppercase // 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) { - return Scan(params, pointer, mapping...) +func Struct(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { + return Scan(params, pointer, paramKeyToAttrMap...) } // StructTag acts as Struct but also with support for priority tag feature, which retrieves the @@ -85,7 +85,7 @@ func doStructWithJsonCheck(params interface{}, pointer interface{}) (err error, } // doStruct is the core internal converting function for any data to struct. -func doStruct(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) { +func doStruct(params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string) (err error) { if params == nil { // If `params` is nil, no conversion. return nil @@ -252,7 +252,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string continue } } - if err = doStruct(paramsMap, elemFieldValue, mapping, priorityTag); err != nil { + if err = doStruct(paramsMap, elemFieldValue, paramKeyToAttrMap, priorityTag); err != nil { return err } } else { @@ -264,7 +264,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string return nil } - // The key of the tagMap is the attribute name of the struct, + // The key of the `attrToTagCheckNameMap` is the attribute name of the struct, // and the value is its replaced tag name for later comparison to improve performance. var ( attrToTagCheckNameMap = make(map[string]string) @@ -292,11 +292,27 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string paramsMap[attributeName] = paramsMap[tagName] } } + + // To convert value base on custom parameter key to attribute name map. + err = doStructBaseOnParamKeyToAttrMap( + pointerElemReflectValue, + paramsMap, + paramKeyToAttrMap, + doneMap, + ) + if err != nil { + return err + } + // Already done all attributes value assignment nothing to do next. + if len(doneMap) == len(attrToCheckNameMap) { + return nil + } + // To convert value base on precise attribute name. err = doStructBaseOnAttribute( pointerElemReflectValue, paramsMap, - mapping, + paramKeyToAttrMap, doneMap, attrToCheckNameMap, ) @@ -307,11 +323,12 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string if len(doneMap) == len(attrToCheckNameMap) { return nil } + // To convert value base on parameter map. err = doStructBaseOnParamMap( pointerElemReflectValue, paramsMap, - mapping, + paramKeyToAttrMap, doneMap, attrToCheckNameMap, attrToTagCheckNameMap, @@ -323,17 +340,47 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string return nil } +func doStructBaseOnParamKeyToAttrMap( + pointerElemReflectValue reflect.Value, + paramsMap map[string]interface{}, + paramKeyToAttrMap map[string]string, + doneAttrMap map[string]struct{}, +) error { + if len(paramKeyToAttrMap) == 0 { + return nil + } + for paramKey, attrName := range paramKeyToAttrMap { + paramValue, ok := paramsMap[paramKey] + if !ok { + continue + } + // If the attribute name is already checked converting, then skip it. + if _, ok = doneAttrMap[attrName]; ok { + continue + } + // Mark it done. + doneAttrMap[attrName] = struct{}{} + if err := bindVarToStructAttr( + pointerElemReflectValue, attrName, paramValue, paramKeyToAttrMap, + ); err != nil { + return err + } + } + return nil +} + func doStructBaseOnAttribute( pointerElemReflectValue reflect.Value, paramsMap map[string]interface{}, - mapping map[string]string, - doneMap map[string]struct{}, + paramKeyToAttrMap map[string]string, + doneAttrMap map[string]struct{}, attrToCheckNameMap map[string]string, ) error { var customMappingAttrMap = make(map[string]struct{}) - if len(mapping) > 0 { + if len(paramKeyToAttrMap) > 0 { + // It ignores the attribute names if it is specified in the `paramKeyToAttrMap`. for paramName := range paramsMap { - if passedAttrKey, ok := mapping[paramName]; ok { + if passedAttrKey, ok := paramKeyToAttrMap[paramName]; ok { customMappingAttrMap[passedAttrKey] = struct{}{} } } @@ -344,17 +391,19 @@ func doStructBaseOnAttribute( if !ok { continue } - // If the attribute name is in custom mapping, it then ignores this converting. + // If the attribute name is in custom paramKeyToAttrMap, it then ignores this converting. if _, ok = customMappingAttrMap[attrName]; ok { continue } // If the attribute name is already checked converting, then skip it. - if _, ok = doneMap[attrName]; ok { + if _, ok = doneAttrMap[attrName]; ok { continue } // Mark it done. - doneMap[attrName] = struct{}{} - if err := bindVarToStructAttr(pointerElemReflectValue, attrName, paramValue, mapping); err != nil { + doneAttrMap[attrName] = struct{}{} + if err := bindVarToStructAttr( + pointerElemReflectValue, attrName, paramValue, paramKeyToAttrMap, + ); err != nil { return err } } @@ -364,8 +413,8 @@ func doStructBaseOnAttribute( func doStructBaseOnParamMap( pointerElemReflectValue reflect.Value, paramsMap map[string]interface{}, - mapping map[string]string, - doneMap map[string]struct{}, + paramKeyToAttrMap map[string]string, + doneAttrMap map[string]struct{}, attrToCheckNameMap map[string]string, attrToTagCheckNameMap map[string]string, tagToAttrNameMap map[string]string, @@ -375,45 +424,35 @@ func doStructBaseOnParamMap( checkName string ) for paramName, paramValue := range paramsMap { - attrName = "" - // It firstly checks the passed mapping rules. - if len(mapping) > 0 { - if passedAttrKey, ok := mapping[paramName]; ok { - attrName = passedAttrKey - } - } - // It secondly checks the predefined tags and matching rules. + // It firstly considers `paramName` as accurate tag name, + // and retrieve attribute name from `tagToAttrNameMap` . + attrName = tagToAttrNameMap[paramName] if attrName == "" { - // It firstly considers `paramName` as accurate tag name, - // and retrieve attribute name from `tagToAttrNameMap` . - attrName = tagToAttrNameMap[paramName] - if attrName == "" { - checkName = utils.RemoveSymbols(paramName) - // Loop to find the matched attribute name with or without - // string cases and chars like '-'/'_'/'.'/' '. - - // Matching the parameters to struct tag names. - // The `attrKey` is the attribute name of the struct. - for attrKey, cmpKey := range attrToTagCheckNameMap { - if strings.EqualFold(checkName, cmpKey) { - attrName = attrKey - break - } + checkName = utils.RemoveSymbols(paramName) + // Loop to find the matched attribute name with or without + // string cases and chars like '-'/'_'/'.'/' '. + + // Matching the parameters to struct tag names. + // The `attrKey` is the attribute name of the struct. + for attrKey, cmpKey := range attrToTagCheckNameMap { + if strings.EqualFold(checkName, cmpKey) { + attrName = attrKey + break } } + } - // Matching the parameters to struct attributes. - if attrName == "" { - for attrKey, cmpKey := range attrToCheckNameMap { - // Eg: - // UserName eq user_name - // User-Name eq username - // username eq userName - // etc. - if strings.EqualFold(checkName, cmpKey) { - attrName = attrKey - break - } + // Matching the parameters to struct attributes. + if attrName == "" { + for attrKey, cmpKey := range attrToCheckNameMap { + // Eg: + // UserName eq user_name + // User-Name eq username + // username eq userName + // etc. + if strings.EqualFold(checkName, cmpKey) { + attrName = attrKey + break } } } @@ -423,12 +462,14 @@ func doStructBaseOnParamMap( continue } // If the attribute name is already checked converting, then skip it. - if _, ok := doneMap[attrName]; ok { + if _, ok := doneAttrMap[attrName]; ok { continue } // Mark it done. - doneMap[attrName] = struct{}{} - if err := bindVarToStructAttr(pointerElemReflectValue, attrName, paramValue, mapping); err != nil { + doneAttrMap[attrName] = struct{}{} + if err := bindVarToStructAttr( + pointerElemReflectValue, attrName, paramValue, paramKeyToAttrMap, + ); err != nil { return err } } @@ -438,7 +479,7 @@ func doStructBaseOnParamMap( // bindVarToStructAttr sets value to struct object attribute by name. func bindVarToStructAttr( structReflectValue reflect.Value, - attrName string, value interface{}, mapping map[string]string, + attrName string, value interface{}, paramKeyToAttrMap map[string]string, ) (err error) { structFieldValue := structReflectValue.FieldByName(attrName) if !structFieldValue.IsValid() { @@ -450,7 +491,7 @@ func bindVarToStructAttr( } defer func() { if exception := recover(); exception != nil { - if err = bindVarToReflectValue(structFieldValue, value, mapping); err != nil { + if err = bindVarToReflectValue(structFieldValue, value, paramKeyToAttrMap); err != nil { err = gerror.Wrapf(err, `error binding value to attribute "%s"`, attrName) } } @@ -575,7 +616,9 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i } // bindVarToReflectValue sets `value` to reflect value object `structFieldValue`. -func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string) (err error) { +func bindVarToReflectValue( + structFieldValue reflect.Value, value interface{}, paramKeyToAttrMap map[string]string, +) (err error) { // JSON content converting. err, ok := doStructWithJsonCheck(value, structFieldValue) if err != nil { @@ -600,7 +643,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma // Converting using reflection by kind. switch kind { case reflect.Map: - return doMapToMap(value, structFieldValue, mapping) + return doMapToMap(value, structFieldValue, paramKeyToAttrMap) case reflect.Struct: // Recursively converting for struct attribute. @@ -716,12 +759,12 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma return err } elem := item.Elem() - if err = bindVarToReflectValue(elem, value, mapping); err == nil { + if err = bindVarToReflectValue(elem, value, paramKeyToAttrMap); err == nil { structFieldValue.Set(elem.Addr()) } } else { // Not empty pointer, it assigns values to it. - return bindVarToReflectValue(structFieldValue.Elem(), value, mapping) + return bindVarToReflectValue(structFieldValue.Elem(), value, paramKeyToAttrMap) } // It mainly and specially handles the interface of nil value. diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index b8c04ff663c..42ec26c3fd9 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -16,8 +16,8 @@ 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) { - return Scan(params, pointer, mapping...) +func Structs(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { + return Scan(params, pointer, paramKeyToAttrMap...) } // StructsTag acts as Structs but also with support for priority tag feature, which retrieves the @@ -34,7 +34,9 @@ func StructsTag(params interface{}, pointer interface{}, priorityTag string) (er // The parameter `pointer` should be type of pointer to slice of struct. // Note that if `pointer` is a pointer to another pointer of type of slice of struct, // it will create the struct/pointer internally. -func doStructs(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) { +func doStructs( + params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string, +) (err error) { if params == nil { // If `params` is nil, no conversion. return nil @@ -133,7 +135,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin if !tempReflectValue.IsValid() { tempReflectValue = reflect.New(itemType.Elem()).Elem() } - if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil { + if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue.Addr()) @@ -147,7 +149,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin } else { tempReflectValue = reflect.New(itemType).Elem() } - if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil { + if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue) diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go index f94bf3e1aa5..560d39461d6 100644 --- a/util/gconv/gconv_z_unit_scan_test.go +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -17,6 +17,33 @@ import ( "github.com/gogf/gf/v2/util/gconv" ) +func Test_Scan_WithMapParameter(t *testing.T) { + type User struct { + Uid int + Name string + } + gtest.C(t, func(t *gtest.T) { + for i := 0; i < 100; i++ { + var ( + user = new(User) + params = g.Map{ + "uid": 1, + "myname": "john", + "name": "smith", + } + ) + err := gconv.Scan(params, user, g.MapStrStr{ + "myname": "Name", + }) + t.AssertNil(err) + t.Assert(user, &User{ + Uid: 1, + Name: "john", + }) + } + }) +} + func Test_Scan_StructStructs(t *testing.T) { type User struct { Uid int