context_func.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package context
  2. import (
  3. "errors"
  4. "reflect"
  5. "sync"
  6. )
  7. // ErrInvalidArgs fires when the `Context.CallFunc`
  8. // is called with invalid number of arguments.
  9. var ErrInvalidArgs = errors.New("invalid arguments")
  10. // Func represents a function registered by the Context.
  11. // See its `buildMeta` and `call` internal methods.
  12. type Func struct {
  13. RegisterName string // the name of which this function is registered, for information only.
  14. Raw interface{} // the Raw function, can be used for custom casting.
  15. PersistenceArgs []interface{} // the persistence input arguments given on registration.
  16. once sync.Once // guards build once, on first call.
  17. // Available after the first call.
  18. Meta *FuncMeta
  19. }
  20. func newFunc(name string, fn interface{}, persistenceArgs ...interface{}) *Func {
  21. return &Func{
  22. RegisterName: name,
  23. Raw: fn,
  24. PersistenceArgs: persistenceArgs,
  25. }
  26. }
  27. // FuncMeta holds the necessary information about a registered
  28. // context function. Built once by the Func.
  29. type FuncMeta struct {
  30. Handler Handler // when it's just a handler.
  31. HandlerWithErr func(*Context) error // when it's just a handler which returns an error.
  32. RawFunc func() // when it's just a func.
  33. RawFuncWithErr func() error // when it's just a func which returns an error.
  34. RawFuncArgs func(...interface{})
  35. RawFuncArgsWithErr func(...interface{}) error
  36. Value reflect.Value
  37. Type reflect.Type
  38. ExpectedArgumentsLength int
  39. PersistenceInputs []reflect.Value
  40. AcceptsContext bool // the Context, if exists should be always first argument.
  41. ReturnsError bool // when the function's last output argument is error.
  42. }
  43. func (f *Func) buildMeta() {
  44. switch fn := f.Raw.(type) {
  45. case Handler:
  46. f.Meta = &FuncMeta{Handler: fn}
  47. return
  48. // case func(*Context):
  49. // f.Meta = &FuncMeta{Handler: fn}
  50. // return
  51. case func(*Context) error:
  52. f.Meta = &FuncMeta{HandlerWithErr: fn}
  53. return
  54. case func():
  55. f.Meta = &FuncMeta{RawFunc: fn}
  56. return
  57. case func() error:
  58. f.Meta = &FuncMeta{RawFuncWithErr: fn}
  59. return
  60. case func(...interface{}):
  61. f.Meta = &FuncMeta{RawFuncArgs: fn}
  62. return
  63. case func(...interface{}) error:
  64. f.Meta = &FuncMeta{RawFuncArgsWithErr: fn}
  65. return
  66. }
  67. fn := f.Raw
  68. meta := FuncMeta{}
  69. if val, ok := fn.(reflect.Value); ok {
  70. meta.Value = val
  71. } else {
  72. meta.Value = reflect.ValueOf(fn)
  73. }
  74. meta.Type = meta.Value.Type()
  75. if meta.Type.Kind() != reflect.Func {
  76. return
  77. }
  78. meta.ExpectedArgumentsLength = meta.Type.NumIn()
  79. skipInputs := len(meta.PersistenceInputs)
  80. if meta.ExpectedArgumentsLength > skipInputs {
  81. meta.AcceptsContext = isContext(meta.Type.In(skipInputs))
  82. }
  83. if numOut := meta.Type.NumOut(); numOut > 0 {
  84. // error should be the last output.
  85. if isError(meta.Type.Out(numOut - 1)) {
  86. meta.ReturnsError = true
  87. }
  88. }
  89. persistenceArgs := f.PersistenceArgs
  90. if len(persistenceArgs) > 0 {
  91. inputs := make([]reflect.Value, 0, len(persistenceArgs))
  92. for _, arg := range persistenceArgs {
  93. if in, ok := arg.(reflect.Value); ok {
  94. inputs = append(inputs, in)
  95. } else {
  96. inputs = append(inputs, reflect.ValueOf(in))
  97. }
  98. }
  99. meta.PersistenceInputs = inputs
  100. }
  101. f.Meta = &meta
  102. }
  103. func (f *Func) call(ctx *Context, args ...interface{}) ([]reflect.Value, error) {
  104. f.once.Do(f.buildMeta)
  105. meta := f.Meta
  106. if meta.Handler != nil {
  107. meta.Handler(ctx)
  108. return nil, nil
  109. }
  110. if meta.HandlerWithErr != nil {
  111. return nil, meta.HandlerWithErr(ctx)
  112. }
  113. if meta.RawFunc != nil {
  114. meta.RawFunc()
  115. return nil, nil
  116. }
  117. if meta.RawFuncWithErr != nil {
  118. return nil, meta.RawFuncWithErr()
  119. }
  120. if meta.RawFuncArgs != nil {
  121. meta.RawFuncArgs(args...)
  122. return nil, nil
  123. }
  124. if meta.RawFuncArgsWithErr != nil {
  125. return nil, meta.RawFuncArgsWithErr(args...)
  126. }
  127. inputs := make([]reflect.Value, 0, f.Meta.ExpectedArgumentsLength)
  128. inputs = append(inputs, f.Meta.PersistenceInputs...)
  129. if f.Meta.AcceptsContext {
  130. inputs = append(inputs, reflect.ValueOf(ctx))
  131. }
  132. for _, arg := range args {
  133. if in, ok := arg.(reflect.Value); ok {
  134. inputs = append(inputs, in)
  135. } else {
  136. inputs = append(inputs, reflect.ValueOf(arg))
  137. }
  138. }
  139. // keep it here, the inptus may contain the context.
  140. if f.Meta.ExpectedArgumentsLength != len(inputs) {
  141. return nil, ErrInvalidArgs
  142. }
  143. outputs := f.Meta.Value.Call(inputs)
  144. return outputs, getError(outputs)
  145. }
  146. var contextType = reflect.TypeOf((*Context)(nil))
  147. // isContext returns true if the "typ" is a type of Context.
  148. func isContext(typ reflect.Type) bool {
  149. return typ == contextType
  150. }
  151. var errTyp = reflect.TypeOf((*error)(nil)).Elem()
  152. // isError returns true if "typ" is type of `error`.
  153. func isError(typ reflect.Type) bool {
  154. return typ.Implements(errTyp)
  155. }
  156. func getError(outputs []reflect.Value) error {
  157. if n := len(outputs); n > 0 {
  158. lastOut := outputs[n-1]
  159. if isError(lastOut.Type()) {
  160. if lastOut.IsNil() {
  161. return nil
  162. }
  163. return lastOut.Interface().(error)
  164. }
  165. }
  166. return nil
  167. }