From bc1e1019c57334bf91254cb3d43b0cdb23100802 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 14 Mar 2025 18:23:07 +0800 Subject: [PATCH] refract(util/gconv): change `Converter` interface definition for more convenient usage (#4202) --- .github/workflows/issue-check-inactive.yml | 2 +- .gitignore | 1 + database/gdb/gdb_type_result.go | 9 +- examples | 2 +- ...ghttp_z_unit_feature_request_param_test.go | 6 + util/gconv/gconv.go | 52 +++++---- util/gconv/gconv_maps.go | 14 ++- util/gconv/gconv_structs.go | 15 ++- util/gconv/gconv_z_unit_converter_test.go | 69 ++++++++++++ util/gconv/gconv_z_unit_map_test.go | 2 +- .../internal/converter/converter_convert.go | 106 +++++++++++++----- .../gconv/internal/converter/converter_map.go | 24 ++-- .../internal/converter/converter_maptomap.go | 15 +-- .../internal/converter/converter_maptomaps.go | 6 +- .../internal/converter/converter_scan.go | 47 +++++--- .../internal/converter/converter_slice_any.go | 9 +- .../converter/converter_slice_float.go | 92 +++++++-------- .../internal/converter/converter_slice_int.go | 85 +++++++------- .../internal/converter/converter_slice_map.go | 22 +++- .../internal/converter/converter_slice_str.go | 45 ++++---- .../converter/converter_slice_uint.go | 85 +++++++------- .../internal/converter/converter_struct.go | 28 +++-- .../internal/converter/converter_structs.go | 35 ++++-- .../gconv/internal/structcache/structcache.go | 36 ++++-- 24 files changed, 529 insertions(+), 278 deletions(-) diff --git a/.github/workflows/issue-check-inactive.yml b/.github/workflows/issue-check-inactive.yml index 4b16b4f9a..274f86d45 100644 --- a/.github/workflows/issue-check-inactive.yml +++ b/.github/workflows/issue-check-inactive.yml @@ -23,6 +23,6 @@ jobs: with: actions: 'check-inactive' inactive-label: 'inactive' - inactive-day: 7 + inactive-day: 30 issue-state: open exclude-labels: 'bug,planned,$exclude-empty' \ No newline at end of file diff --git a/.gitignore b/.gitignore index d18d5fc4a..bc6df0d61 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ go.work.sum node_modules .docusaurus output +.example/ \ No newline at end of file diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index 8c02d828c..e1bff6184 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -201,11 +201,14 @@ func (r Result) Structs(pointer interface{}) (err error) { return nil } var ( - sliceOption = gconv.SliceOption{ContinueOnError: true} - mapOption = gconv.StructOption{ + sliceOption = gconv.SliceOption{ContinueOnError: true} + structOption = gconv.StructOption{ PriorityTag: OrmTagForStruct, ContinueOnError: true, } ) - return converter.Structs(r, pointer, sliceOption, mapOption) + return converter.Structs(r, pointer, gconv.StructsOption{ + SliceOption: sliceOption, + StructOption: structOption, + }) } diff --git a/examples b/examples index bf0ab5ac1..b57e4575c 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit bf0ab5ac16fbf654fc424baf45e96e541a037cb3 +Subproject commit b57e4575ce971e6a6e581b1de0e77eccf61705c4 diff --git a/net/ghttp/ghttp_z_unit_feature_request_param_test.go b/net/ghttp/ghttp_z_unit_feature_request_param_test.go index 3b9000c74..041886225 100644 --- a/net/ghttp/ghttp_z_unit_feature_request_param_test.go +++ b/net/ghttp/ghttp_z_unit_feature_request_param_test.go @@ -1,3 +1,9 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + package ghttp_test import ( diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 38302b1e3..20e2cb844 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -35,7 +35,7 @@ type Converter interface { // ConverterForBasic is the basic converting interface. type ConverterForBasic interface { - Scan(srcValue, dstPointer any, option ScanOption) (err error) + Scan(srcValue, dstPointer any, option ...ScanOption) (err error) String(any any) (string, error) Bool(any any) (bool, error) Rune(any any) (rune, error) @@ -74,37 +74,37 @@ type ConverterForFloat interface { // ConverterForMap is the converting interface for map. type ConverterForMap interface { - Map(v any, option MapOption) (map[string]any, error) - MapStrStr(v any, option MapOption) (map[string]string, error) + Map(v any, option ...MapOption) (map[string]any, error) + MapStrStr(v any, option ...MapOption) (map[string]string, error) } // ConverterForSlice is the converting interface for slice. type ConverterForSlice interface { Bytes(v any) ([]byte, error) Runes(v any) ([]rune, error) - SliceAny(v any, option SliceOption) ([]any, error) - SliceFloat32(v any, option SliceOption) ([]float32, error) - SliceFloat64(v any, option SliceOption) ([]float64, error) - SliceInt(v any, option SliceOption) ([]int, error) - SliceInt32(v any, option SliceOption) ([]int32, error) - SliceInt64(v any, option SliceOption) ([]int64, error) - SliceUint(v any, option SliceOption) ([]uint, error) - SliceUint32(v any, option SliceOption) ([]uint32, error) - SliceUint64(v any, option SliceOption) ([]uint64, error) - SliceStr(v any, option SliceOption) ([]string, error) - SliceMap(v any, sliceOption SliceOption, mapOption MapOption) ([]map[string]any, error) + SliceAny(v any, option ...SliceOption) ([]any, error) + SliceFloat32(v any, option ...SliceOption) ([]float32, error) + SliceFloat64(v any, option ...SliceOption) ([]float64, error) + SliceInt(v any, option ...SliceOption) ([]int, error) + SliceInt32(v any, option ...SliceOption) ([]int32, error) + SliceInt64(v any, option ...SliceOption) ([]int64, error) + SliceUint(v any, option ...SliceOption) ([]uint, error) + SliceUint32(v any, option ...SliceOption) ([]uint32, error) + SliceUint64(v any, option ...SliceOption) ([]uint64, error) + SliceStr(v any, option ...SliceOption) ([]string, error) + SliceMap(v any, option ...SliceMapOption) ([]map[string]any, error) } // ConverterForStruct is the converting interface for struct. type ConverterForStruct interface { - Struct(params, pointer any, option StructOption) (err error) - Structs(params, pointer any, sliceOption SliceOption, structOption StructOption) (err error) + Struct(params, pointer any, option ...StructOption) (err error) + Structs(params, pointer any, option ...StructsOption) (err error) } // ConverterForConvert is the converting interface for custom converting. type ConverterForConvert interface { - ConvertWithRefer(fromValue, referValue any, option ConvertOption) (any, error) - ConvertWithTypeName(fromValue any, toTypeName string, option ConvertOption) (any, error) + ConvertWithRefer(fromValue, referValue any, option ...ConvertOption) (any, error) + ConvertWithTypeName(fromValue any, toTypeName string, option ...ConvertOption) (any, error) } // ConverterForRegister is the converting interface for custom converter registration. @@ -123,12 +123,18 @@ type ( // SliceOption is the option for Slice type converting. SliceOption = converter.SliceOption + // SliceMapOption is the option for SliceMap function. + SliceMapOption = converter.SliceMapOption + // ScanOption is the option for the Scan function. ScanOption = converter.ScanOption // StructOption is the option for Struct converting. StructOption = converter.StructOption + // StructsOption is the option for Structs function. + StructsOption = converter.StructsOption + // ConvertOption is the option for converting. ConvertOption = converter.ConvertOption ) @@ -142,11 +148,6 @@ var ( defaultConverter = converter.NewConverter() ) -// RegisterAnyConverterFunc registers custom type converting function for specified type. -func RegisterAnyConverterFunc(f AnyConvertFunc, types ...reflect.Type) { - defaultConverter.RegisterAnyConverterFunc(f, types...) -} - // NewConverter creates and returns management object for type converting. func NewConverter() Converter { return converter.NewConverter() @@ -162,3 +163,8 @@ func RegisterConverter(fn any) (err error) { func RegisterTypeConverterFunc(fn any) (err error) { return defaultConverter.RegisterTypeConverterFunc(fn) } + +// RegisterAnyConverterFunc registers custom type converting function for specified type. +func RegisterAnyConverterFunc(f AnyConvertFunc, types ...reflect.Type) { + defaultConverter.RegisterAnyConverterFunc(f, types...) +} diff --git a/util/gconv/gconv_maps.go b/util/gconv/gconv_maps.go index b2d574994..6b3a237a2 100644 --- a/util/gconv/gconv_maps.go +++ b/util/gconv/gconv_maps.go @@ -6,7 +6,10 @@ package gconv -import "github.com/gogf/gf/v2/internal/json" +import ( + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/util/gconv/internal/converter" +) // SliceMap is alias of Maps. func SliceMap(any any, option ...MapOption) []map[string]any { @@ -28,9 +31,12 @@ func Maps(value any, option ...MapOption) []map[string]any { if len(option) > 0 { mapOption = option[0] } - result, _ := defaultConverter.SliceMap(value, SliceOption{ - ContinueOnError: true, - }, mapOption) + result, _ := defaultConverter.SliceMap(value, SliceMapOption{ + MapOption: mapOption, + SliceOption: converter.SliceOption{ + ContinueOnError: true, + }, + }) return result } diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 3bfd7b42a..9cc1c0771 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -6,6 +6,8 @@ package gconv +import "github.com/gogf/gf/v2/util/gconv/internal/converter" + // Structs converts any slice to given struct slice. // Also see Scan, Struct. func Structs(params any, pointer any, paramKeyToAttrMap ...map[string]string) (err error) { @@ -21,10 +23,13 @@ func SliceStruct(params any, pointer any, mapping ...map[string]string) (err err // specified priorityTagAndFieldName for `params` key-value items to struct attribute names mapping. // The parameter `priorityTag` supports multiple priorityTagAndFieldName that can be joined with char ','. func StructsTag(params any, pointer any, priorityTag string) (err error) { - return defaultConverter.Structs(params, pointer, SliceOption{ - ContinueOnError: true, - }, StructOption{ - PriorityTag: priorityTag, - ContinueOnError: true, + return defaultConverter.Structs(params, pointer, StructsOption{ + SliceOption: converter.SliceOption{ + ContinueOnError: true, + }, + StructOption: converter.StructOption{ + PriorityTag: priorityTag, + ContinueOnError: true, + }, }) } diff --git a/util/gconv/gconv_z_unit_converter_test.go b/util/gconv/gconv_z_unit_converter_test.go index ccaee32b4..3565eb4de 100644 --- a/util/gconv/gconv_z_unit_converter_test.go +++ b/util/gconv/gconv_z_unit_converter_test.go @@ -171,3 +171,72 @@ func TestNewConverter(t *testing.T) { }) }) } + +type UserInput struct { + Name string + Age int + IsActive bool +} + +type UserModel struct { + ID int + FullName string + Age int + Status int +} + +func userInput2Model(in any, out reflect.Value) error { + if out.Type() == reflect.TypeOf(&UserModel{}) { + if input, ok := in.(UserInput); ok { + model := UserModel{ + ID: 1, + FullName: input.Name, + Age: input.Age, + Status: 0, + } + if input.IsActive { + model.Status = 1 + } + out.Elem().Set(reflect.ValueOf(model)) + return nil + } + return fmt.Errorf("unsupported type %T to UserModel", in) + } + return fmt.Errorf("unsupported type %s", out.Type()) +} + +func TestConverter_RegisterAnyConverterFunc(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + converter := gconv.NewConverter() + converter.RegisterAnyConverterFunc(userInput2Model, reflect.TypeOf(UserModel{})) + var ( + model UserModel + input = UserInput{Name: "sam", Age: 30, IsActive: true} + ) + err := converter.Scan(input, &model) + t.AssertNil(err) + t.Assert(model, UserModel{ + ID: 1, + FullName: "sam", + Age: 30, + Status: 1, + }) + }) + + gtest.C(t, func(t *gtest.T) { + converter := gconv.NewConverter() + converter.RegisterAnyConverterFunc(userInput2Model, reflect.TypeOf(&UserModel{})) + var ( + model UserModel + input = UserInput{Name: "sam", Age: 30, IsActive: true} + ) + err := converter.Scan(input, &model) + t.AssertNil(err) + t.Assert(model, UserModel{ + ID: 1, + FullName: "sam", + Age: 30, + Status: 1, + }) + }) +} diff --git a/util/gconv/gconv_z_unit_map_test.go b/util/gconv/gconv_z_unit_map_test.go index 4619e703a..ac30b0ae5 100644 --- a/util/gconv/gconv_z_unit_map_test.go +++ b/util/gconv/gconv_z_unit_map_test.go @@ -345,7 +345,7 @@ func TestMapToMapExtra(t *testing.T) { expect = make(map[string]interface{}) ) err = gconv.MapToMap(value, &expect) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value["k1"], expect["k1"]) }) diff --git a/util/gconv/internal/converter/converter_convert.go b/util/gconv/internal/converter/converter_convert.go index ec1f545cf..33ea78c28 100644 --- a/util/gconv/internal/converter/converter_convert.go +++ b/util/gconv/internal/converter/converter_convert.go @@ -23,20 +23,27 @@ type ConvertOption struct { StructOption StructOption } +func (c *Converter) getConvertOption(option ...ConvertOption) ConvertOption { + if len(option) > 0 { + return option[0] + } + return ConvertOption{} +} + // ConvertWithTypeName converts the variable `fromValue` to the type `toTypeName`, the type `toTypeName` is specified by string. -func (c *Converter) ConvertWithTypeName(fromValue any, toTypeName string, option ConvertOption) (any, error) { +func (c *Converter) ConvertWithTypeName(fromValue any, toTypeName string, option ...ConvertOption) (any, error) { return c.doConvert( doConvertInput{ FromValue: fromValue, ToTypeName: toTypeName, ReferValue: nil, }, - option, + c.getConvertOption(option...), ) } // ConvertWithRefer converts the variable `fromValue` to the type referred by value `referValue`. -func (c *Converter) ConvertWithRefer(fromValue, referValue any, option ConvertOption) (any, error) { +func (c *Converter) ConvertWithRefer(fromValue, referValue any, option ...ConvertOption) (any, error) { var referValueRf reflect.Value if v, ok := referValue.(reflect.Value); ok { referValueRf = v @@ -49,7 +56,7 @@ func (c *Converter) ConvertWithRefer(fromValue, referValue any, option ConvertOp ToTypeName: referValueRf.Type().String(), ReferValue: referValue, }, - option, + c.getConvertOption(option...), ) } @@ -354,7 +361,10 @@ func (c *Converter) doConvert(in doConvertInput, option ConvertOption) (converte return c.Map(in.FromValue, option.MapOption) case "[]map[string]interface {}": - return c.SliceMap(in.FromValue, option.SliceOption, option.MapOption) + return c.SliceMap(in.FromValue, SliceMapOption{ + SliceOption: option.SliceOption, + MapOption: option.MapOption, + }) case "RawMessage", "json.RawMessage": // issue 3449 @@ -463,7 +473,53 @@ func (c *Converter) doConvertWithReflectValueSet(reflectValue reflect.Value, in return err } -func (c *Converter) getRegisteredConverterFuncAndSrcType( +// callCustomConverter call the custom converter. It will try some possible type. +func (c *Converter) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) { + // search type converter function. + registeredConverterFunc, srcType, ok := c.getRegisteredTypeConverterFuncAndSrcType(srcReflectValue, dstReflectValue) + if ok { + return c.doCallCustomTypeConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType) + } + + // search any converter function. + anyConverterFunc := c.getRegisteredAnyConverterFunc(dstReflectValue) + if anyConverterFunc == nil { + return false, nil + } + err = anyConverterFunc(srcReflectValue.Interface(), dstReflectValue) + if err != nil { + return false, err + } + return true, nil +} + +func (c *Converter) callCustomConverterWithRefer( + srcReflectValue, referReflectValue reflect.Value, +) (dstReflectValue reflect.Value, converted bool, err error) { + // search type converter function. + registeredConverterFunc, srcType, ok := c.getRegisteredTypeConverterFuncAndSrcType( + srcReflectValue, referReflectValue, + ) + if ok { + dstReflectValue = reflect.New(referReflectValue.Type()).Elem() + converted, err = c.doCallCustomTypeConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType) + return + } + + // search any converter function. + anyConverterFunc := c.getRegisteredAnyConverterFunc(referReflectValue) + if anyConverterFunc == nil { + return reflect.Value{}, false, nil + } + dstReflectValue = reflect.New(referReflectValue.Type()).Elem() + err = anyConverterFunc(srcReflectValue.Interface(), dstReflectValue) + if err != nil { + return reflect.Value{}, false, err + } + return dstReflectValue, true, nil +} + +func (c *Converter) getRegisteredTypeConverterFuncAndSrcType( srcReflectValue, dstReflectValueForRefer reflect.Value, ) (f converterFunc, srcType reflect.Type, ok bool) { if len(c.typeConverterFuncMap) == 0 { @@ -499,28 +555,28 @@ func (c *Converter) getRegisteredConverterFuncAndSrcType( return } -func (c *Converter) callCustomConverterWithRefer( - srcReflectValue, referReflectValue reflect.Value, -) (dstReflectValue reflect.Value, converted bool, err error) { - registeredConverterFunc, srcType, ok := c.getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue) - if !ok { - return reflect.Value{}, false, nil +func (c *Converter) getRegisteredAnyConverterFunc(dstReflectValueForRefer reflect.Value) (f AnyConvertFunc) { + if c.internalConverter.IsAnyConvertFuncEmpty() { + return nil } - dstReflectValue = reflect.New(referReflectValue.Type()).Elem() - converted, err = c.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType) - return + if !dstReflectValueForRefer.IsValid() { + return nil + } + var dstType = dstReflectValueForRefer.Type() + if dstType.Kind() == reflect.Pointer { + // Might be **struct, which is support as designed. + if dstType.Elem().Kind() == reflect.Pointer { + dstType = dstType.Elem() + } + } else if dstReflectValueForRefer.IsValid() && dstReflectValueForRefer.CanAddr() { + dstType = dstReflectValueForRefer.Addr().Type() + } else { + dstType = reflect.PointerTo(dstType) + } + return c.internalConverter.GetAnyConvertFuncByType(dstType) } -// callCustomConverter call the custom converter. It will try some possible type. -func (c *Converter) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) { - registeredConverterFunc, srcType, ok := c.getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue) - if !ok { - return false, nil - } - return c.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType) -} - -func (c *Converter) doCallCustomConverter( +func (c *Converter) doCallCustomTypeConverter( srcReflectValue reflect.Value, dstReflectValue reflect.Value, registeredConverterFunc converterFunc, diff --git a/util/gconv/internal/converter/converter_map.go b/util/gconv/internal/converter/converter_map.go index 1b8b93e48..aff383938 100644 --- a/util/gconv/internal/converter/converter_map.go +++ b/util/gconv/internal/converter/converter_map.go @@ -35,34 +35,42 @@ type MapOption struct { ContinueOnError bool } +func (c *Converter) getMapOption(option ...MapOption) MapOption { + if len(option) > 0 { + return option[0] + } + return MapOption{} +} + // Map converts any variable `value` to map[string]any. If the parameter `value` is not a // map/struct/*struct type, then the conversion will fail and returns nil. // // If `value` is a struct/*struct object, the second parameter `priorityTagAndFieldName` specifies the most priority // priorityTagAndFieldName that will be detected, otherwise it detects the priorityTagAndFieldName in order of: // gconv, json, field name. -func (c *Converter) Map(value any, option MapOption) (map[string]any, error) { - return c.doMapConvert(value, RecursiveTypeAuto, false, option) +func (c *Converter) Map(value any, option ...MapOption) (map[string]any, error) { + return c.doMapConvert(value, RecursiveTypeAuto, false, c.getMapOption(option...)) } // MapStrStr converts `value` to map[string]string. // Note that there might be data copy for this map type converting. -func (c *Converter) MapStrStr(value any, option MapOption) (map[string]string, error) { +func (c *Converter) MapStrStr(value any, option ...MapOption) (map[string]string, error) { if r, ok := value.(map[string]string); ok { return r, nil } - m, err := c.Map(value, option) - if err != nil && !option.ContinueOnError { + m, err := c.Map(value, option...) + if err != nil { return nil, err } if len(m) > 0 { var ( - s string - vMap = make(map[string]string, len(m)) + s string + vMap = make(map[string]string, len(m)) + mapOption = c.getMapOption(option...) ) for k, v := range m { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !mapOption.ContinueOnError { return nil, err } vMap[k] = s diff --git a/util/gconv/internal/converter/converter_maptomap.go b/util/gconv/internal/converter/converter_maptomap.go index 2aafabca7..35fa31219 100644 --- a/util/gconv/internal/converter/converter_maptomap.go +++ b/util/gconv/internal/converter/converter_maptomap.go @@ -24,7 +24,7 @@ import ( // 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. func (c *Converter) MapToMap( - params, pointer any, mapping map[string]string, option MapOption, + params, pointer any, mapping map[string]string, option ...MapOption, ) (err error) { var ( paramsRv reflect.Value @@ -41,11 +41,11 @@ func (c *Converter) MapToMap( paramsKind = paramsRv.Kind() } if paramsKind != reflect.Map { - m, err := c.Map(params, option) + m, err := c.Map(params, option...) if err != nil { return err } - return c.MapToMap(m, pointer, mapping, option) + return c.MapToMap(m, pointer, mapping, option...) } // Empty params map, no need continue. if paramsRv.Len() == 0 { @@ -85,10 +85,11 @@ func (c *Converter) MapToMap( pointerValueType = pointerRv.Type().Elem() pointerValueKind = pointerValueType.Kind() dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys)) + mapOption = c.getMapOption(option...) convertOption = ConvertOption{ - StructOption: StructOption{ContinueOnError: option.ContinueOnError}, - SliceOption: SliceOption{ContinueOnError: option.ContinueOnError}, - MapOption: option, + StructOption: StructOption{ContinueOnError: mapOption.ContinueOnError}, + SliceOption: SliceOption{ContinueOnError: mapOption.ContinueOnError}, + MapOption: mapOption, } ) // Retrieve the true element type of target map. @@ -102,7 +103,7 @@ func (c *Converter) MapToMap( structOption := StructOption{ ParamKeyToAttrMap: mapping, PriorityTag: "", - ContinueOnError: option.ContinueOnError, + ContinueOnError: mapOption.ContinueOnError, } if err = c.Struct(paramsRv.MapIndex(key).Interface(), mapValue, structOption); err != nil { return err diff --git a/util/gconv/internal/converter/converter_maptomaps.go b/util/gconv/internal/converter/converter_maptomaps.go index 3420934b5..cb940c407 100644 --- a/util/gconv/internal/converter/converter_maptomaps.go +++ b/util/gconv/internal/converter/converter_maptomaps.go @@ -22,7 +22,7 @@ import ( // 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 (c *Converter) MapToMaps( - params any, pointer any, paramKeyToAttrMap map[string]string, option MapOption, + params any, pointer any, paramKeyToAttrMap map[string]string, option ...MapOption, ) (err error) { // Params and its element type check. var ( @@ -105,13 +105,13 @@ func (c *Converter) MapToMaps( var item reflect.Value if pointerElemType.Kind() == reflect.Ptr { item = reflect.New(pointerElemType.Elem()) - if err = c.MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap, option); err != nil { + if err = c.MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap, option...); err != nil { return err } pointerSlice.Index(i).Set(item) } else { item = reflect.New(pointerElemType) - if err = c.MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap, option); err != nil { + if err = c.MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap, option...); err != nil { return err } pointerSlice.Index(i).Set(item.Elem()) diff --git a/util/gconv/internal/converter/converter_scan.go b/util/gconv/internal/converter/converter_scan.go index b580e9abe..9630ffb2a 100644 --- a/util/gconv/internal/converter/converter_scan.go +++ b/util/gconv/internal/converter/converter_scan.go @@ -25,8 +25,15 @@ type ScanOption struct { ContinueOnError bool } +func (c *Converter) getScanOption(option ...ScanOption) ScanOption { + if len(option) > 0 { + return option[0] + } + return ScanOption{} +} + // Scan automatically checks the type of `pointer` and converts `params` to `pointer`. -func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err error) { +func (c *Converter) Scan(srcValue any, dstPointer any, option ...ScanOption) (err error) { // Check if srcValue is nil, in which case no conversion is needed if srcValue == nil { return nil @@ -90,12 +97,12 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err e // Create a new value for the pointer dereference nextLevelPtr := reflect.New(dstPointerReflectValueElem.Type().Elem()) // Recursively scan into the dereferenced pointer - if err = c.Scan(srcValueReflectValue, nextLevelPtr, option); err == nil { + if err = c.Scan(srcValueReflectValue, nextLevelPtr, option...); err == nil { dstPointerReflectValueElem.Set(nextLevelPtr) } return } - return c.Scan(srcValueReflectValue, dstPointerReflectValueElem, option) + return c.Scan(srcValueReflectValue, dstPointerReflectValueElem, option...) } // Check if srcValue and dstPointer are the same type, in which case direct assignment can be performed @@ -103,11 +110,12 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err e return nil } + scanOption := c.getScanOption(option...) // Handle different destination types switch dstPointerReflectValueElemKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: v, err := c.Int64(srcValue) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } dstPointerReflectValueElem.SetInt(v) @@ -115,7 +123,7 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err e case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: v, err := c.Uint64(srcValue) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } dstPointerReflectValueElem.SetUint(v) @@ -123,7 +131,7 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err e case reflect.Float32, reflect.Float64: v, err := c.Float64(srcValue) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } dstPointerReflectValueElem.SetFloat(v) @@ -131,7 +139,7 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err e case reflect.String: v, err := c.String(srcValue) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } dstPointerReflectValueElem.SetString(v) @@ -139,7 +147,7 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err e case reflect.Bool: v, err := c.Bool(srcValue) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } dstPointerReflectValueElem.SetBool(v) @@ -158,7 +166,7 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err e } // Special handling for struct or map slice elements if dstElemKind == reflect.Struct || dstElemKind == reflect.Map { - return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, option) + return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, scanOption) } // Handle basic type slice conversions var srcValueReflectValueKind = srcValueReflectValue.Kind() @@ -172,48 +180,48 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ScanOption) (err e switch dstElemType.Kind() { case reflect.String: v, err := c.String(srcElem) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } newSlice.Index(i).SetString(v) case reflect.Int: v, err := c.Int64(srcElem) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } newSlice.Index(i).SetInt(v) case reflect.Int64: v, err := c.Int64(srcElem) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } newSlice.Index(i).SetInt(v) case reflect.Float64: v, err := c.Float64(srcElem) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } newSlice.Index(i).SetFloat(v) case reflect.Bool: v, err := c.Bool(srcElem) - if err != nil && !option.ContinueOnError { + if err != nil && !scanOption.ContinueOnError { return err } newSlice.Index(i).SetBool(v) default: return c.Scan( - srcElem, newSlice.Index(i).Addr().Interface(), option, + srcElem, newSlice.Index(i).Addr().Interface(), option..., ) } } dstPointerReflectValueElem.Set(newSlice) return nil } - return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, option) + return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, scanOption) default: // Handle complex types (structs, maps, etc.) - return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, option) + return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, scanOption) } } @@ -282,7 +290,10 @@ func (c *Converter) doScanForComplicatedTypes( ContinueOnError: option.ContinueOnError, } ) - return c.Structs(srcValue, dstPointer, sliceOption, mapOption) + return c.Structs(srcValue, dstPointer, StructsOption{ + SliceOption: sliceOption, + StructOption: mapOption, + }) default: structOption := StructOption{ diff --git a/util/gconv/internal/converter/converter_slice_any.go b/util/gconv/internal/converter/converter_slice_any.go index 8a0402ec1..673c9afdf 100644 --- a/util/gconv/internal/converter/converter_slice_any.go +++ b/util/gconv/internal/converter/converter_slice_any.go @@ -22,8 +22,15 @@ type SliceOption struct { ContinueOnError bool } +func (c *Converter) getSliceOption(option ...SliceOption) SliceOption { + if len(option) > 0 { + return option[0] + } + return SliceOption{} +} + // SliceAny converts `any` to []any. -func (c *Converter) SliceAny(any interface{}, option SliceOption) ([]any, error) { +func (c *Converter) SliceAny(any interface{}, _ ...SliceOption) ([]any, error) { if empty.IsNil(any) { return nil, nil } diff --git a/util/gconv/internal/converter/converter_slice_float.go b/util/gconv/internal/converter/converter_slice_float.go index 27d59c6ab..382ca764e 100644 --- a/util/gconv/internal/converter/converter_slice_float.go +++ b/util/gconv/internal/converter/converter_slice_float.go @@ -17,21 +17,22 @@ import ( ) // SliceFloat32 converts `any` to []float32. -func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32, error) { +func (c *Converter) SliceFloat32(any interface{}, option ...SliceOption) ([]float32, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - f float32 - array []float32 = nil + err error + f float32 + array []float32 = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []string: array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -40,7 +41,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -49,7 +50,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -58,7 +59,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -67,7 +68,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -76,7 +77,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -85,7 +86,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -99,7 +100,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -116,7 +117,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 } if utils.IsNumeric(value) { f, err = c.Float32(value) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []float32{f}, err @@ -125,7 +126,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -134,7 +135,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -143,7 +144,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -152,7 +153,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -163,7 +164,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -172,7 +173,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 array = make([]float32, len(value)) for k, v := range value { f, err = c.Float32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -182,10 +183,10 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 return array, err } if v, ok := any.(localinterface.IFloats); ok { - return c.SliceFloat32(v.Floats(), option) + return c.SliceFloat32(v.Floats(), option...) } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceFloat32(v.Interfaces(), option) + return c.SliceFloat32(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -197,7 +198,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 ) for i := 0; i < length; i++ { f, err = c.Float32(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = f @@ -209,7 +210,7 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 return []float32{}, err } f, err = c.Float32(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []float32{f}, err @@ -217,21 +218,22 @@ func (c *Converter) SliceFloat32(any interface{}, option SliceOption) ([]float32 } // SliceFloat64 converts `any` to []float64. -func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64, error) { +func (c *Converter) SliceFloat64(any interface{}, option ...SliceOption) ([]float64, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - f float64 - array []float64 = nil + err error + f float64 + array []float64 = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []string: array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -240,7 +242,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -249,7 +251,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -258,7 +260,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -267,7 +269,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -276,7 +278,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -285,7 +287,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -299,7 +301,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -316,7 +318,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 } if utils.IsNumeric(value) { f, err = c.Float64(value) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []float64{f}, err @@ -325,7 +327,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -334,7 +336,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -343,7 +345,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -352,7 +354,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -361,7 +363,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -372,7 +374,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 array = make([]float64, len(value)) for k, v := range value { f, err = c.Float64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = f @@ -385,7 +387,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 return v.Floats(), err } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceFloat64(v.Interfaces(), option) + return c.SliceFloat64(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -397,7 +399,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 ) for i := 0; i < length; i++ { f, err = c.Float64(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = f @@ -409,7 +411,7 @@ func (c *Converter) SliceFloat64(any interface{}, option SliceOption) ([]float64 return []float64{}, err } f, err = c.Float64(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []float64{f}, err diff --git a/util/gconv/internal/converter/converter_slice_int.go b/util/gconv/internal/converter/converter_slice_int.go index b1a769d27..388c2075a 100644 --- a/util/gconv/internal/converter/converter_slice_int.go +++ b/util/gconv/internal/converter/converter_slice_int.go @@ -17,21 +17,22 @@ import ( ) // SliceInt converts `any` to []int. -func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { +func (c *Converter) SliceInt(any any, option ...SliceOption) ([]int, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - ii int - array []int = nil + err error + ii int + array []int = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []string: array = make([]int, len(value)) for k, v := range value { ii, err = c.Int(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -85,7 +86,7 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { } if utils.IsNumeric(value) { ii, err = c.Int(value) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []int{ii}, err @@ -118,7 +119,7 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { array = make([]int, len(value)) for k, v := range value { ii, err = c.Int(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -127,7 +128,7 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { array = make([]int, len(value)) for k, v := range value { ii, err = c.Int(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -136,7 +137,7 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { array = make([]int, len(value)) for k, v := range value { ii, err = c.Int(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -145,7 +146,7 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { array = make([]int, len(value)) for k, v := range value { ii, err = c.Int(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -158,7 +159,7 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { return v.Ints(), err } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceInt(v.Interfaces(), option) + return c.SliceInt(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -170,7 +171,7 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { ) for i := 0; i < length; i++ { ii, err = c.Int(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = ii @@ -182,7 +183,7 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { return []int{}, err } ii, err = c.Int(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []int{ii}, err @@ -190,21 +191,22 @@ func (c *Converter) SliceInt(any any, option SliceOption) ([]int, error) { } // SliceInt32 converts `any` to []int32. -func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { +func (c *Converter) SliceInt32(any any, option ...SliceOption) ([]int32, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - ii int32 - array []int32 = nil + err error + ii int32 + array []int32 = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []string: array = make([]int32, len(value)) for k, v := range value { ii, err = c.Int32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -258,7 +260,7 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { } if utils.IsNumeric(value) { ii, err = c.Int32(value) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []int32{ii}, err @@ -291,7 +293,7 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { array = make([]int32, len(value)) for k, v := range value { ii, err = c.Int32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -300,7 +302,7 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { array = make([]int32, len(value)) for k, v := range value { ii, err = c.Int32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -309,7 +311,7 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { array = make([]int32, len(value)) for k, v := range value { ii, err = c.Int32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -318,7 +320,7 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { array = make([]int32, len(value)) for k, v := range value { ii, err = c.Int32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -328,10 +330,10 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { return array, err } if v, ok := any.(localinterface.IInts); ok { - return c.SliceInt32(v.Ints(), option) + return c.SliceInt32(v.Ints(), option...) } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceInt32(v.Interfaces(), option) + return c.SliceInt32(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -343,7 +345,7 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { ) for i := 0; i < length; i++ { ii, err = c.Int32(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = ii @@ -355,7 +357,7 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { return []int32{}, err } ii, err = c.Int32(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []int32{ii}, err @@ -363,21 +365,22 @@ func (c *Converter) SliceInt32(any any, option SliceOption) ([]int32, error) { } // SliceInt64 converts `any` to []int64. -func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { +func (c *Converter) SliceInt64(any any, option ...SliceOption) ([]int64, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - ii int64 - array []int64 = nil + err error + ii int64 + array []int64 = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []string: array = make([]int64, len(value)) for k, v := range value { ii, err = c.Int64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -431,7 +434,7 @@ func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { } if utils.IsNumeric(value) { ii, err = c.Int64(value) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []int64{ii}, err @@ -464,7 +467,7 @@ func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { array = make([]int64, len(value)) for k, v := range value { ii, err = c.Int64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -473,7 +476,7 @@ func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { array = make([]int64, len(value)) for k, v := range value { ii, err = c.Int64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -482,7 +485,7 @@ func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { array = make([]int64, len(value)) for k, v := range value { ii, err = c.Int64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -491,7 +494,7 @@ func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { array = make([]int64, len(value)) for k, v := range value { ii, err = c.Int64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ii @@ -501,10 +504,10 @@ func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { return array, err } if v, ok := any.(localinterface.IInts); ok { - return c.SliceInt64(v.Ints(), option) + return c.SliceInt64(v.Ints(), option...) } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceInt64(v.Interfaces(), option) + return c.SliceInt64(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -516,7 +519,7 @@ func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { ) for i := 0; i < length; i++ { ii, err = c.Int64(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = ii @@ -528,7 +531,7 @@ func (c *Converter) SliceInt64(any any, option SliceOption) ([]int64, error) { return []int64{}, err } ii, err = c.Int64(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []int64{ii}, err diff --git a/util/gconv/internal/converter/converter_slice_map.go b/util/gconv/internal/converter/converter_slice_map.go index 837f51ab9..fb45d3c37 100644 --- a/util/gconv/internal/converter/converter_slice_map.go +++ b/util/gconv/internal/converter/converter_slice_map.go @@ -8,9 +8,22 @@ package converter import "github.com/gogf/gf/v2/internal/json" +// SliceMapOption is the option for SliceMap function. +type SliceMapOption struct { + SliceOption SliceOption + MapOption MapOption +} + +func (c *Converter) getSliceMapOption(option ...SliceMapOption) SliceMapOption { + if len(option) > 0 { + return option[0] + } + return SliceMapOption{} +} + // SliceMap converts `value` to []map[string]any. // Note that it automatically checks and converts json string to []map if `value` is string/[]byte. -func (c *Converter) SliceMap(value any, sliceOption SliceOption, mapOption MapOption) ([]map[string]any, error) { +func (c *Converter) SliceMap(value any, option ...SliceMapOption) ([]map[string]any, error) { if value == nil { return nil, nil } @@ -39,7 +52,8 @@ func (c *Converter) SliceMap(value any, sliceOption SliceOption, mapOption MapOp return r, nil default: - array, err := c.SliceAny(value, sliceOption) + sliceMapOption := c.getSliceMapOption(option...) + array, err := c.SliceAny(value, sliceMapOption.SliceOption) if err != nil { return nil, err } @@ -48,8 +62,8 @@ func (c *Converter) SliceMap(value any, sliceOption SliceOption, mapOption MapOp } list := make([]map[string]any, len(array)) for k, v := range array { - m, err := c.Map(v, mapOption) - if err != nil && !sliceOption.ContinueOnError { + m, err := c.Map(v, sliceMapOption.MapOption) + if err != nil && !sliceMapOption.SliceOption.ContinueOnError { return nil, err } list[k] = m diff --git a/util/gconv/internal/converter/converter_slice_str.go b/util/gconv/internal/converter/converter_slice_str.go index e95f70b8a..87fa83dbd 100644 --- a/util/gconv/internal/converter/converter_slice_str.go +++ b/util/gconv/internal/converter/converter_slice_str.go @@ -16,21 +16,22 @@ import ( ) // SliceStr converts `any` to []string. -func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, error) { +func (c *Converter) SliceStr(any interface{}, option ...SliceOption) ([]string, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - s string - array []string = nil + err error + s string + array []string = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []int: array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -39,7 +40,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -48,7 +49,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -57,7 +58,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -66,7 +67,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -75,7 +76,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -89,7 +90,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -110,7 +111,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -119,7 +120,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -128,7 +129,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -137,7 +138,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -146,7 +147,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -155,7 +156,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -164,7 +165,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -175,7 +176,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err array = make([]string, len(value)) for k, v := range value { s, err = c.String(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = s @@ -188,7 +189,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err return v.Strings(), err } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceStr(v.Interfaces(), option) + return c.SliceStr(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -200,7 +201,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err ) for i := 0; i < length; i++ { s, err = c.String(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = s @@ -212,7 +213,7 @@ func (c *Converter) SliceStr(any interface{}, option SliceOption) ([]string, err return []string{}, err } s, err = c.String(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []string{s}, err diff --git a/util/gconv/internal/converter/converter_slice_uint.go b/util/gconv/internal/converter/converter_slice_uint.go index 173c96469..d6d192194 100644 --- a/util/gconv/internal/converter/converter_slice_uint.go +++ b/util/gconv/internal/converter/converter_slice_uint.go @@ -17,21 +17,22 @@ import ( ) // SliceUint converts `any` to []uint. -func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, error) { +func (c *Converter) SliceUint(any interface{}, option ...SliceOption) ([]uint, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - ui uint - array []uint = nil + err error + ui uint + array []uint = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []string: array = make([]uint, len(value)) for k, v := range value { ui, err = c.Uint(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -80,7 +81,7 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro } if utils.IsNumeric(value) { ui, err = c.Uint(value) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []uint{ui}, err @@ -113,7 +114,7 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro array = make([]uint, len(value)) for k, v := range value { ui, err = c.Uint(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -122,7 +123,7 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro array = make([]uint, len(value)) for k, v := range value { ui, err = c.Uint(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -131,7 +132,7 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro array = make([]uint, len(value)) for k, v := range value { ui, err = c.Uint(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -140,7 +141,7 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro array = make([]uint, len(value)) for k, v := range value { ui, err = c.Uint(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -156,7 +157,7 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro return v.Uints(), err } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceUint(v.Interfaces(), option) + return c.SliceUint(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -168,7 +169,7 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro ) for i := 0; i < length; i++ { ui, err = c.Uint(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = ui @@ -180,7 +181,7 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro return []uint{}, err } ui, err = c.Uint(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []uint{ui}, err @@ -188,21 +189,22 @@ func (c *Converter) SliceUint(any interface{}, option SliceOption) ([]uint, erro } // SliceUint32 converts `any` to []uint32. -func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, error) { +func (c *Converter) SliceUint32(any interface{}, option ...SliceOption) ([]uint32, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - ui uint32 - array []uint32 = nil + err error + ui uint32 + array []uint32 = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []string: array = make([]uint32, len(value)) for k, v := range value { ui, err = c.Uint32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -254,7 +256,7 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, } if utils.IsNumeric(value) { ui, err = c.Uint32(value) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []uint32{ui}, err @@ -284,7 +286,7 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, array = make([]uint32, len(value)) for k, v := range value { ui, err = c.Uint32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -293,7 +295,7 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, array = make([]uint32, len(value)) for k, v := range value { ui, err = c.Uint32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -302,7 +304,7 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, array = make([]uint32, len(value)) for k, v := range value { ui, err = c.Uint32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -311,7 +313,7 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, array = make([]uint32, len(value)) for k, v := range value { ui, err = c.Uint32(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -323,10 +325,10 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, // Default handler. if v, ok := any.(localinterface.IUints); ok { - return c.SliceUint32(v.Uints(), option) + return c.SliceUint32(v.Uints(), option...) } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceUint32(v.Interfaces(), option) + return c.SliceUint32(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -338,7 +340,7 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, ) for i := 0; i < length; i++ { ui, err = c.Uint32(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = ui @@ -350,7 +352,7 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, return []uint32{}, err } ui, err = c.Uint32(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []uint32{ui}, err @@ -358,21 +360,22 @@ func (c *Converter) SliceUint32(any interface{}, option SliceOption) ([]uint32, } // SliceUint64 converts `any` to []uint64. -func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, error) { +func (c *Converter) SliceUint64(any interface{}, option ...SliceOption) ([]uint64, error) { if empty.IsNil(any) { return nil, nil } var ( - err error - ui uint64 - array []uint64 = nil + err error + ui uint64 + array []uint64 = nil + sliceOption = c.getSliceOption(option...) ) switch value := any.(type) { case []string: array = make([]uint64, len(value)) for k, v := range value { ui, err = c.Uint64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -424,7 +427,7 @@ func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, } if utils.IsNumeric(value) { ui, err = c.Uint64(value) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []uint64{ui}, err @@ -454,7 +457,7 @@ func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, array = make([]uint64, len(value)) for k, v := range value { ui, err = c.Uint64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -463,7 +466,7 @@ func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, array = make([]uint64, len(value)) for k, v := range value { ui, err = c.Uint64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -472,7 +475,7 @@ func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, array = make([]uint64, len(value)) for k, v := range value { ui, err = c.Uint64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -481,7 +484,7 @@ func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, array = make([]uint64, len(value)) for k, v := range value { ui, err = c.Uint64(v) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } array[k] = ui @@ -492,10 +495,10 @@ func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, } // Default handler. if v, ok := any.(localinterface.IUints); ok { - return c.SliceUint64(v.Uints(), option) + return c.SliceUint64(v.Uints(), option...) } if v, ok := any.(localinterface.IInterfaces); ok { - return c.SliceUint64(v.Interfaces(), option) + return c.SliceUint64(v.Interfaces(), option...) } // Not a common type, it then uses reflection for conversion. originValueAndKind := reflection.OriginValueAndKind(any) @@ -507,7 +510,7 @@ func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, ) for i := 0; i < length; i++ { ui, err = c.Uint64(originValueAndKind.OriginValue.Index(i).Interface()) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } slice[i] = ui @@ -519,7 +522,7 @@ func (c *Converter) SliceUint64(any interface{}, option SliceOption) ([]uint64, return []uint64{}, err } ui, err = c.Uint64(any) - if err != nil && !option.ContinueOnError { + if err != nil && !sliceOption.ContinueOnError { return nil, err } return []uint64{ui}, err diff --git a/util/gconv/internal/converter/converter_struct.go b/util/gconv/internal/converter/converter_struct.go index 6bf8c6913..1a84e41b6 100644 --- a/util/gconv/internal/converter/converter_struct.go +++ b/util/gconv/internal/converter/converter_struct.go @@ -32,8 +32,15 @@ type StructOption struct { ContinueOnError bool } +func (c *Converter) getStructOption(option ...StructOption) StructOption { + if len(option) > 0 { + return option[0] + } + return StructOption{} +} + // Struct is the core internal converting function for any data to struct. -func (c *Converter) Struct(params, pointer any, option StructOption) (err error) { +func (c *Converter) Struct(params, pointer any, option ...StructOption) (err error) { if params == nil { // If `params` is nil, no conversion. return nil @@ -63,6 +70,7 @@ func (c *Converter) Struct(params, pointer any, option StructOption) (err error) }() var ( + structOption = c.getStructOption(option...) paramsReflectValue reflect.Value paramsInterface any // DO NOT use `params` directly as it might be type `reflect.Value` pointerReflectValue reflect.Value @@ -105,9 +113,13 @@ func (c *Converter) Struct(params, pointer any, option StructOption) (err error) } // custom convert. - if ok, err = c.callCustomConverter(paramsReflectValue, pointerReflectValue); ok { + ok, err = c.callCustomConverter(paramsReflectValue, pointerReflectValue) + if err != nil && !structOption.ContinueOnError { return err } + if ok { + return nil + } // Normal unmarshalling interfaces checks. if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok { @@ -142,7 +154,7 @@ func (c *Converter) Struct(params, pointer any, option StructOption) (err error) // paramsMap is the map[string]any type variable for params. // DO NOT use MapDeep here. paramsMap, err = c.doMapConvert(paramsInterface, RecursiveTypeAuto, true, MapOption{ - ContinueOnError: option.ContinueOnError, + ContinueOnError: structOption.ContinueOnError, }) if err != nil { return err @@ -161,7 +173,7 @@ func (c *Converter) Struct(params, pointer any, option StructOption) (err error) } // Get struct info from cache or parse struct and cache the struct info. cachedStructInfo := c.internalConverter.GetCachedStructInfo( - pointerElemReflectValue.Type(), option.PriorityTag, + pointerElemReflectValue.Type(), structOption.PriorityTag, ) // Nothing to be converted. if cachedStructInfo == nil { @@ -182,7 +194,7 @@ func (c *Converter) Struct(params, pointer any, option StructOption) (err error) // Firstly, search according to custom mapping rules. // If a possible direct assignment is found, reduce the number of subsequent map searches. - for paramKey, fieldName := range option.ParamKeyToAttrMap { + for paramKey, fieldName := range structOption.ParamKeyToAttrMap { paramsValue, ok = paramsMap[paramKey] if !ok { continue @@ -194,13 +206,13 @@ func (c *Converter) Struct(params, pointer any, option StructOption) (err error) cachedFieldInfo, fieldValue, paramsValue, - option, + structOption, ); err != nil { return err } if len(cachedFieldInfo.OtherSameNameField) > 0 { if err = c.setOtherSameNameField( - cachedFieldInfo, paramsValue, pointerReflectValue, option, + cachedFieldInfo, paramsValue, pointerReflectValue, structOption, ); err != nil { return err } @@ -215,7 +227,7 @@ func (c *Converter) Struct(params, pointer any, option StructOption) (err error) return c.bindStructWithLoopFieldInfos( paramsMap, pointerElemReflectValue, usedParamsKeyOrTagNameMap, cachedStructInfo, - option, + structOption, ) } diff --git a/util/gconv/internal/converter/converter_structs.go b/util/gconv/internal/converter/converter_structs.go index d2eeec733..f164382bd 100644 --- a/util/gconv/internal/converter/converter_structs.go +++ b/util/gconv/internal/converter/converter_structs.go @@ -13,6 +13,19 @@ import ( "github.com/gogf/gf/v2/errors/gerror" ) +// StructsOption is the option for Structs function. +type StructsOption struct { + SliceOption SliceOption + StructOption StructOption +} + +func (c *Converter) getStructsOption(option ...StructsOption) StructsOption { + if len(option) > 0 { + return option[0] + } + return StructsOption{} +} + // Structs converts any slice to given struct slice. // // It automatically checks and converts json string to []map if `params` is string/[]byte. @@ -20,9 +33,7 @@ import ( // 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 (c *Converter) Structs( - params any, pointer any, sliceOption SliceOption, structOption StructOption, -) (err error) { +func (c *Converter) Structs(params any, pointer any, option ...StructsOption) (err error) { defer func() { // Catch the panic, especially the reflection operation panics. if exception := recover(); exception != nil { @@ -47,9 +58,10 @@ func (c *Converter) Structs( } // Converting `params` to map slice. var ( - paramsList []any - paramsRv = reflect.ValueOf(params) - paramsKind = paramsRv.Kind() + paramsList []any + paramsRv = reflect.ValueOf(params) + paramsKind = paramsRv.Kind() + structsOption = c.getStructsOption(option...) ) for paramsKind == reflect.Ptr { paramsRv = paramsRv.Elem() @@ -62,8 +74,11 @@ func (c *Converter) Structs( paramsList[i] = paramsRv.Index(i).Interface() } default: - paramsMaps, err := c.SliceMap(params, sliceOption, MapOption{ - ContinueOnError: structOption.ContinueOnError, + paramsMaps, err := c.SliceMap(params, SliceMapOption{ + SliceOption: structsOption.SliceOption, + MapOption: MapOption{ + ContinueOnError: structsOption.StructOption.ContinueOnError, + }, }) if err != nil { return err @@ -95,7 +110,7 @@ func (c *Converter) Structs( if !tempReflectValue.IsValid() { tempReflectValue = reflect.New(itemType.Elem()).Elem() } - if err = c.Struct(paramsList[i], tempReflectValue, structOption); err != nil { + if err = c.Struct(paramsList[i], tempReflectValue, structsOption.StructOption); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue.Addr()) @@ -109,7 +124,7 @@ func (c *Converter) Structs( } else { tempReflectValue = reflect.New(itemType).Elem() } - if err = c.Struct(paramsList[i], tempReflectValue, structOption); err != nil { + if err = c.Struct(paramsList[i], tempReflectValue, structsOption.StructOption); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue) diff --git a/util/gconv/internal/structcache/structcache.go b/util/gconv/internal/structcache/structcache.go index 044f8a935..99f6d2362 100644 --- a/util/gconv/internal/structcache/structcache.go +++ b/util/gconv/internal/structcache/structcache.go @@ -8,9 +8,12 @@ package structcache import ( + "context" "reflect" + "runtime" "sync" + "github.com/gogf/gf/v2/internal/intlog" "github.com/gogf/gf/v2/util/gconv/internal/localinterface" ) @@ -36,6 +39,7 @@ type Converter struct { } // AnyConvertFunc is the function type for converting any to specified type. +// Note that the parameter `to` is usually a pointer type. type AnyConvertFunc func(from any, to reflect.Value) error // NewConverter creates and returns a new Converter object. @@ -56,21 +60,39 @@ func (cf *Converter) MarkTypeConvertFunc(fieldType reflect.Type) { } // RegisterAnyConvertFunc registers custom type converting function for specified type. -func (cf *Converter) RegisterAnyConvertFunc(t reflect.Type, convertFunc AnyConvertFunc) { - if t == nil || convertFunc == nil { +func (cf *Converter) RegisterAnyConvertFunc(dstType reflect.Type, convertFunc AnyConvertFunc) { + if dstType == nil || convertFunc == nil { return } - for t.Kind() == reflect.Ptr { - t = t.Elem() + for dstType.Kind() == reflect.Ptr { + dstType = dstType.Elem() } - if t.Kind() == reflect.Interface { + if dstType.Kind() == reflect.Interface { cf.interfaceToTypeConvertMap = append(cf.interfaceToTypeConvertMap, interfaceTypeConverter{ - interfaceType: t, + interfaceType: dstType, convertFunc: convertFunc, }) return } - cf.anyToTypeConvertMap[t] = convertFunc + cf.anyToTypeConvertMap[dstType] = convertFunc + intlog.Printf( + context.Background(), + `RegisterAnyConvertFunc: %s -> %s`, + dstType.String(), runtime.FuncForPC(reflect.ValueOf(convertFunc).Pointer()).Name(), + ) +} + +// GetAnyConvertFuncByType retrieves and returns the converting function for specified type. +func (cf *Converter) GetAnyConvertFuncByType(dstType reflect.Type) AnyConvertFunc { + if dstType.Kind() == reflect.Ptr { + dstType = dstType.Elem() + } + return cf.anyToTypeConvertMap[dstType] +} + +// IsAnyConvertFuncEmpty checks whether there's any converting function registered. +func (cf *Converter) IsAnyConvertFuncEmpty() bool { + return len(cf.anyToTypeConvertMap) == 0 } func (cf *Converter) checkTypeImplInterface(t reflect.Type) AnyConvertFunc {