github.com/wangyougui/gf/v2@v2.6.5/util/gconv/gconv_converter.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  type (
    17  	converterInType  = reflect.Type
    18  	converterOutType = reflect.Type
    19  	converterFunc    = reflect.Value
    20  )
    21  
    22  // customConverters for internal converter storing.
    23  var customConverters = make(map[converterInType]map[converterOutType]converterFunc)
    24  
    25  // RegisterConverter to register custom converter.
    26  // It must be registered before you use this custom converting feature.
    27  // It is suggested to do it in boot procedure of the process.
    28  //
    29  // Note:
    30  //  1. The parameter `fn` must be defined as pattern `func(T1) (T2, error)`.
    31  //     It will convert type `T1` to type `T2`.
    32  //  2. The `T1` should not be type of pointer, but the `T2` should be type of pointer.
    33  func RegisterConverter(fn interface{}) (err error) {
    34  	var (
    35  		fnReflectType = reflect.TypeOf(fn)
    36  		errType       = reflect.TypeOf((*error)(nil)).Elem()
    37  	)
    38  	if fnReflectType.Kind() != reflect.Func ||
    39  		fnReflectType.NumIn() != 1 || fnReflectType.NumOut() != 2 ||
    40  		!fnReflectType.Out(1).Implements(errType) {
    41  		err = gerror.NewCodef(
    42  			gcode.CodeInvalidParameter,
    43  			"parameter must be type of converter function and defined as pattern `func(T1) (T2, error)`, but defined as `%s`",
    44  			fnReflectType.String(),
    45  		)
    46  		return
    47  	}
    48  
    49  	// The Key and Value of the converter map should not be pointer.
    50  	var (
    51  		inType  = fnReflectType.In(0)
    52  		outType = fnReflectType.Out(0)
    53  	)
    54  	if inType.Kind() == reflect.Pointer {
    55  		err = gerror.NewCodef(
    56  			gcode.CodeInvalidParameter,
    57  			"invalid converter function `%s`: invalid input parameter type `%s`, should not be type of pointer",
    58  			fnReflectType.String(), inType.String(),
    59  		)
    60  		return
    61  	}
    62  	if outType.Kind() != reflect.Pointer {
    63  		err = gerror.NewCodef(
    64  			gcode.CodeInvalidParameter,
    65  			"invalid converter function `%s`: invalid output parameter type `%s` should be type of pointer",
    66  			fnReflectType.String(), outType.String(),
    67  		)
    68  		return
    69  	}
    70  
    71  	registeredOutTypeMap, ok := customConverters[inType]
    72  	if !ok {
    73  		registeredOutTypeMap = make(map[converterOutType]converterFunc)
    74  		customConverters[inType] = registeredOutTypeMap
    75  	}
    76  	if _, ok = registeredOutTypeMap[outType]; ok {
    77  		err = gerror.NewCodef(
    78  			gcode.CodeInvalidOperation,
    79  			"the converter parameter type `%s` to type `%s` has already been registered",
    80  			inType.String(), outType.String(),
    81  		)
    82  		return
    83  	}
    84  	registeredOutTypeMap[outType] = reflect.ValueOf(fn)
    85  	return
    86  }
    87  
    88  func getRegisteredConverterFuncAndSrcType(
    89  	srcReflectValue, dstReflectValueForRefer reflect.Value,
    90  ) (f converterFunc, srcType reflect.Type, ok bool) {
    91  	if len(customConverters) == 0 {
    92  		return reflect.Value{}, nil, false
    93  	}
    94  	srcType = srcReflectValue.Type()
    95  	for srcType.Kind() == reflect.Pointer {
    96  		srcType = srcType.Elem()
    97  	}
    98  	var registeredOutTypeMap map[converterOutType]converterFunc
    99  	// firstly, it searches the map by input parameter type.
   100  	registeredOutTypeMap, ok = customConverters[srcType]
   101  	if !ok {
   102  		return reflect.Value{}, nil, false
   103  	}
   104  	var dstType = dstReflectValueForRefer.Type()
   105  	if dstType.Kind() == reflect.Pointer {
   106  		// Might be **struct, which is support as designed.
   107  		if dstType.Elem().Kind() == reflect.Pointer {
   108  			dstType = dstType.Elem()
   109  		}
   110  	} else if dstReflectValueForRefer.IsValid() && dstReflectValueForRefer.CanAddr() {
   111  		dstType = dstReflectValueForRefer.Addr().Type()
   112  	} else {
   113  		dstType = reflect.PointerTo(dstType)
   114  	}
   115  	// secondly, it searches the input parameter type map
   116  	// and finds the result converter function by the output parameter type.
   117  	f, ok = registeredOutTypeMap[dstType]
   118  	if !ok {
   119  		return reflect.Value{}, nil, false
   120  	}
   121  	return
   122  }
   123  
   124  func callCustomConverterWithRefer(
   125  	srcReflectValue, referReflectValue reflect.Value,
   126  ) (dstReflectValue reflect.Value, converted bool, err error) {
   127  	registeredConverterFunc, srcType, ok := getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue)
   128  	if !ok {
   129  		return reflect.Value{}, false, nil
   130  	}
   131  	dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
   132  	converted, err = doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
   133  	return
   134  }
   135  
   136  // callCustomConverter call the custom converter. It will try some possible type.
   137  func callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
   138  	registeredConverterFunc, srcType, ok := getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
   139  	if !ok {
   140  		return false, nil
   141  	}
   142  	return doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
   143  }
   144  
   145  func doCallCustomConverter(
   146  	srcReflectValue reflect.Value,
   147  	dstReflectValue reflect.Value,
   148  	registeredConverterFunc converterFunc,
   149  	srcType reflect.Type,
   150  ) (converted bool, err error) {
   151  	// Converter function calling.
   152  	for srcReflectValue.Type() != srcType {
   153  		srcReflectValue = srcReflectValue.Elem()
   154  	}
   155  	result := registeredConverterFunc.Call([]reflect.Value{srcReflectValue})
   156  	if !result[1].IsNil() {
   157  		return false, result[1].Interface().(error)
   158  	}
   159  	// The `result[0]` is a pointer.
   160  	if result[0].IsNil() {
   161  		return false, nil
   162  	}
   163  	var resultValue = result[0]
   164  	for {
   165  		if resultValue.Type() == dstReflectValue.Type() && dstReflectValue.CanSet() {
   166  			dstReflectValue.Set(resultValue)
   167  			converted = true
   168  		} else if dstReflectValue.Kind() == reflect.Pointer {
   169  			if resultValue.Type() == dstReflectValue.Elem().Type() && dstReflectValue.Elem().CanSet() {
   170  				dstReflectValue.Elem().Set(resultValue)
   171  				converted = true
   172  			}
   173  		}
   174  		if converted {
   175  			break
   176  		}
   177  		if resultValue.Kind() == reflect.Pointer {
   178  			resultValue = resultValue.Elem()
   179  		} else {
   180  			break
   181  		}
   182  	}
   183  
   184  	return converted, nil
   185  }