gconv_converter.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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/gogf/gf.
  6. package gconv
  7. import (
  8. "reflect"
  9. "github.com/gogf/gf/v2/errors/gcode"
  10. "github.com/gogf/gf/v2/errors/gerror"
  11. )
  12. type (
  13. converterInType = reflect.Type
  14. converterOutType = reflect.Type
  15. converterFunc = reflect.Value
  16. )
  17. // customConverters for internal converter storing.
  18. var customConverters = make(map[converterInType]map[converterOutType]converterFunc)
  19. // RegisterConverter to register custom converter.
  20. // It must be registered before you use this custom converting feature.
  21. // It is suggested to do it in boot.
  22. //
  23. // Note:
  24. // 1. The parameter `fn` must be defined as pattern `func(T1) (T2, error)`.
  25. // It will convert type `T1` to type `T2`.
  26. // 2. The `T1` should not be type of pointer, but the `T2` should be type of pointer.
  27. func RegisterConverter(fn interface{}) (err error) {
  28. var (
  29. fnReflectType = reflect.TypeOf(fn)
  30. errType = reflect.TypeOf((*error)(nil)).Elem()
  31. )
  32. if fnReflectType.Kind() != reflect.Func ||
  33. fnReflectType.NumIn() != 1 || fnReflectType.NumOut() != 2 ||
  34. !fnReflectType.Out(1).Implements(errType) {
  35. err = gerror.NewCodef(
  36. gcode.CodeInvalidParameter,
  37. "parameter must be type of function and defined as pattern `func(T1) (T2, error)`, but defined as `%s`",
  38. fnReflectType.String(),
  39. )
  40. return
  41. }
  42. // The Key and Value of the converter map should not be pointer.
  43. var (
  44. inType = fnReflectType.In(0)
  45. outType = fnReflectType.Out(0)
  46. )
  47. if inType.Kind() == reflect.Pointer {
  48. err = gerror.NewCodef(
  49. gcode.CodeInvalidParameter,
  50. "invalid input parameter type `%s`: should not be type of pointer",
  51. inType.String(),
  52. )
  53. return
  54. }
  55. if outType.Kind() != reflect.Pointer {
  56. err = gerror.NewCodef(
  57. gcode.CodeInvalidParameter,
  58. "invalid output parameter type `%s`: should be type of pointer",
  59. outType.String(),
  60. )
  61. return
  62. }
  63. registeredOutTypeMap, ok := customConverters[inType]
  64. if !ok {
  65. registeredOutTypeMap = make(map[converterOutType]converterFunc)
  66. customConverters[inType] = registeredOutTypeMap
  67. }
  68. if _, ok = registeredOutTypeMap[outType]; ok {
  69. err = gerror.NewCodef(
  70. gcode.CodeInvalidOperation,
  71. "the converter parameter type `%s` to type `%s` has already been registered",
  72. inType.String(), outType.String(),
  73. )
  74. return
  75. }
  76. registeredOutTypeMap[outType] = reflect.ValueOf(fn)
  77. return
  78. }
  79. // callCustomConverter call the custom converter. It will try some possible type.
  80. func callCustomConverter(srcReflectValue reflect.Value, dstReflectValue reflect.Value) (converted bool, err error) {
  81. if len(customConverters) == 0 {
  82. return false, nil
  83. }
  84. var (
  85. ok bool
  86. srcType = srcReflectValue.Type()
  87. )
  88. for srcType.Kind() == reflect.Pointer {
  89. srcType = srcType.Elem()
  90. }
  91. var (
  92. registeredOutTypeMap map[converterOutType]converterFunc
  93. registeredConverterFunc converterFunc
  94. )
  95. // firstly, it searches the map by input parameter type.
  96. registeredOutTypeMap, ok = customConverters[srcType]
  97. if !ok {
  98. return false, nil
  99. }
  100. var dstType = dstReflectValue.Type()
  101. if dstType.Kind() == reflect.Pointer && dstReflectValue.Elem().Kind() == reflect.Pointer {
  102. dstType = dstReflectValue.Elem().Type()
  103. } else if dstType.Kind() != reflect.Pointer && dstReflectValue.CanAddr() {
  104. dstType = dstReflectValue.Addr().Type()
  105. }
  106. // secondly, it searches the input parameter type map
  107. // and finds the result converter function by the output parameter type.
  108. registeredConverterFunc, ok = registeredOutTypeMap[dstType]
  109. if !ok {
  110. return false, nil
  111. }
  112. // Converter function calling.
  113. for srcReflectValue.Type() != srcType {
  114. srcReflectValue = srcReflectValue.Elem()
  115. }
  116. result := registeredConverterFunc.Call([]reflect.Value{srcReflectValue})
  117. if !result[1].IsNil() {
  118. return false, result[1].Interface().(error)
  119. }
  120. // The `result[0]` is a pointer.
  121. if result[0].IsNil() {
  122. return false, nil
  123. }
  124. var resultValue = result[0]
  125. for {
  126. if resultValue.Type() == dstReflectValue.Type() && dstReflectValue.CanSet() {
  127. dstReflectValue.Set(resultValue)
  128. converted = true
  129. } else if dstReflectValue.Kind() == reflect.Pointer {
  130. if resultValue.Type() == dstReflectValue.Elem().Type() && dstReflectValue.Elem().CanSet() {
  131. dstReflectValue.Elem().Set(resultValue)
  132. converted = true
  133. }
  134. }
  135. if converted {
  136. break
  137. }
  138. if resultValue.Kind() == reflect.Pointer {
  139. resultValue = resultValue.Elem()
  140. } else {
  141. break
  142. }
  143. }
  144. return converted, nil
  145. }