github.com/wangyougui/gf/v2@v2.6.5/util/gconv/gconv_maptomap.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  package gconv
     8  
     9  import (
    10  	"reflect"
    11  
    12  	"github.com/wangyougui/gf/v2/errors/gcode"
    13  	"github.com/wangyougui/gf/v2/errors/gerror"
    14  )
    15  
    16  // MapToMap converts any map type variable `params` to another map type variable `pointer`
    17  // using reflect.
    18  // See doMapToMap.
    19  func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {
    20  	return Scan(params, pointer, mapping...)
    21  }
    22  
    23  // doMapToMap converts any map type variable `params` to another map type variable `pointer`.
    24  //
    25  // The parameter `params` can be any type of map, like:
    26  // map[string]string, map[string]struct, map[string]*struct, reflect.Value, etc.
    27  //
    28  // The parameter `pointer` should be type of *map, like:
    29  // map[int]string, map[string]struct, map[string]*struct, reflect.Value, etc.
    30  //
    31  // The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
    32  // sense only if the items of original map `params` is type struct.
    33  func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
    34  	var (
    35  		paramsRv                  reflect.Value
    36  		paramsKind                reflect.Kind
    37  		keyToAttributeNameMapping map[string]string
    38  	)
    39  	if len(mapping) > 0 {
    40  		keyToAttributeNameMapping = mapping[0]
    41  	}
    42  	if v, ok := params.(reflect.Value); ok {
    43  		paramsRv = v
    44  	} else {
    45  		paramsRv = reflect.ValueOf(params)
    46  	}
    47  	paramsKind = paramsRv.Kind()
    48  	if paramsKind == reflect.Ptr {
    49  		paramsRv = paramsRv.Elem()
    50  		paramsKind = paramsRv.Kind()
    51  	}
    52  	if paramsKind != reflect.Map {
    53  		return doMapToMap(Map(params), pointer, mapping...)
    54  	}
    55  	// Empty params map, no need continue.
    56  	if paramsRv.Len() == 0 {
    57  		return nil
    58  	}
    59  	var pointerRv reflect.Value
    60  	if v, ok := pointer.(reflect.Value); ok {
    61  		pointerRv = v
    62  	} else {
    63  		pointerRv = reflect.ValueOf(pointer)
    64  	}
    65  	pointerKind := pointerRv.Kind()
    66  	for pointerKind == reflect.Ptr {
    67  		pointerRv = pointerRv.Elem()
    68  		pointerKind = pointerRv.Kind()
    69  	}
    70  	if pointerKind != reflect.Map {
    71  		return gerror.NewCodef(
    72  			gcode.CodeInvalidParameter,
    73  			`destination pointer should be type of *map, but got: %s`,
    74  			pointerKind,
    75  		)
    76  	}
    77  	defer func() {
    78  		// Catch the panic, especially the reflection operation panics.
    79  		if exception := recover(); exception != nil {
    80  			if v, ok := exception.(error); ok && gerror.HasStack(v) {
    81  				err = v
    82  			} else {
    83  				err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
    84  			}
    85  		}
    86  	}()
    87  	var (
    88  		paramsKeys       = paramsRv.MapKeys()
    89  		pointerKeyType   = pointerRv.Type().Key()
    90  		pointerValueType = pointerRv.Type().Elem()
    91  		pointerValueKind = pointerValueType.Kind()
    92  		dataMap          = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
    93  	)
    94  	// Retrieve the true element type of target map.
    95  	if pointerValueKind == reflect.Ptr {
    96  		pointerValueKind = pointerValueType.Elem().Kind()
    97  	}
    98  	for _, key := range paramsKeys {
    99  		mapValue := reflect.New(pointerValueType).Elem()
   100  		switch pointerValueKind {
   101  		case reflect.Map, reflect.Struct:
   102  			if err = doStruct(
   103  				paramsRv.MapIndex(key).Interface(), mapValue, keyToAttributeNameMapping, "",
   104  			); err != nil {
   105  				return err
   106  			}
   107  		default:
   108  			mapValue.Set(
   109  				reflect.ValueOf(
   110  					doConvert(doConvertInput{
   111  						FromValue:  paramsRv.MapIndex(key).Interface(),
   112  						ToTypeName: pointerValueType.String(),
   113  						ReferValue: mapValue,
   114  						Extra:      nil,
   115  					}),
   116  				),
   117  			)
   118  		}
   119  		var mapKey = reflect.ValueOf(
   120  			doConvert(doConvertInput{
   121  				FromValue:  key.Interface(),
   122  				ToTypeName: pointerKeyType.Name(),
   123  				ReferValue: reflect.New(pointerKeyType).Elem().Interface(),
   124  				Extra:      nil,
   125  			}),
   126  		)
   127  		dataMap.SetMapIndex(mapKey, mapValue)
   128  	}
   129  	pointerRv.Set(dataMap)
   130  	return nil
   131  }