genericfunc.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package help
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strings"
  6. )
  7. // genericType represents a any reflect.Type.
  8. type genericType int
  9. var genericTp = reflect.TypeOf(new(genericType)).Elem()
  10. // functionCache keeps genericFunc reflection objects in cache.
  11. type functionCache struct {
  12. MethodName string
  13. ParamName string
  14. FnValue reflect.Value
  15. FnType reflect.Type
  16. TypesIn []reflect.Type
  17. TypesOut []reflect.Type
  18. }
  19. // genericFunc is a type used to validate and call dynamic functions.
  20. type genericFunc struct {
  21. Cache *functionCache
  22. }
  23. // Call calls a dynamic function.
  24. func (g *genericFunc) Call(params ...interface{}) interface{} {
  25. paramsIn := make([]reflect.Value, len(params))
  26. for i, param := range params {
  27. paramsIn[i] = reflect.ValueOf(param)
  28. }
  29. paramsOut := g.Cache.FnValue.Call(paramsIn)
  30. if len(paramsOut) >= 1 {
  31. return paramsOut[0].Interface()
  32. }
  33. return nil
  34. }
  35. // newGenericFunc instantiates a new genericFunc pointer
  36. func newGenericFunc(methodName, paramName string, fn interface{}, validateFunc func(*functionCache) error) (*genericFunc, error) {
  37. cache := &functionCache{}
  38. cache.FnValue = reflect.ValueOf(fn)
  39. if cache.FnValue.Kind() != reflect.Func {
  40. return nil, fmt.Errorf("%s: parameter [%s] is not a function type. It is a '%s'", methodName, paramName, cache.FnValue.Type())
  41. }
  42. cache.MethodName = methodName
  43. cache.ParamName = paramName
  44. cache.FnType = cache.FnValue.Type()
  45. numTypesIn := cache.FnType.NumIn()
  46. cache.TypesIn = make([]reflect.Type, numTypesIn)
  47. for i := 0; i < numTypesIn; i++ {
  48. cache.TypesIn[i] = cache.FnType.In(i)
  49. }
  50. numTypesOut := cache.FnType.NumOut()
  51. cache.TypesOut = make([]reflect.Type, numTypesOut)
  52. for i := 0; i < numTypesOut; i++ {
  53. cache.TypesOut[i] = cache.FnType.Out(i)
  54. }
  55. if err := validateFunc(cache); err != nil {
  56. return nil, err
  57. }
  58. return &genericFunc{Cache: cache}, nil
  59. }
  60. // simpleParamValidator creates a function to validate genericFunc based in the
  61. // In and Out function parameters.
  62. func simpleParamValidator(In []reflect.Type, Out []reflect.Type) func(cache *functionCache) error {
  63. return func(cache *functionCache) error {
  64. var isValid = func() bool {
  65. if In != nil {
  66. if len(In) != len(cache.TypesIn) {
  67. return false
  68. }
  69. for i, paramIn := range In {
  70. if paramIn != genericTp && paramIn != cache.TypesIn[i] {
  71. return false
  72. }
  73. }
  74. }
  75. if Out != nil {
  76. if len(Out) != len(cache.TypesOut) {
  77. return false
  78. }
  79. for i, paramOut := range Out {
  80. if paramOut != genericTp && paramOut != cache.TypesOut[i] {
  81. return false
  82. }
  83. }
  84. }
  85. return true
  86. }
  87. if !isValid() {
  88. return fmt.Errorf("%s: parameter [%s] has a invalid function signature. Expected: '%s', actual: '%s'", cache.MethodName, cache.ParamName, formatFnSignature(In, Out), formatFnSignature(cache.TypesIn, cache.TypesOut))
  89. }
  90. return nil
  91. }
  92. }
  93. // newElemTypeSlice creates a slice of items elem types.
  94. func newElemTypeSlice(items ...interface{}) []reflect.Type {
  95. typeList := make([]reflect.Type, len(items))
  96. for i, item := range items {
  97. typeItem := reflect.TypeOf(item)
  98. if typeItem.Kind() == reflect.Ptr {
  99. typeList[i] = typeItem.Elem()
  100. }
  101. }
  102. return typeList
  103. }
  104. // formatFnSignature formats the func signature based in the parameters types.
  105. func formatFnSignature(In []reflect.Type, Out []reflect.Type) string {
  106. paramInNames := make([]string, len(In))
  107. for i, typeIn := range In {
  108. if typeIn == genericTp {
  109. paramInNames[i] = "T"
  110. } else {
  111. paramInNames[i] = typeIn.String()
  112. }
  113. }
  114. paramOutNames := make([]string, len(Out))
  115. for i, typeOut := range Out {
  116. if typeOut == genericTp {
  117. paramOutNames[i] = "T"
  118. } else {
  119. paramOutNames[i] = typeOut.String()
  120. }
  121. }
  122. return fmt.Sprintf("func(%s)%s", strings.Join(paramInNames, ","), strings.Join(paramOutNames, ","))
  123. }